From cbc3c80e70249aa4017942f21a5531230cf2645f Mon Sep 17 00:00:00 2001 From: Jan Janak Date: Wed, 8 Mar 2023 22:01:11 -0500 Subject: [PATCH] Implement LPUART1 detaching This feature adds support for detaching the LPUART1 port (the AT command interface port) from its GPIO. This is useful on MKRWAN1310 boards where it can be used to give the host MCU access to the on-board flash which shares lines with the GPIOs used by the LPUART1 port on the Murata modem. This feature is based on a contribution from @disk91. Closes #119 --- .vscode/c_cpp_properties.json | 17 ++- Makefile | 152 +++++++++++++++------- src/cmd.c | 230 +++++++++++++++++++++++----------- src/cmd.h | 4 + src/debug/log.c | 16 +-- src/debug/log.h | 10 +- src/debug/usart.c | 19 ++- src/debug/usart.h | 2 + src/halt.c | 2 +- src/lpuart.c | 185 +++++++++++++++++++++------ src/lpuart.h | 30 ++++- src/lrw.c | 10 +- src/radio.c | 6 +- src/system.c | 50 ++++---- tools/release.sh | 17 +-- 15 files changed, 525 insertions(+), 225 deletions(-) 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"