diff --git a/TinyFrame/TF_Integration.c b/TinyFrame/TF_Integration.c index 0b660dc..31d982f 100644 --- a/TinyFrame/TF_Integration.c +++ b/TinyFrame/TF_Integration.c @@ -8,6 +8,9 @@ #include "task_main.h" #include "comm/messages.h" #include "comm/interfaces.h" +#include "comm/iface_uart.h" +#include "comm/iface_nordic.h" +#include "comm/iface_usb.h" #include "framework/system_settings.h" #include "USB/usbd_cdc_if.h" @@ -17,76 +20,16 @@ extern osSemaphoreId semVcomTxReadyHandle; extern osMutexId mutTinyFrameTxHandle; -/** - * USB transmit implementation - * - * @param tf - TF - * @param buff - buffer to send (can be longer than the buffers) - * @param len - buffer size - */ -static inline void _USB_WriteImpl(TinyFrame *tf, const uint8_t *buff, uint32_t len) -{ -#if 1 - const uint32_t real_size = len; - - // Padding to a multiple of 64 bytes - this is supposed to maximize the bulk transfer speed - if ((len&0x3F) && !SystemSettings.visible_vcom) { // this corrupts VCOM on Linux for some reason - uint32_t pad = (64 - (len&0x3F)); - memset((void *) (buff + len), 0, pad); - len += pad; // padding to a multiple of 64 (size of the endpoint) - } - - // We bypass the USBD driver library's overhead by using the HAL function directly - assert_param(HAL_OK == HAL_PCD_EP_Transmit(hUsbDeviceFS.pData, CDC_IN_EP, (uint8_t *) buff, len)); - - // The buffer is the TF transmit buffer, we can't leave it to work asynchronously because - // the next call could modify it before it's been transmitted (in the case of a chunked / multi-part frame) - - // If this is not the last chunk (assuming all but the last use full 512 bytes of the TF buffer), wait now for completion - if (real_size == TF_SENDBUF_LEN) { - // TODO this seems wrong - investigate - if (pdTRUE != xSemaphoreTake(semVcomTxReadyHandle, 100)) { - TF_Error("Tx stalled in WriteImpl"); - return; - } - } -#else - (void) tf; -#define CHUNK 64 // size of the USB packet - int32_t total = (int32_t) len; - while (total > 0) { - const int32_t mxStatus = osSemaphoreWait(semVcomTxReadyHandle, 100); - if (mxStatus != osOK) { - TF_Error("Tx stalled"); - return; - } - - const uint16_t chunksize = (uint16_t) MIN(total, CHUNK); - - // this is an attempt to speed it up a little by removing a couple levels of indirection - assert_param(HAL_OK == HAL_PCD_EP_Transmit(hUsbDeviceFS.pData, CDC_IN_EP, (uint8_t *) buff, chunksize)); - -// USBD_LL_Transmit(&hUsbDeviceFS, CDC_IN_EP, (uint8_t *) buff, chunksize); -// assert_param(USBD_OK == CDC_Transmit_FS((uint8_t *) buff, chunksize)); - - buff += chunksize; - total -= chunksize; - } -#endif -} - void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, uint32_t len) { if (gActiveComport == COMPORT_USB) { - _USB_WriteImpl(tf, buff, len); + iface_usb_transmit(buff, len); } else if (gActiveComport == COMPORT_USART) { - // TODO rewrite this to use DMA, then wait for the DMA - for(uint32_t i=0;i 0) { + dbg("NRF RX %d bytes", (int)count); + rxQuePostMsg(rx_buffer, count); + } else { + dbg("IRQ but no Rx"); + } +} + +bool iface_nordic_init(void) +{ + dbg("Setting up Nordic..."); + + hw_periph_clock_enable(NRF_SPI); + + // SPI pins + assert_param(E_SUCCESS == hw_configure_gpiorsc_af(NRF_R_SCK, NRF_SPI_AF)); + assert_param(E_SUCCESS == hw_configure_gpiorsc_af(NRF_R_MOSI, NRF_SPI_AF)); + assert_param(E_SUCCESS == hw_configure_gpiorsc_af(NRF_R_MISO, NRF_SPI_AF)); + + // Manual pins + LL_GPIO_SetPinMode(NRF_NSS_GPIO_Port, NRF_NSS_Pin, LL_GPIO_MODE_OUTPUT); + LL_GPIO_SetPinMode(NRF_CE_GPIO_Port, NRF_CE_Pin, LL_GPIO_MODE_OUTPUT); + LL_GPIO_SetPinMode(NRF_IRQ_GPIO_Port, NRF_IRQ_Pin, LL_GPIO_MODE_INPUT); + + LL_SPI_Disable(NRF_SPI); + { + LL_SPI_SetBaudRatePrescaler(NRF_SPI, LL_SPI_BAUDRATEPRESCALER_DIV8); + + LL_SPI_SetClockPolarity(NRF_SPI, LL_SPI_POLARITY_LOW); + LL_SPI_SetClockPhase(NRF_SPI, LL_SPI_PHASE_2EDGE); + LL_SPI_SetTransferDirection(NRF_SPI, LL_SPI_FULL_DUPLEX); + LL_SPI_SetTransferBitOrder(NRF_SPI, LL_SPI_MSB_FIRST); + + LL_SPI_SetNSSMode(NRF_SPI, LL_SPI_NSS_SOFT); + LL_SPI_SetDataWidth(NRF_SPI, LL_SPI_DATAWIDTH_8BIT); + LL_SPI_SetRxFIFOThreshold(NRF_SPI, LL_SPI_RX_FIFO_TH_QUARTER); // trigger RXNE on 1 byte + + LL_SPI_SetMode(NRF_SPI, LL_SPI_MODE_MASTER); + } + LL_SPI_Enable(NRF_SPI); + + + dbg("configure nrf module"); + + // Now configure the radio + NRF_Init(NRF_SPEED_2M); // TODO configurable speed + NRF_SetChannel(SystemSettings.nrf_channel); + NRF_SetBaseAddress(SystemSettings.nrf_network); + NRF_SetRxAddress(RX_PIPE_NUM, SystemSettings.nrf_address); + NRF_ModeRX(); + + dbg("enable exti"); + // EXTI + LL_EXTI_EnableIT_0_31(LL_EXTI_LINES[NRF_EXTI_LINENUM]); + LL_EXTI_EnableFallingTrig_0_31(LL_EXTI_LINES[NRF_EXTI_LINENUM]); + LL_SYSCFG_SetEXTISource(NRF_SYSCFG_EXTI_PORT, LL_SYSCFG_EXTI_LINES[NRF_EXTI_LINENUM]); + irqd_attach(EXTIS[NRF_EXTI_LINENUM], NrfIrqHandler, NULL); + // TODO increase priority in NVIC? + + dbg("nrf setup done"); + return true; +} + +void iface_nordic_deinit(void) +{ + LL_EXTI_DisableIT_0_31(LL_EXTI_LINES[NRF_EXTI_LINENUM]); + LL_EXTI_DisableFallingTrig_0_31(LL_EXTI_LINES[NRF_EXTI_LINENUM]); + irqd_detach(EXTIS[NRF_EXTI_LINENUM], NrfIrqHandler); + hw_periph_clock_disable(NRF_SPI); + + hw_deinit_pin_rsc(NRF_R_SCK); + hw_deinit_pin_rsc(NRF_R_MOSI); + hw_deinit_pin_rsc(NRF_R_MISO); + hw_deinit_pin_rsc(NRF_R_NSS); + hw_deinit_pin_rsc(NRF_R_CE); + hw_deinit_pin_rsc(NRF_R_IRQ); +} + +#define MAX_RETRY 8 + +void iface_nordic_transmit(const uint8_t *buff, uint32_t len) +{ + bool suc = false; + while (len > 0) { + uint8_t chunk = (uint8_t) MIN(32, len); + + uint16_t delay = 1; + for (int i = 0; i < MAX_RETRY; i++) { + suc = NRF_SendPacket(RX_PIPE_NUM, buff, chunk); // use the pipe to retrieve the address + if (!suc) { + vTaskDelay(delay); + delay += 5; // longer delay next time + } else { + break; + } + } + + if (!suc) { + break; + } + + buff += chunk; + len -= chunk; + } + + if (suc) { + dbg("+ NRF Tx OK!"); + } else { + dbg("- NRF sending failed"); + } + + // give when it's done + xSemaphoreGive(semVcomTxReadyHandle); // similar to how it's done in USB - this is called in the Tx Done handler +} diff --git a/comm/iface_nordic.h b/comm/iface_nordic.h new file mode 100644 index 0000000..c07d739 --- /dev/null +++ b/comm/iface_nordic.h @@ -0,0 +1,20 @@ +// +// Created by MightyPork on 2018/04/06. +// + +#ifndef GEX_F072_IFACE_NORDIC_H +#define GEX_F072_IFACE_NORDIC_H + +#include "platform.h" + +void iface_nordic_claim_resources(void); + +void iface_nordic_free_resources(void); + +void iface_nordic_deinit(void); + +bool iface_nordic_init(void); + +void iface_nordic_transmit(const uint8_t *buff, uint32_t len); + +#endif //GEX_F072_IFACE_NORDIC_H diff --git a/comm/iface_uart.c b/comm/iface_uart.c new file mode 100644 index 0000000..0d4393e --- /dev/null +++ b/comm/iface_uart.c @@ -0,0 +1,105 @@ +// +// Created by MightyPork on 2018/04/06. +// + +#include "platform.h" +#include "iface_uart.h" +#include "resources.h" +#include "hw_utils.h" +#include "irq_dispatcher.h" +#include "task_msg.h" +#include "system_settings.h" + +extern osSemaphoreId semVcomTxReadyHandle; + +static uint32_t usart_rxi = 0; +static uint8_t usart_rx_buffer[MSG_QUE_SLOT_SIZE]; +static uint32_t last_rx_time = 0; + +void iface_uart_claim_resources(void) +{ + assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, R_USART2)); + assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, R_PA2)); + assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, R_PA3)); +} + +void iface_uart_free_resources(void) +{ + rsc_free(&UNIT_SYSTEM, R_USART2); + rsc_free(&UNIT_SYSTEM, R_PA2); + rsc_free(&UNIT_SYSTEM, R_PA3); +} + +/** Handler for the USART transport */ +static void com_UsartIrqHandler(void *arg) +{ + (void)arg; + if (LL_USART_IsActiveFlag_RXNE(USART2)) { + vPortEnterCritical(); + { + usart_rx_buffer[usart_rxi++] = LL_USART_ReceiveData8(USART2); + if (usart_rxi == MSG_QUE_SLOT_SIZE) { + rxQuePostMsg(usart_rx_buffer, MSG_QUE_SLOT_SIZE); // avoid it happening in the irq + usart_rxi = 0; + } + last_rx_time = HAL_GetTick(); + } + vPortExitCritical(); + } +} + +/** this is called from the hal tick irq */ +void com_iface_flush_buffer(void) +{ + if (usart_rxi > 0 && (HAL_GetTick()-last_rx_time)>=2) { + vPortEnterCritical(); + { + rxQuePostMsg(usart_rx_buffer, usart_rxi); + usart_rxi = 0; + } + vPortExitCritical(); + } +} + +bool iface_uart_init(void) +{ + dbg("Setting up UART transfer"); + assert_param(E_SUCCESS == hw_configure_gpiorsc_af(R_PA2, LL_GPIO_AF_1)); + assert_param(E_SUCCESS == hw_configure_gpiorsc_af(R_PA3, LL_GPIO_AF_1)); + + __HAL_RCC_USART2_CLK_ENABLE(); + __HAL_RCC_USART2_FORCE_RESET(); + __HAL_RCC_USART2_RELEASE_RESET(); + + LL_USART_Disable(USART2); + + LL_USART_SetBaudRate(USART2, PLAT_APB1_HZ, LL_USART_OVERSAMPLING_16, SystemSettings.comm_uart_baud); + dbg("baud = %d", (int)SystemSettings.comm_uart_baud); + + irqd_attach(USART2, com_UsartIrqHandler, NULL); + LL_USART_EnableIT_RXNE(USART2); + LL_USART_SetTransferDirection(USART2, LL_USART_DIRECTION_TX_RX); + + LL_USART_Enable(USART2); + + return true; // always OK (TODO check voltage on Rx if it's 3V3 when idle?) +} + +void iface_uart_deinit(void) +{ + // this doesn't normally happen + hw_deinit_pin_rsc(R_PA2); + hw_deinit_pin_rsc(R_PA3); + __HAL_RCC_USART2_CLK_DISABLE(); + irqd_detach(USART2, com_UsartIrqHandler); +} + +void iface_uart_transmit(const uint8_t *buff, uint32_t len) +{ + // TODO rewrite this to use DMA, then wait for the DMA + for(uint32_t i=0;iDADDR & USB_DADDR_ADD); +} + +/** + * USB transmit implementation + * + * @param buff - buffer to send (can be longer than the buffers) + * @param len - buffer size + */ +void iface_usb_transmit(const uint8_t *buff, uint32_t len) +{ +#if 1 + const uint32_t real_size = len; + + // Padding to a multiple of 64 bytes - this is supposed to maximize the bulk transfer speed + if ((len&0x3F) && !SystemSettings.visible_vcom) { // this corrupts VCOM on Linux for some reason + uint32_t pad = (64 - (len&0x3F)); + memset((void *) (buff + len), 0, pad); + len += pad; // padding to a multiple of 64 (size of the endpoint) + } + + // We bypass the USBD driver library's overhead by using the HAL function directly + assert_param(HAL_OK == HAL_PCD_EP_Transmit(hUsbDeviceFS.pData, CDC_IN_EP, (uint8_t *) buff, len)); + + // The buffer is the TF transmit buffer, we can't leave it to work asynchronously because + // the next call could modify it before it's been transmitted (in the case of a chunked / multi-part frame) + + // If this is not the last chunk (assuming all but the last use full 512 bytes of the TF buffer), wait now for completion + if (real_size == TF_SENDBUF_LEN) { + // TODO this seems wrong - investigate + if (pdTRUE != xSemaphoreTake(semVcomTxReadyHandle, 100)) { + TF_Error("Tx stalled in WriteImpl"); + return; + } + } +#else + (void) tf; +#define CHUNK 64 // size of the USB packet + int32_t total = (int32_t) len; + while (total > 0) { + const int32_t mxStatus = osSemaphoreWait(semVcomTxReadyHandle, 100); + if (mxStatus != osOK) { + TF_Error("Tx stalled"); + return; + } + + const uint16_t chunksize = (uint16_t) MIN(total, CHUNK); + + // this is an attempt to speed it up a little by removing a couple levels of indirection + assert_param(HAL_OK == HAL_PCD_EP_Transmit(hUsbDeviceFS.pData, CDC_IN_EP, (uint8_t *) buff, chunksize)); + +// USBD_LL_Transmit(&hUsbDeviceFS, CDC_IN_EP, (uint8_t *) buff, chunksize); +// assert_param(USBD_OK == CDC_Transmit_FS((uint8_t *) buff, chunksize)); + + buff += chunksize; + total -= chunksize; + } +#endif +} diff --git a/comm/iface_usb.h b/comm/iface_usb.h new file mode 100644 index 0000000..d83f370 --- /dev/null +++ b/comm/iface_usb.h @@ -0,0 +1,20 @@ +// +// Created by MightyPork on 2018/04/06. +// + +#ifndef GEX_F072_IFACE_USB_H +#define GEX_F072_IFACE_USB_H + +#include "platform.h" + +bool iface_usb_ready(void); + +/** + * USB transmit implementation + * + * @param buff - buffer to send (can be longer than the buffers) + * @param len - buffer size + */ +void iface_usb_transmit(const uint8_t *buff, uint32_t len); + +#endif //GEX_F072_IFACE_USB_H diff --git a/comm/interfaces.c b/comm/interfaces.c index 2772182..595beec 100644 --- a/comm/interfaces.c +++ b/comm/interfaces.c @@ -2,7 +2,6 @@ // Created by MightyPork on 2018/03/23. // -#include #include "platform.h" #include "usbd_core.h" #include "USB/usb_device.h" @@ -12,6 +11,10 @@ #include "framework/resources.h" #include "platform/hw_utils.h" #include "framework/unit_base.h" +#include "nrf_pins.h" +#include "iface_uart.h" +#include "iface_usb.h" +#include "iface_nordic.h" enum ComportSelection gActiveComport = COMPORT_USB; // start with USB so the handlers work correctly initially @@ -35,36 +38,31 @@ void com_switch_transfer_if_needed(void) // Check if USB is enumerated - const uint32_t uadr = (USB->DADDR & USB_DADDR_ADD); - if (0 == uadr) { + if (!iface_usb_ready()) { dbg("Not enumerated, assuming USB is dead"); // Fallback to radio or bare USART do { if (SystemSettings.use_comm_nordic) { - if (configure_interface(COMPORT_NORDIC)) { - break; - } + if (configure_interface(COMPORT_NORDIC)) break; } +#if 0 if (SystemSettings.use_comm_lora) { - if (configure_interface(COMPORT_LORA)) { - break; - } + if (configure_interface(COMPORT_LORA)) break; } +#endif if (SystemSettings.use_comm_uart) { // after nordic/lora - if (configure_interface(COMPORT_USART)) { - break; - } + if (configure_interface(COMPORT_USART)) break; } dbg("No alternate com interface configured."); gActiveComport = COMPORT_NONE; } while (0); } else { - dbg("USB got address 0x%02x - OK", (int)uadr); + dbg("USB got address - OK"); } xfer_verify_done = true; @@ -76,24 +74,11 @@ void com_switch_transfer_if_needed(void) void com_claim_resources_for_alt_transfers(void) { if (SystemSettings.use_comm_uart) { - do { - if (E_SUCCESS != rsc_claim(&UNIT_SYSTEM, R_USART2)) { - SystemSettings.use_comm_uart = false; - break; - } + iface_uart_claim_resources(); + } - if (E_SUCCESS != rsc_claim(&UNIT_SYSTEM, R_PA2)) { - SystemSettings.use_comm_uart = false; - rsc_free(&UNIT_SYSTEM, R_USART2); - break; - } - if (E_SUCCESS != rsc_claim(&UNIT_SYSTEM, R_PA3)) { - SystemSettings.use_comm_uart = false; - rsc_free(&UNIT_SYSTEM, R_USART2); - rsc_free(&UNIT_SYSTEM, R_PA2); - break; - } - } while (0); + if (SystemSettings.use_comm_nordic) { + iface_nordic_claim_resources(); } } @@ -101,46 +86,14 @@ void com_claim_resources_for_alt_transfers(void) void com_release_resources_for_alt_transfers(void) { if (SystemSettings.use_comm_uart) { - rsc_free(&UNIT_SYSTEM, R_USART2); - rsc_free(&UNIT_SYSTEM, R_PA2); - rsc_free(&UNIT_SYSTEM, R_PA3); + iface_uart_free_resources(); } -} - -static uint32_t usart_rxi = 0; -static uint8_t usart_rx_buffer[MSG_QUE_SLOT_SIZE]; -static uint32_t last_rx_time = 0; -/** Handler for the USART transport */ -static void com_UsartIrqHandler(void *arg) -{ - (void)arg; - if (LL_USART_IsActiveFlag_RXNE(USART2)) { - vPortEnterCritical(); - { - usart_rx_buffer[usart_rxi++] = LL_USART_ReceiveData8(USART2); - if (usart_rxi == MSG_QUE_SLOT_SIZE) { - rxQuePostMsg(usart_rx_buffer, MSG_QUE_SLOT_SIZE); // avoid it happening in the irq - usart_rxi = 0; - } - last_rx_time = HAL_GetTick(); - } - vPortExitCritical(); + if (SystemSettings.use_comm_nordic) { + iface_nordic_free_resources(); } } -/** this is called from the hal tick irq */ -void com_iface_flush_buffer(void) -{ - if (usart_rxi > 0 && (HAL_GetTick()-last_rx_time)>=2) { - vPortEnterCritical(); - { - rxQuePostMsg(usart_rx_buffer, usart_rxi); - usart_rxi = 0; - } - vPortExitCritical(); - } -} static bool configure_interface(enum ComportSelection iface) { @@ -151,14 +104,10 @@ static bool configure_interface(enum ComportSelection iface) __HAL_RCC_USB_CLK_DISABLE(); } else if (gActiveComport == COMPORT_USART) { - // this doesn't normally happen - hw_deinit_pin_rsc(R_PA2); - hw_deinit_pin_rsc(R_PA3); - __HAL_RCC_USART2_CLK_DISABLE(); - irqd_detach(USART2, com_UsartIrqHandler); + iface_uart_deinit(); } else if (gActiveComport == COMPORT_NORDIC) { - // TODO + iface_nordic_deinit(); } @@ -170,45 +119,20 @@ static bool configure_interface(enum ComportSelection iface) return false; } else if (iface == COMPORT_USART) { - dbg("Setting up UART transfer"); - assert_param(E_SUCCESS == hw_configure_gpiorsc_af(R_PA2, LL_GPIO_AF_1)); - assert_param(E_SUCCESS == hw_configure_gpiorsc_af(R_PA3, LL_GPIO_AF_1)); - - __HAL_RCC_USART2_CLK_ENABLE(); - __HAL_RCC_USART2_FORCE_RESET(); - __HAL_RCC_USART2_RELEASE_RESET(); - - LL_USART_Disable(USART2); - - LL_USART_SetBaudRate(USART2, PLAT_APB1_HZ, LL_USART_OVERSAMPLING_16, SystemSettings.comm_uart_baud); - dbg("baud = %d", (int)SystemSettings.comm_uart_baud); - - irqd_attach(USART2, com_UsartIrqHandler, NULL); - LL_USART_EnableIT_RXNE(USART2); - LL_USART_SetTransferDirection(USART2, LL_USART_DIRECTION_TX_RX); - - LL_USART_Enable(USART2); - - return true; // always OK (TODO check voltage on Rx if it's 3V3 when idle?) + return iface_uart_init(); } else if (iface == COMPORT_NORDIC) { - // Try to configure nordic - dbg("Setting up nRF transfer"); - - // TODO set up and check nRF transport - - - // On failure, try setting up LoRa - dbg("nRF failed to init"); - return false; + return iface_nordic_init(); } +#if 0 else if (iface == COMPORT_LORA) { // Try to configure nordic dbg("Setting up LoRa transfer"); // TODO set up and check LoRa transport - dbg("LoRa failed to init"); + dbg("LoRa not impl!"); return false; } +#endif else { trap("Bad iface %d", iface); } diff --git a/comm/nrf.c b/comm/nrf.c new file mode 100644 index 0000000..dd7e5aa --- /dev/null +++ b/comm/nrf.c @@ -0,0 +1,467 @@ +// +// Created by MightyPork on 2018/04/02. +// + +#include "platform.h" +#include "nrf.h" + +/** + * Read a register + * + * @param reg - register number (max 83) + * @return register value + */ +static uint8_t NRF_ReadRegister(uint8_t reg); + +/** + * Write a register + * + * @param reg - register number (max 83) + * @param value - register value + * @return status register value + */ +static uint8_t NRF_WriteRegister(uint8_t reg, uint8_t value); + +// Internal use +static uint8_t NRF_WriteBuffer(uint8_t reg, const uint8_t *pBuf, uint8_t bytes); + +static uint8_t NRF_ReadBuffer(uint8_t reg, uint8_t *pBuf, uint8_t bytes); + +/** + * Set TX address for next frame + * + * @param SendTo - addr + */ +static void NRF_SetTxAddress(uint8_t ToAddr); + +//---------------------------------------------------------- + +/** Tight asm loop */ +#define __asm_loop(cycles) \ +do { \ + register uint32_t _count asm ("r4") = cycles; \ + asm volatile( \ + ".syntax unified\n" \ + ".thumb\n" \ + "0:" \ + "subs %0, #1\n" \ + "bne 0b\n" \ + : "+r" (_count)); \ +} while(0) + +// 12 for 72 MHz +#define _delay_us(n) __asm_loop((n)*8) + +static inline void CE(bool level) { + if (level) LL_GPIO_SetOutputPin(NRF_CE_GPIO_Port, NRF_CE_Pin); + else LL_GPIO_ResetOutputPin(NRF_CE_GPIO_Port, NRF_CE_Pin); +} + +static inline void NSS(bool level) { + if (level) LL_GPIO_SetOutputPin(NRF_NSS_GPIO_Port, NRF_NSS_Pin); + else LL_GPIO_ResetOutputPin(NRF_NSS_GPIO_Port, NRF_NSS_Pin); +} + +static uint8_t spi(uint8_t tx) { + while (! LL_SPI_IsActiveFlag_TXE(NRF_SPI)); + LL_SPI_TransmitData8(NRF_SPI, tx); + while (! LL_SPI_IsActiveFlag_RXNE(NRF_SPI)); + return LL_SPI_ReceiveData8(NRF_SPI); +} + +//------- + +/* + * from: http://barefootelectronics.com/NRF24L01.aspx + */ + +#define CMD_READ_REG 0x00 // Read command to register +#define CMD_WRITE_REG 0x20 // Write command to register +#define CMD_RD_RX_PLD 0x61 // Read RX payload register address +#define CMD_WR_TX_PLD 0xA0 // Write TX payload register address +#define CMD_FLUSH_TX 0xE1 // Flush TX register command +#define CMD_FLUSH_RX 0xE2 // Flush RX register command +#define CMD_REUSE_TX_PL 0xE3 // Reuse TX payload register command +#define CMD_RD_RX_PL_WIDTH 0x60 // Read RX Payload Width +#define CMD_WR_ACK_PLD 0xA8 // Write ACK payload for Pipe (Add pipe number 0-6) +#define CMD_WR_TX_PLD_NK 0xB0 // Write ACK payload for not ack +#define CMD_NOP 0xFF // No Operation, might be used to read status register + +// SPI(nRF24L01) registers(addresses) +#define RG_CONFIG 0x00 // 'Config' register address +#define RG_EN_AA 0x01 // 'Enable Auto Acknowledgment' register address +#define RG_EN_RXADDR 0x02 // 'Enabled RX addresses' register address +#define RG_SETUP_AW 0x03 // 'Setup address width' register address +#define RG_SETUP_RETR 0x04 // 'Setup Auto. Retrans' register address +#define RG_RF_CH 0x05 // 'RF channel' register address +#define RG_RF_SETUP 0x06 // 'RF setup' register address +#define RG_STATUS 0x07 // 'Status' register address +#define RG_OBSERVE_TX 0x08 // 'Observe TX' register address +#define RG_CD 0x09 // 'Carrier Detect' register address +#define RG_RX_ADDR_P0 0x0A // 'RX address pipe0' register address +#define RG_RX_ADDR_P1 0x0B // 'RX address pipe1' register address +#define RG_RX_ADDR_P2 0x0C // 'RX address pipe2' register address +#define RG_RX_ADDR_P3 0x0D // 'RX address pipe3' register address +#define RG_RX_ADDR_P4 0x0E // 'RX address pipe4' register address +#define RG_RX_ADDR_P5 0x0F // 'RX address pipe5' register address +#define RG_TX_ADDR 0x10 // 'TX address' register address +#define RG_RX_PW_P0 0x11 // 'RX payload width, pipe0' register address +#define RG_RX_PW_P1 0x12 // 'RX payload width, pipe1' register address +#define RG_RX_PW_P2 0x13 // 'RX payload width, pipe2' register address +#define RG_RX_PW_P3 0x14 // 'RX payload width, pipe3' register address +#define RG_RX_PW_P4 0x15 // 'RX payload width, pipe4' register address +#define RG_RX_PW_P5 0x16 // 'RX payload width, pipe5' register address +#define RG_FIFO_STATUS 0x17 // 'FIFO Status Register' register address +#define RG_DYNPD 0x1C // 'Enable dynamic payload length +#define RG_FEATURE 0x1D // 'Feature register + +#define RD_STATUS_TX_FULL 0x01 // tx queue full +#define RD_STATUS_RX_PNO 0x0E // pipe number of the received payload +#define RD_STATUS_MAX_RT 0x10 // max retransmissions +#define RD_STATUS_TX_DS 0x20 // data sent +#define RD_STATUS_RX_DR 0x40 // data ready + +#define RD_CONFIG_PRIM_RX 0x01 // is primary receiver +#define RD_CONFIG_PWR_UP 0x02 // power up +#define RD_CONFIG_CRCO 0x04 // 2-byte CRC +#define RD_CONFIG_EN_CRC 0x08 // enable CRC +#define RD_CONFIG_DISABLE_IRQ_MAX_RT 0x10 +#define RD_CONFIG_DISABLE_IRQ_TX_DS 0x20 +#define RD_CONFIG_DISABLE_IRQ_RX_DR 0x40 + +// Config register bits (excluding the bottom two that are changed dynamically) +// enable only Rx IRQ +#define ModeBits (RD_CONFIG_DISABLE_IRQ_MAX_RT | \ + RD_CONFIG_DISABLE_IRQ_TX_DS | \ + RD_CONFIG_EN_CRC | \ + RD_CONFIG_CRCO) + +#define CEHIGH CE(1) +#define CELOW CE(0) + +static inline uint8_t CS(uint8_t hl) +{ + if (hl == 1) { + NSS(0); + } + else { + NSS(1); + } + return hl; +} + +#define CHIPSELECT for (uint8_t cs = CS(1); cs==1; cs = CS(0)) + +// Base NRF address - byte 4 may be modified before writing to a pipe register, +// the first 4 bytes are shared in the network. Master is always code 0. +static uint8_t nrf_base_address[5]; + +static uint8_t nrf_pipe_addr[6]; +static bool nrf_pipe_enabled[6]; + +static uint8_t NRF_WriteRegister(uint8_t reg, uint8_t value) +{ + uint8_t status = 0; + CHIPSELECT { + status = spi(CMD_WRITE_REG | reg); + spi(value); + } + return status; +} + +static uint8_t NRF_ReadRegister(uint8_t reg) +{ + uint8_t reg_val = 0; + CHIPSELECT { + spi(CMD_READ_REG | reg); + reg_val = spi(0); + } + return reg_val; +} + +static uint8_t NRF_ReadStatus(void) +{ + uint8_t reg_val = 0; + CHIPSELECT { + reg_val = spi(CMD_NOP); + } + return reg_val; +} + +static uint8_t NRF_WriteBuffer(uint8_t reg, const uint8_t *pBuf, uint8_t bytes) +{ + uint8_t status = 0, i = 0; + CHIPSELECT { + status = spi(CMD_WRITE_REG | reg); + for (i = 0; i < bytes; i++) { + spi(*pBuf++); + } + } + return status; +} + +static uint8_t NRF_ReadBuffer(uint8_t reg, uint8_t *pBuf, uint8_t bytes) +{ + uint8_t status = 0, i; + CHIPSELECT { + status = spi(CMD_READ_REG | reg); + for (i = 0; i < bytes; i++) { + pBuf[i] = spi(0); + } + } + return status; +} + +void NRF_SetBaseAddress(const uint8_t *Bytes4) +{ + memcpy(nrf_base_address, Bytes4, 4); + nrf_base_address[4] = 0; +} + +void NRF_SetRxAddress(uint8_t pipenum, uint8_t AddrByte) +{ + if (pipenum > 5) { + dbg("!! bad pipe %d", pipenum); + return; + } + + nrf_base_address[4] = AddrByte; + + nrf_pipe_addr[pipenum] = AddrByte; + + if (pipenum == 0) { + NRF_WriteBuffer(RG_RX_ADDR_P0, nrf_base_address, 5); + } + else if (pipenum == 1) { + NRF_WriteBuffer(RG_RX_ADDR_P1, nrf_base_address, 5); + } + else { + NRF_WriteRegister(RG_RX_ADDR_P2 + (pipenum - 2), AddrByte); + } +} + +bool NRF_AddPipe(uint8_t AddrByte, uint8_t *PipeNum) +{ + for (uint8_t i = 0; i < 6; i++) { + if(!nrf_pipe_enabled[i]) { + NRF_SetRxAddress(i, AddrByte); + *PipeNum = i; + NRF_EnablePipe(i); + return true; + } + } + + return false; +} + +uint8_t NRF_PipeNum2Addr(uint8_t pipe_num) +{ + if (pipe_num > 5) return 0; // fail + return nrf_pipe_addr[pipe_num]; +} + +uint8_t NRF_Addr2PipeNum(uint8_t addr) +{ + for (int i = 0; i < 6; i++) { + if (nrf_pipe_addr[i] == addr) return (uint8_t) i; + } + return 0xFF; +} + +void NRF_EnablePipe(uint8_t pipenum) +{ + uint8_t enabled = NRF_ReadRegister(RG_EN_RXADDR); + enabled |= 1 << pipenum; + NRF_WriteRegister(RG_EN_RXADDR, enabled); + + nrf_pipe_enabled[pipenum] = 1; +} + +void NRF_DisablePipe(uint8_t pipenum) +{ + uint8_t enabled = NRF_ReadRegister(RG_EN_RXADDR); + enabled &= ~(1 << pipenum); + NRF_WriteRegister(RG_EN_RXADDR, enabled); + + nrf_pipe_enabled[pipenum] = 0; +} + +static void NRF_SetTxAddress(uint8_t SendTo) +{ + nrf_base_address[4] = SendTo; + NRF_WriteBuffer(RG_TX_ADDR, nrf_base_address, 5); + NRF_WriteBuffer(RG_RX_ADDR_P0, nrf_base_address, 5); // the ACK will come to pipe 0 +} + +void NRF_PowerDown(void) +{ + CELOW; + NRF_WriteRegister(RG_CONFIG, ModeBits); +} + +void NRF_ModeTX(void) +{ + CELOW; + uint8_t m = NRF_ReadRegister(RG_CONFIG); + NRF_WriteRegister(RG_CONFIG, ModeBits | RD_CONFIG_PWR_UP); + if ((m & RD_CONFIG_PWR_UP) == 0) LL_mDelay(5); +} + +void NRF_ModeRX(void) +{ + NRF_WriteRegister(RG_CONFIG, ModeBits | RD_CONFIG_PWR_UP | RD_CONFIG_PRIM_RX); + NRF_SetRxAddress(0, nrf_pipe_addr[0]); // set the P0 address - it was changed during Rx for ACK reception + CEHIGH; + + //if ((m&2)==0) LL_mDelay()(5); You don't need to wait. Just nothing will come for 5ms or more +} + +uint8_t NRF_IsModePowerDown(void) +{ + uint8_t ret = 0; + if ((NRF_ReadRegister(RG_CONFIG) & RD_CONFIG_PWR_UP) == 0) ret = 1; + return ret; +} + +uint8_t NRF_IsModeTX(void) +{ + uint8_t m = NRF_ReadRegister(RG_CONFIG); + if ((m & RD_CONFIG_PWR_UP) == 0) return 0; // OFF + if ((m & RD_CONFIG_PRIM_RX) == 0) return 1; + return 0; +} + +uint8_t NRF_IsModeRx(void) +{ + uint8_t m = NRF_ReadRegister(RG_CONFIG); + if ((m & RD_CONFIG_PWR_UP) == 0) return 0; // OFF + if ((m & RD_CONFIG_PRIM_RX) == 0) return 0; + return 1; +} + +void NRF_SetChannel(uint8_t Ch) +{ + NRF_WriteRegister(RG_RF_CH, Ch); +} + +uint8_t NRF_ReceivePacket(uint8_t *Packet, uint8_t *PipeNum) +{ + uint8_t pw = 0, status; + if (!NRF_IsRxPacket()) return 0; + + CELOW; + CHIPSELECT { + status = spi(CMD_RD_RX_PL_WIDTH); + pw = spi(0); + } + + if (pw > 32) { + CHIPSELECT { + spi(CMD_FLUSH_RX); + } + NRF_WriteRegister(RG_STATUS, RD_STATUS_RX_DR); + return 0; + } + + // Read the reception pipe number + *PipeNum = (status & RD_STATUS_RX_PNO) >> 1; + + CHIPSELECT { + spi(CMD_RD_RX_PLD); + for (uint8_t i = 0; i < pw; i++) Packet[i] = spi(0); + } + NRF_WriteRegister(RG_STATUS, RD_STATUS_RX_DR); // Clear the RX_DR interrupt + CEHIGH; + return pw; +} + +bool NRF_IsRxPacket(void) +{ + uint8_t ret = NRF_ReadRegister(RG_STATUS) & RD_STATUS_RX_DR; + return 0 != ret; +} + +bool NRF_SendPacket(uint8_t PipeNum, const uint8_t *Packet, uint8_t Length) +{ + if (!nrf_pipe_enabled[PipeNum]) { + dbg("!! Pipe %d not enabled", PipeNum); + return 0; + } + + const uint8_t orig_conf = NRF_ReadRegister(RG_CONFIG); + CELOW; + NRF_ModeTX(); // Make sure in TX mode + NRF_SetTxAddress(nrf_pipe_addr[PipeNum]); + + CHIPSELECT { + spi(CMD_FLUSH_TX); + }; + + CHIPSELECT { + spi(CMD_WR_TX_PLD); + for (uint8_t i = 0; i < Length; i++) spi(Packet[i]); + }; + + CEHIGH; + _delay_us(15); // At least 10 us + CELOW; + + uint8_t st = 0; + while ((st & (RD_STATUS_MAX_RT|RD_STATUS_TX_DS)) == 0) { + st = NRF_ReadStatus(); // Packet acked or timed out + } + + NRF_WriteRegister(RG_STATUS, st & (RD_STATUS_MAX_RT|RD_STATUS_TX_DS)); // Clear the bit + + if ((orig_conf & RD_CONFIG_PWR_UP) == 0) { + NRF_PowerDown(); + } + else if ((orig_conf & RD_CONFIG_PRIM_RX) == RD_CONFIG_PRIM_RX) { + NRF_ModeRX(); + } + + return 0 != (st & RD_STATUS_TX_DS); // success +} + +void NRF_Reset(void) +{ + NSS(1); + CELOW; + NRF_PowerDown(); + + NRF_WriteRegister(RG_EN_RXADDR, 0); // disable all pipes + + for (int i = 0; i < 6; i++) { + nrf_pipe_addr[i] = 0; + nrf_pipe_enabled[i] = 0; + } +} + +void NRF_Init(uint8_t pSpeed) +{ + // Set the required output pins + NSS(1); + CELOW; + + LL_mDelay(200); + + NRF_WriteRegister(RG_CONFIG, ModeBits); + NRF_WriteRegister(RG_SETUP_AW, 0b11); // 5 byte addresses + + // default +// NRF_EnablePipe(0); + + NRF_WriteRegister(RG_SETUP_RETR, 0x18); // 8 retries + NRF_WriteRegister(RG_RF_CH, 2); // channel 2 NO HIGHER THAN 83 in USA! + + NRF_WriteRegister(RG_RF_SETUP, pSpeed); + + NRF_WriteRegister(RG_DYNPD, 0b111111); // Dynamic packet length + NRF_WriteRegister(RG_FEATURE, 0b100); // Enable dynamic payload, and no payload in the ack. + + for (int i = 0; i < 6; i++) { + NRF_WriteRegister(RG_RX_PW_P0+i, 32); // Receive 32 byte packets - XXX this is probably not needed with dynamic length + } + + //NRFModePowerDown(); // Already in power down mode, dummy +} diff --git a/comm/nrf.h b/comm/nrf.h new file mode 100644 index 0000000..a2e1dc2 --- /dev/null +++ b/comm/nrf.h @@ -0,0 +1,159 @@ +// +// Created by MightyPork on 2018/04/02. +// + +#ifndef GEX_NRF_NRF_H +#define GEX_NRF_NRF_H + +/* + * nordic.h + * + * Created:12/16/2013 3:36:04 PM + * Author: Tom + * + * NRF24L01+ Library II + * + */ + + +#ifndef NORDIC_H_ +#define NORDIC_H_ + +#include "platform.h" +#include "resources.h" +#include "nrf_pins.h" + +// Initialize SPI and the Nordic + +/** + * Initialize the NRF module + * + * @param pSpeed + */ +void NRF_Init(uint8_t pSpeed); + +#define NRF_SPEED_500k 0b00100110 +#define NRF_SPEED_2M 0b00001110 +#define NRF_SPEED_1M 0b00000110 + +/** + * Set reception address + * @param pipenum - pipe to set + * @param AddrByte - byte 0 + */ +void NRF_SetRxAddress(uint8_t pipenum, uint8_t AddrByte); + +/** + * Set communication channel + */ +void NRF_SetChannel(uint8_t Ch) ; // 0 through 83 only! + +/** + * Power down the transceiver + */ +void NRF_PowerDown(void); + +/** + * Selecr RX mode + */ +void NRF_ModeRX(void); + +/** + * Select TX mode + */ +void NRF_ModeTX(void); + +/** + * @return NRF is power down + */ +uint8_t NRF_IsModePowerDown(void); + +/** + * @return NRF in TX mode + */ +uint8_t NRF_IsModeTX(void); + +/** + * @return NRF in RX mode + */ +uint8_t NRF_IsModeRx(void); + +/** + * Add a pipe to the next free slot + * + * @param AddrByte - address byte of the peer + * @param[out] PipeNum - pipe number is written here + * @return success + */ +bool NRF_AddPipe(uint8_t AddrByte, uint8_t *PipeNum); + +/** + * Convert pipe number to address byte + * + * @param pipe_num - pipe number + * @return address byte + */ +uint8_t NRF_PipeNum2Addr(uint8_t pipe_num); + +/** + * Convert address to a pipe number + * + * @param addr + * @return + */ +uint8_t NRF_Addr2PipeNum(uint8_t addr); + +/** + * Reset as much as possible (incl. removing pipes) + */ +void NRF_Reset(void); + +/** + * Send a packet (takes care of mode switching etc) + * + * @param PipeNum - pipe number + * @param Packet - packet bytes + * @param Length - packet length + * @return success (ACK'd) + */ +bool NRF_SendPacket(uint8_t PipeNum, const uint8_t *Packet, uint8_t Length); + +/** + * Receive a packet + * + * @param[out] Packet - the receiuved packet - buffer that is written + * @param[out] PipeNum - pipe number that was received from + * @return packet size, 0 on failure + */ +uint8_t NRF_ReceivePacket(uint8_t *Packet, uint8_t *PipeNum); + +/** + * Set base address + * @param Bytes4 + */ +void NRF_SetBaseAddress(const uint8_t *Bytes4); + +/** + * Check if there's a packet to be read + * + * @return 1 if any to read + */ +bool NRF_IsRxPacket(void); + +/** + * Enable a pipe + * + * @param pipenum - pipe number 0-5 + */ +void NRF_EnablePipe(uint8_t pipenum); + +/** + * Disable a pipe + * + * @param pipenum - pipe number 0-5 + */ +void NRF_DisablePipe(uint8_t pipenum); + +#endif /* NORDIC_H_ */ + +#endif //GEX_NRF_NRF_H diff --git a/comm/nrf_pins.h b/comm/nrf_pins.h new file mode 100644 index 0000000..c30b4aa --- /dev/null +++ b/comm/nrf_pins.h @@ -0,0 +1,33 @@ +// +// Created by MightyPork on 2018/04/06. +// + +#ifndef GEX_F072_NRF_PINS_H +#define GEX_F072_NRF_PINS_H + +#include "platform.h" + +// TODO move those to plat_compat? +#define NRF_SPI SPI1 +#define NRF_R_SPI R_SPI1 + +#define NRF_IRQ_Pin LL_GPIO_PIN_10 +#define NRF_IRQ_GPIO_Port GPIOB +#define NRF_R_IRQ R_PB10 +#define NRF_EXTI_LINENUM 10 +#define NRF_SYSCFG_EXTI_PORT LL_SYSCFG_EXTI_PORTB + +#define NRF_NSS_Pin LL_GPIO_PIN_11 +#define NRF_NSS_GPIO_Port GPIOB +#define NRF_R_NSS R_PB11 + +#define NRF_CE_Pin LL_GPIO_PIN_12 +#define NRF_CE_GPIO_Port GPIOB +#define NRF_R_CE R_PB12 + +#define NRF_R_SCK R_PA5 +#define NRF_R_MISO R_PA6 +#define NRF_R_MOSI R_PA7 +#define NRF_SPI_AF LL_GPIO_AF_0 + +#endif //GEX_F072_NRF_PINS_H diff --git a/framework/system_settings.c b/framework/system_settings.c index d8f462c..3dbda4a 100644 --- a/framework/system_settings.c +++ b/framework/system_settings.c @@ -49,7 +49,7 @@ void systemsettings_init(void) void systemsettings_save(PayloadBuilder *pb) { pb_char(pb, 'S'); - pb_u8(pb, 2); // settings format version + pb_u8(pb, 3); // settings format version { // system settings pb_bool(pb, SystemSettings.visible_vcom); @@ -63,9 +63,52 @@ void systemsettings_save(PayloadBuilder *pb) pb_bool(pb, SystemSettings.use_comm_lora); pb_u32(pb, SystemSettings.comm_uart_baud); pb_bool(pb, SystemSettings.enable_debug_uart); + // 3 + pb_u8(pb, SystemSettings.nrf_channel); + pb_u8(pb, SystemSettings.nrf_address); + pb_buf(pb, SystemSettings.nrf_network, 4); } // end system settings } + +// from binary +bool systemsettings_load(PayloadParser *pp) +{ + if (pp_char(pp) != 'S') return false; + + systemsettings_begin_load(); + + uint8_t version = pp_u8(pp); + + { // system settings + SystemSettings.visible_vcom = pp_bool(pp); + SystemSettings.ini_comments = pp_bool(pp); + + // conditional fields based on version + if (version >= 1) { + SystemSettings.enable_mco = pp_bool(pp); + SystemSettings.mco_prediv = pp_u8(pp); + } + if (version >= 2) { + SystemSettings.use_comm_uart = pp_bool(pp); + SystemSettings.use_comm_nordic = pp_bool(pp); + SystemSettings.use_comm_lora = pp_bool(pp); + SystemSettings.comm_uart_baud = pp_u32(pp); + SystemSettings.enable_debug_uart = pp_bool(pp); + } + if (version >= 3) { + SystemSettings.nrf_channel = pp_u8(pp); + SystemSettings.nrf_address = pp_u8(pp); + pp_buf(pp, SystemSettings.nrf_network, 4); + } + } // end system settings + + systemsettings_finalize_load(); + + return pp->ok; +} + + void systemsettings_mco_teardown(void) { if (SystemSettings.enable_mco) { @@ -80,8 +123,7 @@ void systemsettings_mco_init(void) assert_param(rsc_claim(&UNIT_SYSTEM, R_PA8) == E_SUCCESS); assert_param(E_SUCCESS == hw_configure_gpiorsc_af(R_PA8, LL_GPIO_AF_0)); - LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_SYSCLK, - SystemSettings.mco_prediv << RCC_CFGR_MCOPRE_Pos); + LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_SYSCLK, SystemSettings.mco_prediv << RCC_CFGR_MCOPRE_Pos); } else { LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_NOCLOCK, 0); } @@ -115,39 +157,6 @@ void systemsettings_finalize_load(void) com_claim_resources_for_alt_transfers(); } -// from binary -bool systemsettings_load(PayloadParser *pp) -{ - if (pp_char(pp) != 'S') return false; - - systemsettings_begin_load(); - - uint8_t version = pp_u8(pp); - - { // system settings - SystemSettings.visible_vcom = pp_bool(pp); - SystemSettings.ini_comments = pp_bool(pp); - - // conditional fields based on version - if (version >= 1) { - SystemSettings.enable_mco = pp_bool(pp); - SystemSettings.mco_prediv = pp_u8(pp); - } - if (version >= 2) { - SystemSettings.use_comm_uart = pp_bool(pp); - SystemSettings.use_comm_nordic = pp_bool(pp); - SystemSettings.use_comm_lora = pp_bool(pp); - SystemSettings.comm_uart_baud = pp_u32(pp); - SystemSettings.enable_debug_uart = pp_bool(pp); - } - } // end system settings - - systemsettings_finalize_load(); - - return pp->ok; -} - - /** * Write system settings to INI (without section) */ diff --git a/gex_hooks.c b/gex_hooks.c index c0a920a..9da2fb5 100644 --- a/gex_hooks.c +++ b/gex_hooks.c @@ -11,13 +11,17 @@ #include "gex_hooks.h" #include "unit_registry.h" #include "comm/interfaces.h" +#include "system_settings.h" /** * This is a systick callback for GEX application logic */ void GEX_MsTick(void) { - com_iface_flush_buffer(); + if (gActiveComport == COMPORT_USART) { + com_iface_flush_buffer(); + } + TF_Tick(comm); Indicator_Tick(); ureg_tick_units(); @@ -28,6 +32,11 @@ void GEX_MsTick(void) */ void GEX_PreInit(void) { + // this is a hack to make logging of the initial messages work + // it's problematic because we shouldn't even enable the debug uart if it's disabled in the system settings + // TODO move system settings load earlier and check the uart flag in advance + SystemSettings.enable_debug_uart = true; + __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); diff --git a/platform/platform.c b/platform/platform.c index 4e1ebf3..b4dda15 100644 --- a/platform/platform.c +++ b/platform/platform.c @@ -41,6 +41,9 @@ void plat_init_resources(void) ureg_add_type(&UNIT_TEST); #endif + // EXTI are always available + rsc_free_range(NULL, R_EXTI0, R_EXTI15); + // --- platform specific resource releases and claims --- #if defined(GEX_PLAT_F103_BLUEPILL) diff --git a/units/digital_in/_din_init.c b/units/digital_in/_din_init.c index 7d501ce..ff3edff 100644 --- a/units/digital_in/_din_init.c +++ b/units/digital_in/_din_init.c @@ -54,7 +54,19 @@ error_t DIn_init(Unit *unit) // Claim all needed pins TRY(rsc_claim_gpios(unit, priv->port_name, priv->pins)); - uint16_t mask = 1; + uint16_t mask; + + // claim the needed EXTIs + mask = 1; + for (int i = 0; i < 16; i++, mask <<= 1) { + if (priv->pins & mask) { + if ((priv->trig_rise|priv->trig_fall) & mask) { + TRY(rsc_claim(unit, R_EXTI0+i)); + } + } + } + + mask = 1; for (int i = 0; i < 16; i++, mask <<= 1) { if (priv->pins & mask) { uint32_t ll_pin = hw_pin2ll((uint8_t) i, &suc);