Merge branch 'nordic'

remotes/github/master
Ondřej Hruška 7 years ago
commit 4ede660471
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 73
      TinyFrame/TF_Integration.c
  2. 175
      comm/iface_nordic.c
  3. 20
      comm/iface_nordic.h
  4. 105
      comm/iface_uart.c
  5. 20
      comm/iface_uart.h
  6. 73
      comm/iface_usb.c
  7. 20
      comm/iface_usb.h
  8. 179
      comm/interfaces.c
  9. 2
      comm/interfaces.h
  10. 46
      comm/messages.c
  11. 530
      comm/nrf.c
  12. 157
      comm/nrf.h
  13. 33
      comm/nrf_pins.h
  14. 135
      framework/system_settings.c
  15. 3
      framework/system_settings.h
  16. 2
      gex.mk
  17. 9
      gex_hooks.c
  18. 43
      platform/cfg_utils.c
  19. 12
      platform/cfg_utils.h
  20. 3
      platform/platform.c
  21. 14
      units/digital_in/_din_init.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<len;i++) {
while(!LL_USART_IsActiveFlag_TXE(USART2));
LL_USART_TransmitData8(USART2, buff[i]);
iface_uart_transmit(buff, len);
}
xSemaphoreGive(semVcomTxReadyHandle); // act as if we just finished it and this is perhaps the DMA irq
else if (gActiveComport == COMPORT_NORDIC) {
iface_nordic_transmit(buff, len);
}
else {
// TODO other transports
@ -104,7 +47,7 @@ bool TF_ClaimTx(TinyFrame *tf)
// The last chunk from some previous frame may still be being transmitted,
// wait for it to finish (the semaphore is given in the CDC tx done handler)
if (pdTRUE != xSemaphoreTake(semVcomTxReadyHandle, 100)) {
if (pdTRUE != xSemaphoreTake(semVcomTxReadyHandle, 200)) {
TF_Error("Tx stalled in Claim");
// release the guarding mutex again

@ -0,0 +1,175 @@
//
// Created by MightyPork on 2018/04/06.
//
#include "iface_nordic.h"
#include "nrf_pins.h"
#include "resources.h"
#include "hw_utils.h"
#include "nrf.h"
#include "unit_base.h"
#include "system_settings.h"
#include "utils/hexdump.h"
extern osSemaphoreId semVcomTxReadyHandle;
#define RX_PIPE_NUM 0
void iface_nordic_claim_resources(void)
{
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_SPI));
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_CE));
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_NSS));
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_IRQ));
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_MISO));
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_MOSI));
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_SCK));
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, R_EXTI0+NRF_EXTI_LINENUM));
}
void iface_nordic_free_resources(void)
{
rsc_free(&UNIT_SYSTEM, NRF_R_SPI);
rsc_free(&UNIT_SYSTEM, NRF_R_CE);
rsc_free(&UNIT_SYSTEM, NRF_R_NSS);
rsc_free(&UNIT_SYSTEM, NRF_R_IRQ);
rsc_free(&UNIT_SYSTEM, NRF_R_MISO);
rsc_free(&UNIT_SYSTEM, NRF_R_MOSI);
rsc_free(&UNIT_SYSTEM, NRF_R_SCK);
rsc_free(&UNIT_SYSTEM, R_EXTI0+NRF_EXTI_LINENUM);
}
static uint8_t rx_buffer[32];
static void NrfIrqHandler(void *arg)
{
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINES[NRF_EXTI_LINENUM]);
dbg_nrf("[EXTI] ---");
while (NRF_IsRxPacket()) {
uint8_t pipenum;
uint8_t count = NRF_ReceivePacket(rx_buffer, &pipenum);
if (count > 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
}

@ -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

@ -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;i<len;i++) {
while(!LL_USART_IsActiveFlag_TXE(USART2));
LL_USART_TransmitData8(USART2, buff[i]);
}
xSemaphoreGive(semVcomTxReadyHandle); // act as if we just finished it and this is perhaps the DMA irq
}

@ -0,0 +1,20 @@
//
// Created by MightyPork on 2018/04/06.
//
#ifndef GEX_F072_IFACE_UART_H
#define GEX_F072_IFACE_UART_H
#include "platform.h"
void com_iface_flush_buffer(void);
void iface_uart_deinit(void);
bool iface_uart_init(void);
void iface_uart_free_resources(void);
void iface_uart_claim_resources(void);
void iface_uart_transmit(const uint8_t *buff, uint32_t len);
#endif //GEX_F072_IFACE_UART_H

@ -0,0 +1,73 @@
//
// Created by MightyPork on 2018/04/06.
//
#include "iface_usb.h"
#include "TinyFrame.h"
#include "system_settings.h"
#include "USB/usb_device.h"
extern osSemaphoreId semVcomTxReadyHandle;
extern osMutexId mutTinyFrameTxHandle;
bool iface_usb_ready(void)
{
return 0 != (USB->DADDR & 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
}

@ -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

@ -2,7 +2,6 @@
// Created by MightyPork on 2018/03/23.
//
#include <stm32f072xb.h>
#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
// Fallback to radio or bare USART
do {
if (SystemSettings.use_comm_nordic) {
if (configure_interface(COMPORT_NORDIC)) break;
}
else if (SystemSettings.use_comm_lora) {
configure_interface(COMPORT_LORA);
#if 0
if (SystemSettings.use_comm_lora) {
if (configure_interface(COMPORT_LORA)) break;
}
else {
dbg("No alternate com interface configured, leaving USB enabled.");
#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);
}
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
return iface_uart_init();
}
else if (iface == COMPORT_NORDIC) {
return iface_nordic_init();
}
if (iface == COMPORT_LORA) {
#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");
iface = COMPORT_NONE; // fail
}
dbg("LoRa not impl!");
return false;
}
if (iface == COMPORT_NONE) {
dbg("NO COM PORT AVAILABLE!");
#endif
else {
trap("Bad iface %d", iface);
}
gActiveComport = iface;
}

@ -15,6 +15,8 @@ enum ComportSelection {
COMPORT_LORA = 4,
};
extern const char * COMPORT_NAMES[];
/**
* The currently active communication port
*/

@ -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;
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->read = ini_bulkread_cb;
bulk->userdata = (void *) (int)filenum;
if (filenum == 0) {
bulk->len = iw_measure_total(settings_build_units_ini, 1);
bulk->read = settings_bulkread_cb;
bulk->userdata = NULL;
}
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;
}

@ -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
// }
}

@ -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

@ -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

@ -2,8 +2,6 @@
// Created by MightyPork on 2017/12/02.
//
#include <platform/debug_uart.h>
#include <comm/interfaces.h>
#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<<SystemSettings.mco_prediv));
iw_cmt_newline(iw);
iw_comment(iw, "Allowed fallback communication ports");
iw_comment(iw, "--- Allowed fallback communication ports ---");
iw_comment(iw, "UART Tx:PA2, Rx:PA2");
iw_cmt_newline(iw);
iw_comment(iw, "UART Tx:PA2, Rx:PA3");
iw_entry_s(iw, "com-uart", str_yn(SystemSettings.use_comm_uart));
iw_entry_d(iw, "com-uart-baud", SystemSettings.comm_uart_baud);
iw_cmt_newline(iw);
iw_comment(iw, "nRF24L01+ radio");
iw_entry_s(iw, "com-nrf", str_yn(SystemSettings.use_comm_nordic));
iw_comment(iw, "Radio channel (0-125)");
iw_entry_d(iw, "nrf-channel", SystemSettings.nrf_channel);
iw_comment(iw, "Network prefix (hex, 4 bytes)");
iw_entry(iw, "nrf-network", "%02X:%02X:%02X:%02X",
SystemSettings.nrf_network[0],
SystemSettings.nrf_network[1],
SystemSettings.nrf_network[2],
SystemSettings.nrf_network[3]);
iw_comment(iw, "Node address (1-255)");
iw_entry(iw, "nrf-address", "%d", (int)SystemSettings.nrf_address);
// those aren't implement yet, don't tease the user
// TODO show pin-out, extra settings if applicable
#if 0
iw_comment(iw, "nRF24L01+");
iw_entry_s(iw, "com-nordic", str_yn(SystemSettings.use_comm_nrf24l01p));
iw_comment(iw, "LoRa/GFSK sx127x");
iw_entry_s(iw, "com-lora", str_yn(SystemSettings.use_comm_sx127x));
#endif
@ -241,12 +274,24 @@ bool systemsettings_load_ini(const char *restrict key, const char *restrict valu
if (suc) SystemSettings.comm_uart_baud = baud;
}
#if 0
if (streq(key, "com-nordic")) {
if (streq(key, "com-nrf")) {
bool yn = cfg_bool_parse(value, &suc);
if (suc) SystemSettings.use_comm_nordic = yn;
}
if (streq(key, "nrf-channel")) {
SystemSettings.nrf_channel = cfg_u8_parse(value, &suc);
}
if (streq(key, "nrf-address")) {
SystemSettings.nrf_address = cfg_u8_parse(value, &suc);
}
if (streq(key, "nrf-network")) {
cfg_hex_parse(&SystemSettings.nrf_network[0], 4, value, &suc);
}
#if 0
if (streq(key, "com-lora")) {
bool yn = cfg_bool_parse(value, &suc);
if (suc) SystemSettings.use_comm_lora = yn;

@ -27,6 +27,9 @@ struct system_settings {
uint32_t comm_uart_baud; // baud rate for the uart transport
bool use_comm_lora; // SX1276/8
bool use_comm_nordic; // nRF24L01+
uint8_t nrf_channel;
uint8_t nrf_network[4];
uint8_t nrf_address;
// Support flags put here for scoping, but not atcually part of the persistent settings
volatile bool editable; //!< True if we booted with the LOCK jumper removed

@ -95,7 +95,7 @@ GEX_CDEFS = $(GEX_CDEFS_BASE) \
-DDEBUG_VFS=0 \
-DDEBUG_FLASH_WRITE=0 \
-DVERBOSE_HARDFAULT=1 \
-DUSE_STACK_MONITOR=1 \
-DUSE_STACK_MONITOR=0 \
-DUSE_DEBUG_UART=1 \
-DDEBUG_MALLOC=0 \
-DDEBUG_RSC=0

@ -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)
{
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();

@ -297,3 +297,46 @@ uint32_t cfg_enum4_parse(const char *value,
*suc = false;
return na;
}
void cfg_hex_parse(uint8_t *dest, uint32_t count, const char *value, bool *suc)
{
// discard possible leading 0x
if (value[0] == '0' && value[1] == 'x') {
value += 2;
}
uint8_t bytebuf = 0;
for (uint32_t digit = 0; digit < count * 2;) {
char v = *value;
if (v != 0) value++;
uint8_t nibble = 0;
if (v == ' ' || v == '.' || v == ',' || v == '-' || v == ':') {
continue; // junk
}
else if (v >= '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;
}
}
}

@ -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"))

@ -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)

@ -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);

Loading…
Cancel
Save