diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 1a8639a..1b6ca9a 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -19,8 +19,13 @@ "ENABLED_REGIONS=\"AS923 AU915 CN470 CN779 EU433 EU868 IN865 KR920 RU864 US915\"", "DEFAULT_ACTIVE_REGION=\"EU868\"", "DEFAULT_UART_BAUDRATE=19200", - "DEBUG_PORT=1", - "TCXO_PIN=1" + "FACTORY_RESET_PIN=1", + "RESTORE_CHMASK_AFTER_JOIN=0", + "TCXO_PIN=1", + "DETACHABLE_LPUART=1", + "DEBUG_LOG=1", + "ENABLE_SWD=1", + "DEBUG_MCU=1" ], "includePath": [ "${workspaceFolder}/**", @@ -47,7 +52,13 @@ "ENABLED_REGIONS=\"AS923 AU915 CN470 CN779 EU433 EU868 IN865 KR920 RU864 US915\"", "DEFAULT_ACTIVE_REGION=\"EU868\"", "DEFAULT_UART_BAUDRATE=19200", - "TCXO_PIN=1" + "FACTORY_RESET_PIN=1", + "RESTORE_CHMASK_AFTER_JOIN=0", + "TCXO_PIN=1", + "DETACHABLE_LPUART=1", + "DEBUG_LOG=0", + "ENABLE_SWD=0", + "DEBUG_MCU=0" ], "includePath": [ "${workspaceFolder}/**", diff --git a/Makefile b/Makefile index 63384d2..0846044 100644 --- a/Makefile +++ b/Makefile @@ -15,19 +15,6 @@ ENABLED_REGIONS ?= AS923 AU915 EU868 KR920 IN865 US915 RU864 # application. DEFAULT_ACTIVE_REGION ?= EU868 -# Uncomment the following line to configure pin 14 (GPIOB15/SPI2_MOSI) as a -# factory reset pin. During normal operation, the pin should be pulled up or -# left floating. When continuously pulled down for more than five seconds and -# then pulled up, the modem resets itself to factory defaults. -# -# This functionality is only available in release builds. In debug builds, the -# same pin is used by the SWD debugging interface. -# -# Since the modem can also be reset through the AT command interface, this -# functionality is disabled by default to save a little bit of power. -# -# ENABLE_FACTORY_RESET_PIN = 1 - # The default channel plan for the AS923 region. One of: # - CHANNEL_PLAN_GROUP_AS923_1 # - CHANNEL_PLAN_GROUP_AS923_2 @@ -59,17 +46,24 @@ VERSION_COMPAT ?= 1.1.06 # build date of Murata Modem in version configured through VERSION_COMPAT. BUILD_DATE_COMPAT ?= Aug 24 2020 16:11:57 +# Set the following variable to 1 configure pin 14 (GPIOB15/SPI2_MOSI) as a +# factory reset pin. During normal operation, the pin should be pulled up or +# left floating. When continuously pulled down for more than five seconds and +# then pulled up, the modem resets itself to factory defaults. +# +# Since the modem can also be reset through the AT command interface, this +# functionality is disabled by default to save a little bit of power. +# +# Used GPIOs: PB15 +FACTORY_RESET_PIN ?= 0 + # The LoRaWAN network server may reconfigure the node's channel mask in the Join # Accept message. If you want to prevent that from happening, e.g., if you work -# with an incorrectly configured LoRaWAN network server, uncomment the following -# variable. Use with caution. This feature is designed as a work-around for +# with an incorrectly-configured LoRaWAN network server, set the following +# variable to 1. Use with caution. This feature is designed as a work-around for # incorrectly configured LoRa networks. If you are unsure, leave the variable -# commented out. -# RESTORE_CHMASK_AFTER_JOIN = 1 - -# Select the USART port number which will receive debug messages when the -# firmware is built in debugging mode. You can select 1 or 2 here. -DEBUG_PORT ?= 1 +# set to 0. +RESTORE_CHMASK_AFTER_JOIN ?= 0 # Select the GPIO pin connected to VDD_TCXO (pin 48). The modem will use this # pin to control power to the TCXO IC. The TCXO IC generates clock for the @@ -83,8 +77,62 @@ DEBUG_PORT ?= 1 # also the default value. Hardwario LoRa devices use this pin. # # 2 - PB6 (pin 39). Arduino MKRWAN1310 boards use this pin. +# +# Used GPIOs: PA12 (1), PB6 (2) TCXO_PIN ?= 1 +# Arduino MKRWAN boards share some lines between the TypeABZ module's LPUART +# interface (the AT command interface) and SPI. On MKRWAN1310 boards, the SPI +# interface is also used to communicate with the on-board SPI flash. As a +# consequence, the host cannot access the flash while the TypeABZ AT command +# interface is active. This is a design flaw of the Arduino MKRWAN1310 board. +# +# The Arduino documentation recommends that the host MCU keeps the TypeABZ modem +# in a reset state while it accesses the on-board flash. Resetting the TypeABZ +# modem could result in the loss of some internal LoRaWAN state maintained by +# the modem and is not recommended unless absolutely necessary. +# +# When the following option is set to 1, the host can request to detach LPUART +# GPIO pins with the AT command AT$DETACH. The GPIO PB12 must be set to 1 at +# that time. While detached, the host can use SPI to communicate with the +# on-board flash. To reattach LPUART GPIO pins, the host pulls PB12 GPIO down. +# The modem emits "+EVENT=0,9" to indicate that the LPUART port has been +# attached and the AT command interface is available again. +# +# Used GPIOs: PA2, PA3 (LPUART1), PB12 (attach LPUART1 signal) +DETACHABLE_LPUART ?= 0 + +# Select the target for the debugging logger. The target can be one of: +# 0 - No target, disable the debugging logger +# 1 - Send debugging messages to USART1 +# 2 - Send debugging messages to USART2 +# 3 - Send debugging messages to Segger RTT +# +# By default, the variable is set to 0 in release mode and to 1 in debug mode. +# +# Used GPIOs: PA9 (USART1), PA2 (USART2) +#DEBUG_LOG = + +# Enable (1) or disable (0) the SWD debugging interface. This is most useful +# when the firmware is being built in debugging mode. When set to 0, the SWD +# interface will be disabled at startup. The interface should be disabled when +# the firmware is being built in release mode. +# +# By default, the variable is set to 0 in release mode and to 1 in debug mode. +# +# Used GPIOs: PA13, PA14 +#DEBUG_SWD = + +# Enable (1) or disable (0) the MCU debugging interface. Consider enabling the +# interface when compiling the firmware in debugging mode. You may need to +# disable other features that use the same ports when you enable the MCU +# debugging interface. +# +# By default, the variable is set to 0 in release mode and to 1 in debug mode. +# +# Used GPIOs: PB12, PB13, PB14, PB15 +#DEBUG_MCU = + ################################################################################ # You shouldn't need to edit the text below under normal circumstances. # ################################################################################ @@ -97,7 +145,7 @@ OUT_DIR ?= out # The current compilation type (either debug or release). Passed recursively # across make invocations. -TYPE ?= debug +TYPE ?= release ELF ?= $(OUT_DIR)/$(TYPE)/$(BASENAME).elf MAP ?= $(OUT_DIR)/$(TYPE)/$(BASENAME).map @@ -110,7 +158,10 @@ HEX ?= $(OUT_DIR)/$(TYPE)/$(BASENAME).hex # Add all the application files to the list of directories to scan. SRC_DIRS = $(SRC_DIR) -SRC_DIRS_DEBUG = $(SRC_DIR)/debug + +ifneq ($(DEBUG_LOG),0) +SRC_DIRS += $(SRC_DIR)/debug +endif # Include only the following selected sources from the STM HAL and everything # from stm/src @@ -134,17 +185,23 @@ stm_hal = \ stm32l0xx_hal_uart_ex.c \ stm32l0xx_ll_dma.c -stm_hal_debug = \ +ifneq ($(DEBUG_LOG),0) +stm_hal += \ stm32l0xx_ll_usart.c \ stm32l0xx_ll_rcc.c +endif SRC_FILES += $(patsubst %.c,$(LIB_DIR)/stm/STM32L0xx_HAL_Driver/Src/%.c,$(stm_hal)) -SRC_FILES_DEBUG = $(patsubst %.c,$(LIB_DIR)/stm/STM32L0xx_HAL_Driver/Src/%.c,$(stm_hal_debug)) SRC_DIRS += $(LIB_DIR)/stm/src -# Include all source code from rtt and LoRaWAN lib subdirectories -SRC_DIRS_DEBUG += $(LIB_DIR)/rtt +# If we log to Segger RTT, include the source code from the rtt lib +# subdirectory. +ifeq ($(DEBUG_LOG),3) +SRC_DIRS += $(LIB_DIR)/rtt +endif + +# Include all source code from LoRaWAN lib subdirectories SRC_DIRS += $(LIB_DIR)/LoRaWAN/Utilities # Include the core LoRa MAC stack with only the base regional files @@ -310,8 +367,6 @@ CFLAGS += -DUSE_FULL_LL_DRIVER CFLAGS += -DDEFAULT_UART_BAUDRATE=$(DEFAULT_UART_BAUDRATE) -CFLAGS += -DTCXO_PIN=$(TCXO_PIN) - # Extra flags to be only applied when we compile the souce files from the lib # subdirectory. Since that sub-directory contains third-party code, disable some # of the warnings. @@ -322,7 +377,6 @@ CFLAGS_LIBS += -Wno-int-conversion CFLAGS_DEBUG += -g3 CFLAGS_DEBUG += -Og CFLAGS_DEBUG += -DDEBUG -CFLAGS_DEBUG += -DDEBUG_PORT=$(DEBUG_PORT) CFLAGS_RELEASE += -Os CFLAGS_RELEASE += -DRELEASE @@ -336,18 +390,18 @@ CFLAGS += -DDEFAULT_ACTIVE_REGION='"$(DEFAULT_ACTIVE_REGION)"' CFLAGS += -DREGION_AS923_DEFAULT_CHANNEL_PLAN=$(AS923_DEFAULT_CHANNEL_PLAN) CFLAGS += -DREGION_CN470_DEFAULT_CHANNEL_PLAN=$(CN470_DEFAULT_CHANNEL_PLAN) - -ifdef ENABLE_FACTORY_RESET_PIN -CFLAGS += -DENABLE_FACTORY_RESET_PIN -endif - ifneq (,$(LORAMAC_ABP_VERSION)) CFLAGS += -DLORAMAC_ABP_VERSION=$(LORAMAC_ABP_VERSION) endif -ifdef RESTORE_CHMASK_AFTER_JOIN -CFLAGS += -DRESTORE_CHMASK_AFTER_JOIN -endif +CFLAGS += -DFACTORY_RESET_PIN=$(FACTORY_RESET_PIN) +CFLAGS += -DRESTORE_CHMASK_AFTER_JOIN=$(RESTORE_CHMASK_AFTER_JOIN) +CFLAGS += -DTCXO_PIN=$(TCXO_PIN) +CFLAGS += -DDETACHABLE_LPUART=$(DETACHABLE_LPUART) + +CFLAGS += -DDEBUG_LOG=$(DEBUG_LOG) +CFLAGS += -DDEBUG_SWD=$(DEBUG_SWD) +CFLAGS += -DDEBUG_MCU=$(DEBUG_MCU) ################################################################################ # Compiler flags for .s files # @@ -386,7 +440,6 @@ LDFLAGS += --specs=nosys.specs ################################################################################ SRC_FILES += $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c)) -SRC_FILES_DEBUG += $(foreach dir,$(SRC_DIRS_DEBUG),$(wildcard $(dir)/*.c)) OBJ_C = $(SRC_FILES:%.c=$(OBJ_DIR)/$(TYPE)/%.o) OBJ_S = $(ASM_SOURCES:%.s=$(OBJ_DIR)/$(TYPE)/%.o) @@ -397,21 +450,26 @@ DEP = $(OBJ:%.o=%.d) # Build targets # ################################################################################ -.PHONY: debug -debug: export TYPE=debug -debug: export CFLAGS=$(CFLAGS_DEBUG) -debug: export ASFLAGS=$(ASFLAGS_DEBUG) -debug: export SRC_FILES=$(SRC_FILES_DEBUG) -debug: - $(Q)$(MAKE) install - .PHONY: release release: export TYPE=release +release: export DEBUG_LOG ?= 0 +release: export DEBUG_SWD ?= 0 +release: export DEBUG_MCU ?= 0 release: export CFLAGS=$(CFLAGS_RELEASE) release: export ASFLAGS=$(ASFLAGS_RELEASE) release: $(Q)$(MAKE) install +.PHONY: debug +debug: export TYPE=debug +debug: export DEBUG_LOG ?= 1 +debug: export DEBUG_SWD ?= 1 +debug: export DEBUG_MCU ?= 1 +debug: export CFLAGS=$(CFLAGS_DEBUG) +debug: export ASFLAGS=$(ASFLAGS_DEBUG) +debug: + $(Q)$(MAKE) install + .PHONY: install install: $(BIN) $(HEX) $(MAKEFILE_LIST) $(Q)$(ECHO) "Copying $(BIN) to ./$(BASENAME).bin..." diff --git a/src/cmd.c b/src/cmd.c index 6400326..edb83dd 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -43,7 +43,8 @@ typedef enum cmd_errno { ERR_DUTYCYCLE = -18, // Cannot transmit due to duty cycling ERR_NO_CHANNEL = -19, // Channel unavailable due to LBT or error ERR_TOO_MANY = -20, // Too many link check requests - ERR_ACCESS_DENIED = -50 // Read access to security keys is denied + ERR_ACCESS_DENIED = -50, // Read access to security keys is denied + ERR_DETACH_DENIED = -51 // The re-attach GPIO is active } cmd_errno_t; @@ -53,6 +54,14 @@ static TimerEvent_t payload_timer; bool schedule_reset = false; +#if DETACHABLE_LPUART == 1 + +static Gpio_t attach_pin = { + .port = GPIOB, + .pinIndex = GPIO_PIN_12 +}; + +#endif #define abort(num) do { \ atci_printf("+ERR=%d" ATCI_EOL, (num)); \ @@ -1655,7 +1664,7 @@ static void set_netid(atci_param_t *param) } -#if defined(DEBUG) +#if DEBUG_LOG != 0 static void dbg(atci_param_t *param) { (void)param; @@ -1977,7 +1986,7 @@ static void set_rfpower(atci_param_t *param) OK_(); } -#if defined(DEBUG) +#if DEBUG_LOG != 0 static void get_loglevel(void) { OK("%d", log_get_level()); @@ -2107,78 +2116,155 @@ static void lock_keys(atci_param_t *param) } +#if DETACHABLE_LPUART == 1 + +#if FACTORY_RESET_PIN != 0 +# error DETACHABLE_LPUART and FACTORY_RESET_PIN cannot be enabled at the same time. +#endif + +#if DEBUG_MCU != 0 +# error DETACHABLE_LPUART and DEBUG_MCU cannot be enabled at the same time. +#endif + +static void detach_lpuart(atci_param_t *param) +{ + (void)param; + + // First check if the LPUART wake-up GPIO pin is low. If it is, the host + // indicates that it wants to reattach the port. If that's the case, we + // return an error. + int v = gpio_read(attach_pin.port, attach_pin.pinIndex); + if (v == 0) abort(ERR_DETACH_DENIED); + + // The SPI lines are connected to PB12, PB13, PB14, and PB15. We use PB12 as + // the wake-up signal. The remaining pins are configured in analog mode with + // no pull-up unless the factory reset pin or the debug MCU features are + // enabled. Hence the error reported above if either of those features is + // enabled with this feature. We would need to reconfigure those pins in + // input mode and will not be able to put them back into the original + // configuration when the modem is reattached. + + // Send an OK and wait for the OK to be also transmitted to the remote peer. + OK_(); + atci_flush(); + + // Finally, detach the LPUART port from its GPIOs. This operation stops DMA + // and reconfigures LPUART GPIOs in analog input mode. + lpuart_detach(); + + // From this moment on, the modem cannot be woken up with ATCI activity. The + // host has to pull lpuart_attach_pin down to wake the modem up and make it + // reattach LPUART. Any incoming LoRaWAN downlinks will be buffered until + // the ATCI port is attached again. +} + + +static void attach_isr(void *ctx) +{ + (void)ctx; + lpuart_attach(); +} + + +void cmd_init_attach_pin(void) +{ + // Note: This function is mutually exclusive with init_dbgmcu (PB12 conflict). + + GPIO_InitTypeDef gpio = { + .Mode = GPIO_MODE_IT_FALLING, + .Pull = GPIO_PULLUP, + .Speed = GPIO_SPEED_HIGH, + }; + + if (attach_pin.port == GPIOA) __GPIOA_CLK_ENABLE(); + else if (attach_pin.port == GPIOB) __GPIOB_CLK_ENABLE(); + else if (attach_pin.port == GPIOC) __GPIOC_CLK_ENABLE(); + else if (attach_pin.port == GPIOD) __GPIOD_CLK_ENABLE(); + else if (attach_pin.port == GPIOE) __GPIOE_CLK_ENABLE(); + else if (attach_pin.port == GPIOH) __GPIOH_CLK_ENABLE(); + + gpio_init(attach_pin.port, attach_pin.pinIndex, &gpio); + gpio_set_irq(attach_pin.port, attach_pin.pinIndex, 0, attach_isr); +} + +#endif + + static const atci_command_t cmds[] = { - {"+UART", NULL, set_uart, get_uart, NULL, "Configure UART interface"}, - {"+VER", NULL, NULL, get_version_comp, NULL, "Firmware version and build time"}, - {"+DEV", NULL, NULL, get_model, NULL, "Device model"}, - {"+REBOOT", reboot, NULL, NULL, NULL, "Reboot the modem"}, - {"+FACNEW", facnew, NULL, NULL, NULL, "Restore modem to factory defaults"}, - {"+BAND", NULL, set_band, get_band, NULL, "Configure radio band (region)"}, - {"+CLASS", NULL, set_class, get_class, NULL, "Configure LoRaWAN class"}, - {"+MODE", NULL, set_mode, get_mode, NULL, "Configure activation mode (1:OTTA 0:ABP)"}, - {"+DEVADDR", NULL, set_devaddr, get_devaddr, NULL, "Configure DevAddr"}, - {"+DEVEUI", NULL, set_deveui, get_deveui, NULL, "Configure DevEUI"}, - {"+APPEUI", NULL, set_joineui, get_joineui, NULL, "Configure AppEUI (JoinEUI)"}, - {"+NWKSKEY", NULL, set_nwkskey, get_nwkskey, NULL, "Configure NwkSKey (LoRaWAN 1.0)"}, - {"+APPSKEY", NULL, set_appskey, get_appskey, NULL, "Configure AppSKey"}, - {"+APPKEY", NULL, set_appkey_10, get_appkey, NULL, "Configure AppKey (LoRaWAN 1.0)"}, - {"+JOIN", join, NULL, NULL, NULL, "Send OTAA Join packet"}, - {"+JOINDC", NULL, set_joindc, get_joindc, NULL, "Configure OTAA Join duty cycling"}, - {"+LNCHECK", lncheck, lncheck, NULL, NULL, "Perform link check"}, - {"+RFPARAM", NULL, set_rfparam, get_rfparam, NULL, "Configure RF channel parameters"}, - {"+RFPOWER", NULL, set_rfpower_comp, get_rfpower_comp, NULL, "Configure RF power"}, - {"+NWK", NULL, set_nwk, get_nwk, NULL, "Configure public/private LoRa network setting"}, - {"+ADR", NULL, set_adr, get_adr, NULL, "Configure adaptive data rate (ADR)"}, - {"+DR", NULL, set_dr_comp, get_dr_comp, NULL, "Configure data rate (DR)"}, - {"+DELAY", NULL, set_delay, get_delay, NULL, "Configure receive window offsets"}, - {"+ADRACK", NULL, set_adrack, get_adrack, NULL, "Configure ADR ACK parameters"}, - {"+RX2", NULL, set_rx2_comp, get_rx2_comp, NULL, "Configure RX2 window frequency and data rate"}, - {"+DUTYCYCLE", NULL, set_dutycycle, get_dutycycle, NULL, "Configure duty cycling in EU868"}, - {"+SLEEP", NULL, set_sleep, get_sleep, NULL, "Configure low power (sleep) mode"}, - {"+PORT", NULL, set_port, get_port, NULL, "Configure default port number for uplink messages <1,223>"}, - {"+REP", NULL, set_rep, get_rep, NULL, "Unconfirmed message repeats [1..15]"}, - {"+DFORMAT", NULL, set_dformat, get_dformat, NULL, "Configure payload format used by the modem"}, - {"+TO", NULL, set_to, get_to, NULL, "Configure UART port timeout"}, - {"+UTX", utx, NULL, NULL, NULL, "Send unconfirmed uplink message"}, - {"+CTX", ctx, NULL, NULL, NULL, "Send confirmed uplink message"}, - {"+MCAST", NULL, set_mcast, get_mcast, NULL, "Configure multicast addresses and keys"}, - {"+PUTX", putx, NULL, NULL, NULL, "Send unconfirmed uplink message to port"}, - {"+PCTX", pctx, NULL, NULL, NULL, "Send confirmed uplink message to port"}, - {"+FRMCNT", NULL, NULL, get_frmcnt, NULL, "Return current values for uplink and downlink counters"}, - {"+MSIZE", NULL, NULL, get_msize, NULL, "Return maximum payload size for current data rate"}, - {"+RFQ", NULL, NULL, get_rfq, NULL, "Return RSSI and SNR of the last received message"}, - {"+DWELL", NULL, set_dwell, get_dwell, NULL, "Configure dwell setting for AS923"}, - {"+MAXEIRP", NULL, set_maxeirp, get_maxeirp, NULL, "Configure maximum EIRP"}, - {"+RSSITH", NULL, set_rssith, get_rssith, NULL, "Configure RSSI threshold for LBT"}, - {"+CST", NULL, set_cst, get_cst, NULL, "Configure carrier sensor time (CST) for LBT"}, - {"+BACKOFF", NULL, NULL, get_backoff, NULL, "Return duty cycle backoff time for EU868"}, - {"+CHMASK", NULL, set_chmask_comp, get_chmask_comp, NULL, "Configure channel mask"}, - {"+RTYNUM", NULL, set_rtynum, get_rtynum, NULL, "Configure number of confirmed uplink message retries"}, - {"+NETID", NULL, set_netid, get_netid, NULL, "Configure LoRaWAN network identifier"}, - {"$VER", NULL, NULL, get_version, NULL, "Firmware version and build time"}, -#if defined(DEBUG) - {"$DBG", dbg, NULL, NULL, NULL, ""}, + {"+UART", NULL, set_uart, get_uart, NULL, "Configure UART interface"}, + {"+VER", NULL, NULL, get_version_comp, NULL, "Firmware version and build time"}, + {"+DEV", NULL, NULL, get_model, NULL, "Device model"}, + {"+REBOOT", reboot, NULL, NULL, NULL, "Reboot the modem"}, + {"+FACNEW", facnew, NULL, NULL, NULL, "Restore modem to factory defaults"}, + {"+BAND", NULL, set_band, get_band, NULL, "Configure radio band (region)"}, + {"+CLASS", NULL, set_class, get_class, NULL, "Configure LoRaWAN class"}, + {"+MODE", NULL, set_mode, get_mode, NULL, "Configure activation mode (1:OTTA 0:ABP)"}, + {"+DEVADDR", NULL, set_devaddr, get_devaddr, NULL, "Configure DevAddr"}, + {"+DEVEUI", NULL, set_deveui, get_deveui, NULL, "Configure DevEUI"}, + {"+APPEUI", NULL, set_joineui, get_joineui, NULL, "Configure AppEUI (JoinEUI)"}, + {"+NWKSKEY", NULL, set_nwkskey, get_nwkskey, NULL, "Configure NwkSKey (LoRaWAN 1.0)"}, + {"+APPSKEY", NULL, set_appskey, get_appskey, NULL, "Configure AppSKey"}, + {"+APPKEY", NULL, set_appkey_10, get_appkey, NULL, "Configure AppKey (LoRaWAN 1.0)"}, + {"+JOIN", join, NULL, NULL, NULL, "Send OTAA Join packet"}, + {"+JOINDC", NULL, set_joindc, get_joindc, NULL, "Configure OTAA Join duty cycling"}, + {"+LNCHECK", lncheck, lncheck, NULL, NULL, "Perform link check"}, + {"+RFPARAM", NULL, set_rfparam, get_rfparam, NULL, "Configure RF channel parameters"}, + {"+RFPOWER", NULL, set_rfpower_comp, get_rfpower_comp, NULL, "Configure RF power"}, + {"+NWK", NULL, set_nwk, get_nwk, NULL, "Configure public/private LoRa network setting"}, + {"+ADR", NULL, set_adr, get_adr, NULL, "Configure adaptive data rate (ADR)"}, + {"+DR", NULL, set_dr_comp, get_dr_comp, NULL, "Configure data rate (DR)"}, + {"+DELAY", NULL, set_delay, get_delay, NULL, "Configure receive window offsets"}, + {"+ADRACK", NULL, set_adrack, get_adrack, NULL, "Configure ADR ACK parameters"}, + {"+RX2", NULL, set_rx2_comp, get_rx2_comp, NULL, "Configure RX2 window frequency and data rate"}, + {"+DUTYCYCLE", NULL, set_dutycycle, get_dutycycle, NULL, "Configure duty cycling in EU868"}, + {"+SLEEP", NULL, set_sleep, get_sleep, NULL, "Configure low power (sleep) mode"}, + {"+PORT", NULL, set_port, get_port, NULL, "Configure default port number for uplink messages <1,223>"}, + {"+REP", NULL, set_rep, get_rep, NULL, "Unconfirmed message repeats [1..15]"}, + {"+DFORMAT", NULL, set_dformat, get_dformat, NULL, "Configure payload format used by the modem"}, + {"+TO", NULL, set_to, get_to, NULL, "Configure UART port timeout"}, + {"+UTX", utx, NULL, NULL, NULL, "Send unconfirmed uplink message"}, + {"+CTX", ctx, NULL, NULL, NULL, "Send confirmed uplink message"}, + {"+MCAST", NULL, set_mcast, get_mcast, NULL, "Configure multicast addresses and keys"}, + {"+PUTX", putx, NULL, NULL, NULL, "Send unconfirmed uplink message to port"}, + {"+PCTX", pctx, NULL, NULL, NULL, "Send confirmed uplink message to port"}, + {"+FRMCNT", NULL, NULL, get_frmcnt, NULL, "Return current values for uplink and downlink counters"}, + {"+MSIZE", NULL, NULL, get_msize, NULL, "Return maximum payload size for current data rate"}, + {"+RFQ", NULL, NULL, get_rfq, NULL, "Return RSSI and SNR of the last received message"}, + {"+DWELL", NULL, set_dwell, get_dwell, NULL, "Configure dwell setting for AS923"}, + {"+MAXEIRP", NULL, set_maxeirp, get_maxeirp, NULL, "Configure maximum EIRP"}, + {"+RSSITH", NULL, set_rssith, get_rssith, NULL, "Configure RSSI threshold for LBT"}, + {"+CST", NULL, set_cst, get_cst, NULL, "Configure carrier sensor time (CST) for LBT"}, + {"+BACKOFF", NULL, NULL, get_backoff, NULL, "Return duty cycle backoff time for EU868"}, + {"+CHMASK", NULL, set_chmask_comp, get_chmask_comp, NULL, "Configure channel mask"}, + {"+RTYNUM", NULL, set_rtynum, get_rtynum, NULL, "Configure number of confirmed uplink message retries"}, + {"+NETID", NULL, set_netid, get_netid, NULL, "Configure LoRaWAN network identifier"}, + {"$VER", NULL, NULL, get_version, NULL, "Firmware version and build time"}, +#if DEBUG_LOG != 0 + {"$DBG", dbg, NULL, NULL, NULL, ""}, #endif - {"$HALT", do_halt, NULL, NULL, NULL, "Halt the modem"}, - {"$JOINEUI", NULL, set_joineui, get_joineui, NULL, "Configure JoinEUI"}, - {"$NWKKEY", NULL, set_nwkkey, get_nwkkey, NULL, "Configure NwkKey (LoRaWAN 1.1)"}, - {"$APPKEY", NULL, set_appkey_11, get_appkey, NULL, "Configure AppKey (LoRaWAN 1.1)"}, - {"$FNWKSINTKEY", NULL, set_fnwksintkey, get_fnwksintkey, NULL, "Configure FNwkSIntKey (LoRaWAN 1.1)"}, - {"$SNWKSINTKEY", NULL, set_snwksintkey, get_snwksintkey, NULL, "Configure SNwkSIntKey (LoRaWAN 1.1)"}, - {"$NWKSENCKEY", NULL, set_nwksenckey, get_nwksenckey, NULL, "Configure NwkSEncKey (LoRaWAN 1.1)"}, - {"$CHMASK", NULL, set_chmask, get_chmask, NULL, "Configure channel mask"}, - {"$RX2", NULL, set_rx2, get_rx2, NULL, "Configure RX2 window frequency and data rate"}, - {"$DR", NULL, set_dr, get_dr, NULL, "Configure data rate (DR)"}, - {"$RFPOWER", NULL, set_rfpower, get_rfpower, NULL, "Configure RF power"}, -#if defined(DEBUG) - {"$LOGLEVEL", NULL, set_loglevel, get_loglevel, NULL, "Configure logging on USART port"}, + {"$HALT", do_halt, NULL, NULL, NULL, "Halt the modem"}, + {"$JOINEUI", NULL, set_joineui, get_joineui, NULL, "Configure JoinEUI"}, + {"$NWKKEY", NULL, set_nwkkey, get_nwkkey, NULL, "Configure NwkKey (LoRaWAN 1.1)"}, + {"$APPKEY", NULL, set_appkey_11, get_appkey, NULL, "Configure AppKey (LoRaWAN 1.1)"}, + {"$FNWKSINTKEY", NULL, set_fnwksintkey, get_fnwksintkey, NULL, "Configure FNwkSIntKey (LoRaWAN 1.1)"}, + {"$SNWKSINTKEY", NULL, set_snwksintkey, get_snwksintkey, NULL, "Configure SNwkSIntKey (LoRaWAN 1.1)"}, + {"$NWKSENCKEY", NULL, set_nwksenckey, get_nwksenckey, NULL, "Configure NwkSEncKey (LoRaWAN 1.1)"}, + {"$CHMASK", NULL, set_chmask, get_chmask, NULL, "Configure channel mask"}, + {"$RX2", NULL, set_rx2, get_rx2, NULL, "Configure RX2 window frequency and data rate"}, + {"$DR", NULL, set_dr, get_dr, NULL, "Configure data rate (DR)"}, + {"$RFPOWER", NULL, set_rfpower, get_rfpower, NULL, "Configure RF power"}, +#if DEBUG_LOG != 0 + {"$LOGLEVEL", NULL, set_loglevel, get_loglevel, NULL, "Configure logging on USART port"}, +#endif + {"$CERT", NULL, set_cert, get_cert, NULL, "Enable or disable LoRaWAN certification port"}, + {"$SESSION", NULL, NULL, get_session, NULL, "Get network session information"}, + {"$CW", cw, NULL, NULL, NULL, "Start continuous carrier wave transmission"}, + {"$CM", cm, NULL, NULL, NULL, "Start continuous modulated FSK transmission"}, + {"$NVM", nvm_userdata, NULL, NULL, NULL, "Manage data in NVM user registers"}, + {"$LOCKKEYS", lock_keys, NULL, NULL, NULL, "Prevent read access to security keys from ATCI"}, +#if DETACHABLE_LPUART == 1 + {"$DETACH", detach_lpuart, NULL, NULL, NULL, "Disconnect LPUART (ATCI) GPIOs"}, #endif - {"$CERT", NULL, set_cert, get_cert, NULL, "Enable or disable LoRaWAN certification port"}, - {"$SESSION", NULL, NULL, get_session, NULL, "Get network session information"}, - {"$CW", cw, NULL, NULL, NULL, "Start continuous carrier wave transmission"}, - {"$CM", cm, NULL, NULL, NULL, "Start continuous modulated FSK transmission"}, - {"$NVM", nvm_userdata, NULL, NULL, NULL, "Manage data in NVM user registers"}, - {"$LOCKKEYS", lock_keys, NULL, NULL, NULL, "Prevent read access to security keys from ATCI"}, ATCI_COMMAND_CLAC, ATCI_COMMAND_HELP}; diff --git a/src/cmd.h b/src/cmd.h index fb0a514..d560861 100644 --- a/src/cmd.h +++ b/src/cmd.h @@ -47,6 +47,10 @@ void cmd_event(unsigned int type, unsigned subtype); void cmd_ans(unsigned int margin, unsigned int gwcnt); +#if DETACHABLE_LPUART == 1 +void cmd_init_attach_pin(void); +#endif + #define cmd_process atci_process #define cmd_print atci_print #define cmd_printf atci_printf diff --git a/src/debug/log.c b/src/debug/log.c index c11327c..8f0d300 100644 --- a/src/debug/log.c +++ b/src/debug/log.c @@ -39,11 +39,9 @@ void _log_init(log_level_t level, log_timestamp_t timestamp) _log.initialized = true; _log.state = LOG_STATE_SIMPLE_MSG; -#if LOG_TO_USART != 0 +#if DEBUG_LOG != 3 usart_init(); -#endif - -#if LOG_TO_RTT != 0 +#else SEGGER_RTT_Init(); #endif } @@ -63,11 +61,9 @@ void _log_set_level(log_level_t level) static void _write(const char *buf, size_t len) { -#if LOG_TO_USART != 0 +#if DEBUG_LOG != 3 usart_write(buf, len); -#endif - -#if LOG_TO_RTT != 0 +#else SEGGER_RTT_Write(0, buf, len); #endif } @@ -75,7 +71,7 @@ static void _write(const char *buf, size_t len) static void _write_message(log_level_t level, char id, const char *format, va_list ap) { -#if LOG_TO_USART == 0 && LOG_TO_RTT == 0 +#if DEBUG_LOG == 0 return; #endif @@ -131,7 +127,7 @@ void _log_dump(const void *buffer, size_t length, const char *format, ...) uint32_t line_size; uint32_t i; -#if LOG_TO_USART == 0 && LOG_TO_RTT == 0 +#if DEBUG_LOG == 0 return; #endif diff --git a/src/debug/log.h b/src/debug/log.h index 85a37ef..16728cb 100644 --- a/src/debug/log.h +++ b/src/debug/log.h @@ -7,14 +7,6 @@ #define LOG_BUFFER_SIZE 256 #endif -#ifndef LOG_TO_USART -#define LOG_TO_USART 1 -#endif - -#ifndef LOG_TO_RTT -#define LOG_TO_RTT 0 -#endif - #define LOG_DUMP_WIDTH 8 //! @brief Log level @@ -60,7 +52,7 @@ typedef enum //! @param[in] level Minimum required message level for propagation //! @param[in] timestamp Timestamp logging setting -#if defined(DEBUG) +#if DEBUG_LOG != 0 void _log_init(log_level_t level, log_timestamp_t timestamp); diff --git a/src/debug/usart.c b/src/debug/usart.c index c8ed8a8..b372f34 100644 --- a/src/debug/usart.c +++ b/src/debug/usart.c @@ -6,25 +6,22 @@ #include "system.h" #include "halt.h" +#if DEBUG_LOG != 3 -#if !defined(DEBUG_PORT) -# error DEBUG_PORT is not defined -#endif - -#if DEBUG_PORT == 1 +#if DEBUG_LOG == 1 # define PORT USART1 # define IRQn USART1_IRQn # define CLK_ENABLE __USART1_CLK_ENABLE # define PIN GPIO_PIN_9 # define ALTERNATE GPIO_AF4_USART1 -#elif DEBUG_PORT == 2 +#elif DEBUG_LOG == 2 # define PORT USART2 # define IRQn USART2_IRQn # define CLK_ENABLE __USART2_CLK_ENABLE # define PIN GPIO_PIN_2 # define ALTERNATE GPIO_AF4_USART2 #else -# error Unsupported DEBUG_PORT value +# error Unsupported DEBUG_LOG value #endif @@ -113,12 +110,12 @@ size_t usart_write(const char *buffer, size_t length) } -#if DEBUG_PORT == 1 +#if DEBUG_LOG == 1 void USART1_IRQHandler(void) -#elif DEBUG_PORT == 2 +#elif DEBUG_LOG == 2 void USART2_IRQHandler(void) #else -#error Unsupport DEBUG_PORT +#error Unsupport DEBUG_LOG #endif { uint8_t c; @@ -136,3 +133,5 @@ void USART2_IRQHandler(void) system_stop_lock &= ~SYSTEM_MODULE_USART; } } + +#endif \ No newline at end of file diff --git a/src/debug/usart.h b/src/debug/usart.h index b99204c..dbef7bb 100644 --- a/src/debug/usart.h +++ b/src/debug/usart.h @@ -1,5 +1,6 @@ #ifndef __USART_H__ #define __USART_H__ +#if DEBUG_LOG != 3 #include @@ -14,4 +15,5 @@ void usart_init(void); size_t usart_write(const char *buffer, size_t length); +#endif #endif /* __USART_H__ */ diff --git a/src/halt.c b/src/halt.c index 8144389..c83d98e 100644 --- a/src/halt.c +++ b/src/halt.c @@ -9,7 +9,7 @@ __attribute__((noreturn)) void halt(const char *msg) { -#if defined(DEBUG) +#if DEBUG_LOG != 0 const char prefix[] = "Halted"; #endif cmd_event(CMD_EVENT_MODULE, CMD_MODULE_HALT); diff --git a/src/lpuart.c b/src/lpuart.c index 8ceb335..7ad5e55 100644 --- a/src/lpuart.c +++ b/src/lpuart.c @@ -8,7 +8,7 @@ #include "log.h" #include "irq.h" #include "system.h" - +#include "cmd.h" #ifndef LPUART_BUFFER_SIZE #define LPUART_BUFFER_SIZE 512 @@ -31,6 +31,13 @@ static unsigned char rx_buffer[LPUART_BUFFER_SIZE]; volatile cbuf_t lpuart_rx_fifo; +#if DETACHABLE_LPUART == 1 + +static bool volatile attached; + +#endif // DETACHABLE_LPUART + + // This function is invoked from the IRQ handler context static void enqueue(unsigned char *data, size_t len) { @@ -64,6 +71,9 @@ void lpuart_init(unsigned int baudrate) cbuf_init(&lpuart_tx_fifo, tx_buffer, sizeof(tx_buffer)); cbuf_init(&lpuart_rx_fifo, rx_buffer, sizeof(rx_buffer)); tx_idle = 1; +#if DETACHABLE_LPUART == 1 + attached = true; +#endif uint32_t masked = disable_irq(); @@ -133,6 +143,7 @@ static void init_gpio(void) { GPIO_InitTypeDef gpio = { .Mode = GPIO_MODE_AF_PP, + .Alternate = GPIO_AF6_LPUART1, .Speed = GPIO_SPEED_HIGH }; @@ -140,12 +151,10 @@ static void init_gpio(void) __HAL_RCC_GPIOA_CLK_ENABLE(); gpio.Pin = GPIO_PIN_2; - gpio.Alternate = GPIO_AF6_LPUART1; gpio.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &gpio); gpio.Pin = GPIO_PIN_3; - gpio.Alternate = GPIO_AF6_LPUART1; gpio.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &gpio); } @@ -158,6 +167,7 @@ static void deinit_gpio(void) .Pull = GPIO_NOPULL }; + __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); gpio.Pin = GPIO_PIN_2; @@ -255,6 +265,31 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef *port) } +static void flush_tx_fifo(void) +{ + cbuf_view_t v; + + if (lpuart_tx_fifo.length > 0) { + // We need to reacreate the sleep lock here even when tx_idle is false + // in case this function is invoked after reattaching the port. + system_stop_lock |= SYSTEM_MODULE_LPUART_TX; + + if (tx_idle) { + tx_idle = 0; + + cbuf_head(&lpuart_tx_fifo, &v); + if (v.len[0]) { + HAL_UART_Transmit_DMA(&port, (unsigned char *)v.ptr[0], v.len[0]); + tx_len = v.len[0]; + } else { + HAL_UART_Transmit_DMA(&port, (unsigned char *)v.ptr[1], v.len[1]); + tx_len = v.len[1]; + } + } + } +} + + size_t lpuart_write(const char *buffer, size_t length) { uint32_t masked = disable_irq(); @@ -268,20 +303,10 @@ size_t lpuart_write(const char *buffer, size_t length) masked = disable_irq(); cbuf_produce(&lpuart_tx_fifo, written); - if (tx_idle && lpuart_tx_fifo.length > 0) { - tx_idle = 0; - system_stop_lock |= SYSTEM_MODULE_LPUART_TX; - - cbuf_head(&lpuart_tx_fifo, &v); - if (v.len[0]) { - HAL_UART_Transmit_DMA(&port, (unsigned char *)v.ptr[0], v.len[0]); - tx_len = v.len[0]; - } else { - HAL_UART_Transmit_DMA(&port, (unsigned char *)v.ptr[1], v.len[1]); - tx_len = v.len[1]; - } - } - +#if DETACHABLE_LPUART == 1 + if (attached) +#endif + flush_tx_fifo(); reenable_irq(masked); return written; } @@ -302,7 +327,7 @@ void lpuart_write_blocking(const char *buffer, size_t length) // If the TX FIFO is at full capacity, we invoke system_idle to // put the MCU to sleep until there is some space in the output // FIFO which will be signalled by the ISR when the DMA transfer - // finishes. Since transmission happens via DMA, system_idle + // finishes. Since the transmission happens via DMA, system_idle // used below must not enter the Stop mode. That is, however, // guaranteed, since the function luart_write above creates a // stop mode wake lock which will still be in place when the @@ -320,21 +345,31 @@ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *port) { cbuf_view_t v; - if (tx_len) cbuf_consume(&lpuart_tx_fifo, tx_len); + if (tx_len) { + cbuf_consume(&lpuart_tx_fifo, tx_len); + tx_len = 0; + } - if (lpuart_tx_fifo.length) { - cbuf_head(&lpuart_tx_fifo, &v); - if (v.len[0]) { - HAL_UART_Transmit_DMA(port, (unsigned char *)v.ptr[0], v.len[0]); - tx_len = v.len[0]; - } else { - HAL_UART_Transmit_DMA(port, (unsigned char *)v.ptr[1], v.len[1]); - tx_len = v.len[1]; - } - } else { + if (!lpuart_tx_fifo.length) { system_stop_lock &= ~SYSTEM_MODULE_LPUART_TX; - tx_len = 0; tx_idle = 1; + return; + } + +#if DETACHABLE_LPUART == 1 + if (!attached) { + system_stop_lock &= ~SYSTEM_MODULE_LPUART_TX; + return; + } +#endif + + cbuf_head(&lpuart_tx_fifo, &v); + if (v.len[0]) { + HAL_UART_Transmit_DMA(port, (unsigned char *)v.ptr[0], v.len[0]); + tx_len = v.len[0]; + } else { + HAL_UART_Transmit_DMA(port, (unsigned char *)v.ptr[1], v.len[1]); + tx_len = v.len[1]; } } @@ -402,22 +437,73 @@ void DMA1_Channel4_5_6_7_IRQHandler(void) } +static void pause_rx_dma(void) +{ + if ((HAL_IS_BIT_SET(port.Instance->CR3, USART_CR3_DMAR)) && + (port.RxState == HAL_UART_STATE_BUSY_RX)) { + CLEAR_BIT(port.Instance->CR3, USART_CR3_DMAR); + } +} + + +static void pause_tx_dma(void) +{ + if ((HAL_IS_BIT_SET(port.Instance->CR3, USART_CR3_DMAT)) && + (port.gState == HAL_UART_STATE_BUSY_TX)) { + CLEAR_BIT(port.Instance->CR3, USART_CR3_DMAT); + } +} + + +static void pause_dma(void) +{ + pause_rx_dma(); + pause_tx_dma(); +} + + void lpuart_before_stop(void) { + pause_dma(); HAL_UART_DMAPause(&port); LL_LPUART_EnableIT_WKUP(port.Instance); } +static void resume_tx_dma(void) +{ + if (port.gState == HAL_UART_STATE_BUSY_TX) { + SET_BIT(port.Instance->CR3, USART_CR3_DMAT); + } +} + + +static void resume_rx_dma(void) +{ + if (port.RxState == HAL_UART_STATE_BUSY_RX) { + /* Clear the Overrun flag before resuming the Rx transfer */ + __HAL_UART_CLEAR_FLAG(&port, UART_CLEAR_OREF); + + /* Enable the UART DMA Rx request */ + SET_BIT(port.Instance->CR3, USART_CR3_DMAR); + } +} + + void lpuart_after_stop(void) { LL_LPUART_DisableIT_WKUP(port.Instance); - HAL_UART_DMAResume(&port); - // Resuming DMA re-enables the LPUART1 error interrupt, so we need to - // disable it here again. We ignore all errors on LPUART1 and let upper - // layers (ATCI) deal with it. - LL_LPUART_DisableIT_ERROR(port.Instance); + // We cannot use HAL_UART_DMAResume provided by the STM HAL here. That + // function resumes both RX and TX DMA transfers and additionally re-enables + // error interrupts. We need to resume the TX DMA here if and only if the + // port is attached. Resuming a TX DMA while the port is detached from GPIO + // would result in lost data. + resume_rx_dma(); +#if DETACHABLE_LPUART == 1 + if (attached) +#endif + resume_tx_dma(); } @@ -458,4 +544,31 @@ void HAL_UART_ErrorCallback(UART_HandleTypeDef *port) { (void)port; log_error("LPUART1 error: %ld", port->ErrorCode); -} \ No newline at end of file +} + + +#if DETACHABLE_LPUART == 1 + +void lpuart_detach(void) +{ + if (!attached) return; + pause_tx_dma(); + deinit_gpio(); + attached = false; +} + + +void lpuart_attach(void) +{ + + if (attached) return; + init_gpio(); + resume_tx_dma(); + + uint32_t masked = disable_irq(); + flush_tx_fifo(); + reenable_irq(masked); + attached = true; +} + +#endif // DETACHABLE_LPUART diff --git a/src/lpuart.h b/src/lpuart.h index 53c89ea..b1a5f33 100644 --- a/src/lpuart.h +++ b/src/lpuart.h @@ -2,13 +2,42 @@ #define __LPUART_H__ #include +#include #include "cbuf.h" +#include "gpio.h" extern volatile cbuf_t lpuart_tx_fifo; extern volatile cbuf_t lpuart_rx_fifo; +#if DETACHABLE_LPUART == 1 +/*! @brief Detach from the ATCI LPUART port + * + * Detach (disconnect) from the LPUART port used by the AT command interface. + * Detaching pauses active DMA transfer (if any) and reconfigures the GPIO ports + * used by the LPUART port in analog mode. + * + * This function is intended for use on boards that share the LPUART lines with + * some other peripheral. This is the case, e.g., on MKRWAN boards. Forcing the + * modem to detach allows the host to temporarily use the lines to communicate + * with the other peripheral. + */ +void lpuart_detach(void); + +/*! @brief Attach the ATCI LPUART port + * + * Attach to the LPUART port used by the AT command interface. Calling this + * function reconfigures the GPIO ports used by LPUART1 and if there is an + * active DMA transfer, it is resumed. + * + * This function is intended to restore ATCI functionality after the modem has + * detached from LPUART1 (used by the ATCI). The function should be invoked + * after an interrupt on a preconfigured GPIO pin or timeout. + */ +void lpuart_attach(void); + +#endif /*! @brief Initialize LPUART1 * @@ -96,5 +125,4 @@ void lpuart_before_stop(void); */ void lpuart_after_stop(void); - #endif /* __LPUART_H__ */ diff --git a/src/lrw.c b/src/lrw.c index 4a279cb..b3256bb 100644 --- a/src/lrw.c +++ b/src/lrw.c @@ -59,7 +59,7 @@ static struct { }; -#ifdef RESTORE_CHMASK_AFTER_JOIN +#if RESTORE_CHMASK_AFTER_JOIN == 1 static uint16_t saved_chmask[REGION_NVM_CHANNELS_MASK_SIZE]; #endif @@ -73,7 +73,7 @@ static int region2id(const char *name) return -2; } -#if defined(DEBUG) +#if DEBUG_LOG != 0 static const char *region2str(int id) { for (unsigned int i = 0; i < sizeof(region_map) / sizeof(region_map[0]); i++) @@ -396,7 +396,7 @@ static int set_abp_mac_version(void) #endif -#ifdef RESTORE_CHMASK_AFTER_JOIN +#if RESTORE_CHMASK_AFTER_JOIN == 1 static void save_chmask(void) { MibRequestConfirm_t r = { .Type = MIB_CHANNELS_DEFAULT_MASK }; @@ -466,7 +466,7 @@ static void stop_join(unsigned int status) // sysconf.device_class here. sync_device_class(); -#ifdef RESTORE_CHMASK_AFTER_JOIN +#if RESTORE_CHMASK_AFTER_JOIN == 1 MibRequestConfirm_t r = { .Type = MIB_NETWORK_ACTIVATION }; LoRaMacMibGetRequestConfirm(&r); @@ -929,7 +929,7 @@ int lrw_join(uint8_t datarate, uint8_t tries) join_datarate = datarate; -#ifdef RESTORE_CHMASK_AFTER_JOIN +#if RESTORE_CHMASK_AFTER_JOIN == 1 save_chmask(); #endif LoRaMacStatus_t rc = send_join(); diff --git a/src/radio.c b/src/radio.c index 844707c..102d8d4 100644 --- a/src/radio.c +++ b/src/radio.c @@ -10,7 +10,7 @@ int8_t radio_snr; // The original callback (the one from LoRaMac-node) is kept here. static void (*OrigRxDone)(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr); -#if defined (DEBUG) +#if DEBUG_LOG != 0 static const char *modem2str(RadioModems_t modem) { @@ -81,7 +81,7 @@ static void SetTxConfig(RadioModems_t modem, int8_t power, uint32_t fdev, uint16_t preambleLen, bool fixLen, bool crcOn, bool freqHopOn, uint8_t hopPeriod, bool iqInverted, uint32_t timeout) { -#if defined (DEBUG) +#if DEBUG_LOG != 0 log_compose(); log_debug("SX1276SetTxConfig: %d dBm", power); log_debug(" %s", modem2str(modem)); @@ -116,7 +116,7 @@ static void SetRxConfig(RadioModems_t modem, uint32_t bandwidth, uint32_t datara uint16_t symbTimeout, bool fixLen, uint8_t payloadLen, bool crcOn, bool freqHopOn, uint8_t hopPeriod, bool iqInverted, bool rxContinuous) { -#if defined (DEBUG) +#if DEBUG_LOG != 0 log_compose(); log_debug("SX1276SetRxConfig: %s", modem2str(modem)); diff --git a/src/system.c b/src/system.c index 8d3aa73..9798397 100644 --- a/src/system.c +++ b/src/system.c @@ -8,6 +8,7 @@ #include "gpio.h" #include "nvm.h" #include "lrw.h" +#include "cmd.h" // Unique Devices IDs register set ( STM32L0xxx ) @@ -158,21 +159,23 @@ static void init_gpio(void) } -#if defined(DEBUG) +#if DEBUG_MCU == 1 + static void init_dbgmcu(void) { - // Note: This function is mutually-exclusive with init_facnew_gpio (they - // share the same GPIO pin). + // Note: This function is mutually exclusive with the factory reset pin + // feature (conflict on PB15) and with the detachable LPUART1 feature + // (conflict on PB12). // Enable the GPIO B clock __GPIOB_CLK_ENABLE(); // Configure debugging GPIO pins GPIO_InitTypeDef gpio = { - .Mode = GPIO_MODE_OUTPUT_PP, - .Pull = GPIO_PULLUP, + .Mode = GPIO_MODE_OUTPUT_PP, + .Pull = GPIO_PULLUP, .Speed = GPIO_SPEED_HIGH, - .Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15 + .Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15 }; HAL_GPIO_Init(GPIOB, &gpio); @@ -187,16 +190,14 @@ static void init_dbgmcu(void) HAL_DBGMCU_EnableDBGStopMode(); HAL_DBGMCU_EnableDBGStandbyMode(); } -#endif +#endif // DEBUG_MCU + + +#if DEBUG_SWD == 0 -#if defined(RELEASE) static void disable_swd(void) { - // init_gpio called before this function does not touch GPIO A 13 & 14 (SWD) - // to keep the SWD port operational. In release mode, we re-configure the - // two pins in analog mode to minimize power consumption. - GPIO_InitTypeDef gpio = { .Mode = GPIO_MODE_ANALOG, .Pull = GPIO_NOPULL, @@ -214,8 +215,10 @@ static void disable_swd(void) __DBGMCU_CLK_DISABLE(); } +#endif // DEBUG_SWD + +#if FACTORY_RESET_PIN == 1 -#if defined(ENABLE_FACTORY_RESET_PIN) static Gpio_t facnew_pin = { .port = GPIOB, .pinIndex = GPIO_PIN_15 @@ -249,10 +252,9 @@ static void facnew_isr(void *ctx) static void init_facnew_gpio(void) { - // Note: This function is mutually-exclusive with init_dbgmcu (they share - // the same GPIO pin). + // Note: This function is mutually exclusive with init_dbgmcu (PB15 conflict). - __GPIOA_CLK_ENABLE(); + __GPIOB_CLK_ENABLE(); GPIO_InitTypeDef gpio = { .Mode = GPIO_MODE_IT_RISING_FALLING, .Pull = GPIO_PULLUP, @@ -261,8 +263,8 @@ static void init_facnew_gpio(void) gpio_init(facnew_pin.port, facnew_pin.pinIndex, &gpio); gpio_set_irq(facnew_pin.port, facnew_pin.pinIndex, 0, facnew_isr); } -#endif // ENABLE_FACTORY_RESET_PIN -#endif // RELEASE + +#endif // FACTORY_RESET_PIN static void init_clock(void) @@ -325,13 +327,19 @@ void system_init(void) HAL_Init(); init_flash(); init_gpio(); -#if defined(RELEASE) +#if DEBUG_SWD == 0 + // init_gpio does not touch PA13 & PA14 (SWD) to keep the SWD port + // operational. If the SWD feature is disabled (e.g., in release mode), + // reconfigure the pins in analog mode to minimize power consumption. disable_swd(); -#if defined(ENABLE_FACTORY_RESET_PIN) +#endif +#if FACTORY_RESET_PIN == 1 init_facnew_gpio(); #endif +#if DETACHABLE_LPUART == 1 + cmd_init_attach_pin(); #endif -#if defined(DEBUG) +#if DEBUG_MCU == 1 init_dbgmcu(); #endif init_clock(); diff --git a/tools/release.sh b/tools/release.sh index 5aaef43..82b7535 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -113,10 +113,11 @@ $tar --exclude .editorconfig \ echo "done." # Build both release and debug versions of the firmware binary. This is the -# default build variant that uses PA12 (as recommended in the datasheet) to -# control TCXO_VDD. Debug builds have a debugging logger on USART1. -make TCXO_PIN=1 release -make TCXO_PIN=1 DEBUG_PORT=1 debug +# default build variant for the Hardwario LoRa modem that uses PA12 (as +# recommended in the datasheet) to control TCXO_VDD, has the factory reset +# pin disabled, and does not support LPUART1 detaching. +make FACTORY_RESET_PIN=0 TCXO_PIN=1 DETACHABLE_LPUART=0 release +make FACTORY_RESET_PIN=0 TCXO_PIN=1 DETACHABLE_LPUART=0 debug # And copy the resulting binary files into the firmware release directory. cp -f out/release/firmware.bin "$firmware_dir/$name.bin" @@ -126,10 +127,12 @@ cp -f out/debug/firmware.hex "$firmware_dir/$name.debug.hex" cp -f out/debug/firmware.map "$firmware_dir/$name.debug.map" # Now build the variants for Arduino MKRWAN boards. These build variants use PB6 -# to control TCXO power. Debug builds start the debugging logger on USART2. +# to control TCXO power. We also enable support for detaching the ATCI UART port +# so that the host MCU can access the on-board SPI flash. Debug builds start the +# debugging logger on USART2. make clean -make TCXO_PIN=2 release -make TCXO_PIN=2 DEBUG_PORT=2 debug +make FACTORY_RESET_PIN=0 TCXO_PIN=2 DETACHABLE_LPUART=1 release +make FACTORY_RESET_PIN=0 TCXO_PIN=2 DETACHABLE_LPUART=1 DEBUG_LOG=2 DEBUG_MCU=0 debug # And copy the resulting binary files to the firmware release directory. cp -f out/release/firmware.bin "$firmware_dir/$name.mkrwan.bin"