From 6c4c5fb1fde01da6489a25c908241c6ecbcb304f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 3 Apr 2018 00:59:53 +0200 Subject: [PATCH] nrf control, stub of sending data --- Inc/crc32.h | 15 ++ Inc/gex_gateway.h | 3 +- Inc/msg_queue.h | 3 +- Inc/nrf.h | 157 ++++++++++++++++ Makefile | 2 + Src/crc32.c | 67 +++++++ Src/gex_gateway.c | 150 +++++++++++---- Src/gpio.c | 4 +- Src/main.c | 24 ++- Src/msg_queue.c | 3 +- Src/nrf.c | 470 ++++++++++++++++++++++++++++++++++++++++++++++ Src/spi.c | 5 +- 12 files changed, 847 insertions(+), 56 deletions(-) create mode 100644 Inc/crc32.h create mode 100644 Inc/nrf.h create mode 100644 Src/crc32.c create mode 100644 Src/nrf.c diff --git a/Inc/crc32.h b/Inc/crc32.h new file mode 100644 index 0000000..b4eaa86 --- /dev/null +++ b/Inc/crc32.h @@ -0,0 +1,15 @@ +// +// Created by MightyPork on 2018/04/03. +// + +#ifndef GEX_NRF_CRC32_H +#define GEX_NRF_CRC32_H + +uint32_t CRC32_Start(void); + +uint32_t CRC32_Add(uint32_t cksum, uint8_t byte); + +uint32_t CRC32_End(uint32_t cksum); + + +#endif //GEX_NRF_CRC32_H diff --git a/Inc/gex_gateway.h b/Inc/gex_gateway.h index 85e5564..c32f065 100644 --- a/Inc/gex_gateway.h +++ b/Inc/gex_gateway.h @@ -6,7 +6,8 @@ #define GEX_NRF_GEX_GATEWAY_H #include "main.h" -void gw_process(void); void gw_handle_usb_out(uint8_t *buffer); +void gw_setup_radio(void); + #endif //GEX_NRF_GEX_GATEWAY_H diff --git a/Inc/msg_queue.h b/Inc/msg_queue.h index df64232..3790d97 100644 --- a/Inc/msg_queue.h +++ b/Inc/msg_queue.h @@ -21,8 +21,7 @@ typedef struct { volatile uint32_t nw; } MQueue; -extern MQueue usb_rxq; -extern MQueue usb_txq; +extern MQueue usb_inq; void mq_init(MQueue *mq); diff --git a/Inc/nrf.h b/Inc/nrf.h new file mode 100644 index 0000000..d147727 --- /dev/null +++ b/Inc/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 "main.h" + +// Initialize SPI and the Nordic + +/** + * Initialize the NRF module + * + * @param pSpeed + */ +void NRF_Init(uint8_t pSpeed); + +#define NRF_SPEED_500k 0b00100110 +#define NRF_SPEED_2M 0b00001110 +#define NRF_SPEED_1M 0b00000110 + +/** + * Set reception address + * @param pipenum - pipe to set + * @param AddrByte - byte 0 + */ +void NRF_SetRxAddress(uint8_t pipenum, uint8_t AddrByte); + +/** + * Set communication channel + */ +void NRF_SetChannel(uint8_t Ch) ; // 0 through 83 only! + +/** + * Power down the transceiver + */ +void NRF_PowerDown(void); + +/** + * Selecr RX mode + */ +void NRF_ModeRX(void); + +/** + * Select TX mode + */ +void NRF_ModeTX(void); + +/** + * @return NRF is power down + */ +uint8_t NRF_IsModePowerDown(void); + +/** + * @return NRF in TX mode + */ +uint8_t NRF_IsModeTX(void); + +/** + * @return NRF in RX mode + */ +uint8_t NRF_IsModeRx(void); + +/** + * Add a pipe to the next free slot + * + * @param AddrByte - address byte of the peer + * @param[out] PipeNum - pipe number is written here + * @return success + */ +bool NRF_AddPipe(uint8_t AddrByte, uint8_t *PipeNum); + +/** + * Convert pipe number to address byte + * + * @param pipe_num - pipe number + * @return address byte + */ +uint8_t NRF_PipeNum2Addr(uint8_t pipe_num); + +/** + * Convert address to a pipe number + * + * @param addr + * @return + */ +uint8_t NRF_Addr2PipeNum(uint8_t addr); + +/** + * Reset as much as possible (incl. removing pipes) + */ +void NRF_Reset(void); + +/** + * Send a packet (takes care of mode switching etc) + * + * @param PipeNum - pipe number + * @param Packet - packet bytes + * @param Length - packet length + * @return success (ACK'd) + */ +bool NRF_SendPacket(uint8_t PipeNum, const uint8_t *Packet, uint8_t Length); + +/** + * Receive a packet + * + * @param[out] Packet - the receiuved packet - buffer that is written + * @param[out] PipeNum - pipe number that was received from + * @return packet size, 0 on failure + */ +uint8_t NRF_ReceivePacket(uint8_t *Packet, uint8_t *PipeNum); + +/** + * Set base address + * @param Bytes4 + */ +void NRF_SetBaseAddress(const uint8_t *Bytes4); + +/** + * Check if there's a packet to be read + * + * @return 1 if any to read + */ +bool NRF_IsRxPacket(void); + +/** + * Enable a pipe + * + * @param pipenum - pipe number 0-5 + */ +void NRF_EnablePipe(uint8_t pipenum); + +/** + * Disable a pipe + * + * @param pipenum - pipe number 0-5 + */ +void NRF_DisablePipe(uint8_t pipenum); + +#endif /* NORDIC_H_ */ + +#endif //GEX_NRF_NRF_H diff --git a/Makefile b/Makefile index 52f8b0c..abc1431 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,8 @@ Src/msg_queue.c \ Src/gex_gateway.c \ Src/payload_builder.c \ Src/payload_parser.c \ +Src/nrf.c \ +Src/crc32.c \ Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim.c \ Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc.c \ Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_cortex.c \ diff --git a/Src/crc32.c b/Src/crc32.c new file mode 100644 index 0000000..f3262d9 --- /dev/null +++ b/Src/crc32.c @@ -0,0 +1,67 @@ +// +// Created by MightyPork on 2018/04/03. +// + +#include "main.h" +#include "crc32.h" + +static const uint32_t crc32_table[] = { /* CRC polynomial 0xedb88320 */ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +uint32_t CRC32_Start(void) +{ + return (uint32_t) 0xFFFFFFFF; +} + +uint32_t CRC32_Add(uint32_t cksum, uint8_t byte) +{ + return crc32_table[((cksum) ^ ((uint8_t) byte)) & 0xff] ^ ((cksum) >> 8); +} + +uint32_t CRC32_End(uint32_t cksum) +{ + return (uint32_t) ~cksum; +} diff --git a/Src/gex_gateway.c b/Src/gex_gateway.c index 5fa5312..c381a1c 100644 --- a/Src/gex_gateway.c +++ b/Src/gex_gateway.c @@ -9,49 +9,61 @@ #include "main.h" #include "minmax.h" #include "payload_parser.h" +#include "nrf.h" +#include "crc32.h" +#include "payload_builder.h" + +static uint8_t gex_network[4]; // USB RX -enum URX_STATE { - URXS_IDLE = 0, - URXS_MSG4SLAVE = 1, +enum USB_RX_STATE { + CMD_STATE_IDLE = 0, + CMD_STATE_TXMSG = 1, }; #define MAX_FRAME_LEN 512 -static enum URX_STATE urx_state = URXS_IDLE; -static uint32_t msg4slave_len = 0; // total len to be collected -static uint32_t msg4slave_already = 0; // already collected len -static uint8_t msg4slave[MAX_FRAME_LEN]; // equal buffer size in GEX -static uint8_t msg4slave_addr = 0; -static uint8_t msg4slave_cksum = 0; +static enum USB_RX_STATE cmd_state = CMD_STATE_IDLE; + +static uint32_t txmsg_len = 0; // total len to be collected +static uint32_t txmsg_collected = 0; // already collected len +static uint8_t txmsg_payload[MAX_FRAME_LEN]; // equal buffer size in GEX +static uint8_t txmsg_addr = 0; +static uint8_t txmsg_cksum = 0; #define MAGIC_GW_COMMAND 0x47U // 'G' enum GW_CMD { - CMD_GET_ID = 'i', // 105 - CMD_MSG4SLAVE = 'm', // 109 + CMD_GET_ID = 'i', // 105 - get network ID + CMD_RESET = 'r', // reset the radio and network + CMD_ADD_NODE = 'n', // add a node by address byte + CMD_TXMSG = 'm', // 109 - send a message }; -void respond_gw_id() {} // TODO impl +void respond_gw_id(void); + +void handle_cmd_addnode(PayloadParser *pp); + +void handle_cmd_reset(void); void start_slave_cmd(uint8_t slave_addr, uint16_t frame_len, uint8_t cksum) { - msg4slave_len = frame_len; - msg4slave_already = 0; - msg4slave_addr = slave_addr; - msg4slave_cksum = cksum; + txmsg_len = frame_len; + txmsg_collected = 0; + txmsg_addr = slave_addr; + txmsg_cksum = cksum; } void gw_handle_usb_out(uint8_t *buffer) { - if (urx_state == URXS_IDLE) { + if (cmd_state == CMD_STATE_IDLE) { PayloadParser pp = pp_start(buffer, MQ_SLOT_LEN, NULL); // handle binary commands for the gateway // magic twice, one inverted - denotes a gateway command - uint16_t magic1 = pp_u8(&pp); - uint16_t magic2 = pp_u8(&pp); + const uint16_t magic1 = pp_u8(&pp); + const uint16_t magic2 = pp_u8(&pp); if (magic1 == MAGIC_GW_COMMAND && magic2 == (0xFFU & (~MAGIC_GW_COMMAND))) { // third byte is the command code switch (pp_u8(&pp)) { @@ -59,7 +71,15 @@ void gw_handle_usb_out(uint8_t *buffer) respond_gw_id(); break; - case CMD_MSG4SLAVE:; + case CMD_RESET: + handle_cmd_reset(); + break; + + case CMD_ADD_NODE: + handle_cmd_addnode(&pp); + break; + + case CMD_TXMSG:; uint8_t slave_addr = pp_u8(&pp); uint16_t frame_len = pp_u16(&pp); uint8_t cksum = pp_u8(&pp); @@ -71,7 +91,7 @@ void gw_handle_usb_out(uint8_t *buffer) start_slave_cmd(slave_addr, frame_len, cksum); dbg("Collecting frame for slave %02x: %d bytes", (int)slave_addr, (int)frame_len); - urx_state = URXS_MSG4SLAVE; + cmd_state = CMD_STATE_TXMSG; break; default: @@ -82,38 +102,96 @@ void gw_handle_usb_out(uint8_t *buffer) dbg("Bad USB frame, starts %x,%x", buffer[0],buffer[1]); } } - else if (urx_state == URXS_MSG4SLAVE) { - uint32_t wanted = MIN(msg4slave_len-msg4slave_already, MQ_SLOT_LEN); - memcpy(&msg4slave[msg4slave_already], buffer, wanted); - msg4slave_already += wanted; + else if (cmd_state == CMD_STATE_TXMSG) { + uint32_t wanted = MIN(txmsg_len - txmsg_collected, MQ_SLOT_LEN); + memcpy(&txmsg_payload[txmsg_collected], buffer, wanted); + txmsg_collected += wanted; if (wanted < MQ_SLOT_LEN) { // this was the end uint8_t ck = 0; - for (int i = 0; i < msg4slave_len; i++) { - ck ^= msg4slave[i]; + for (int i = 0; i < txmsg_len; i++) { + ck ^= txmsg_payload[i]; } ck = ~ck; - if (ck != msg4slave_cksum) { + if (ck != txmsg_cksum) { dbg("Checksum mismatch!"); } else { - dbg("Verified, sending a %d B frame to slave.", (int) msg4slave_len); + dbg("Verified, sending a %d B frame to slave.", (int) txmsg_len); // TODO send to slave } - urx_state = URXS_IDLE; + cmd_state = CMD_STATE_IDLE; } } } -/** called from the main loop, periodically */ -void gw_process(void) +void handle_cmd_reset(void) +{ + NRF_Reset(); + // TODO also clear queues? +} + +void handle_cmd_addnode(PayloadParser *pp) +{ + uint8_t node = pp_u8(pp); + uint8_t pipenum; + bool suc = NRF_AddPipe(node, &pipenum); + if (!suc) dbg("Failed to add node."); + // TODO response +} + +void respond_gw_id(void) { -// static uint8_t buffer[MQ_SLOT_LEN]; -// while (mq_read(&usb_rxq, buffer)) { // greedy - handle as many as possible -// gw_handle_usb_out(buffer); -// } + // TODO implement (after response system is added) } +/** + * Compute the GEX network ID based on the gateway MCU unique ID. + * The ID is a 4-byte code that must be entered in each node to join the network. + */ +static void compute_network_id(void) +{ + uint8_t uid[12]; + PayloadBuilder pb = pb_start(uid, 12, NULL); + pb_u32(&pb, LL_GetUID_Word0()); + pb_u32(&pb, LL_GetUID_Word1()); + pb_u32(&pb, LL_GetUID_Word2()); + + uint32_t ck = CRC32_Start(); + for (int i = 0; i < 12; i++) { + ck = CRC32_Add(ck, uid[i]); + } + ck = CRC32_End(ck); + + pb = pb_start(gex_network, 4, NULL); + pb_u32(&pb, ck); + + dbg("Dongle network ID: %02X-%02X-%02X-%02X", + gex_network[0], gex_network[1], gex_network[2], gex_network[3]); +} + +void gw_setup_radio(void) +{ + bool suc; + dbg("Init NRF"); + + NRF_Init(NRF_SPEED_2M); + + compute_network_id(); + NRF_SetBaseAddress(gex_network); + + // TODO by config + uint8_t pipenum; + suc = NRF_AddPipe(0x01, &pipenum); + dbg("Pipe added? %d, num %d", (int)suc, (int)pipenum); + + NRF_ModeRX(); // base state is RX + + dbg("Send a packet"); + + suc = NRF_SendPacket(pipenum, (uint8_t *) "AHOJ", 5); + dbg("Suc? %d", (int)suc); +} diff --git a/Src/gpio.c b/Src/gpio.c index 81012be..b02972d 100644 --- a/Src/gpio.c +++ b/Src/gpio.c @@ -82,7 +82,7 @@ void MX_GPIO_Init(void) LL_GPIO_ResetOutputPin(LED_GPIO_Port, LED_Pin); /**/ - LL_GPIO_ResetOutputPin(GPIOA, NRF_CE_Pin|NRF_NSS_Pin); +// LL_GPIO_ResetOutputPin(GPIOA, NRF_CE_Pin|NRF_NSS_Pin); /**/ GPIO_InitStruct.Pin = LED_Pin; @@ -98,6 +98,7 @@ void MX_GPIO_Init(void) GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; LL_GPIO_Init(GPIOA, &GPIO_InitStruct); +#if 0 /**/ LL_GPIO_AF_SetEXTISource(LL_GPIO_AF_EXTI_PORTA, LL_GPIO_AF_EXTI_LINE2); @@ -110,6 +111,7 @@ void MX_GPIO_Init(void) /**/ LL_GPIO_SetPinMode(NRF_IRQ_GPIO_Port, NRF_IRQ_Pin, LL_GPIO_MODE_FLOATING); +#endif } diff --git a/Src/main.c b/Src/main.c index 135a165..c8c82eb 100644 --- a/Src/main.c +++ b/Src/main.c @@ -47,8 +47,6 @@ ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ -#include -#include #include "main.h" #include "stm32f1xx_hal.h" #include "dma.h" @@ -57,6 +55,8 @@ #include "usb_device.h" #include "gpio.h" #include "debug.h" +#include "gex_gateway.h" +#include "msg_queue.h" /* USER CODE BEGIN Includes */ @@ -121,23 +121,21 @@ int main(void) /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ - dbg("Main loop starts."); - mq_init(&usb_rxq); - mq_init(&usb_txq); + gw_setup_radio(); + mq_init(&usb_inq); + dbg("Main loop starts."); /* Infinite loop */ /* USER CODE BEGIN WHILE */ int cnt = 0; - while (1) - { - if (cnt++ > 100000) { - GPIOC->ODR ^= (1 << 13); - cnt = 0; + while (1) { + if (cnt++ > 4000000) { + LL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); +// GPIOC->ODR ^= (1 << 13); + cnt = 0; + } } - - gw_process(); - } /* USER CODE END 3 */ } diff --git a/Src/msg_queue.c b/Src/msg_queue.c index f96d0a5..dc839e1 100644 --- a/Src/msg_queue.c +++ b/Src/msg_queue.c @@ -9,8 +9,7 @@ #include "msg_queue.h" // the two USB messaging queues -MQueue usb_rxq; -MQueue usb_txq; +MQueue usb_inq; void mq_init(MQueue *mq) { diff --git a/Src/nrf.c b/Src/nrf.c new file mode 100644 index 0000000..abc7088 --- /dev/null +++ b/Src/nrf.c @@ -0,0 +1,470 @@ +// +// Created by MightyPork on 2018/04/02. +// + +#include +#include "nrf.h" +#include "main.h" +#include "debug.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) + +// inaccurate! +#define _delay_us(n) __asm_loop((n)*12) + +static inline void CE(bool level) { + if (level) LL_GPIO_SetOutputPin(NRF_CE_GPIO_Port, NRF_CE_Pin); + else LL_GPIO_ResetOutputPin(NRF_CE_GPIO_Port, NRF_CE_Pin); +} + +static inline void NSS(bool level) { + if (level) LL_GPIO_SetOutputPin(NRF_NSS_GPIO_Port, NRF_NSS_Pin); + else LL_GPIO_ResetOutputPin(NRF_NSS_GPIO_Port, NRF_NSS_Pin); +} + +static uint8_t spi(uint8_t tx) { + while (! LL_SPI_IsActiveFlag_TXE(SPI1)); + LL_SPI_TransmitData8(SPI1, tx); + + while (! LL_SPI_IsActiveFlag_RXNE(SPI1)); + return LL_SPI_ReceiveData8(SPI1); +} + +//------- + +/* + * from: http://barefootelectronics.com/NRF24L01.aspx + */ + +#define CMD_READ_REG 0x00 // Read command to register +#define CMD_WRITE_REG 0x20 // Write command to register +#define CMD_RD_RX_PLD 0x61 // Read RX payload register address +#define CMD_WR_TX_PLD 0xA0 // Write TX payload register address +#define CMD_FLUSH_TX 0xE1 // Flush TX register command +#define CMD_FLUSH_RX 0xE2 // Flush RX register command +#define CMD_REUSE_TX_PL 0xE3 // Reuse TX payload register command +#define CMD_RD_RX_PL_WIDTH 0x60 // Read RX Payload Width +#define CMD_WR_ACK_PLD 0xA8 // Write ACK payload for Pipe (Add pipe number 0-6) +#define CMD_WR_TX_PLD_NK 0xB0 // Write ACK payload for not ack +#define CMD_NOP 0xFF // No Operation, might be used to read status register + +// SPI(nRF24L01) registers(addresses) +#define RG_CONFIG 0x00 // 'Config' register address +#define RG_EN_AA 0x01 // 'Enable Auto Acknowledgment' register address +#define RG_EN_RXADDR 0x02 // 'Enabled RX addresses' register address +#define RG_SETUP_AW 0x03 // 'Setup address width' register address +#define RG_SETUP_RETR 0x04 // 'Setup Auto. Retrans' register address +#define RG_RF_CH 0x05 // 'RF channel' register address +#define RG_RF_SETUP 0x06 // 'RF setup' register address +#define RG_STATUS 0x07 // 'Status' register address +#define RG_OBSERVE_TX 0x08 // 'Observe TX' register address +#define RG_CD 0x09 // 'Carrier Detect' register address +#define RG_RX_ADDR_P0 0x0A // 'RX address pipe0' register address +#define RG_RX_ADDR_P1 0x0B // 'RX address pipe1' register address +#define RG_RX_ADDR_P2 0x0C // 'RX address pipe2' register address +#define RG_RX_ADDR_P3 0x0D // 'RX address pipe3' register address +#define RG_RX_ADDR_P4 0x0E // 'RX address pipe4' register address +#define RG_RX_ADDR_P5 0x0F // 'RX address pipe5' register address +#define RG_TX_ADDR 0x10 // 'TX address' register address +#define RG_RX_PW_P0 0x11 // 'RX payload width, pipe0' register address +#define RG_RX_PW_P1 0x12 // 'RX payload width, pipe1' register address +#define RG_RX_PW_P2 0x13 // 'RX payload width, pipe2' register address +#define RG_RX_PW_P3 0x14 // 'RX payload width, pipe3' register address +#define RG_RX_PW_P4 0x15 // 'RX payload width, pipe4' register address +#define RG_RX_PW_P5 0x16 // 'RX payload width, pipe5' register address +#define RG_FIFO_STATUS 0x17 // 'FIFO Status Register' register address +#define RG_DYNPD 0x1C // 'Enable dynamic payload length +#define RG_FEATURE 0x1D // 'Feature register + +#define RD_STATUS_TX_FULL 0x01 // tx queue full +#define RD_STATUS_RX_PNO 0x0E // pipe number of the received payload +#define RD_STATUS_MAX_RT 0x10 // max retransmissions +#define RD_STATUS_TX_DS 0x20 // data sent +#define RD_STATUS_RX_DR 0x40 // data ready + +#define RD_CONFIG_PRIM_RX 0x01 // is primary receiver +#define RD_CONFIG_PWR_UP 0x02 // power up +#define RD_CONFIG_CRCO 0x04 // 2-byte CRC +#define RD_CONFIG_EN_CRC 0x08 // enable CRC +#define RD_CONFIG_DISABLE_IRQ_MAX_RT 0x10 +#define RD_CONFIG_DISABLE_IRQ_TX_DS 0x20 +#define RD_CONFIG_DISABLE_IRQ_RX_DR 0x40 + +// Config register bits (excluding the bottom two that are changed dynamically) +// enable only Rx IRQ +#define ModeBits (RD_CONFIG_DISABLE_IRQ_MAX_RT | \ + RD_CONFIG_DISABLE_IRQ_TX_DS | \ + RD_CONFIG_EN_CRC | \ + RD_CONFIG_CRCO) + +#define CEHIGH CE(1) +#define CELOW CE(0) + +static inline uint8_t CS(uint8_t hl) +{ + if (hl == 1) { + NSS(0); + } + else { + NSS(1); + } + return hl; +} + +#define CHIPSELECT for (uint8_t cs = CS(1); cs==1; cs = CS(0)) + +// Base NRF address - byte 4 may be modified before writing to a pipe register, +// the first 4 bytes are shared in the network. Master is always code 0. +static uint8_t nrf_base_address[5]; + +static uint8_t nrf_pipe_addr[6]; +static bool nrf_pipe_enabled[6]; + +static uint8_t NRF_WriteRegister(uint8_t reg, uint8_t value) +{ + uint8_t status = 0; + CHIPSELECT { + status = spi(CMD_WRITE_REG | reg); + spi(value); + } + return status; +} + +static uint8_t NRF_ReadRegister(uint8_t reg) +{ + uint8_t reg_val = 0; + CHIPSELECT { + spi(CMD_READ_REG | reg); + reg_val = spi(0); + } + return reg_val; +} + +static uint8_t NRF_ReadStatus(void) +{ + uint8_t reg_val = 0; + CHIPSELECT { + reg_val = spi(CMD_NOP); + } + return reg_val; +} + +static uint8_t NRF_WriteBuffer(uint8_t reg, const uint8_t *pBuf, uint8_t bytes) +{ + uint8_t status = 0, i = 0; + CHIPSELECT { + status = spi(CMD_WRITE_REG | reg); + for (i = 0; i < bytes; i++) { + spi(*pBuf++); + } + } + return status; +} + +static uint8_t NRF_ReadBuffer(uint8_t reg, uint8_t *pBuf, uint8_t bytes) +{ + uint8_t status = 0, i; + CHIPSELECT { + status = spi(CMD_READ_REG | reg); + for (i = 0; i < bytes; i++) { + pBuf[i] = spi(0); + } + } + return status; +} + +void NRF_SetBaseAddress(const uint8_t *Bytes4) +{ + memcpy(nrf_base_address, Bytes4, 4); + nrf_base_address[4] = 0; +} + +void NRF_SetRxAddress(uint8_t pipenum, uint8_t AddrByte) +{ + if (pipenum > 5) { + dbg("!! bad pipe %d", pipenum); + return; + } + + nrf_base_address[4] = AddrByte; + + nrf_pipe_addr[pipenum] = AddrByte; + + if (pipenum == 0) { + NRF_WriteBuffer(RG_RX_ADDR_P0, nrf_base_address, 5); + } + else if (pipenum == 1) { + NRF_WriteBuffer(RG_RX_ADDR_P1, nrf_base_address, 5); + } + else { + NRF_WriteRegister(RG_RX_ADDR_P2 + (pipenum - 2), AddrByte); + } +} + +bool NRF_AddPipe(uint8_t AddrByte, uint8_t *PipeNum) +{ + for (uint8_t i = 0; i < 6; i++) { + if(!nrf_pipe_enabled[i]) { + NRF_SetRxAddress(i, AddrByte); + *PipeNum = i; + NRF_EnablePipe(i); + return true; + } + } + + return false; +} + +uint8_t NRF_PipeNum2Addr(uint8_t pipe_num) +{ + if (pipe_num > 5) return 0; // fail + return nrf_pipe_addr[pipe_num]; +} + +uint8_t NRF_Addr2PipeNum(uint8_t addr) +{ + for (int i = 0; i < 6; i++) { + if (nrf_pipe_addr[i] == addr) return (uint8_t) i; + } + return 0xFF; +} + +void NRF_EnablePipe(uint8_t pipenum) +{ + uint8_t enabled = NRF_ReadRegister(RG_EN_RXADDR); + enabled |= 1 << pipenum; + NRF_WriteRegister(RG_EN_RXADDR, enabled); + + nrf_pipe_enabled[pipenum] = 1; +} + +void NRF_DisablePipe(uint8_t pipenum) +{ + uint8_t enabled = NRF_ReadRegister(RG_EN_RXADDR); + enabled &= ~(1 << pipenum); + NRF_WriteRegister(RG_EN_RXADDR, enabled); + + nrf_pipe_enabled[pipenum] = 0; +} + +static void NRF_SetTxAddress(uint8_t SendTo) +{ + nrf_base_address[4] = SendTo; + NRF_WriteBuffer(RG_TX_ADDR, nrf_base_address, 5); + NRF_WriteBuffer(RG_RX_ADDR_P0, nrf_base_address, 5); // the ACK will come to pipe 0 +} + +void NRF_PowerDown(void) +{ + CELOW; + NRF_WriteRegister(RG_CONFIG, ModeBits); +} + +void NRF_ModeTX(void) +{ + CELOW; + uint8_t m = NRF_ReadRegister(RG_CONFIG); + NRF_WriteRegister(RG_CONFIG, ModeBits | RD_CONFIG_PWR_UP); + if ((m & RD_CONFIG_PWR_UP) == 0) LL_mDelay(5); +} + +void NRF_ModeRX(void) +{ + NRF_WriteRegister(RG_CONFIG, ModeBits | RD_CONFIG_PWR_UP | RD_CONFIG_PRIM_RX); + NRF_SetRxAddress(0, nrf_pipe_addr[0]); // set the P0 address - it was changed during Rx for ACK reception + CEHIGH; + + //if ((m&2)==0) LL_mDelay()(5); You don't need to wait. Just nothing will come for 5ms or more +} + +uint8_t NRF_IsModePowerDown(void) +{ + uint8_t ret = 0; + if ((NRF_ReadRegister(RG_CONFIG) & RD_CONFIG_PWR_UP) == 0) ret = 1; + return ret; +} + +uint8_t NRF_IsModeTX(void) +{ + uint8_t m = NRF_ReadRegister(RG_CONFIG); + if ((m & RD_CONFIG_PWR_UP) == 0) return 0; // OFF + if ((m & RD_CONFIG_PRIM_RX) == 0) return 1; + return 0; +} + +uint8_t NRF_IsModeRx(void) +{ + uint8_t m = NRF_ReadRegister(RG_CONFIG); + if ((m & RD_CONFIG_PWR_UP) == 0) return 0; // OFF + if ((m & RD_CONFIG_PRIM_RX) == 0) return 0; + return 1; +} + +void NRF_SetChannel(uint8_t Ch) +{ + NRF_WriteRegister(RG_RF_CH, Ch); +} + +uint8_t NRF_ReceivePacket(uint8_t *Packet, uint8_t *PipeNum) +{ + uint8_t pw = 0, status; + if (!NRF_IsRxPacket()) return 0; + + CELOW; + CHIPSELECT { + status = spi(CMD_RD_RX_PL_WIDTH); + pw = spi(0); + } + + if (pw > 32) { + CHIPSELECT { + spi(CMD_FLUSH_RX); + } + NRF_WriteRegister(RG_STATUS, RD_STATUS_RX_DR); + return 0; + } + + // Read the reception pipe number + *PipeNum = (status & RD_STATUS_RX_PNO) >> 1; + + CHIPSELECT { + spi(CMD_RD_RX_PLD); + for (uint8_t i = 0; i < pw; i++) Packet[i] = spi(0); + } + NRF_WriteRegister(RG_STATUS, RD_STATUS_RX_DR); // Clear the RX_DR interrupt + CEHIGH; + return pw; +} + +bool NRF_IsRxPacket(void) +{ + uint8_t ret = NRF_ReadRegister(RG_STATUS) & RD_STATUS_RX_DR; + return 0 != ret; +} + +bool NRF_SendPacket(uint8_t PipeNum, const uint8_t *Packet, uint8_t Length) +{ + if (!nrf_pipe_enabled[PipeNum]) { + dbg("!! Pipe %d not enabled", PipeNum); + return 0; + } + + const uint8_t orig_conf = NRF_ReadRegister(RG_CONFIG); + CELOW; + NRF_ModeTX(); // Make sure in TX mode + NRF_SetTxAddress(nrf_pipe_addr[PipeNum]); + + CHIPSELECT { + spi(CMD_FLUSH_TX); + }; + + CHIPSELECT { + spi(CMD_WR_TX_PLD); + for (uint8_t i = 0; i < Length; i++) spi(Packet[i]); + }; + + CEHIGH; + _delay_us(15); // At least 10 us + CELOW; + + uint8_t st = 0; + while ((st & (RD_STATUS_MAX_RT|RD_STATUS_TX_DS)) == 0) { + st = NRF_ReadStatus(); // Packet acked or timed out + } + + NRF_WriteRegister(RG_STATUS, st & (RD_STATUS_MAX_RT|RD_STATUS_TX_DS)); // Clear the bit + + if ((orig_conf & RD_CONFIG_PWR_UP) == 0) { + NRF_PowerDown(); + } + else if ((orig_conf & RD_CONFIG_PRIM_RX) == RD_CONFIG_PRIM_RX) { + NRF_ModeRX(); + } + + return 0 != (st & RD_STATUS_TX_DS); // success +} + +void NRF_Reset(void) +{ + NSS(1); + CELOW; + NRF_PowerDown(); + + NRF_WriteRegister(RG_EN_RXADDR, 0); // disable all pipes + + for (int i = 0; i < 6; i++) { + nrf_pipe_addr[i] = 0; + nrf_pipe_enabled[i] = 0; + } +} + +void NRF_Init(uint8_t pSpeed) +{ + // Set the required output pins + NSS(1); + CELOW; + + LL_mDelay(200); + + NRF_WriteRegister(RG_CONFIG, ModeBits); + NRF_WriteRegister(RG_SETUP_AW, 0b11); // 5 byte addresses + + // default +// NRF_EnablePipe(0); + + NRF_WriteRegister(RG_SETUP_RETR, 0x18); // 8 retries + NRF_WriteRegister(RG_RF_CH, 2); // channel 2 NO HIGHER THAN 83 in USA! + + NRF_WriteRegister(RG_RF_SETUP, pSpeed); + + NRF_WriteRegister(RG_DYNPD, 0b111111); // Dynamic packet length + NRF_WriteRegister(RG_FEATURE, 0b100); // Enable dynamic payload, and no payload in the ack. + + for (int i = 0; i < 6; i++) { + NRF_WriteRegister(RG_RX_PW_P0+i, 32); // Receive 32 byte packets - XXX this is probably not needed with dynamic length + } + + //NRFModePowerDown(); // Already in power down mode, dummy +} diff --git a/Src/spi.c b/Src/spi.c index 9e8a9d8..51ba696 100644 --- a/Src/spi.c +++ b/Src/spi.c @@ -82,7 +82,8 @@ void MX_SPI1_Init(void) LL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* SPI1 DMA Init */ - + +#if 0 /* SPI1_RX Init */ LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2, LL_DMA_DIRECTION_PERIPH_TO_MEMORY); @@ -112,6 +113,7 @@ void MX_SPI1_Init(void) LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_BYTE); LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MDATAALIGN_BYTE); +#endif SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX; SPI_InitStruct.Mode = LL_SPI_MODE_MASTER; @@ -125,6 +127,7 @@ void MX_SPI1_Init(void) SPI_InitStruct.CRCPoly = 10; LL_SPI_Init(SPI1, &SPI_InitStruct); + LL_SPI_Enable(SPI1); } /* USER CODE BEGIN 1 */