Compare commits

...

11 Commits

70 changed files with 829 additions and 1225 deletions

121
README.md
View File

@ -1,4 +1,4 @@
This firmware template is designed for development boards based around [STM32 F1 series micro-controller](http://www.st.com/web/en/catalog/mmc/FM141/SC1169/SS1031).
YouGotParcel is a pair of devices that remotely tells if a parcel has been put in the post box.
project
=======
@ -6,50 +6,97 @@ project
summary
-------
*describe project purpose*
one of the device goes into the post box.
using switches it can detect when the door of lid have been opened.
it then transmits the activity to the other device over radio.
the second device receives the activity messages, and shows the status of the post box using LEDs.
if the lid is opened, the green LED will light up, indicating a package is present in the post box.
if the door is opened, the red LED will light up, indicating the post box have been emptied.
if no indication has been received (> 1h), both LEDs will switch off.
technology
----------
*described electronic details*
to transmit the status, the LoRa radio protocol is used.
Semtech SX1728 modules for the 433 MHz band (420 - 450 MHz) are used, but any SX172x module for any band (allowed by the local regulation) can be used.
it does not use LoRaWAN (which requires an infrastructure and the right band support), but just LoRa directly between the two modules.
each part has one of the LoRa modules.
one will be used to transmit (the one in the post box), while the other while be used to receive (at the remote location).
the modules are the same, and can be used to transmit and receive.
LoRa is used because, as the name says, it can transmit data over a long range.
this is at the price of baud rate, but since we only need to transmit if lid or door has been opened (e.g. 1 bit), this is sufficient.
the module is configured for most resilient transmission:
- lowest bandwidth: 62.5 kHz (minimum when using a XTAL clock source)
- largest spreading factor: 12
- largest coding rate: 4/8
- explicit header: providing length
- CRC checksum: providing forward error correction
- use power amplifier: providing a 20 dBm signal output
- no output power reduction: to use the maximum 20 dBm signal output
- maximum gain amplifier: 1
- least active frequency: 447.681 MHz (found by scanning the frequency range using the 'scan' action in the receiver menu and using selecting the frequency with the lowest RSSI).
using these parameters it take almost 2 seconds to transmit 1 byte of payload.
my flat is only ~ 30 m direct line from the post box.
LoRa claims to be able to transmit up to 5-15 km.
but the transmitter with be in a post box made of 0.5 mm metal (e.g. almost a faraday cage), and there are several concrete walls in between.
in the end, the transmission is reliable enough and 19/20 packets are received.
the post box unit will transmit as soon as the lid or door are opened.
it will also transmit the last data every 15 minutes.
this will allow the receiver to be restarted and get the status even without activity.
this also allows to recover from lost packet transmissions.
the receiver device requires a permanent power source (e.g. a USB power port) since it continuously listens to incoming data.
is also allow to have the LED constantly on.
the transmitted device is battery operated because no power source is available in the post box.
to save power, the transmitter (MCU and radio) are put in sleep mode unless data is transmitted.
a LiPo battery (with embedded protection) is directly connected to the USB input.
the 3.6-4.2 V is sufficient to provide the 3.3 V (using an LDO) required by MCU and radio.
during transmission, the transmitter uses 62 mA, lasting 2 seconds.
during sleep, the transmitter uses 0.3 mA.
thus, the device uses (2 * 62 + 0.3 * (15 * 60 - 2)) / (15 * 60) = 0.44 mA on average.
using a 100 mAh battery, this should last for (1000 / 0.44) / 24 = 94 days.
since I go at least once a week to the post box, and can swap the battery, this is plenty sufficient.
board
=====
The current implementation uses a [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board).
The underlying template also supports following board:
- [Maple Mini](http://leaflabs.com/docs/hardware/maple-mini.html), based on a STM32F103CBT6
- [System Board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#system_board), based on a STM32F103C8T6
- [blue pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#blue_pill), based on a STM32F103C8T6
- [black pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#black_pill), based on a STM32F103C8T6
- [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board), based on a STM32F103C8T6
- [ST-LINK V2 mini](https://wiki.cuvoodoo.info/doku.php?id=jtag#mini_st-link_v2), a ST-LINK/V2 clone based on a STM32F101C8T6
- [USB-Blaster](https://wiki.cuvoodoo.info/doku.php?id=jtag#armjishu_usb-blaster), an Altera USB-Blaster clone based on a STM32F101C8T6
**Which board is used is defined in the Makefile**.
This is required to map the user LED and button provided on the board
The ST-LINK V2 mini clone has SWD test points on the board.
Because read protection is enabled, you will first need to remove the protection to be able to flash the firmware.
To remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected.
The Altera USB-Blaster clone has a pin header for SWD and UART1 on the board.
SWD is disabled in the main firmware, and it has read protection.
To be able to flash using SWD (or the serial port), the BOOT0 pin must be set to 1 to boot the system memory install of the flash memory.
To set BOOT0 to 1, apply 3.3 V on R11, between the resistor and the reference designator, when powering the device.
The red LED should stay off while the green LED is on.
Now you can remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected.
the current implementation uses a [black pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#black_pill) development board, based on a STM32F103C8T6.
this board includes an 32.768 kHz oscillator, used for the RTC in the MCU to periodically wake up.
connections
===========
Connect the peripherals the following way (STM32F10X signal; STM32F10X pin; peripheral pin; peripheral signal; comment):
connect the peripherals the following way (STM32F10X signal; STM32F10X pin; peripheral pin; peripheral signal; comment).
- *list board to peripheral pin connections*
common to both devices:
- SPI2_NSS; PB12; SX1728 module; NSS;
- SPI2_SCK; PB13; SX1728 module; SLCK;
- SPI2_MISO; PB14; SX1728 module; MISO;
- SPI2_MOSI; PB15; SX1728 module; MOSI;
- GPIO; PB6; SX1728 module; DIO0; used as SX1728 IRQ output
- GPIO; PB7; SX1728 module; REST;
- GND; GND; SX1728 module; GND;
- 3V3; 3V3; SX1728 module; VCC;
All pins are configured using `define`s in the corresponding source code.
on the transmitter device:
- GPIO; PB8; board; GND; use a jumper. this is used to differentiate the transmitter from the receiver
- GPIO; PB10; lid button; common; place button on post box lid to detect when it has been opened
- GPIO; PB1; lid button; normally open; don't use the normally closed pin since the lid might not be able to close again is the parcel does not fit in the box
- GPIO; PA7; door button; common; place button so it gets pressed when the door is closed
- GPIO; PB0; door button; normally open; can also be placed on the normally closed pin since the door will be opened and closed when emptying it.
on the receiver side:
- GPIO; PB10; green LED; anode; used to provide 3.3V
- GPIO; PB1; green LED; cathode; with inline 1 kO resistor. used to indicate when the lid of the post box has been opened (e.g. a letter/parcel has arrived)
- GPIO; PA7; red LED; anode; used to provide 3.3V
- GPIO; PB0; red LED; cathode; with inline 1 kO resistor. used to indicate when the door of the post box has been opened (e.g. the box has been emptied)
all pins are configured using `define`s in the corresponding source code.
code
====
@ -83,13 +130,21 @@ It is up to the application to advertise USB DFU support (i.e. as does the provi
The `bootlaoder` image will be flashed using SWD (Serial Wire Debug).
For that you need an SWD adapter.
The `Makefile` uses a Black Magic Probe (per default), or a ST-Link V2 along OpenOCD software.
The `Makefile` uses a ST-Link V2 along OpenOCD software.
To flash the `booltoader` using SWD run `rake flash_booloader`.
Once the `bootloader` is flashed it is possible to flash the `application` over USB using the DFU protocol by running `rake flash`.
To force the bootloader to start the DFU mode press the user button or short a pin, depending on the board.
It is also possible to flash the `application` image using SWD by running `rake flash_application`.
Once the transmitter firmware has been flash over DFU, it is recommended to flash it over SWD using `rake flash_application`.
This is because the transmitter does not provide present itself as USB device, so to save energy.
Also, since the transmitter goes to sleep after a couple of second, it is recommended to flash it just after reset.
Alternatively, short BOOT1 to 3.3V and reset the device.
This will force the USB DFU bootloader, and also to flash using DFU.
The firmware is the same for transmitter and receiver.
debug
-----
@ -99,4 +154,4 @@ To start the debugging session run `rake debug`.
USB
---
The firmware offers serial communication over USART1 and USB (using the CDC ACM device class).
The firmware offers serial communication over USART1 (transmitter and receiver) and USB (only receiver) (using the CDC ACM device class).

View File

@ -15,7 +15,7 @@ FIRMWARES = [BOOTLOADER, APPLICATION]
# which development board is used
# supported are: SYSTEM_BOARD, MAPLE_MINI, BLUE_PILL, BLACK_PILL, CORE_BOARD, STLINKV2, BLASTER, BUSVOODOO
BOARD = ENV["BOARD"] || "BLUE_PILL"
BOARD = ENV["BOARD"] || "BLACK_PILL"
# libopencm3 definitions
LIBOPENCM3_DIR = "libopencm3"

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** STM32F1 application example
/** YouGotParcel firmware
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2016-2020
*/
@ -47,6 +34,7 @@
#include "usb_cdcacm.h" // USB CDC ACM utilities
#include "terminal.h" // handle the terminal interface
#include "menu.h" // menu utilities
#include "radio_sx172x.h" // LoRa module utilities
/** watchdog period in ms */
#define WATCHDOG_PERIOD 10000
@ -71,9 +59,39 @@ static time_t time_start = 0;
/** @defgroup main_flags flag set in interrupts to be processed in main task
* @{
*/
volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */
static volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */
static volatile bool radio_sx172x_irq_flag = false; /**< interrupt flag for the LoRa module */
static volatile bool keep_alive_flag = false; /**< periodic wake up to show current status */
static volatile bool lid_flag = false; /**< flag set when post box lid is opened */
static volatile bool door_flag = false; /**< flag set when post box door is opened */
/** @} */
/** time before sending/checking for keep alive, in RTC ticks */
#define KEEP_ALIVE_PERIOD (RTC_TICKS_SECOND * 60 * 15)
/** the pin to decide the role of this device: if it should transmit (connected to ground), or receive constantly (open) */
#define ROLE_PIN PB8
/** DIO0 pin which will be used as interrupt from the LoRa module */
#define RADIO_SX172X_GPIO_IRQ PB6
/** frequency for the LoRa communication, in Hz */
#define LORA_FREQ 447.681E6
/** the common pin of the lid switch or LED anode */
#define LID_COMMON PB10
/** the normally open pin of the lid switch or LED cathode */
#define LID_NO PB1
/** value of message when lid is opened */
#define LID_VALUE 0xaa
/** the common pin of the door switch or LED anode */
#define DOOR_COMMON PA7
/** the normally open pin of the door switch or LED cathode */
#define DOOR_NO PB0
/** value of message when door is opened */
#define DOOR_VALUE 0x55
/** maximum number of missed messaged before we indicate communication failed */
#define MAX_MISSED 5
/** if this device will transmit or receive */
static bool role_transmit = false;
size_t putc(char c)
{
size_t length = 0; // number of characters printed
@ -83,14 +101,18 @@ size_t putc(char c)
#if !defined(STLINKV2)
uart_putchar_nonblocking('\r'); // send CR over USART
#endif
usb_cdcacm_putchar('\r'); // send CR over USB
if (!role_transmit || DEBUG) {
usb_cdcacm_putchar('\r'); // send CR over USB
}
length++; // remember we printed 1 character
}
}
#if !defined(STLINKV2)
uart_putchar_nonblocking(c); // send byte over USART
#endif
usb_cdcacm_putchar(c); // send byte over USB
if (!role_transmit || DEBUG) {
usb_cdcacm_putchar(c); // send byte over USB
}
length++; // remember we printed 1 character
last_c = c; // remember last character
return length; // return number of characters printed
@ -128,6 +150,29 @@ static void command_reset(void* argument);
*/
static void command_bootloader(void* argument);
static void command_scan(void* argument)
{
(void)argument; // we won't use the argument
printf("scanning band:\n");
const uint8_t mode = radio_sx172x_read_register(RADIO_SX172X_REG_OP_MODE); // backup original mode
uint32_t frf = (radio_sx172x_read_register(RADIO_SX172X_REG_FRF_MSB) << 8) + (radio_sx172x_read_register(RADIO_SX172X_REG_FRF_MID) << 8) + (radio_sx172x_read_register(RADIO_SX172X_REG_FRF_LSB) << 0); // backup frequency
for (uint32_t freq = 0x690000; freq < 0x708000; freq += 100) { // 0x690000 = 420 MHz, 0x708000 = 450 MHz
radio_sx172x_write_register(RADIO_SX172X_REG_OP_MODE, (mode & 0xf8) | 1); // go to standby mode to change frequency
radio_sx172x_write_register(RADIO_SX172X_REG_FRF_MSB, (uint8_t)(freq >> 16)); // set frequency
radio_sx172x_write_register(RADIO_SX172X_REG_FRF_MID, (uint8_t)(freq >> 8)); // set frequency
radio_sx172x_write_register(RADIO_SX172X_REG_FRF_LSB, (uint8_t)(freq >> 0)); // set frequency
radio_sx172x_write_register(RADIO_SX172X_REG_OP_MODE, (mode & 0xf8) | 5); // start continuous listening
sleep_ms(100); // wait a be to get measurements
const int16_t rssi = -164 + radio_sx172x_read_register(RADIO_SX172X_REG_LORA_RSSI_VALUE);
printf("frequency: %.03f MHz (%+x), RSSI: %d dBm\n", freq * 32E6 / (1 << 19) / 1E6, freq, rssi);
}
radio_sx172x_write_register(RADIO_SX172X_REG_OP_MODE, (mode & 0xf8) | 1); // go to standby mode to change frequency
radio_sx172x_write_register(RADIO_SX172X_REG_FRF_MSB, (uint8_t)(frf >> 16)); // set original frequency
radio_sx172x_write_register(RADIO_SX172X_REG_FRF_MID, (uint8_t)(frf >> 8)); // set original frequency
radio_sx172x_write_register(RADIO_SX172X_REG_FRF_LSB, (uint8_t)(frf >> 0)); // set original frequency
radio_sx172x_write_register(RADIO_SX172X_REG_OP_MODE, mode); // start original mode
}
/** list of all supported commands */
static const struct menu_command_t menu_commands[] = {
{
@ -180,6 +225,14 @@ static const struct menu_command_t menu_commands[] = {
.argument_description = NULL,
.command_handler = &command_bootloader,
},
{
.shortcut = 's',
.name = "scan",
.command_description = "get RSSI for RF band",
.argument = MENU_ARGUMENT_NONE,
.argument_description = NULL,
.command_handler = &command_scan,
},
};
static void command_help(void* argument)
@ -192,132 +245,10 @@ static void command_help(void* argument)
static void command_version(void* argument)
{
(void)argument; // we won't use the argument
bool fake = false; // if details indicate it's not an STM32
printf("firmware date: %04u-%02u-%02u\n", BUILD_YEAR, BUILD_MONTH, BUILD_DAY); // show firmware build date
puts("chip family: ");
const uint16_t dev_id = DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK;
switch (dev_id) {
case 0: // DBGMCU_IDCODE is only accessible in debug mode (this is a known issue documented in STM32F10xxC/D/E Errata sheet, without workaround)
puts("not readable, retry with debug attached");
break;
// from RM0008 STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx
case 0x412:
puts("STM32F10x low-density");
break;
case 0x410:
puts("STM32F10x medium-density");
break;
case 0x414:
puts("STM32F10x high-density");
break;
case 0x430:
puts("STM32F10x XL-density");
break;
case 0x418:
puts("STM32F10xconnectivity");
break;
// from RM0091 STM32F0x8
case 0x444:
puts("STM32F03x");
break;
case 0x445:
puts("STM32F04x");
break;
case 0x440:
puts("STM32F05x");
break;
case 0x448:
puts("STM32F07x");
break;
case 0x442:
puts("STM32F09x");
break;
// from RM0444 STM32G0x1
case 0x460:
puts("STM32G071xx/STM32G081xx");
break;
case 0x466:
puts("STM32G031xx/STM32G041xx");
break;
// from RM0090 STM32F4x5/STM32F4x7
case 0x413:
puts("STM32F405/STM32F407/STM32F415/STM32F417");
break;
case 0x419:
puts("STM32F42x/STM32F43x");
break;
// from RM0368
case 0x423:
puts("STM32F401xB/C");
break;
case 0x433:
puts("STM32F401xD/E");
break;
// from RM0383
case 0x431:
puts("STM32F411xC/E");
break;
default:
puts("unknown");
fake = true;
break;
}
printf(" (DEV_ID=0x%03x)\n", dev_id);
puts("chip revision: ");
const uint16_t rev_id = DBGMCU_IDCODE >> 16;
switch (DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK) {
case 0x412:
if (0x1000 == rev_id) {
putc('A');
} else {
puts("unknown");
}
break;
case 0x410:
if (0x0000 == rev_id) {
putc('A');
} else if (0x2000 == rev_id) {
putc('B');
} else if (0x2001 == rev_id) {
putc('Z');
} else if (0x2003 == rev_id) {
puts("1/2/3/X/Y");
} else {
puts("unknown");
}
break;
case 0x414:
if (0x1000 == rev_id) {
puts("A/1");
} else if (0x1001 == rev_id) {
putc('Z');
} else if (0x1003 == rev_id) {
puts("1/2/3/X/Y");
} else {
puts("unknown");
}
break;
case 0x430:
if (0x1003 == rev_id) {
puts("A/1");
} else {
puts("unknown");
}
break;
case 0x418:
if (0x1000 == rev_id) {
putc('A');
} else if (0x1001 == rev_id) {
putc('Z');
} else {
puts("unknown");
}
break;
default:
printf("unknown");
break;
}
printf(" (REV_ID=0x%04x)\n", rev_id);
printf("MCU_ID: DEV_ID=0x%03x REV_ID=0x%04x\n", dev_id, rev_id);
// show flash size
puts("flash size: ");
if (0xffff == DESIG_FLASH_SIZE) {
@ -327,106 +258,13 @@ static void command_version(void* argument)
}
// display device identity
printf("device id: %08x%08x%04x%04x\n", DESIG_UNIQUE_ID2, DESIG_UNIQUE_ID1, DESIG_UNIQUE_ID0 & 0xffff, DESIG_UNIQUE_ID0 >> 16);
// from RM0091 STM32F0x8 reference manual (not sure if it applies to F1)
printf("- X,Y wafer coordinate: %08x\n", DESIG_UNIQUE_ID0);
printf("- lot number: %c%c%c%c%c%c%c\n", DESIG_UNIQUE_ID2 >> 24, DESIG_UNIQUE_ID2 >> 16, DESIG_UNIQUE_ID2 >> 8, DESIG_UNIQUE_ID2 >> 0, DESIG_UNIQUE_ID1 >> 24, DESIG_UNIQUE_ID1 >> 16, DESIG_UNIQUE_ID1 >> 8);
printf("- wafer number: %u\n", DESIG_UNIQUE_ID1 & 0xff);
// from ARMv7-M and Cortex-M3 TRM
// ARMv7-M B3.2.3
printf("CPUID: 0x%08x\n", SCB_CPUID);
const uint8_t cpuid_implementer = (SCB_CPUID & SCB_CPUID_IMPLEMENTER) >> SCB_CPUID_IMPLEMENTER_LSB;
printf("- implementer: %s (0x%02x)\n", 0x41 == cpuid_implementer ? "ARM" : "unknown", cpuid_implementer);
const uint8_t cpuid_architecture = (SCB_CPUID & SCB_CPUID_CONSTANT) >> SCB_CPUID_CONSTANT_LSB;
puts("- architecture: ");
switch (cpuid_architecture) {
case 0xc:
puts("ARMv6-M");
break;
case 0xf:
puts("ARMv7-M");
break;
default:
fake = true;
puts("unknown");
}
printf(" (0x%x)\n", cpuid_architecture);
const uint16_t cpuid_partno = (SCB_CPUID & SCB_CPUID_PARTNO) >> SCB_CPUID_PARTNO_LSB;
puts("- part number: ");
switch (cpuid_partno) {
case 0xC60:
puts("Cortex-M0+");
break;
case 0xC20:
puts("CortexM0");
break;
case 0xC23: // the ARM spec actually mentions 0xC24
puts("CortexM3");
break;
case 0xC24:
puts("CortexM4");
break;
case 0xC27:
puts("CortexM7");
break;
default:
fake = true;
puts("unknown");
}
printf(" (0x%03x)\n", cpuid_partno);
const uint8_t cpuid_variant = (SCB_CPUID & SCB_CPUID_VARIANT) >> SCB_CPUID_VARIANT_LSB;
printf("- variant: %u\n", cpuid_variant);
const uint8_t cpuid_revision = (SCB_CPUID & SCB_CPUID_REVISION) >> SCB_CPUID_REVISION_LSB;
printf("- revision: %u\n", cpuid_revision);
// ARM CoreSight B2.2.2
const uint8_t jep106_continuation = *(uint32_t*)0xE00FFFD0 & 0x0f; // DES_2, PIDR4 bits[3:0]
const uint8_t jep106_identification = ((*(uint32_t*)0xE00FFFE8 & 0x7) << 4) + ((*(uint32_t*)0xE00FFFE4 >> 4) & 0xf); // DES_0, PIDR1 bits[7:4] JEP106 identification code bits[3:0], DES_1, PIDR2 bits[2:0] JEP106 identification code bits[6:4]
const uint16_t pidr_partno = ((*(uint32_t*)0xE00FFFE4 & 0xf) << 8) + (*(uint32_t*)0xE00FFFE0 & 0xff); // PART_0, PIDR0 bits[7:0] Part number bits[7:0], PART_1, PIDR1 bits[3:0] Part number bits[11:8]
puts("JEP106 ID: ");
if (0 == jep106_continuation && 0x20 == jep106_identification) {
puts("STM");
} else if (7 == jep106_continuation && 0x51 == jep106_identification) {
puts("GigaDevice");
} else if (4 == jep106_continuation && 0x3b == jep106_identification) {
puts("ARM");
} else {
puts("unknown");
}
printf(" (cont.=%u, ID=0x%02x), part=0x%03x\n", jep106_continuation, jep106_identification, pidr_partno);
// guess the micro-controller
puts("MCU: ");
if (1 == cpuid_variant && 1 == cpuid_revision && 0 == jep106_continuation && 0x20 == jep106_identification) { // STM32 uses Cortex-M3 r1p1 and the right JEP106 ID
puts("STM32");
} else if (2 == cpuid_variant && 1 == cpuid_revision && 7 == jep106_continuation && 0x51 == jep106_identification) { // GD32 uses Cortex-M3 r2p1 and the right JEP106 ID
puts("GD32");
fake = true;
} else if (2 == cpuid_variant && 1 == cpuid_revision && 4 == jep106_continuation && 0x3b == jep106_identification) { // GD32 uses Cortex-M3 r2p1 and ARM JEP106 ID
puts("CS32");
fake = true;
} else {
puts("unknown");
fake = true;
}
putc('\n');
// detect fake STM32
if (0x412 == dev_id || 0x410 == dev_id || 0x414 == dev_id || 0x430 == dev_id || 0x418 == dev_id) { // STM32F10x
// the original STM32F10x uses a Cortex-M3 r1p1
if (0xC23 != cpuid_partno) { // Cortex-M3
fake = true;
}
if (1 != cpuid_variant) { // r1
fake = true;
}
if (1 != cpuid_revision) { // p1
fake = true;
}
}
printf("this %s to be a genuine STM32\n", fake ? "does not seem" : "seems");
}
static void command_uptime(void* argument)
{
(void)argument; // we won't use the argument
uint32_t uptime = (rtc_get_counter_val() - time_start) / RTC_TICKS_SECOND; // get time from internal RTC
const uint32_t uptime = (rtc_get_counter_val() - time_start) / RTC_TICKS_SECOND; // get time from internal RTC
printf("uptime: %u.%02u:%02u:%02u\n", uptime / (24 * 60 * 60), (uptime / (60 * 60)) % 24, (uptime / 60) % 60, uptime % 60);
}
@ -510,7 +348,8 @@ static void process_command(char* str)
void main(void);
void main(void)
{
rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock
//rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock
rcc_clock_setup_in_hsi_out_48mhz(); // use internal HSI at low speed to save energy, but fast enough for USB
#if DEBUG
// enable functionalities for easier debug
@ -521,18 +360,31 @@ void main(void)
DBGMCU_CR |= DBGMCU_CR_SLEEP; // allow debug also in sleep mode (keep clock powered)
#else
// setup watchdog to reset in case we get stuck (i.e. when an error occurred)
iwdg_set_period_ms(WATCHDOG_PERIOD); // set independent watchdog period
iwdg_start(); // start independent watchdog
//iwdg_set_period_ms(WATCHDOG_PERIOD); // set independent watchdog period
//iwdg_start(); // start independent watchdog
// IMPORTANT: we can't use the watchdog because we can't disable it, it keeps running in stop mode, and we don't want to wake up every 20 second to just kick the dog.
#endif
board_setup(); // setup board
// check role (transmit or receive)
puts("device role: ");
rcc_periph_clock_enable(GPIO_RCC(ROLE_PIN)); // enable clock for pin peripheral
gpio_set(GPIO_PORT(ROLE_PIN), GPIO_PIN(ROLE_PIN)); // pull up to detect high when not tied to ground
gpio_set_mode(GPIO_PORT(ROLE_PIN), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_PIN(ROLE_PIN)); // set button pin to input
sleep_us(100); // wait a bit to settle
role_transmit = (0 == gpio_get(GPIO_PORT(ROLE_PIN), GPIO_PIN(ROLE_PIN))); // if tied to ground, do into transmit mode
#if !defined(STLINKV2)
uart_setup(); // setup USART (for printing)
#endif
usb_cdcacm_setup(); // setup USB CDC ACM (for printing)
puts("\nwelcome to the CuVoodoo STM32F1 example application\n"); // print welcome message
if (!role_transmit || DEBUG) {
usb_cdcacm_setup(); // setup USB CDC ACM (for printing)
}
printf("\nwelcome to the CuVoodoo YouGotParcel notifier: %s\n", role_transmit ? "transmitter" : "receiver"); // print welcome message
#if DEBUG
/*
// show reset cause
if (RCC_CSR & (RCC_CSR_LPWRRSTF | RCC_CSR_WWDGRSTF | RCC_CSR_IWDGRSTF | RCC_CSR_SFTRSTF | RCC_CSR_PORRSTF | RCC_CSR_PINRSTF)) {
puts("reset cause(s):");
@ -557,8 +409,10 @@ void main(void)
putc('\n');
RCC_CSR |= RCC_CSR_RMVF; // clear reset flags
}
*/
#endif
#if !(DEBUG)
/*
// show watchdog information
printf("setup watchdog: %.2fs", WATCHDOG_PERIOD / 1000.0);
if (FLASH_OBR & FLASH_OBR_OPTERR) {
@ -568,6 +422,7 @@ void main(void)
} else {
puts(" (hardware watchdog used, automatically started at reset)\n");
}
*/
#endif
// setup RTC
@ -578,52 +433,283 @@ void main(void)
#else // for boards with an precise Low Speed External oscillator
rtc_auto_awake(RCC_LSE, 32768 / RTC_TICKS_SECOND - 1); // ensure internal RTC is on, uses the 32.678 kHz LSE, and the prescale is set to our tick speed, else update backup registers accordingly (power off the micro-controller for the change to take effect)
#endif
time_start = rtc_get_counter_val(); // get start time from internal RTC
rtc_interrupt_enable(RTC_SEC); // enable RTC interrupt on "seconds"
nvic_enable_irq(NVIC_RTC_IRQ); // allow the RTC to interrupt
time_start = rtc_get_counter_val(); // get start time from internal RTC
// configure the Auto-Wake-Up (AWU) using the RTC alarm
rtc_set_alarm_time(rtc_get_counter_val() + KEEP_ALIVE_PERIOD); // set the alarm period
rtc_enable_alarm(); // provide RTC alarm flag (and signal for EXTI)
rtc_interrupt_enable(RTC_ALR); // enable RTC interrupt on alarm
exti_set_trigger(EXTI17, EXTI_TRIGGER_RISING); // trigger on RTC alarm
exti_enable_request(EXTI17); // use EXTI line to be able to wake up from stop (curious this is not needed for standby)
nvic_enable_irq(NVIC_RTC_ALARM_IRQ); // allow the alarm to interrupt
puts("OK\n");
// setup switch input/LED output
printf("setup %s: ", role_transmit ? "switches" : "LEDs");
rcc_periph_clock_enable(GPIO_RCC(LID_COMMON)); // enable clock for pin peripheral
gpio_set(GPIO_PORT(LID_COMMON), GPIO_PIN(LID_COMMON)); // set high
gpio_set_mode(GPIO_PORT(LID_COMMON), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LID_COMMON)); // set to output
rcc_periph_clock_enable(GPIO_RCC(DOOR_COMMON)); // enable clock for pin peripheral
gpio_set(GPIO_PORT(DOOR_COMMON), GPIO_PIN(DOOR_COMMON)); // set high
gpio_set_mode(GPIO_PORT(DOOR_COMMON), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(DOOR_COMMON)); // set to output
rcc_periph_clock_enable(GPIO_RCC(LID_NO)); // enable clock for pin peripheral
rcc_periph_clock_enable(GPIO_RCC(DOOR_NO)); // enable clock for pin peripheral
if (role_transmit) {
rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt
gpio_clear(GPIO_PORT(LID_NO), GPIO_PIN(LID_NO)); // pull low to detect closing
gpio_set_mode(GPIO_PORT(LID_NO), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_PIN(LID_NO)); // set pin to input
sleep_ms(10); // let pin settle
exti_select_source(GPIO_EXTI(LID_NO), GPIO_PORT(LID_NO)); // mask external interrupt of the pin only for this port
exti_set_trigger(GPIO_EXTI(LID_NO), EXTI_TRIGGER_RISING); // switch goes high when activated
exti_reset_request(GPIO_EXTI(LID_NO)); // clear possible interrupt
exti_enable_request(GPIO_EXTI(LID_NO)); // enable external interrupt
nvic_enable_irq(GPIO_NVIC_EXTI_IRQ(LID_NO)); // enable interrupt
gpio_clear(GPIO_PORT(DOOR_NO), GPIO_PIN(DOOR_NO)); // pull low to detect closing
gpio_set_mode(GPIO_PORT(DOOR_NO), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_PIN(DOOR_NO)); // set pin to input
sleep_ms(10); // let pin settle
exti_select_source(GPIO_EXTI(DOOR_NO), GPIO_PORT(DOOR_NO)); // mask external interrupt of the pin only for this port
exti_set_trigger(GPIO_EXTI(DOOR_NO), EXTI_TRIGGER_RISING); // switch goes high when activated
exti_reset_request(GPIO_EXTI(DOOR_NO)); // clear possible interrupt
exti_enable_request(GPIO_EXTI(DOOR_NO)); // enable external interrupt
nvic_enable_irq(GPIO_NVIC_EXTI_IRQ(DOOR_NO)); // enable interrupt
} else {
gpio_set(GPIO_PORT(LID_NO), GPIO_PIN(LID_NO)); // set high to switch LED off
gpio_set_mode(GPIO_PORT(LID_NO), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LID_NO)); // set to output
gpio_set(GPIO_PORT(DOOR_NO), GPIO_PIN(DOOR_NO)); // set high to switch LED off
gpio_set_mode(GPIO_PORT(DOOR_NO), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(DOOR_NO)); // set to output
}
puts("OK\n");
// setup LoRa communication using SX172x module
printf("setup LoRa communication (%u.%02u MHz): ", (uint32_t)(LORA_FREQ / 1E6), (uint32_t)(LORA_FREQ / 10E3) % 100);
const uint8_t mode_lora = (1 << 7) | (1 << 3); // use LoRa mode, access low frequency (433 < 800 MHz)
if (radio_sx172x_setup()) {
radio_sx172x_reset(); // reset registers to default
radio_sx172x_write_register(RADIO_SX172X_REG_LORA_DETECT_OPTIMIZE, (0 << 7) | (0x03 << 0)); // set AutomaticIFOn to 0 after reset (see errata)
radio_sx172x_write_register(RADIO_SX172X_REG_OP_MODE, 0); // put in sleep mode to be able to change mode
radio_sx172x_write_register(RADIO_SX172X_REG_OP_MODE, mode_lora); // set LoRa mode
radio_sx172x_write_register(RADIO_SX172X_REG_PA_CONFIG, (1 << 7) | (7 << 4) | (15 << 0)); // use power amplifier (select boost, use max power and output) IMPORTANT transmission will not work with the module I used
radio_sx172x_write_register(RADIO_SX172X_REG_LNA, (1 << 5)); // use maximum gain for LNA
radio_sx172x_write_register(RADIO_SX172X_REG_LORA_MODEM_CONFIG_1, (6 << 4) | (4 << 1) | (0 << 0)); // use lowest bandwidth (62.5 kHz using XTAL) and coding rate (4/8) to have best sensitivity (we don't care about the bandwidth) + explicit header
radio_sx172x_write_register(RADIO_SX172X_REG_LORA_MODEM_CONFIG_2, (12 << 4) | (1 << 2)); // use largest spreading factor (12) to get best SNR (-20 dB), add CRC on payload
radio_sx172x_write_register(RADIO_SX172X_REG_LORA_MODEM_CONFIG_3, (1 << 3) | (1 << 2)); // optimize for low data rate and use AGC for LNA
// NOTE: with these settings it take almost 2 seconds to send one byte
const uint32_t lora_frf = ((LORA_FREQ * (1 << 19)) / 32E6); // the register frequency value
radio_sx172x_write_register(RADIO_SX172X_REG_FRF_MSB, (uint8_t)(lora_frf >> 16)); // set frequency
radio_sx172x_write_register(RADIO_SX172X_REG_FRF_MID, (uint8_t)(lora_frf >> 8)); // set frequency
radio_sx172x_write_register(RADIO_SX172X_REG_FRF_LSB, (uint8_t)(lora_frf >> 0)); // set frequency
// setup interrupt
rcc_periph_clock_enable(GPIO_RCC(RADIO_SX172X_GPIO_IRQ)); // enable clock for GPIO port
gpio_set_mode(GPIO_PORT(RADIO_SX172X_GPIO_IRQ), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(RADIO_SX172X_GPIO_IRQ)); // set interrupt as input
rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt
exti_select_source(GPIO_EXTI(RADIO_SX172X_GPIO_IRQ), GPIO_PORT(RADIO_SX172X_GPIO_IRQ)); // mask external interrupt of the IRQ pin only for this port
exti_set_trigger(GPIO_EXTI(RADIO_SX172X_GPIO_IRQ), EXTI_TRIGGER_RISING); // IRQ goes high in interrupt
exti_reset_request(GPIO_EXTI(RADIO_SX172X_GPIO_IRQ)); // clear possible interrupt
exti_enable_request(GPIO_EXTI(RADIO_SX172X_GPIO_IRQ)); // enable external interrupt
nvic_enable_irq(GPIO_NVIC_EXTI_IRQ(RADIO_SX172X_GPIO_IRQ)); // enable interrupt
radio_sx172x_write_register(RADIO_SX172X_REG_LORA_IRQ_FLAGS, 0xff); // clear all flags
if (role_transmit) {
radio_sx172x_write_register(RADIO_SX172X_REG_DIO_MAPPING_1, (1 << 6)); // map DIO0 for TxDone output
radio_sx172x_write_register(RADIO_SX172X_REG_OP_MODE, mode_lora); // put in sleep mode
} else {
radio_sx172x_write_register(RADIO_SX172X_REG_DIO_MAPPING_1, (0 << 6)); // map DIO0 for RxDone output
radio_sx172x_write_register(RADIO_SX172X_REG_OP_MODE, mode_lora | 5); // start continuous listening
}
puts("OK\n");
} else {
puts("could not switch on\n");
}
// setup terminal
terminal_prefix = ""; // set default prefix
terminal_process = &process_command; // set central function to process commands
terminal_setup(); // start terminal
// blink on-board LED to indicate we started
led_off();
for (uint8_t i = 0; i < 6; i++) {
led_toggle();
sleep_ms(100);
}
led_off();
// start main loop
bool action = false; // if an action has been performed don't go to sleep
button_flag = false; // reset button flag
keep_alive_flag = true; // set status over LoRa on boot
uint8_t keep_alive_missed = MAX_MISSED; // number of times we did not get a keep alive message (start with no message received)
bool yougotparcel = false; // if a parcel is in the post box
while (true) { // infinite loop
iwdg_reset(); // kick the dog
//iwdg_reset(); // kick the dog
if (user_input_available) { // user input is available
action = true; // action has been performed
led_toggle(); // toggle LED
char c = user_input_get(); // store receive character
terminal_send(c); // send received character to terminal
}
if (button_flag) { // user pressed button
action = true; // action has been performed
puts("button pressed\n");
led_toggle(); // toggle LED
if (lid_flag) {
puts("lid opened\n");
sleep_ms(100); // wait a bit to remove noise and double trigger
button_flag = false; // reset flag
lid_flag = false; // reset flag
if (!yougotparcel) { // empty post box got filled
yougotparcel = true; // remember we received a parcel
keep_alive_flag = true; // set message
}
action = true; // action has been performed
}
if (door_flag) {
puts("door opened\n");
sleep_ms(100); // wait a bit to remove noise and double trigger
door_flag = false; // reset flag
if (yougotparcel) { // full post box got emptied
yougotparcel = false; // remember we removed the parcel from the post box
keep_alive_flag = true; // set message
}
action = true; // action has been performed
}
if (rtc_internal_tick_flag) { // the internal RTC ticked
rtc_internal_tick_flag = false; // reset flag
if (0 == (rtc_get_counter_val() % RTC_TICKS_SECOND)) { // one second has passed
//led_toggle(); // toggle LED (good to indicate if main function is stuck). don't use LED since it's used for SX172x chip select
//printf("modem status: %+05b, flags: %+08b\n", radio_sx172x_read_register(RADIO_SX172X_REG_LORA_MODEM_STAT) & 0x1f, radio_sx172x_read_register(RADIO_SX172X_REG_LORA_IRQ_FLAGS));
}
action = true; // action has been performed
if (0 == (rtc_get_counter_val() % RTC_TICKS_SECOND)) { // one seond has passed
led_toggle(); // toggle LED (good to indicate if main function is stuck)
}
radio_sx172x_irq_flag = (0 != gpio_get(GPIO_PORT(RADIO_SX172X_GPIO_IRQ), GPIO_PIN(RADIO_SX172X_GPIO_IRQ))); // update interrupt status
if (radio_sx172x_irq_flag) { // LoRa module signals activity
const uint8_t lora_flags = radio_sx172x_read_register(RADIO_SX172X_REG_LORA_IRQ_FLAGS); // read flags
if (role_transmit && (lora_flags & (1 << 3))) { // transmit completed
puts("OK\n"); // should end the TX: line start
radio_sx172x_write_register(RADIO_SX172X_REG_LORA_IRQ_FLAGS, (1 << 3)); // clear TxDone flag
radio_sx172x_write_register(RADIO_SX172X_REG_OP_MODE, mode_lora); // put back to sleep mode
} else if (!role_transmit && (lora_flags & (1 << 6))) { // packet has been received
const uint32_t uptime = (rtc_get_counter_val() - time_start) / RTC_TICKS_SECOND; // get time from internal RTC
printf("%u.%02u:%02u:%02u ", uptime / (24 * 60 * 60), (uptime / (60 * 60)) % 24, (uptime / 60) % 60, uptime % 60); // show time stamp
puts("RX: ");
if (lora_flags & (1 << 5)) { // CRC error
goto clear_rx;
}
if (!(lora_flags & (1 << 4))) { // header invalid
goto clear_rx;
}
const uint8_t payload_length = radio_sx172x_read_register(RADIO_SX172X_REG_LORA_RX_NB_BYTES); // get number of bytes received
if (1 != payload_length) { // unexpected payload length
goto clear_rx;
}
const uint8_t payload_addr = radio_sx172x_read_register(RADIO_SX172X_REG_LORA_FIFO_RX_CURRENT_ADDR); // get address in FIFO of data received
uint8_t payload;
radio_sx172x_read_fifo(payload_addr, &payload, payload_length); // read received data
const int8_t packet_snr = (int8_t)radio_sx172x_read_register(RADIO_SX172X_REG_LORA_PKT_SNR_VALUE) / 4; // read SNR value
const int16_t packet_rssi = -164 + radio_sx172x_read_register(RADIO_SX172X_REG_LORA_PKT_RSSI_VALUE); // read RSSI value
printf("%+02x (SNR: %d dB, RSSI: %d dBm)", payload, packet_snr, packet_rssi);
keep_alive_missed = 0; // reset the missed counter
// remember if we received a parcel
if (LID_VALUE == payload) {
yougotparcel = true;
} else if (DOOR_VALUE == payload) {
yougotparcel = false;
}
clear_rx:
radio_sx172x_write_register(RADIO_SX172X_REG_LORA_IRQ_FLAGS, (1 << 6) | (1 << 5) | (1 << 4)); // clear RxDone, PayloadCrcError, ValidHeader flags
const uint8_t rx_addr = radio_sx172x_read_register(RADIO_SX172X_REG_LORA_FIFO_RX_BASE_ADDR); // get start of receive buffer
radio_sx172x_write_register(RADIO_SX172X_REG_LORA_FIFO_ADDR_PTR, rx_addr); // reset receive FIFO
putc('\n');
} else {
printf("unhandled LoRa interrupt: %+08b\n", lora_flags);
}
radio_sx172x_write_register(RADIO_SX172X_REG_LORA_IRQ_FLAGS, 0xff); // clear all flags
radio_sx172x_irq_flag = false; // reset notification
action = true; // action has been performed
}
if (keep_alive_flag) {
keep_alive_flag = false; // reset flag
if (keep_alive_missed < MAX_MISSED) {
keep_alive_missed++; // increase missed counter, which is reset when receiving a message
}
rtc_set_alarm_time(rtc_get_counter_val() + KEEP_ALIVE_PERIOD); // reset the alarm
if (role_transmit && 3 != (radio_sx172x_read_register(RADIO_SX172X_REG_OP_MODE) & 0x7)) { // periodically transmit (when no already transmitting
const uint8_t tx_data = (yougotparcel ? LID_VALUE : DOOR_VALUE); // transmit which has been opened
const uint32_t uptime = (rtc_get_counter_val() - time_start) / RTC_TICKS_SECOND; // get time from internal RTC
printf("%u.%02u:%02u:%02u ", uptime / (24 * 60 * 60), (uptime / (60 * 60)) % 24, (uptime / 60) % 60, uptime % 60);
printf("TX: %+02x ... ", tx_data);
const uint8_t fifx_tx_addr = radio_sx172x_read_register(RADIO_SX172X_REG_LORA_FIFO_TX_BASE_ADDR); // get the FIFO address to write the data
radio_sx172x_write_fifo(fifx_tx_addr, &tx_data, 1); // write payload data to TX FIFO
radio_sx172x_write_register(RADIO_SX172X_REG_LORA_PAYLOAD_LENGTH, 1); // indicate the payload length to be transmitted
radio_sx172x_write_register(RADIO_SX172X_REG_LORA_IRQ_FLAGS, (1 << 3)); // clear TxDone flag
radio_sx172x_write_register(RADIO_SX172X_REG_OP_MODE, mode_lora | 3); // start transmission
}
action = true; // action has been performed
}
// update LED status
if (!role_transmit) {
if (keep_alive_missed >= MAX_MISSED) {
gpio_clear(GPIO_PORT(LID_NO), GPIO_PIN(LID_NO)); // set low to switch LED on
gpio_clear(GPIO_PORT(DOOR_NO), GPIO_PIN(DOOR_NO)); // set low to switch LED on
} else if (yougotparcel) {
gpio_clear(GPIO_PORT(LID_NO), GPIO_PIN(LID_NO)); // set low to switch LED on
gpio_set(GPIO_PORT(DOOR_NO), GPIO_PIN(DOOR_NO)); // set high to switch LED off
} else {
gpio_set(GPIO_PORT(LID_NO), GPIO_PIN(LID_NO)); // set high to switch LED off
gpio_clear(GPIO_PORT(DOOR_NO), GPIO_PIN(DOOR_NO)); // set low to switch LED on
}
}
if (action) { // go to sleep if nothing had to be done, else recheck for activity
action = false;
} else {
__WFI(); // go to sleep
if (role_transmit && !DEBUG) { // only go to stop mode when transmitting, to save battery (the receiver needs/has a permanent power source)
puts("zzz\n");
uart_flush(); // wait for all communication to complete
SCB_SCR |= SCB_SCR_SLEEPDEEP; // set deep sleep in CPU
pwr_set_stop_mode(); // clear power control
pwr_voltage_regulator_low_power_in_stop(); // save even more power at the cost of wake up time
}
__WFI(); // go to sleep (or stop mode)
SCB_SCR &= ~SCB_SCR_SLEEPDEEP; // stop going to deep sleep
if (role_transmit) {
rcc_clock_setup_in_hsi_out_48mhz(); // after exiting stop mode, default HSI RC is used as clock, we need to set it again
}
}
} // main loop
}
/** @brief interrupt service routine called when tick passed on RTC */
/** interrupt service routine called when tick passed or alarm triggered on RTC */
void rtc_isr(void)
{
rtc_clear_flag(RTC_SEC); // clear flag
rtc_internal_tick_flag = true; // notify to show new time
if (rtc_check_flag(RTC_SEC)) { // tick passed
rtc_clear_flag(RTC_SEC); // clear flag
rtc_internal_tick_flag = true; // notify to show new time
} else if (rtc_check_flag(RTC_ALR)) { // alarm triggered
rtc_clear_flag(RTC_ALR); // clear flag
keep_alive_flag = true; // notify user
}
}
/** interrupt service routine called when alarm triggered on RTC */
void rtc_alarm_isr(void)
{
exti_reset_request(EXTI17); // reset interrupt
keep_alive_flag = true; // notify user
}
/** interrupt service routine called upon LoRa IRQ */
void GPIO_EXTI_ISR(RADIO_SX172X_GPIO_IRQ)(void)
{
radio_sx172x_irq_flag = true; // notify main loop (the IRQ lien will actually be verified by the main loop, but at least this should start the loop/wake up)
exti_reset_request(GPIO_EXTI(RADIO_SX172X_GPIO_IRQ)); // reset interrupt
}
/** interrupt service routine called upon post box lid being opened */
void GPIO_EXTI_ISR(LID_NO)(void)
{
lid_flag = true; // notify main loop
exti_reset_request(GPIO_EXTI(LID_NO)); // reset interrupt
}
/** interrupt service routine called upon post box door being opened */
void GPIO_EXTI_ISR(DOOR_NO)(void)
{
door_flag = true; // notify main loop
exti_reset_request(GPIO_EXTI(DOOR_NO)); // reset interrupt
}

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** USB DFU bootloader
* @file bootloader.c
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2017-2019
*/
/* standard libraries */

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** global definitions and methods (code)
/** global definitions and methods
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2016-2020
*/
/* standard libraries */

View File

@ -1,26 +1,13 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** global definitions and methods (API)
/** global definitions and methods
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2016-2020
*/
#pragma once
/** enable debugging functionalities */
#define DEBUG true
#define DEBUG false
/** get the length of an array */
#define LENGTH(x) (sizeof(x) / sizeof((x)[0]))

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** library to read/write internal flash
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2016-2020
* @note peripherals used: none
*/

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** library to read/write internal flash
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2016-2020
* @note peripherals used: none
*/

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** library to communicate with an SD card flash memory using the SPI mode (code)
* @file flash_sdcard.c
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2017
* @note peripherals used: SPI @ref flash_sdcard_spi
* @warning all calls are blocking

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** library to communicate with an SD card flash memory using the SPI mode (API)
* @file flash_sdcard.h
/** library to communicate with an SD card flash memory using the SPI mode
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2017
* @note peripherals used: SPI @ref flash_sdcard_spi
* @warning all calls are blocking

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** library to communicate using I²C as master (code)
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2017-2020
* @note peripherals used: I2C
*/

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** library to communicate using I²C as master (API)
/** library to communicate using I²C as master
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2017-2020
* @note peripherals used: I2C
*/

View File

@ -1,19 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/** BusVoodoo runtime interrupt table
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2018
*/
#include "interrupt.h" // own definitions

View File

@ -1,19 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/** BusVoodoo runtime interrupt table
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2018
*/
#pragma once

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** library to decode InfraRed NEC code
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2018-2020
* @note peripherals used: timer channel @ref ir_nec_timer
*/

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** library to decode InfraRed NEC code
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2018-2020
* @note peripherals used: timer channel @ref ir_nec_timer
*/

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** library to communication with Hitacho HD44780 LCD controller
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2019-2020
* @note peripherals used: GPIO @ref lcd_hd44780_gpio
*/

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** library to communication with Hitacho HD44780 LCD controller
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2019-2020
* @note peripherals used: GPIO @ref lcd_hd44780_gpio, I²C @ref lcd_hd44780_i2c
*/

View File

@ -1,20 +1,7 @@
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*