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("NRF RX %d bytes", (int) count); + rxQuePostMsg(rx_buffer, count); + } + else { + dbg("IRQ but no Rx"); + } + } + + dbg_nrf("--- end [EXTI]"); +} + +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_DIV32); + //LL_SPI_BAUDRATEPRESCALER_DIV8 + + LL_SPI_SetClockPolarity(NRF_SPI, LL_SPI_POLARITY_LOW); + LL_SPI_SetClockPhase(NRF_SPI, LL_SPI_PHASE_1EDGE); + 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_EnablePipe(RX_PIPE_NUM); + 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); +} + +// FIXME +#define MAX_RETRY 5 + +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; + //hexDump("Tx chunk", buff, chunk); + 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 *= 2; // longer delay next time + } else { + break; + } + } + + if (!suc) { + break; + } + + buff += chunk; + len -= chunk; + } + + if (suc) { + dbg_nrf("+ 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 13086ec..0ab281c 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,13 +11,26 @@ #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" + +const char * COMPORT_NAMES[] = { + "NONE", + "USB", + "UART", + "NRF", + "LORA", +}; + enum ComportSelection gActiveComport = COMPORT_USB; // start with USB so the handlers work correctly initially static uint32_t last_switch_time = 0; // started with USB static bool xfer_verify_done = false; -static void configure_interface(enum ComportSelection iface); +static bool configure_interface(enum ComportSelection iface); /** Switch com transfer if the current one doesnt seem to work */ void com_switch_transfer_if_needed(void) @@ -30,30 +42,36 @@ void com_switch_transfer_if_needed(void) if (gActiveComport == COMPORT_USB) { if (elapsed > 1000) { - // USB may or may not work, depending on whether the module is plugged - - // in or running from a battery/external supply remotely. + // USB may or may not work, depending on whether the module is plugged in + // or running from a battery/external supply remotely. // 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 bare USART - if (SystemSettings.use_comm_uart) { - configure_interface(COMPORT_USART); - } - else if (SystemSettings.use_comm_nordic) { - configure_interface(COMPORT_NORDIC); // this fallbacks to LoRa if LoRa enabled - } - else if (SystemSettings.use_comm_lora) { - configure_interface(COMPORT_LORA); - } - else { - dbg("No alternate com interface configured, leaving USB enabled."); - } + // Fallback to radio or bare USART + do { + if (SystemSettings.use_comm_nordic) { + if (configure_interface(COMPORT_NORDIC)) break; + } + +#if 0 + if (SystemSettings.use_comm_lora) { + if (configure_interface(COMPORT_LORA)) break; + } +#endif + + if (SystemSettings.use_comm_uart) { + // after nordic/lora + 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; @@ -65,24 +83,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(); } } @@ -90,48 +95,16 @@ 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 void configure_interface(enum ComportSelection iface) +static bool configure_interface(enum ComportSelection iface) { // Teardown if (gActiveComport == COMPORT_USB) { @@ -140,68 +113,36 @@ static void 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(); } - gActiveComport = COMPORT_NONE; + else if (gActiveComport == COMPORT_NORDIC) { + iface_nordic_deinit(); + } + + + gActiveComport = iface; // Init if (iface == COMPORT_USB) { trap("illegal"); // this never happens + 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 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"); - if (SystemSettings.use_comm_lora) { - iface = COMPORT_LORA; - } else { - iface = COMPORT_NONE; // fail - } - } - - 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"); - iface = COMPORT_NONE; // fail - } + else if (iface == COMPORT_NORDIC) { + return iface_nordic_init(); } - - if (iface == COMPORT_NONE) { - dbg("NO COM PORT AVAILABLE!"); +#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 not impl!"); + return false; + } +#endif + else { + trap("Bad iface %d", iface); } - - gActiveComport = iface; } diff --git a/comm/interfaces.h b/comm/interfaces.h index 5d65766..fe56abe 100644 --- a/comm/interfaces.h +++ b/comm/interfaces.h @@ -15,6 +15,8 @@ enum ComportSelection { COMPORT_LORA = 4, }; +extern const char * COMPORT_NAMES[]; + /** * The currently active communication port */ diff --git a/comm/messages.c b/comm/messages.c index e1b679a..170d8b9 100644 --- a/comm/messages.c +++ b/comm/messages.c @@ -11,6 +11,7 @@ #include "framework/system_settings.h" #include "utils/malloc_safe.h" #include "platform/status_led.h" +#include "interfaces.h" static TinyFrame tf_; TinyFrame *comm = &tf_; @@ -23,7 +24,7 @@ TinyFrame *comm = &tf_; static TF_Result lst_ping(TinyFrame *tf, TF_Msg *msg) { com_respond_snprintf(msg->frame_id, MSG_SUCCESS, - "GEX v%s on %s", GEX_VERSION, GEX_PLATFORM); + "GEX v%s on %s (%s)", GEX_VERSION, GEX_PLATFORM, COMPORT_NAMES[gActiveComport]); return TF_STAY; } @@ -58,26 +59,33 @@ static TF_Result lst_list_units(TinyFrame *tf, TF_Msg *msg) // --------------------------------------------------------------------------- -/** Callback for bulk read of the settings file */ -static void settings_bulkread_cb(BulkRead *bulk, uint32_t chunk, uint8_t *buffer) +/** Callback for bulk read of a settings file */ +static void ini_bulkread_cb(BulkRead *bulk, uint32_t chunk, uint8_t *buffer) { // clean-up request if (buffer == NULL) { free_ck(bulk); iw_end(); -// dbg("INI read complete."); return; } if (bulk->offset == 0) iw_begin(); IniWriter iw = iw_init((char *)buffer, bulk->offset, chunk); - iw.tag = 1; - settings_build_units_ini(&iw); + iw.tag = 1; // indicates this is read via the API (affects some comments) + + uint8_t filenum = (uint8_t) (int) bulk->userdata; + + if (filenum == 0) { + settings_build_units_ini(&iw); + } + else if (filenum == 1) { + settings_build_system_ini(&iw); + } } /** - * Listener: Export INI file via TF + * Listener: Export a file via TF */ static TF_Result lst_ini_export(TinyFrame *tf, TF_Msg *msg) { @@ -86,14 +94,29 @@ static TF_Result lst_ini_export(TinyFrame *tf, TF_Msg *msg) BulkRead *bulk = malloc_ck(sizeof(BulkRead)); assert_param(bulk != NULL); + uint8_t filenum = 0; + + // if any payload, the first byte defines the file to read + // 0 - units + // 1 - system + // (this is optional for backwards compatibility) + if (msg->len > 0) { + filenum = msg->data[0]; + } + bulk->frame_id = msg->frame_id; - bulk->len = iw_measure_total(settings_build_units_ini, 1); - bulk->read = settings_bulkread_cb; - bulk->userdata = NULL; + bulk->read = ini_bulkread_cb; + bulk->userdata = (void *) (int)filenum; + + if (filenum == 0) { + bulk->len = iw_measure_total(settings_build_units_ini, 1); + } + else if (filenum == 1) { + bulk->len = iw_measure_total(settings_build_system_ini, 1); + } bulkread_start(tf, bulk); Indicator_Effect(STATUS_DISK_BUSY_SHORT); - return TF_STAY; } @@ -142,7 +165,7 @@ static TF_Result lst_ini_import(TinyFrame *tf, TF_Msg *msg) PayloadParser pp = pp_start(msg->data, msg->len, NULL); uint32_t len = pp_u32(&pp); if (!pp.ok) { - com_respond_error(msg->frame_id, E_PROTOCOL_BREACH); + com_respond_error(msg->frame_id, E_MALFORMED_COMMAND); goto done; } @@ -151,11 +174,8 @@ static TF_Result lst_ini_import(TinyFrame *tf, TF_Msg *msg) settings_load_ini_begin(); ini_parse_begin(iniparser_cb, NULL); - bulkwrite_start(tf, bulk); - Indicator_Effect(STATUS_DISK_BUSY); - done: return TF_STAY; } diff --git a/comm/nrf.c b/comm/nrf.c new file mode 100644 index 0000000..45670e2 --- /dev/null +++ b/comm/nrf.c @@ -0,0 +1,530 @@ +// +// 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) { + __asm_loop(1); + if (level) LL_GPIO_SetOutputPin(NRF_CE_GPIO_Port, NRF_CE_Pin); + else LL_GPIO_ResetOutputPin(NRF_CE_GPIO_Port, NRF_CE_Pin); + __asm_loop(1); +} + +static inline void NSS(bool level) { + __asm_loop(1); + if (level) LL_GPIO_SetOutputPin(NRF_NSS_GPIO_Port, NRF_NSS_Pin); + else LL_GPIO_ResetOutputPin(NRF_NSS_GPIO_Port, NRF_NSS_Pin); + __asm_loop(1); +} + +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 + +#define RD_FIFO_STATUS_RX_EMPTY 0x01 +#define RD_FIFO_STATUS_RX_FULL 0x02 +#define RD_FIFO_STATUS_TX_EMPTY 0x10 +#define RD_FIFO_STATUS_TX_FULL 0x20 +#define RD_FIFO_STATUS_TX_REUSE 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) + +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); + } + dbg_nrf("Wr[0x%02x] := 0x%02x", (int)reg, (int) value); + + + uint8_t reg_val = 0; + CHIPSELECT { + spi(CMD_READ_REG | reg); + reg_val = spi(0); + } + dbg_nrf(" verify 0x%02x", (int)reg_val); + if (reg_val != value) + dbg_nrf(" !!!"); + else + dbg_nrf(" OK"); + + + 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); + } + dbg_nrf("Rd[0x%02x] = 0x%02x", (int)reg, (int) reg_val); + 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; + + // write it to the two full-width address registers + NRF_WriteBuffer(RG_RX_ADDR_P0, nrf_base_address, 5); + nrf_base_address[4] = 1;// to be different + NRF_WriteBuffer(RG_RX_ADDR_P1, nrf_base_address, 5); +} + +void NRF_SetRxAddress(uint8_t pipenum, uint8_t AddrByte) +{ + if (pipenum > 5) { + dbg_nrf("!! bad pipe %d", pipenum); + return; + } + + nrf_base_address[4] = AddrByte; + + nrf_pipe_addr[pipenum] = AddrByte; + + dbg_nrf("Set Rx addr (pipe %d) = 0x%02x", (int)pipenum, AddrByte); + if (pipenum == 0) { + dbg_nrf("W ADDR_PA0: %02X-%02X-%02X-%02X-%02X", nrf_base_address[0], nrf_base_address[1], nrf_base_address[2], nrf_base_address[3], nrf_base_address[4]); + NRF_WriteBuffer(RG_RX_ADDR_P0, nrf_base_address, 5); + } + else if (pipenum == 1) { + dbg_nrf("W ADDR_PA1: %02X-%02X-%02X-%02X-%02X", nrf_base_address[0], nrf_base_address[1], nrf_base_address[2], nrf_base_address[3], nrf_base_address[4]); + 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) +{ + dbg_nrf("Enable pipe num %d", (int)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; + + dbg_nrf("W Tx_ADDR + Rx0: %02X-%02X-%02X-%02X-%02X", + nrf_base_address[0], nrf_base_address[1], nrf_base_address[2], nrf_base_address[3], nrf_base_address[4]); + + 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) +{ + dbg_nrf("PDn"); + CE(0); + NRF_WriteRegister(RG_CONFIG, ModeBits); +} + +void NRF_ModeTX(void) +{ + dbg_nrf("Tx Mode"); + + CE(0); + uint8_t m = NRF_ReadRegister(RG_CONFIG); + NRF_WriteRegister(RG_CONFIG, ModeBits | RD_CONFIG_PWR_UP); + if ((m & RD_CONFIG_PWR_UP) == 0) { + // switching on + LL_mDelay(5); + } +} + +void NRF_ModeRX(void) +{ + dbg_nrf("Rx Mode"); + 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 + CE(1); + + //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()) { +// dbg("rx queue empty"); +// return 0; +// } + +// const uint8_t orig_conf = NRF_ReadRegister(RG_CONFIG); +// CE(0); // quit Rx mode - go idle + + CHIPSELECT { + status = spi(CMD_RD_RX_PL_WIDTH); + pw = spi(0); + } + + if (pw == 0) { + dbg("empty pld"); + } + + if (pw > 32) { + CHIPSELECT { + spi(CMD_FLUSH_RX); + } + pw = 0; + dbg("over 32"); + } else { + // 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 + +// if ((orig_conf & RD_CONFIG_PWR_UP) == 0) { +// dbg_nrf("going back PwrDn"); +// NRF_PowerDown(); +// } +// else if ((orig_conf & RD_CONFIG_PRIM_RX) == RD_CONFIG_PRIM_RX) { +// dbg_nrf("going back PwrUp+Rx"); +// NRF_ModeRX(); +// } +// CE(1); // back to rx + return pw; +} + +bool NRF_IsRxPacket(void) +{ + return 0 == (NRF_ReadRegister(RG_FIFO_STATUS) & RD_FIFO_STATUS_RX_EMPTY); +// 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_nrf("!! Pipe %d not enabled", PipeNum); + return 0; + } + + dbg_nrf("Will tx to addr %02x", nrf_pipe_addr[PipeNum]); + + const uint8_t orig_conf = NRF_ReadRegister(RG_CONFIG); + CE(0); + NRF_ModeTX(); // Make sure in TX mode + NRF_SetTxAddress(nrf_pipe_addr[PipeNum]); // this sets the Tx addr and also pipe 0 addr for ACK + + CHIPSELECT { + spi(CMD_FLUSH_TX); + }; + + CHIPSELECT { + spi(CMD_WR_TX_PLD); + for (uint8_t i = 0; i < Length; i++) spi(Packet[i]); + }; + + // CE pulse + CE(1); + _delay_us(20); // At least 10 us + CE(0); + + uint8_t st = 0; + while ((st & (RD_STATUS_MAX_RT|RD_STATUS_TX_DS)) == 0) { + st = NRF_ReadStatus(); // Packet acked or timed out + } + + dbg_nrf("Send status: 0x%02x - MAX_RT %d, SENT %d", (int)st, + (st&RD_STATUS_MAX_RT) != 0, (st&RD_STATUS_TX_DS) != 0); + + NRF_WriteRegister(RG_STATUS, st & (RD_STATUS_MAX_RT|RD_STATUS_TX_DS)); // Clear the bit + + if ((orig_conf & RD_CONFIG_PWR_UP) == 0) { + dbg_nrf("going back PwrDn"); + NRF_PowerDown(); + } + else if ((orig_conf & RD_CONFIG_PRIM_RX) == RD_CONFIG_PRIM_RX) { + dbg_nrf("going back PwrUp+Rx"); + NRF_ModeRX(); + } + + return 0 != (st & RD_STATUS_TX_DS); // success +} + +void NRF_Init(uint8_t pSpeed) +{ + // Set the required output pins + NSS(1); + CE(0); + + LL_mDelay(200); + + for (int i = 0; i < 6; i++) { + nrf_pipe_addr[i] = 0; + nrf_pipe_enabled[i] = 0; + } + + // clear flags etc + NRF_PowerDown(); + CHIPSELECT { spi(CMD_FLUSH_RX); } + CHIPSELECT { spi(CMD_FLUSH_TX); } + NRF_WriteRegister(RG_STATUS, 0x70); + + NRF_WriteRegister(RG_CONFIG, ModeBits); + NRF_WriteRegister(RG_SETUP_AW, 0b11); // 5 byte addresses + + NRF_WriteRegister(RG_EN_RXADDR, 0x01); // disable all, enable pipe 0 - this is required for shockburst, despite not being specified in the DS + + 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 +// } +} diff --git a/comm/nrf.h b/comm/nrf.h new file mode 100644 index 0000000..e671d71 --- /dev/null +++ b/comm/nrf.h @@ -0,0 +1,157 @@ +// +// 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" + +#define dbg_nrf(...) do{}while(0) +//#define dbg_nrf(...) dbg(##__VA_ARGS__) + +// 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); + +/** + * 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 8e49142..affead3 100644 --- a/framework/system_settings.c +++ b/framework/system_settings.c @@ -2,8 +2,6 @@ // Created by MightyPork on 2017/12/02. // -#include -#include #include "platform.h" #include "system_settings.h" #include "utils/str_utils.h" @@ -11,6 +9,8 @@ #include "cfg_utils.h" #include "resources.h" #include "unit_base.h" +#include "platform/debug_uart.h" +#include "comm/interfaces.h" static void systemsettings_mco_teardown(void); static void systemsettings_mco_init(void); @@ -32,6 +32,15 @@ void systemsettings_loadDefaults(void) SystemSettings.use_comm_nordic = false; SystemSettings.comm_uart_baud = 115200; // TODO + // just a demo, user must change this + SystemSettings.nrf_network[0] = 0x12; + SystemSettings.nrf_network[2] = 0x09; + SystemSettings.nrf_network[3] = 0x4c; + SystemSettings.nrf_network[4] = 0x61; + + SystemSettings.nrf_address = 1; + SystemSettings.nrf_channel = 76; + SystemSettings.enable_debug_uart = true; } @@ -49,7 +58,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 +72,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 +132,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 +166,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) */ @@ -171,18 +189,33 @@ void systemsettings_build_ini(IniWriter *iw) iw_entry_d(iw, "mco-prediv", (1<= '0' && v <= '9') { + nibble = (uint8_t) (v - '0'); + } + else if (v >= 'a' && v <= 'f') { + nibble = (uint8_t) (10 + (v - 'a')); + } + else if (v >= 'A' && v <= 'F') { + nibble = (uint8_t) (10 + (v - 'A')); + } + else if (v == 0) { + nibble = 0; // pad with zeros + } + else { + *suc = false; + return; + } + + digit++; + bytebuf <<= 4; + bytebuf |= nibble; + if ((digit % 2 == 0) && digit > 0) { // whole byte + *dest++ = bytebuf; + bytebuf = 0; + } + } +} diff --git a/platform/cfg_utils.h b/platform/cfg_utils.h index 05f8ebe..de34c19 100644 --- a/platform/cfg_utils.h +++ b/platform/cfg_utils.h @@ -116,6 +116,18 @@ uint32_t cfg_enum4_parse(const char *tpl, const char *d, uint32_t nd, bool *suc); +/** + * Parse a hexa string to a byte array. + * Skips 0x prefix, '.', '-', ':', ' '. + * + * @param[out] dest - storage array + * @param[in] count - expected number of bytes + * @param[in] value - parsed string + * @param[out] suc - success flag + */ +void cfg_hex_parse(uint8_t *dest, uint32_t count, + const char *value, bool *suc); + /** Convert bool to a Y or N constant string */ #define str_yn(cond) ((cond) ? ("Y") : ("N")) 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);