diff --git a/Makefile b/Makefile index 323a702c..43d67d61 100644 --- a/Makefile +++ b/Makefile @@ -114,20 +114,35 @@ ifeq ($(MCU_SUB_VARIANT),nrf52) DFU_DEV_REV = 0xADAF CFLAGS += -DNRF52 -DNRF52832_XXAA -DS132 DFU_APP_DATA_RESERVED=7*4096 + BOARD_USE_USB = 0 + BOARD_USE_UART = 1 else ifeq ($(MCU_SUB_VARIANT),nrf52833) SD_NAME = s140 DFU_DEV_REV = 52833 CFLAGS += -DNRF52833_XXAA -DS140 DFU_APP_DATA_RESERVED=7*4096 + BOARD_USE_USB = 1 else ifeq ($(MCU_SUB_VARIANT),nrf52840) SD_NAME = s140 DFU_DEV_REV = 52840 CFLAGS += -DNRF52840_XXAA -DS140 DFU_APP_DATA_RESERVED=10*4096 + BOARD_USE_USB = 1 else $(error Sub Variant $(MCU_SUB_VARIANT) is unknown) endif +DFU_EXTERNAL_FLASH ?= 0 +CFLAGS += -DDFU_EXTERNAL_FLASH=$(DFU_EXTERNAL_FLASH) + +DFU_PIN_ACTIVATION ?= 0 +ifeq ($(DFU_PIN_ACTIVATION),1) + BOARD_USE_UART = 1 +endif + +BOARD_USE_UART ?= 0 +CFLAGS += -DBOARD_USE_UART=$(BOARD_USE_UART) + #------------------------------------------------------------------------------ # SOURCE FILES #------------------------------------------------------------------------------ @@ -145,6 +160,11 @@ C_SRC += src/boards/boards.c # nrfx C_SRC += $(NRFX_PATH)/drivers/src/nrfx_power.c C_SRC += $(NRFX_PATH)/drivers/src/nrfx_nvmc.c + +ifeq ($(DFU_EXTERNAL_FLASH),1) +C_SRC += $(NRFX_PATH)/drivers/src/nrfx_qspi.c +endif + C_SRC += $(NRFX_PATH)/mdk/system_$(MCU_SUB_VARIANT).c # SDK 11 files: serial + OTA DFU @@ -169,9 +189,7 @@ C_SRC += $(SDK_PATH)/libraries/hci/hci_slip.c C_SRC += $(SDK_PATH)/libraries/hci/hci_transport.c C_SRC += $(SDK_PATH)/libraries/util/nrf_assert.c -# UART or USB Serial -ifeq ($(MCU_SUB_VARIANT),nrf52) - +ifeq ($(BOARD_USE_UART),1) C_SRC += $(SDK_PATH)/libraries/uart/app_uart.c C_SRC += $(SDK_PATH)/drivers_nrf/uart/nrf_drv_uart.c C_SRC += $(SDK_PATH)/drivers_nrf/common/nrf_drv_common.c @@ -179,10 +197,10 @@ C_SRC += $(SDK_PATH)/drivers_nrf/common/nrf_drv_common.c IPATH += $(SDK11_PATH)/libraries/util IPATH += $(SDK_PATH)/drivers_nrf/common IPATH += $(SDK_PATH)/drivers_nrf/uart +endif -else - -# pinconfig is required for 840 for CF2 +ifeq ($(BOARD_USE_USB),1) +# pinconfig is required for CF2 C_SRC += src/boards/$(BOARD)/pinconfig.c # USB Application ( MSC + UF2 ) @@ -201,12 +219,9 @@ C_SRC += \ $(TUSB_PATH)/class/cdc/cdc_device.c \ $(TUSB_PATH)/class/msc/msc_device.c \ $(TUSB_PATH)/tusb.c - endif -#------------------------------------------------------------------------------ # Assembly Files -#------------------------------------------------------------------------------ ASM_SRC = $(NRFX_PATH)/mdk/gcc_startup_$(MCU_SUB_VARIANT).S #------------------------------------------------------------------------------ diff --git a/lib/sdk/components/libraries/hci/hci_mem_pool.c b/lib/sdk/components/libraries/hci/hci_mem_pool.c index 82412399..ae378a92 100644 --- a/lib/sdk/components/libraries/hci/hci_mem_pool.c +++ b/lib/sdk/components/libraries/hci/hci_mem_pool.c @@ -39,6 +39,7 @@ */ #include "sdk_common.h" #if NRF_MODULE_ENABLED(HCI_MEM_POOL) +#include "app_util_platform.h" #include "hci_mem_pool.h" #include #include @@ -134,6 +135,7 @@ uint32_t hci_mem_pool_rx_produce(uint32_t length, void ** pp_buffer) } *pp_buffer = NULL; + NRFX_CRITICAL_SECTION_ENTER(); if (m_rx_buffer_queue.free_window_count != 0) { if (length <= HCI_RX_BUF_SIZE) @@ -163,6 +165,7 @@ uint32_t hci_mem_pool_rx_produce(uint32_t length, void ** pp_buffer) { err_code = NRF_ERROR_NO_MEM; } + NRFX_CRITICAL_SECTION_EXIT(); return err_code; } diff --git a/lib/sdk/components/libraries/hci/hci_slip.c b/lib/sdk/components/libraries/hci/hci_slip.c index f7673f63..c47fc364 100644 --- a/lib/sdk/components/libraries/hci/hci_slip.c +++ b/lib/sdk/components/libraries/hci/hci_slip.c @@ -43,9 +43,23 @@ #include #include "app_uart.h" #include "nrf_error.h" +#include "board.h" + +#if USE_RUNTIME_SELECTION +#pragma message "USB and UART enabled" +static bool selectSerial = false; // use USB by default + +void useSerialTransport() { + selectSerial = true; +} + +bool usingSerialTransport() { + return selectSerial; +} +#endif // nRF has native usb peripheral -#ifdef NRF_USBD +#ifdef USE_USB #include "tusb.h" #endif @@ -122,17 +136,24 @@ static uint32_t send_tx_byte_end(void); */ uint32_t (*send_tx_byte) (void) = send_tx_byte_default; -#ifdef NRF_USBD -static uint32_t serial_put(char ch) +#if USE_USB && !USE_RUNTIME_SELECTION +uint32_t serial_put(char ch) { return tud_cdc_write_char(ch) ? NRF_SUCCESS : NRF_ERROR_NO_MEM; } -#else +#elif USE_SERIAL && !USE_RUNTIME_SELECTION #define serial_put app_uart_put +#else + +uint32_t serial_put(char ch) +{ + return selectSerial ? app_uart_put(ch) : (tud_cdc_write_char(ch) ? NRF_SUCCESS : NRF_ERROR_NO_MEM); +} + #endif static uint32_t send_tx_byte_end(void) @@ -350,9 +371,8 @@ static bool rx_buffer_overflowed(void) return false; } -#ifdef NRF_USBD - -static uint32_t slip_uart_open(void) +#if USE_USB +static uint32_t slip_uart_open_usb_cdc(void) { m_current_state = SLIP_READY; return NRF_SUCCESS; @@ -367,7 +387,9 @@ void tud_cdc_rx_cb(uint8_t port) } } -#else +#endif + +#if USE_SERIAL /** @brief Function for handling the UART module event. It parses events from the UART when * bytes are received/transmitted. @@ -390,7 +412,7 @@ static void slip_uart_eventhandler(app_uart_evt_t * uart_event) /** @brief Function for enabling the UART module when the SLIP layer is opened. */ -static uint32_t slip_uart_open(void) +static uint32_t slip_uart_open_serial(void) { uint32_t err_code; @@ -419,6 +441,27 @@ static uint32_t slip_uart_open(void) #endif +#if !USE_SERIAL && !USE_USB +#error at least one of USE_SERIAL and USE_USB must be enabled +#endif + +static uint32_t slip_uart_open(void) { + #if USE_RUNTIME_SELECTION + if (selectSerial) { + return slip_uart_open_serial(); + } + else { + return slip_uart_open_usb_cdc(); + } + #elif USE_SERIAL + return slip_uart_open_serial(); + #else + return slip_uart_open_usb_cdc(); + #endif + +} + + uint32_t hci_slip_evt_handler_register(hci_slip_event_handler_t event_handler) { m_slip_event_handler = event_handler; @@ -447,7 +490,7 @@ uint32_t hci_slip_close() { m_current_state = SLIP_OFF; -#ifdef NRF_USBD +#if USE_USB return NRF_SUCCESS; #else uint32_t err_code = app_uart_close(); @@ -456,7 +499,6 @@ uint32_t hci_slip_close() } - uint32_t hci_slip_write(const uint8_t * p_buffer, uint32_t length) { if (p_buffer == NULL) diff --git a/lib/sdk/components/libraries/hci/hci_slip.h b/lib/sdk/components/libraries/hci/hci_slip.h index 16040fb4..da8a1216 100644 --- a/lib/sdk/components/libraries/hci/hci_slip.h +++ b/lib/sdk/components/libraries/hci/hci_slip.h @@ -59,11 +59,31 @@ #define HCI_SLIP_H__ #include +#include "boards.h" + +// NRF_USBD +#ifdef NRF_USBD +#define USE_USB 1 +#else +#define USE_USB 0 +#endif + +/** + * @brief Enable UART interface with DFU_ACTIVATION present for MCU to MCU transfers + */ +#define USE_SERIAL (PIN_DFU_ACTIVATE_PRESENT || BOARD_USE_UART) +#define USE_RUNTIME_SELECTION (USE_USB && USE_SERIAL) + #ifdef __cplusplus extern "C" { #endif +#if USE_RUNTIME_SELECTION +void useSerialTransport(void); +bool usingSerialTransport(void); +#endif + /**@brief Event types from the SLIP Layer. */ typedef enum { diff --git a/lib/sdk/components/libraries/hci/hci_transport.c b/lib/sdk/components/libraries/hci/hci_transport.c index 35064763..f9b85f8e 100644 --- a/lib/sdk/components/libraries/hci/hci_transport.c +++ b/lib/sdk/components/libraries/hci/hci_transport.c @@ -39,6 +39,7 @@ */ #include "sdk_common.h" #if NRF_MODULE_ENABLED(HCI_TRANSPORT) +#include "app_util_platform.h" #include "hci_transport.h" #include "hci_slip.h" #include "crc16.h" @@ -286,6 +287,7 @@ static void rx_vendor_specific_pkt_type_handle(const uint8_t * p_buffer, uint32_ err_code = hci_mem_pool_rx_produce(HCI_RX_BUF_SIZE, (void **)&mp_slip_used_rx_buffer); APP_ERROR_CHECK_BOOL((err_code == NRF_SUCCESS) || (err_code == NRF_ERROR_NO_MEM)); + // If memory pool RX buffer produce succeeded we register that buffer to slip layer // otherwise we register the internal acknowledgement buffer. err_code = hci_slip_rx_buffer_register( @@ -781,7 +783,9 @@ uint32_t hci_transport_rx_pkt_extract(uint8_t ** pp_buffer, uint16_t * p_length) if (m_is_slip_decode_ready) { m_is_slip_decode_ready = false; + CRITICAL_REGION_ENTER(); err_code = hci_mem_pool_rx_extract(pp_buffer, &length); + CRITICAL_REGION_EXIT(); length -= (PKT_HDR_SIZE + PKT_CRC_SIZE); *p_length = (uint16_t)length; @@ -800,9 +804,12 @@ uint32_t hci_transport_rx_pkt_extract(uint8_t ** pp_buffer, uint16_t * p_length) return err_code; } - uint32_t hci_transport_rx_pkt_consume(uint8_t * p_buffer) { - return (hci_mem_pool_rx_consume(p_buffer - PKT_HDR_SIZE)); + uint32_t r; + CRITICAL_REGION_ENTER(); + r = hci_mem_pool_rx_consume(p_buffer - PKT_HDR_SIZE); + CRITICAL_REGION_EXIT(); + return r; } #endif //NRF_MODULE_ENABLED(HCI_TRANSPORT) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h index efd72e75..2cf52f72 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h @@ -50,7 +50,6 @@ uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms, bool cancel_timeout * The SoftDevice vector table base for interrupt forwarding will be set the application * address. * - * @param[in] app_addr Address to the region where the application is stored. */ void bootloader_app_start(void); diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu.h b/lib/sdk11/components/libraries/bootloader_dfu/dfu.h index 8916b7a3..c869d713 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu.h @@ -129,6 +129,18 @@ uint32_t dfu_sd_image_swap(void); */ uint32_t dfu_init_pkt_complete(void); + +#if DFU_EXTERNAL_FLASH +uint32_t dfu_external_begin_pkt_handle(dfu_update_packet_t* p_packet); + +uint32_t dfu_erase_pkt_handle(dfu_erase_packet_t* p_packet); +uint32_t dfu_write_pkt_handle(dfu_write_packet_t* p_packet); +uint32_t dfu_checksum_pkt_handle(dfu_checksum_packet_t* p_packet); + +uint32_t dfu_external_end_pkt_handle(dfu_update_packet_t* p_packet); + +#endif + #endif // DFU_H__ /** @} */ diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c index fa69ec83..13cde008 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c @@ -24,6 +24,9 @@ #include "nrf_mbr.h" #include "dfu_init.h" #include "sdk_common.h" +#if DFU_EXTERNAL_FLASH +#include "nrfx_qspi.h" +#endif #include "boards.h" @@ -41,7 +44,6 @@ static pstorage_handle_t * mp_storage_handle_active; /**< Pointer to static dfu_callback_t m_data_pkt_cb; /**< Callback from DFU Bank module for notification of asynchronous operation such as flash prepare. */ static dfu_bank_func_t m_functions; /**< Structure holding operations for the selected update process. */ - /**@brief Function for handling callbacks from pstorage module. * * @details Handles pstorage results for clear and storage operation. For detailed description of @@ -101,7 +103,8 @@ static void dfu_prepare_func_app_erase(uint32_t image_size) } else { - uint32_t const page_count = NRFX_CEIL_DIV(m_image_size, CODE_PAGE_SIZE); + // MRFX_CEIL_DIV fails when the first operand is 0 + uint32_t const page_count = m_image_size ? NRFX_CEIL_DIV(m_image_size, CODE_PAGE_SIZE) : 0; for ( uint32_t i = 0; i < page_count; i++ ) { @@ -521,6 +524,108 @@ void dfu_reset(void) } +#if DFU_EXTERNAL_FLASH +static const nrfx_qspi_config_t qspi_config = NRFX_QSPI_DEFAULT_CONFIG(QSPI_SCK, QSPI_CS, QSPI_DATA0, QSPI_DATA1, QSPI_DATA2, QSPI_DATA3); + +nrfx_err_t dfu_init_qspi_flash(void) { + static bool inited = false; + uint32_t err_code = NRFX_SUCCESS; + if (!inited) { + // use blocking mode for simplicity + err_code = nrfx_qspi_init(&qspi_config, NULL, NULL); + if (err_code==NRFX_SUCCESS) { + inited = true; + } + NRFX_DELAY_MS(5); + } + return err_code; +} + +uint32_t dfu_external_begin_pkt_handle(dfu_update_packet_t* p_packet) { + if (dfu_init_qspi_flash()!=NRFX_SUCCESS) + return NRF_ERROR_BUSY; + return NRF_SUCCESS; +} + +uint32_t dfu_external_end_pkt_handle(dfu_update_packet_t* p_packet) { + // this crashes the device. reason unknown. + //nrfx_qspi_uninit(); + return NRF_SUCCESS; +} + + +dfu_erase_packet_t* m_erase_pkt; +uint32_t dfu_erase_pkt_handle(dfu_erase_packet_t * p_packet) +{ + VERIFY_PARAM_NOT_NULL(p_packet); + m_erase_pkt = p_packet; + + if (m_erase_pkt->memory_region != DFU_MEMORY_EXTERNAL_FLASH) { + return NRF_ERROR_NOT_SUPPORTED; + } + + dfu_init_qspi_flash(); + while (m_erase_pkt->length) { + if (m_erase_pkt->length < 4096) { + return NRF_ERROR_INVALID_LENGTH; + } + if (nrfx_qspi_erase((m_erase_pkt->length>4096) ? NRF_QSPI_ERASE_LEN_64KB : NRF_QSPI_ERASE_LEN_4KB, m_erase_pkt->address)!=NRFX_SUCCESS) + return NRF_ERROR_NOT_FOUND; + const uint32_t block_size = (m_erase_pkt->length>4096) ? 64*1024 : 4*1024; + m_erase_pkt->length -= block_size; + m_erase_pkt->address += block_size; + } + return NRF_SUCCESS; +} + +#define XIP_BASE (0x12000000) + +uint32_t dfu_write_pkt_handle(dfu_write_packet_t * p_packet) +{ + VERIFY_PARAM_NOT_NULL(p_packet); + + if (p_packet->memory_region != DFU_MEMORY_EXTERNAL_FLASH) { + return NRF_ERROR_NOT_SUPPORTED; + } + dfu_init_qspi_flash(); + + unsigned retries = 5; + + uint32_t remaining = p_packet->length; + uint32_t address = p_packet->address; + const char* data = (const char*)p_packet->data; + uint32_t error = NRF_ERROR_NOT_FOUND; + + while (retries-->0 && error != NRF_SUCCESS) { + // the nrfx interface uses error codes in a different range of numbers, so these need to be mapped + // to regular nrf error codes + if (nrfx_qspi_write(data, remaining, address)!=NRFX_SUCCESS) { + error = NRF_ERROR_NOT_FOUND; + } + else { + // verify against the xip region + if (memcmp(data, (void*)(XIP_BASE+address), remaining)) + error = NRF_ERROR_INVALID_DATA; + else + error = NRF_SUCCESS; + } + } + return error; +} + +uint32_t dfu_checksum_pkt_handle(dfu_checksum_packet_t * p_packet) +{ + VERIFY_PARAM_NOT_NULL(p_packet); + + if (p_packet->memory_region != DFU_MEMORY_EXTERNAL_FLASH) { + return NRF_ERROR_NOT_SUPPORTED; + } + + return NRF_SUCCESS; +} + +#endif + static uint32_t dfu_compare_block(uint32_t * ptr1, uint32_t * ptr2, uint32_t len) { sd_mbr_command_t sd_mbr_cmd; diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c index 71f08526..840c7985 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c @@ -11,6 +11,8 @@ */ #include "dfu_transport.h" +#include "app_util_platform.h" +#include #include #include "dfu.h" #include @@ -66,7 +68,7 @@ typedef struct { dfu_update_packet_t data_packet[MAX_BUFFERS]; /**< Bootloader data packets used when processing data from the UART. */ - volatile uint8_t count; /**< Counter to maintain number of elements in the queue. */ +_Atomic volatile uint8_t count; /**< Counter to maintain number of elements in the queue. */ } dfu_data_queue_t; static dfu_data_queue_t m_data_queue; /**< Received-data packet queue. */ @@ -109,14 +111,16 @@ static uint32_t data_queue_element_free(uint8_t element_index) if (MAX_BUFFERS > element_index) { + CRITICAL_REGION_ENTER(); p_data = (uint8_t *)DATA_QUEUE_ELEMENT_GET_PDATA(element_index); if (INVALID_PACKET != DATA_QUEUE_ELEMENT_GET_PTYPE(element_index)) { - m_data_queue.count--; data_queue_element_init (element_index); + m_data_queue.count--; retval = hci_transport_rx_pkt_consume((p_data - 4)); APP_ERROR_CHECK(retval); } + CRITICAL_REGION_EXIT(); } else { @@ -190,6 +194,7 @@ static void dfu_cb_handler(uint32_t packet, uint32_t result, uint8_t * p_data) APP_ERROR_CHECK(result); } +extern void tud_cdc_write_str(const char* s); static void process_dfu_packet(void * p_event_data, uint16_t event_size) { @@ -239,6 +244,35 @@ static void process_dfu_packet(void * p_event_data, uint16_t event_size) // Break the loop by returning. return; +#if DFU_EXTERNAL_FLASH + case EXTERNAL_FLASH_BEGIN_PACKET: + retval = dfu_external_begin_pkt_handle(packet); + APP_ERROR_CHECK(retval); + break; + + case EXTERNAL_FLASH_ERASE_PACKET: + retval = dfu_erase_pkt_handle((dfu_erase_packet_t*)&packet->params.data_packet.p_data_packet[0]); + APP_ERROR_CHECK(retval); + break; + + case EXTERNAL_FLASH_CHECKSUM_PACKET: + retval = dfu_checksum_pkt_handle((dfu_checksum_packet_t*)&packet->params.data_packet.p_data_packet[0]); + APP_ERROR_CHECK(retval); + break; + + case EXTERNAL_FLASH_WRITE_PACKET: + retval = dfu_write_pkt_handle((dfu_write_packet_t*)&packet->params.data_packet.p_data_packet[0]); + APP_ERROR_CHECK(retval); + break; + + case EXTERNAL_FLASH_END_PACKET: + retval = dfu_external_end_pkt_handle(packet); + APP_ERROR_CHECK(retval); + break; + +#endif + + case PING_PACKET: default: // No implementation needed. break; diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h b/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h index 4148e1c9..6a06577c 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h @@ -81,10 +81,11 @@ static inline bool is_sd_existed(void) #define INVALID_PACKET 0x00 /**< Invalid packet identifies. */ #define INIT_PACKET 0x01 /**< Packet identifies for initialization packet. */ -#define STOP_INIT_PACKET 0x02 /**< Packet identifies for stop initialization packet. Used when complete init packet has been received so that the init packet can be used for pre validaiton. */ +//#define STOP_INIT_PACKET 0x02 // unused /**< Packet identifies for stop initialization packet. Used when complete init packet has been received so that the init packet can be used for pre validaiton. */ #define START_PACKET 0x03 /**< Packet identifies for the Data Start Packet. */ #define DATA_PACKET 0x04 /**< Packet identifies for a Data Packet. */ #define STOP_DATA_PACKET 0x05 /**< Packet identifies for the Data Stop Packet. */ +#define PING_PACKET 0x09 // used for synchronization and testing connectivity #define DFU_UPDATE_SD 0x01 /**< Bit field indicating update of SoftDevice is ongoing. */ #define DFU_UPDATE_BL 0x02 /**< Bit field indicating update of bootloader is ongoing. */ @@ -93,6 +94,14 @@ static inline bool is_sd_existed(void) #define DFU_INIT_RX 0x00 /**< Op Code identifies for receiving init packet. */ #define DFU_INIT_COMPLETE 0x01 /**< Op Code identifies for transmission complete of init packet. */ +#if DFU_EXTERNAL_FLASH +#define EXTERNAL_FLASH_BEGIN_PACKET 0x10 +#define EXTERNAL_FLASH_ERASE_PACKET 0x11 +#define EXTERNAL_FLASH_CHECKSUM_PACKET 0x12 +#define EXTERNAL_FLASH_WRITE_PACKET 0x13 +#define EXTERNAL_FLASH_END_PACKET 0x14 +#endif + // Safe guard to ensure during compile time that the DFU_APP_DATA_RESERVED is a multiple of page size. STATIC_ASSERT((((DFU_APP_DATA_RESERVED) & (CODE_PAGE_SIZE - 1)) == 0x00)); @@ -155,6 +164,55 @@ typedef struct /**@brief Update complete handler type. */ typedef void (*dfu_complete_handler_t)(dfu_update_status_t dfu_update_status); +#if DFU_EXTERNAL_FLASH + +typedef enum +{ + DFU_MEMORY_INTERNAL_FLASH = 0, // unsupported + DFU_MEMORY_EXTERNAL_FLASH = 16, +} dfu_memory_region_t; + +STATIC_ASSERT((sizeof(dfu_memory_region_t) == 1)); + + +typedef struct +{ + dfu_memory_region_t memory_region; + uint8_t reserved[3]; + uint32_t length; + uint32_t address; +} dfu_erase_packet_t; + +STATIC_ASSERT((sizeof(dfu_erase_packet_t) == 12)); + + +typedef struct +{ + dfu_memory_region_t memory_region; + uint8_t reserved[3]; + uint32_t address; // must be 8 byte aligned + uint32_t length; // the number of bytes of data to write + uint8_t data[]; // the data to write at the given address +} dfu_write_packet_t; + +STATIC_ASSERT((sizeof(dfu_write_packet_t) == 12)); + +typedef uint8_t md5_t[16]; +typedef struct +{ + dfu_memory_region_t memory_region; + uint8_t reserved[3]; + uint32_t address; + uint32_t length; + md5_t md5; +} dfu_checksum_packet_t; + +STATIC_ASSERT((sizeof(dfu_checksum_packet_t) == 28)); + + +#endif + + #endif // DFU_TYPES_H__ /**@} */ diff --git a/src/boards/boards.c b/src/boards/boards.c index e86edd63..c9314976 100644 --- a/src/boards/boards.c +++ b/src/boards/boards.c @@ -44,27 +44,21 @@ #endif //------------- IMPLEMENTATION -------------// -void button_init(uint32_t pin) +void button_init(uint32_t pin, uint32_t pull) { - if ( BUTTON_PULL == NRF_GPIO_PIN_PULLDOWN ) - { - nrf_gpio_cfg_sense_input(pin, BUTTON_PULL, NRF_GPIO_PIN_SENSE_HIGH); - } - else - { - nrf_gpio_cfg_sense_input(pin, BUTTON_PULL, NRF_GPIO_PIN_SENSE_LOW); - } + nrf_gpio_cfg_sense_input(pin, pull, pull == NRF_GPIO_PIN_PULLDOWN ? NRF_GPIO_PIN_SENSE_HIGH : NRF_GPIO_PIN_SENSE_LOW); } -bool button_pressed(uint32_t pin) +bool button_pressed(uint32_t pin, uint32_t pull) { - uint32_t const active_state = (BUTTON_PULL == NRF_GPIO_PIN_PULLDOWN ? 1 : 0); + uint32_t const active_state = (pull == NRF_GPIO_PIN_PULLDOWN ? 1 : 0); return nrf_gpio_pin_read(pin) == active_state; } // This is declared so that a board specific init can be called from here. void __attribute__((weak)) board_init_extra(void) { } void board_init(void) + { // stop LF clock just in case we jump from application without reset NRF_CLOCK->TASKS_LFCLKSTOP = 1UL; @@ -73,8 +67,18 @@ void board_init(void) NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_RC; NRF_CLOCK->TASKS_LFCLKSTART = 1UL; - button_init(BUTTON_DFU); - button_init(BUTTON_FRESET); + button_init(BUTTON_DFU, BUTTON_DFU_PULL); + +#if defined(PIN_DFU_ACTIVATE) +#if PIN_DFU_ACTIVATE!=BUTTON_FRESET + button_init(BUTTON_FRESET, BUTTON_FRESET_PULL); +#endif +#endif + +#if defined(PIN_DFU_ACTIVATE) + button_init(PIN_DFU_ACTIVATE, PIN_DFU_ACTIVATE_PULL); +#endif + NRFX_DELAY_US(100); // wait for the pin state is stable #if LEDS_NUMBER > 0 @@ -275,18 +279,17 @@ void led_state(uint32_t state) uint32_t temp_color = 0; switch (state) { case STATE_USB_MOUNTED: - new_rgb_color = 0x00ff00; - primary_cycle_length = 3000; + new_rgb_color = 0x00ff00; // green break; case STATE_BOOTLOADER_STARTED: case STATE_USB_UNMOUNTED: - new_rgb_color = 0xff0000; + new_rgb_color = 0xff0000; // red primary_cycle_length = 300; break; case STATE_WRITING_STARTED: - temp_color = 0xff0000; + temp_color = 0xff0000; // red primary_cycle_length = 100; break; @@ -296,7 +299,7 @@ void led_state(uint32_t state) break; case STATE_BLE_CONNECTED: - new_rgb_color = 0x0000ff; + new_rgb_color = 0x0000ff; // green #ifdef LED_SECONDARY_PIN secondary_cycle_length = 3000; #else @@ -305,7 +308,7 @@ void led_state(uint32_t state) break; case STATE_BLE_DISCONNECTED: - new_rgb_color = 0xff00ff; + new_rgb_color = 0xff00ff; // purple #ifdef LED_SECONDARY_PIN secondary_cycle_length = 300; #else @@ -313,8 +316,16 @@ void led_state(uint32_t state) #endif break; + case STATE_UART_ACTIVE: + new_rgb_color = 0x00ffff; // cyan + break; + + case STATE_UART_TIMEOUT: + new_rgb_color = 0x202020; // grey + break; + default: - break; + break; } uint8_t* final_color = NULL; new_rgb_color &= BOARD_RGB_BRIGHTNESS; diff --git a/src/boards/boards.h b/src/boards/boards.h index 8aa0ece6..5513f6d8 100644 --- a/src/boards/boards.h +++ b/src/boards/boards.h @@ -45,6 +45,32 @@ #define BUTTON_FRESET BUTTON_2 #endif +#ifndef BUTTON_1_PULL +#define BUTTON_1_PULL BUTTON_PULL +#endif + +#ifndef BUTTON_2_PULL +#define BUTTON_2_PULL BUTTON_PULL +#endif + +#ifndef BUTTON_DFU_PULL +#define BUTTON_DFU_PULL BUTTON_1_PULL +#endif + +#ifndef BUTTON_RESET_PULL +#define BUTTON_RESET_PULL BUTTON_2_PULL +#endif + +#if defined(PIN_DFU_ACTIVATE) != defined(PIN_DFU_ACTIVATE_PULL) +#error "both PIN_DFU_ACTIVATE and PIN_DFU_ACTIVATE_PULL should be defined to use an additional pin for DFU activation." +#endif + +#if defined(PIN_DFU_ACTIVATE) && defined(PIN_DFU_ACTIVATE_PULL) +#define PIN_DFU_ACTIVATE_PRESENT 1 +#else +#define PIN_DFU_ACTIVATE_PRESENT 0 +#endif + // The primary LED is usually Red but not in all cases. #define LED_PRIMARY 0 // The secondary LED, when available, is usually blue. @@ -91,7 +117,9 @@ enum { STATE_WRITING_STARTED, STATE_WRITING_FINISHED, STATE_BLE_CONNECTED, - STATE_BLE_DISCONNECTED + STATE_BLE_DISCONNECTED, + STATE_UART_ACTIVE, + STATE_UART_TIMEOUT }; void led_pwm_init(uint32_t led_index, uint32_t led_pin); @@ -109,8 +137,8 @@ void led_tick(void); #error "At least two buttons required in the BSP (see 'BUTTONS_NUMBER')" #endif -void button_init(uint32_t pin); -bool button_pressed(uint32_t pin); +void button_init(uint32_t pin, uint32_t pull); +bool button_pressed(uint32_t pin, uint32_t pull); bool is_ota(void); diff --git a/src/boards/feather_nrf52840_express/board.h b/src/boards/feather_nrf52840_express/board.h index 3be5593b..e69ab98b 100644 --- a/src/boards/feather_nrf52840_express/board.h +++ b/src/boards/feather_nrf52840_express/board.h @@ -47,6 +47,10 @@ #define BUTTON_2 _PINNUM(0, 10) #define BUTTON_PULL NRF_GPIO_PIN_PULLUP +// use the same pin as BUTTON_2/NRST which disables the NRST functionality +#define PIN_DFU_ACTIVATE BUTTON_2 +#define PIN_DFU_ACTIVATE_PULL NRF_GPIO_PIN_PULLDOWN + //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ @@ -66,4 +70,24 @@ #define UF2_BOARD_ID "nRF52840-Feather-revD" #define UF2_INDEX_URL "https://www.adafruit.com/product/4062" +/*------------------------------------------------------------------*/ +/* UART + *------------------------------------------------------------------*/ +#define RX_PIN_NUMBER 24 +#define TX_PIN_NUMBER 25 +#define CTS_PIN_NUMBER 0 +#define RTS_PIN_NUMBER 0 +#define HWFC false + +/*------------------------------------------------------------------*/ +/* QSPI external flash + *------------------------------------------------------------------*/ +#define QSPI_DATA0 _PINNUM(0, 17) +#define QSPI_DATA1 _PINNUM(0, 22) +#define QSPI_DATA2 _PINNUM(0, 23) +#define QSPI_DATA3 _PINNUM(0, 21) +#define QSPI_SCK _PINNUM(0, 19) +#define QSPI_CS _PINNUM(0, 20) + + #endif // _FEATHER_NRF52840_H diff --git a/src/boards/feather_nrf52840_express/board.mk b/src/boards/feather_nrf52840_express/board.mk index 9d29ac69..dbd68b5a 100644 --- a/src/boards/feather_nrf52840_express/board.mk +++ b/src/boards/feather_nrf52840_express/board.mk @@ -1 +1,4 @@ MCU_SUB_VARIANT = nrf52840 +DFU_PIN_ACTIVATION = 1 +DFU_EXTERNAL_FLASH = 1 + diff --git a/src/main.c b/src/main.c index 47386ca8..5cf705fe 100644 --- a/src/main.c +++ b/src/main.c @@ -66,8 +66,9 @@ #include "nrf_mbr.h" #include "pstorage.h" #include "nrfx_nvmc.h" +#include "hci_slip.h" -#ifdef NRF_USBD +#if USE_USB #include "uf2/uf2.h" #include "nrf_usbd.h" #include "tusb.h" @@ -78,13 +79,42 @@ void usb_teardown(void); // tinyusb function that handles power event (detected, ready, removed) // We must call it within SD's SOC event handler, or set it as power event handler if SD is not enabled. extern void tusb_hal_nrf_power_event(uint32_t event); +#endif -#else +#if USE_SERIAL +#define uart_init(x) led_state(STATE_UART_ACTIVE) // mark nrf52832 as mounted +#define uart_teardown() +#endif -#define usb_init(x) led_state(STATE_USB_MOUNTED) // mark nrf52832 as mounted -#define usb_teardown() +extern void serial_put(char c); +void channel_init(bool cdc_only) { +#if USE_RUNTIME_SELECTION + if (usingSerialTransport()) { + uart_init(cdc_only); + } else { + usb_init(cdc_only); + } +#elif USE_USB + usb_init(cdc_only); +#elif USE_SERIAL + uart_init(cdc_only); #endif +} + +void channel_teardown(void) { +#if USE_RUNTIME_SELECTION + if (usingSerialTransport()) { + uart_teardown(); + } else { + usb_teardown(); + } +#elif USE_USB + usb_teardown(); +#else USE_SERIAL + uart_teardown(); +#endif +} //--------------------------------------------------------------------+ // @@ -230,6 +260,7 @@ int main(void) NVIC_SystemReset(); } +// Perform DFU if requested via flags, button pushes or the DFU activation pin. static void check_dfu_mode(void) { uint32_t const gpregret = NRF_POWER->GPREGRET; @@ -257,13 +288,31 @@ static void check_dfu_mode(void) // skip dfu entirely if (dfu_skip) return; - /*------------- Determine DFU mode (Serial, OTA, FRESET or normal) -------------*/ - // DFU button pressed - dfu_start = dfu_start || button_pressed(BUTTON_DFU); + // dfu activation pin for external control of DFU + bool dfu_activate = false; + bool frst_skip = false; + +#if PIN_DFU_ACTIVATE_PRESENT + dfu_activate = button_pressed(PIN_DFU_ACTIVATE, PIN_DFU_ACTIVATE_PULL); + // when the FRST button is defined the same as the DFU activation line, FRST is not used. + frst_skip = (PIN_DFU_ACTIVATE == BUTTON_FRESET); + #pragma message "FRST is overridden by the DFU activate setting" +#endif + + dfu_start = dfu_start || dfu_activate; - // DFU + FRESET are pressed --> OTA - _ota_dfu = _ota_dfu || ( button_pressed(BUTTON_DFU) && button_pressed(BUTTON_FRESET) ) ; + // DFU activation via the designated pin takes precedence + if (!PIN_DFU_ACTIVATE_PRESENT || !dfu_activate) { + /*------------- Determine DFU mode (Serial, OTA, FRESET or normal) -------------*/ + // DFU button pressed + dfu_start = dfu_start || button_pressed(BUTTON_DFU, BUTTON_DFU_PULL); + // DFU + FRESET are pressed --> OTA + if (!frst_skip) { // skip button sense if on the same pin as DFU activation + _ota_dfu = _ota_dfu || ( button_pressed(BUTTON_DFU, BUTTON_DFU_PULL) && button_pressed(BUTTON_FRESET, BUTTON_RESET_PULL) ) ; + } + } + bool const valid_app = bootloader_app_is_valid(); bool const just_start_app = valid_app && !dfu_start && (*dbl_reset_mem) == DFU_DBL_RESET_APP; @@ -301,6 +350,13 @@ static void check_dfu_mode(void) (*dbl_reset_mem) = 0; } + // Enable UART for MCU to MCU transfers. +#if PIN_DFU_ACTIVATE_PRESENT + if (dfu_activate) { + useSerialTransport(); + } +#endif + // Enter DFU mode accordingly to input if ( dfu_start || !valid_app ) { @@ -316,7 +372,8 @@ static void check_dfu_mode(void) else { led_state(STATE_USB_UNMOUNTED); - usb_init(serial_only_dfu); + + channel_init(serial_only_dfu); } // Initiate an update of the firmware. @@ -334,11 +391,20 @@ static void check_dfu_mode(void) if ( _ota_dfu ) { sd_softdevice_disable(); - }else + } + else { - usb_teardown(); + channel_teardown(); } } + + // Simply reset, allowing the host MCU to control the behavior on boot via the activation pin. + // The host MCU may keep the device in bootloader mode via the DFU activation pin when there are more + // binaries to flash (e.g. QSPI flash data) + if (PIN_DFU_ACTIVATE_PRESENT && dfu_activate) + { + NVIC_SystemReset(); + } } @@ -476,7 +542,7 @@ uint32_t proc_soc(void) { pstorage_sys_event_handler(soc_evt); -#ifdef NRF_USBD +#if USE_USB /*------------- usb power event handler -------------*/ int32_t usbevt = (soc_evt == NRF_EVT_POWER_USB_DETECTED ) ? NRFX_POWER_USB_EVT_DETECTED: (soc_evt == NRF_EVT_POWER_USB_POWER_READY) ? NRFX_POWER_USB_EVT_READY : diff --git a/src/nrfx_config.h b/src/nrfx_config.h index b626e000..b0cdf287 100644 --- a/src/nrfx_config.h +++ b/src/nrfx_config.h @@ -16,9 +16,15 @@ #define NRFX_PWM2_ENABLED 0 #define NRFX_PWM3_ENABLED 0 -// UART -#ifdef NRF52832_XXAA +#if DFU_EXTERNAL_FLASH +#define NRFX_QSPI_ENABLED 1 +#define NRFX_QSPI_DEFAULT_CONFIG_IRQ_PRIORITY 8 +#define NRF_QSPI_HAS_XIP_ENC 0 +#define NRF_QSPI_HAS_DMA_ENC 0 +#endif +// UART +#if BOARD_USE_UART #define NRFX_UART_ENABLED 1 #define NRFX_UART0_ENABLED 1 diff --git a/src/nrfx_glue.h b/src/nrfx_glue.h index 0d18d234..d382989d 100644 --- a/src/nrfx_glue.h +++ b/src/nrfx_glue.h @@ -175,12 +175,12 @@ static inline bool _NRFX_IRQ_IS_PENDING(IRQn_Type irq_number) /** * @brief Macro for entering into a critical section. */ -#define NRFX_CRITICAL_SECTION_ENTER() // CRITICAL_REGION_ENTER() +#define NRFX_CRITICAL_SECTION_ENTER() CRITICAL_REGION_ENTER() /** * @brief Macro for exiting from a critical section. */ -#define NRFX_CRITICAL_SECTION_EXIT() // CRITICAL_REGION_EXIT() +#define NRFX_CRITICAL_SECTION_EXIT() CRITICAL_REGION_EXIT() //------------------------------------------------------------------------------