Hi,
We have modified the PTX "main.c" and "esb.c" file for NRF5840 to transmit and receive data packets with our Patient monitor.
The packets are transmitted at 992HZ and received at 4HZ. We have modified the "cmsis_timer.c" file to create a microsecond timer.
The payload for transmit is 21bytes and for receive is 9bytes respectively.
We enable the receive initially and we have setup a timer for 992Hz timing. Inside the timer callback function we stop the receive, start the transmit and then start the receive functions respectively.
With this arrangement, we have observed a strange behaviour that the transmit and receive both work together only when we add a print statement as mentioned below.
/* * Copyright (c) 2018 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ #include <zephyr/drivers/clock_control.h> #include <zephyr/drivers/clock_control/nrf_clock_control.h> #include <zephyr/drivers/gpio.h> #include <zephyr/irq.h> #include <zephyr/logging/log.h> #include <nrf.h> #include <esb.h> #include <zephyr/device.h> #include <zephyr/devicetree.h> #include <zephyr/kernel.h> #include <zephyr/types.h> #include <zephyr/zephyr.h> #include <cmsis_os.h> #include <sys/printk.h> // #include <sys/unistd.h> // #include <zephyr/posix/unistd.h> /* specify delay between greetings (in us); compute equivalent in ticks */ // #define TIMER_TICKS 1008 #define TIMER_TICKS 978 #define DELAY_TIMER_TICKS 250 #define TIMER_SEC_TICKS 1000000 #define ECG_FW_CH_LENTYPE 0x5A // 5A for MR400. 59 for Expression #define ECG_FW_CH_PREAMBLE 0xAA #define TOT_CMD_DATA_BYTES 4 #define TOT_SPARE_BYTES 3 #define TOT_ADDRESS_BYTES 4 #define TOT_ECG_DATA_BYTES 18 #define TOT_FWC_BYTES 26 #define NELEMS(x) (sizeof(x) / sizeof((x)[0])) extern int dbg_events[]; extern int dbg_events_idx; extern bool end_event; extern bool tx_start_event; void timer_callback(void const *arg); void delay_function(void); void delay_timer_callback(void const *arg); osTimerDef(myTimer, timer_callback); osTimerDef(myTimer_delay, delay_timer_callback); //One second timer for debug print of total messages transmitted and received over past second void sec_timer_callback(void const *arg); osTimerDef(my_sec_timer, sec_timer_callback); //Back channel message received for channel selection (commands 0x04 and 0x05) unsigned char channel1_filter = 0x03; unsigned char channel2_filter = 0x03; int filter_recvd; //Functions to set LED's 3 and 4 to visually indicate filter selection int channel1_filter_led_set(void); int channel2_filter_led_set(void); void decimalToBinary(int num, int *val1, int *val2); //Initialize ECG Packet for Forward Channel Transmission at 992 Hz void initFwdChData(void); //Channel 4 setting for Primary frequency of 2.440 GHz and Secondary frequency of 2.448 GHz typedef enum hop_frequency { primary_hop = 40, secondary_hop = 48 } hop_values; int frequency_hop; int network; int networkGroup; unsigned int networkAddress; int counter; bool packet_tx_done = true; bool packet_ready = true; bool delay_timer_flag = false; //Counters for numer of TX messages and RX messages since last reset (1 second) int second_rx_cnt=0; int second_tx_cnt=0; //ECG data sample variables for 2 Leads and 3 time slices (T0, T1, T2) short int ch1DataA; short int ch1DataB; short int ch1DataC; short int ch2DataA; short int ch2DataB; short int ch2DataC; int rawLeadDataCh1; int rawLeadDataCh2; int led_count; int led_value = 0; osTimerId delay_timer_id; bool tx_delay_done = false; bool print_metrics = false; //ECG Waveform Simulation Array from MR400 divided by 10 static const short ECG_DAT[] = { -34, -32, -23, -23, -18, -18, -18, -12, -15, -20, -11, -18, -29, -32, -34, -29, -25, -28, -32, -19, -15, -6, -13, -16, -21, -21, -20, -14, -25, -28, -38, -36, -30, -28, -37, -25, -20, -13, -11, -9, -14, -18, -13, -17, -27, -28, -35, -39, -35, -31, -33, -36, -32, -25, -13, -17, -20, -13, -14, -15, -24, -25, -30, -29, -41, -35, -32, -39, -25, -19, -20, -16, -14, -18, -10, -7, -14, -15, -31, -27, -33, -29, -35, -23, -24, -19, -9, -8, -10, -12, -5, -7, -10, -9, -24, -31, -32, -33, -40, -36, // 100 -29, -26, -17, -17, -11, -8, -12, -5, -3, -11, -13, -19, -29, -35, -32, -26, -26, -25, -18, -19, -15, -5, -6, -12, -14, -18, -18, -23, -32, -35, -25, -32, -28, -33, -27, -17, -14, -7, -14, -5, -10, -8, -18, -10, -18, -31, -27, -37, -31, -30, -28, -19, -6, -1, -1, -1, 6, 3, 10, 8, 12, 1, 9, 6, 10, 14, 23, 18, 31, 36, 46, 52, 49, 56, 56, 56, 50, 52, 49, 39, 40, 46, 48, 51, 51, 55, 63, 65, 66, 73, 69, 73, 68, 58, 57, 50, 46, 50, 43, 42, // 200 52, 55, 50, 61, 57, 54, 60, 47, 45, 37, 36, 19, 19, 20, 13, 16, 9, 11, 18, 16, 20, 22, 17, 13, 4, -2, -14, -18, -26, -32, -29, -36, -25, -27, -22, -13, -12, -14, -15, -8, -9, -12, -10, -13, -32, -33, -33, -28, -25, -31, -28, -20, -19, -14, -10, -14, -6, -4, -13, -14, -20, -31, -37, -34, -28, -28, -28, -20, -21, -18, -19, -19, -12, -15, -8, -9, -27, -35, -38, -27, -34, -30, -34, -28, -17, -15, -7, -15, -6, -12, -10, -8, -13, -21, -22, -30, -28, -34, -32, -29, // 300 -19, -18, -12, -12, -12, -17, -9, -15, -18, -25, -36, -29, -32, -39, -34, -37, -41, -39, -34, -35, -41, -43, -49, -49, -61, -68, -79, -82, -81, -81, -53, -41, -28, 6, 32, 62, 99, 123, 154, 187, 217, 249, 277, 301, 331, 363, 391, 431, 452, 484, 522, 540, 563, 572, 571, 557, 548, 539, 511, 490, 452, 417, 382, 350, 313, 279, 240, 218, 176, 153, 117, 88, 60, 28, 0, -34, -60, -79, -85, -96, -106, -110, -116, -103, -98, -99, -91, -80, -73, -72, -59, -54, -54, -46, -45, -45, -40, -38, -45, -43, // 400 -36, -37, -32, -29, -34, -38, -34, -29, -30, -47, -44, -50, -47, -42, -43, -43, -37, -38, -35, -24, -37, -30, -32, -24, -24, -41, -49, -51, -41, -48, -44, -48, -43, -33, -31, -24, -31, -23, -28, -26, -23, -27, -35, -36, -44, -41, -36, -35, -45, -35, -21, -28, -15, -27, -20, -23, -17, -20, -28, -27, -32, -35, -30, -38, -28, -32, -31, -25, -15, -9, -12, -6, -7, -7, -15, -25, -28, -25, -24, -30, -15, -23, -23, -6, 2, 4, 5, -1, 7, 0, -5, -3, -17, -11, -15, -11, -18, -7, -10, -6, // 500 1, 13, 8, 6, 13, 11, 9, 12, -1, -6, -5, -5, -11, -8, -2, 0, 7, 17, 20, 22, 17, 25, 16, 21, 9, 5, 9, 3, 7, 12, 0, 11, 17, 26, 28, 25, 35, 30, 40, 36, 37, 21, 26, 25, 24, 17, 22, 17, 22, 31, 44, 37, 41, 48, 42, 43, 46, 43, 36, 37, 30, 33, 39, 41, 42, 51, 51, 56, 67, 65, 71, 67, 74, 71, 65, 55, 52, 62, 56, 61, 70, 64, 76, 79, 87, 92, 88, 94, 93, 94, 88, 79, 78, 69, 72, 78, 80, 83, 82, 85, // 600 91, 92, 104, 97, 93, 98, 82, 74, 74, 68, 65, 70, 63, 61, 69, 71, 77, 75, 81, 77, 71, 71, 70, 62, 51, 48, 37, 39, 44, 35, 39, 39, 44, 41, 44, 34, 29, 25, 29, 12, 13, -2, -10, -15, -23, -30, -19, -22, -29, -21, -21, -12, -15, -21, -22, -24, -33, -35, -53, -52, -52, -59, -55, -50, -48, -41, -42, -37, -34, -39, -31, -29, -36, -37, -53, -50, -55, -52, -58, -46, -47, -41, -43, -28, -31, -32, -37, -39, -30, -42, -45, -53, -55, -56, -51, -48, -53, -48, -51, -49, // 700 -41, -49, -40, -45, -31, -41, -45, -52, -52, -49, -58, -53, -51, -49, -40, -39, -33, -33, -32, -25, -29, -22, -25, -33, -43, -48, -50, -45, -52, -43, -36, -35, -31, -22, -28, -19, -24, -24, -24, -30, -40, -42, -51, -49, -43, -53, -49, -50, -34, -39, -25, -25, -31, -23, -30, -34, -43, -43, -49, -52, -48, -43, -44, -48, -45, -26, -28, -21, -24, -29, -30, -31, -27, -39, -43, -42, -53, -47, -44, -51, -37, -32, -34, -31, -29, -22, -27, -23, -29, -29, -44, -51, -56, -39, -46, -47, -36, -32, -23, -23, // 800 -26, -28, -21, -22, -25, -23, -37, -43, -44, -44, -50, -47, -40, -38, -30, -31, -25, -22, -27, -19, -17, -25, -26, -43, -40, -46, -43, -37, -37, -38, -31, -20, -18, -20, -21, -14, -16, -20, -19, -35, -43, -45, -46, -41, -37, -43, -38, -41, -27, -20, -28, -20, -25, -23, -20, -23, -30, -30, -38, -35, -42, -40, -38, -29, -28, -23, -24, -24, -17, -21, -15, -17, -24, -35, -39, -40, -47, -41, -44, -36, -24, -21, -12, -20, -23, -17, -17, -17, -23, -32, -33, -42, -39, -33, -43, -39, -28, -25, -18, -17, // 900 -17, -23, -15, -22, -26, -23, -35, -41, -43, -38, -33, -35, -27, -24, -18, -20, -13, -16, -21, -21, -22, -18, -30, -34, -33, -32, -39, -36, -31, -30, -24, -15, -11, -9, -14, -18, -14, -20, -19, -34, -30, -35, -32, -39, -40, -29, -24, -16, -15, -6, -8, -14, -15, -17, -14, -28, -34, -35, -35, -42, -39, -33, -31, -23, -24, -19, -16, -20, -12, -9, -17, -18, -35, -32, -38, -35, -30, -30, -30, -24, -25, -22, -12, -13, -18, -20, -12, -12, -29, -37, -39, -28, -32768}; int total_items_ecg_dat = NELEMS(ECG_DAT); //Structure used to set up Nordic Radio Packet and settings typedef struct NetTable_t { unsigned int addr; char cfgHi; char cfgLo1; char cfgLo2; } NetTable; //Table of Address Bytes, CFGHi, CFGLo1, and CFGLo2 for Channels 1-10 NetTable NetworkTable[] = { {0xBB44BBB5, 0xAF, 138, 154}, {0x9B449B95, 0xAF, 72, 88}, {0xBA44BAB5, 0xAF, 74, 90}, {0x99449996, 0xAF, 80, 96}, {0xA944A9A5, 0xAF, 70, 86}, {0xA6565656, 0xAF, 144, 160}, {0xC5556666, 0xAF, 110, 126}, {0xA6669999, 0xAF, 108, 124}, {0x96969696, 0xAF, 116, 132}, {0xCCCC5555, 0xAF, 106, 122}, {0xBBBBBBBB, 0xAF, 4, 20}, {0x9B9B9B9B, 0xAF, 6, 22}, {0xBABABABA, 0xAF, 12, 28}, {0x99999999, 0xAF, 14, 30}, {0xA9A9A9A9, 0xAF, 36, 52} }; // Structure for ECG Forward Channel Packet typedef struct { // char wPreamble; // char wAddress[TOT_ADDRESS_BYTES]; char wLenType; char wECGData[TOT_ECG_DATA_BYTES]; char wEcgSeqNumber; char wEcgStatusData; } EcgForwardChannelPacket; static EcgForwardChannelPacket EcgFWCPckt = { // 0x00, // {0x00,0x00,0x00,0x00}, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0x00, 0x00}; LOG_MODULE_REGISTER(esb_ptx, CONFIG_ESB_PTX_APP_LOG_LEVEL); #if 0 static const struct gpio_dt_spec leds[] = { GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios), GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios), GPIO_DT_SPEC_GET(DT_ALIAS(led2), gpios), GPIO_DT_SPEC_GET(DT_ALIAS(led3), gpios), }; BUILD_ASSERT(DT_SAME_NODE(DT_GPIO_CTLR(DT_ALIAS(led0), gpios), DT_GPIO_CTLR(DT_ALIAS(led1), gpios)) && DT_SAME_NODE(DT_GPIO_CTLR(DT_ALIAS(led0), gpios), DT_GPIO_CTLR(DT_ALIAS(led2), gpios)) && DT_SAME_NODE(DT_GPIO_CTLR(DT_ALIAS(led0), gpios), DT_GPIO_CTLR(DT_ALIAS(led3), gpios)), "All LEDs must be on the same port"); #endif /* The devicetree node identifier for the "led3" alias. */ #define LED0_NODE DT_ALIAS(led0) static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios); #define LED1_NODE DT_ALIAS(led1) static const struct gpio_dt_spec led_1 = GPIO_DT_SPEC_GET(LED1_NODE, gpios); #define LED2_NODE DT_ALIAS(led2) static const struct gpio_dt_spec led_3 = GPIO_DT_SPEC_GET(LED2_NODE, gpios); #define LED3_NODE DT_ALIAS(led3) static const struct gpio_dt_spec led_4 = GPIO_DT_SPEC_GET(LED3_NODE, gpios); int led_count; static bool ready = false; static struct esb_payload rx_payload; static struct esb_payload tx_payload = ESB_CREATE_PAYLOAD(0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); #define _RADIO_SHORTS_COMMON \ (RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk | \ RADIO_SHORTS_ADDRESS_RSSISTART_Msk | \ RADIO_SHORTS_DISABLED_RSSISTOP_Msk) //Function to Process Nordic Events void event_handler(struct esb_evt const *event) { ready = true; switch (event->evt_id) { case ESB_EVENT_TX_SUCCESS: // LOG_DBG("TX SUCCESS EVENT"); break; case ESB_EVENT_TX_FAILED: LOG_DBG("TX FAILED EVENT"); break; case ESB_EVENT_RX_RECEIVED: if (esb_read_rx_payload(&rx_payload) == 0) { #if 0 LOG_DBG("Packet received, len %d, data: " "0x%02x, 0x%02x, 0x%02x, 0x%02x, " "0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x ", rx_payload.length, rx_payload.data[0], rx_payload.data[1], rx_payload.data[2], rx_payload.data[3], rx_payload.data[4], rx_payload.data[5], rx_payload.data[6], rx_payload.data[7],rx_payload.data[8]); #endif #if 0 if(rx_payload.data[1] == 0x04) { printk("Filter data 0x04 : 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x \r\n",rx_payload.data[1], rx_payload.data[2], rx_payload.data[3], rx_payload.data[4], rx_payload.data[5]); channel1_filter = rx_payload.data[2]; //filter_recvd = 1; } #endif //if ((rx_payload.data[1] == 0x05) && (channel2_filter != rx_payload.data[2])) if (rx_payload.data[1] == 0x05) { printk("Filter data 0x05 : 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x \r\n", rx_payload.data[1], rx_payload.data[2], rx_payload.data[3], rx_payload.data[4], rx_payload.data[5]); if (channel2_filter != rx_payload.data[2]) { channel2_filter = rx_payload.data[2]; filter_recvd = 2; } } second_rx_cnt++; } break; } } //Function to start system clocks int clocks_start(void) { int err; int res; struct onoff_manager *clk_mgr; struct onoff_client clk_cli; clk_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF); if (!clk_mgr) { LOG_ERR("Unable to get the Clock manager"); return -ENXIO; } sys_notify_init_spinwait(&clk_cli.notify); err = onoff_request(clk_mgr, &clk_cli); if (err < 0) { LOG_ERR("Clock request failed: %d", err); return err; } do { err = sys_notify_fetch_result(&clk_cli.notify, &res); if (!err && res) { LOG_ERR("Clock could not be started: %d", res); return res; } } while (err); LOG_DBG("HF clock started"); return 0; } //Function to initialize Nordic Radio at Start Up for RX int esb_initialize(void) { int err; //Nordic radio can have 8 Address Bytes. MR400 implementation uses 4 Address Bytes in base_addr_0 uint8_t base_addr_0[4] = {0x99, 0x44, 0x99, 0x96}; uint8_t base_addr_1[4] = {0x00, 0x00, 0x00, 0x00}; //Nordic address prefix MR400 uses 1 and it is set to LS Byte of Address Bytes uint8_t addr_prefix[8] = {0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; struct esb_config config = ESB_DEFAULT_CONFIG; config.protocol = ESB_PROTOCOL_ESB; config.retransmit_delay = 600; config.bitrate = ESB_BITRATE_1MBPS; config.event_handler = event_handler; config.mode = ESB_MODE_PRX; config.selective_auto_ack = true; config.payload_length = 9; err = esb_init(&config); if (err) { printk("esb_init failed \r\n"); return err; } err = esb_set_base_address_0(base_addr_0); if (err) { return err; } err = esb_set_base_address_1(base_addr_1); if (err) { return err; } err = esb_set_prefixes(addr_prefix, ARRAY_SIZE(addr_prefix)); if (err) { return err; } tx_payload.noack = 1; rx_payload.noack = 1; return 0; } //Function to set Nordic Radio in TX mode int esb_initialize_tx(void) { int err; struct esb_config config = ESB_DEFAULT_CONFIG; config.protocol = ESB_PROTOCOL_ESB; config.retransmit_delay = 600; config.bitrate = ESB_BITRATE_1MBPS; config.event_handler = event_handler; config.mode = ESB_MODE_PTX; config.selective_auto_ack = true; config.payload_length = 21; err = esb_init_tx(&config); if (err) { return err; } return 0; } //Function to set Nordic Radio in RX mode int esb_initialize_rx(void) { int err; struct esb_config config = ESB_DEFAULT_CONFIG; config.protocol = ESB_PROTOCOL_ESB; config.retransmit_delay = 600; config.bitrate = ESB_BITRATE_1MBPS; config.event_handler = event_handler; config.mode = ESB_MODE_PRX; config.selective_auto_ack = true; config.payload_length = 9; err = esb_init_rx(&config); if (err) { return err; } return 0; } //Function to initialize LED's 1, 3, 4 static int leds_init(void) { int ret = 0; if (!device_is_ready(led.port)) { printk(" error device_is_ready \r\n"); return -ENODEV; } ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); if (ret < 0) { printk(" error gpio_pin_configure_dt \r\n"); return -2; } if (!device_is_ready(led_1.port)) { printk(" error device_is_ready \r\n"); return -ENODEV; } ret = gpio_pin_configure_dt(&led_1, GPIO_OUTPUT_ACTIVE); if (ret < 0) { printk(" error gpio_pin_configure_dt \r\n"); return -2; } if (!device_is_ready(led_3.port)) { printk(" error device_is_ready \r\n"); return -ENODEV; } ret = gpio_pin_configure_dt(&led_3, GPIO_OUTPUT_ACTIVE); if (ret < 0) { printk(" error gpio_pin_configure_dt \r\n"); return -2; } if (!device_is_ready(led_4.port)) { printk(" error device_is_ready \r\n"); return -ENODEV; } ret = gpio_pin_configure_dt(&led_4, GPIO_OUTPUT_ACTIVE); if (ret < 0) { printk(" error gpio_pin_configure_dt \r\n"); return -2; } return 0; } //Function to initialize the Forward Channel Message. void initFwdChData(void) { int ret = 0; // Each FWC packet consists of 28 bytes (26 data bytes + 2 CRC bytes) which is transmitted at a rate of 992 Hz (1.008065 ms) // 1. Header - Preamble (8 Bits) + Address (32 Bits) + Length (4 Bits) + Type (4 Bits) // 2. ECG DATA "A" - Data1 (16 bits) + Data2 (16 Bits) + Data3 (16 bits) // 3. ECG DATA "B" - Data1 (16 bits) + Data2 (16 Bits) + Data3 (16 bits) // 4. ECG DATA "C" - Data1 (16 bits) + Data2 (16 Bits) + Data3 (16 bits) // 5. Status Information - Status Code (8 bits) + Status Data (8 bits) // 6. CRC - CRC data (16 bits). The CRC bytes are calculated by the Radio firmware block and not calculated here. // EcgFWCPckt.wPreamble = ECG_FW_CH_PREAMBLE; // Preamble EcgFWCPckt.wLenType = ECG_FW_CH_LENTYPE; // Length/type EcgFWCPckt.wEcgSeqNumber = 0x00; EcgFWCPckt.wEcgStatusData = 0x00; network = 4; // Get the Network Address from the network table networkAddress = NetworkTable[network - 1].addr; // EcgFWCPckt.wAddress[0] = (networkAddress >> 24) & 0xFF; // EcgFWCPckt.wAddress[1] = (networkAddress >> 16) & 0xFF; // EcgFWCPckt.wAddress[2] = (networkAddress >> 8) & 0xFF; // EcgFWCPckt.wAddress[3] = networkAddress & 0xFF; ch1DataA = 0; ch1DataB = 0; ch1DataC = 0; ch2DataA = 0; ch2DataB = 0; ch2DataC = 0; // ECG DATA "A" // DATA 1 EcgFWCPckt.wECGData[0] = ch1DataA & 0xFF; EcgFWCPckt.wECGData[1] = (ch1DataA & 0xFF00) >> 8; // DATA 2 EcgFWCPckt.wECGData[2] = ch2DataA & 0xFF; EcgFWCPckt.wECGData[3] = (ch2DataA & 0xFF00) >> 8; // DATA 3 // The 0x55 value for Data A-3 prevents a long series of 0 bit values // in the packet transmission EcgFWCPckt.wECGData[4] = 0x55; EcgFWCPckt.wECGData[5] = 0x55; // ECG DATA "B" // DATA 1 EcgFWCPckt.wECGData[6] = ch1DataB & 0xFF; EcgFWCPckt.wECGData[7] = (ch1DataB & 0xFF00) >> 8; // DATA 2 EcgFWCPckt.wECGData[8] = ch2DataB & 0xFF; EcgFWCPckt.wECGData[9] = (ch2DataB & 0xFF00) >> 8; // DATA 3 // The 0x55 value for Data B-3 prevents a long series of 0 bit values // in the packet transmission EcgFWCPckt.wECGData[10] = 0x55; EcgFWCPckt.wECGData[11] = 0x55; // ECG DATA "C" // DATA 1 EcgFWCPckt.wECGData[12] = ch1DataC & 0xFF; EcgFWCPckt.wECGData[13] = (ch1DataC & 0xFF00) >> 8; // DATA 2 EcgFWCPckt.wECGData[14] = ch2DataC & 0xFF; EcgFWCPckt.wECGData[15] = (ch2DataC & 0xFF00) >> 8; // DATA 3 // The 0x55 value for Data C-3 prevents a long series of 0 bit values // in the packet transmission EcgFWCPckt.wECGData[16] = 0x55; EcgFWCPckt.wECGData[17] = 0x55; // Status Code/ Sequence # EcgFWCPckt.wEcgSeqNumber++; EcgFWCPckt.wEcgSeqNumber = EcgFWCPckt.wEcgSeqNumber & 0xFF; bool packet_tx_done = true; bool packet_ready = false; channel2_filter = 0x03; channel1_filter = 0x03; // Set the radio frequency and start rx frequency_hop = primary_hop; esb_set_rf_channel(frequency_hop); esb_start_rx(); frequency_hop = secondary_hop; ret = gpio_pin_set(led_3.port, led_3.pin, 0); if (ret) { LOG_ERR("LED3 set 1 error: %d", ret); } ret = gpio_pin_set(led_4.port, led_4.pin, 1); if (ret) { LOG_ERR("LED4 set 1 error: %d", ret); } } void delay_function(void) { delay_timer_flag = true; while (delay_timer_flag == false); } void delay_timer_callback(void const *arg) { tx_delay_done = true; } void sec_timer_callback(void const *arg) { print_metrics = true; } void decimalToBinary(int num, int *val1, int *val2) { // Stores binary representation of number. int binaryNum[3] = {0, 0, 0}; // Assuming 32 bit integer. int i = 0; for (; num > 0;) { binaryNum[i++] = num % 2; num /= 2; } *val1 = binaryNum[1]; *val2 = binaryNum[0]; } int channel1_filter_led_set(void) { int ret = 0, value_led2 = 0, value_led4 = 0; decimalToBinary((channel1_filter - 2), &value_led2, &value_led4); ret = gpio_pin_set(led_3.port, led_3.pin, value_led2); if (ret) { LOG_ERR("LED3 set 1 error: %d", ret); } ret = gpio_pin_set(led_4.port, led_4.pin, value_led4); if (ret) { LOG_ERR("LED4 set 1 error: %d", ret); } } int channel2_filter_led_set(void) { int ret = 0, value_led2 = 0, value_led4 = 0; decimalToBinary((channel2_filter - 2), &value_led2, &value_led4); ret = gpio_pin_set(led_3.port, led_3.pin, value_led2); if (ret) { LOG_ERR("LED3 set 1 error: %d", ret); } ret = gpio_pin_set(led_4.port, led_4.pin, value_led4); if (ret) { LOG_ERR("LED4 set 1 error: %d", ret); } filter_recvd = 0; } void timer_callback(void const *arg) { int ret = 0, err = 0; //*************** Tasks to do here **************** // 1. In init(), set radio for RX // 2. Set radio Frequency Register with frequency variable // 3. Set Radio for TX (TX_EN) (There may be a sequence required and a wait for READY event) //************************************************* if ((packet_ready == true) && (packet_tx_done == false)) { esb_stop_rx(); // delay_function(); esb_set_rf_channel(frequency_hop); esb_initialize_tx(); // delay_function(); esb_flush_tx(); err = esb_write_payload(&tx_payload); if (err) { LOG_ERR("Payload write failed, err %d", err); } second_tx_cnt++; esb_flush_tx(); // delay_function(); // turn off/on led // usleep(10); // k_sleep(K_USEC(10)); // printk("\r\n Inside timer handler, after write payload \r\n"); printk("\n \n"); //if (esb_is_idle() != true) //{ // printk("ESB not idle \r\n"); //} //printk("\r \r"); //printk("\r\r\r\r\r\r\r\r\r\r\n"); packet_tx_done = true; //printk("BEfore while \r\n"); //while((tx_start_event == true)&&(end_event == false)) //{ //} //printk("tx complete\r\n"); //tx_start_event = false; //end_event = false; //esb_stop_tx(); #if 0 led_value = !led_value; printk("led_value : %d \r\n",led_value); ret = gpio_pin_set(&led_1.port, led_1.pin, led_value); if (ret) { LOG_ERR("LED1 set 0 error: %d", ret); } #endif ret = gpio_pin_toggle_dt(&led_1); if (ret < 0) { return; } esb_set_rf_channel(primary_hop); esb_initialize_rx(); // delay_function(); esb_start_rx(); } //**************** After TX Success *************** // 4. Set Radio for RX (RX_EN) (There may be a sequence required and a wait for READY event) //************************************************* } void main(void) { int err, i = 0, ret = 0; osTimerId timer_id; osTimerId timer_sec_id; LOG_INF("Enhanced ShockBurst ptx sample"); printk(" \r\n ** total_items_ecg_dat: %d **\r\n", total_items_ecg_dat); err = clocks_start(); if (err) { return; } err = leds_init(); if (err) { return; } err = esb_initialize(); if (err) { LOG_ERR("ESB initialization failed, err %d", err); return; } initFwdChData(); LOG_INF("Initialization complete"); LOG_INF("Sending test packet"); delay_timer_id = osTimerCreate_US(osTimer(myTimer_delay), osTimerOnce, NULL); timer_id = osTimerCreate_US(osTimer(myTimer), osTimerPeriodic, NULL); osTimerStart_US(timer_id, TIMER_TICKS); timer_sec_id = osTimerCreate_US(osTimer(my_sec_timer), osTimerPeriodic, NULL); osTimerStart_US(timer_sec_id, TIMER_SEC_TICKS); while (1) { if (packet_tx_done == true) { packet_ready == false; //********************* Tasks to do ************* // 1. Set a frequency variable to Primary or Secondary frequency for next transmission based on hopping pattern/network //************************************************ if (frequency_hop == primary_hop) { frequency_hop = secondary_hop; } else if (frequency_hop == secondary_hop) { frequency_hop = primary_hop; } else { frequency_hop = primary_hop; } // At Max QRS value, set LED 4 ON if (ECG_DAT[counter] == -572) { // turn on led ret = gpio_pin_set(&led, led.pin, 1); if (ret) { LOG_ERR("LED3 set 1 error: %d", ret); } led_count = 1; } // Move T-1 Data to T-2 Data and T Data to T-1 Data ch1DataC = ch1DataB; ch2DataC = ch2DataB; ch1DataB = ch1DataA; ch2DataB = ch2DataA; ch1DataA = ECG_DAT[counter]; ch2DataA = ECG_DAT[counter]; // ECG DATA "A" // DATA 1 EcgFWCPckt.wECGData[0] = ch1DataA & 0xFF; EcgFWCPckt.wECGData[1] = (ch1DataA & 0xFF00) >> 8; // DATA 2 EcgFWCPckt.wECGData[2] = ch2DataA & 0xFF; EcgFWCPckt.wECGData[3] = (ch2DataA & 0xFF00) >> 8; // DATA 3 // The 0x55 value for Data A-3 prevents a long series of 0 bit values // in the packet transmission EcgFWCPckt.wECGData[4] = 0x55; EcgFWCPckt.wECGData[5] = 0x55; // ECG DATA "B" // DATA 1 EcgFWCPckt.wECGData[6] = ch1DataB & 0xFF; EcgFWCPckt.wECGData[7] = (ch1DataB & 0xFF00) >> 8; // DATA 2 EcgFWCPckt.wECGData[8] = ch2DataB & 0xFF; EcgFWCPckt.wECGData[9] = (ch2DataB & 0xFF00) >> 8; // DATA 3 // The 0x55 value for Data B-3 prevents a long series of 0 bit values // in the packet transmission EcgFWCPckt.wECGData[10] = 0x55; EcgFWCPckt.wECGData[11] = 0x55; // ECG DATA "C" // DATA 1 EcgFWCPckt.wECGData[12] = ch1DataC & 0xFF; EcgFWCPckt.wECGData[13] = (ch1DataC & 0xFF00) >> 8; // DATA 2 EcgFWCPckt.wECGData[14] = ch2DataC & 0xFF; EcgFWCPckt.wECGData[15] = (ch2DataC & 0xFF00) >> 8; // DATA 3 // The 0x55 value for Data C-3 prevents a long series of 0 bit values // in the packet transmission EcgFWCPckt.wECGData[16] = 0x55; EcgFWCPckt.wECGData[17] = 0x55; // Set sequence number for next packet transmission EcgFWCPckt.wEcgSeqNumber++; EcgFWCPckt.wEcgSeqNumber = EcgFWCPckt.wEcgSeqNumber & 0xFF; //*************** Tasks to do ****************** // 1. Write EcgFWCPckt.wEcgStatusData = New Structure[EcgFWCPckt.wEcgSeqNumber & 0x3F] //********************************************** // Copy ECG packet data into transmit payload memcpy(tx_payload.data, &EcgFWCPckt, 21); // Set counter index for ECG simulated data if (counter < total_items_ecg_dat) { counter = counter + 1; } else { counter = 0; } if (led_count > 0) { led_count++; } if (led_count > 250) { // turn off led ret = gpio_pin_set(&led, led.pin, 0); if (ret) { LOG_ERR("LED3 set 0 error: %d", ret); } led_count = 0; } esb_flush_tx(); packet_ready == true; packet_tx_done = false; } else { // packet_tx_done = false #if 0 if(filter_recvd == 1) { channel1_filter_led_set(); } #endif if (filter_recvd == 2) { channel2_filter_led_set(); } if (delay_timer_flag == true) { osTimerStart_US(delay_timer_id, DELAY_TIMER_TICKS); while (tx_delay_done == false); tx_delay_done = false; osTimerStop(delay_timer_id); delay_timer_flag = false; } if( print_metrics == true ) { printk("second_tx_cnt: %d second_rx_cnt: %d \r\n", second_tx_cnt, second_rx_cnt); second_rx_cnt = 0; second_tx_cnt = 0; print_metrics = false; } //*************** Tasks to do *********************** // 1. Set up a structure of 64 chars to hold status data // 2. In init() function initialize structure with current value sent as data. // 3. Based on index (SequenceNumber & 0x3F), fill in designated RX data // a. Plan is to fill in Filter setting with data received from MR400 // b. Based on Filter Setting, set battery remaining time to designated value // 4. Process any received data into Status Data //*************************************************** } // k_sleep(K_MSEC(100)); } }
/* * Copyright (c) 2018 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ #include <errno.h> #include <zephyr/irq.h> #include <zephyr/sys/byteorder.h> #include <nrf.h> #include <esb.h> #ifdef DPPI_PRESENT #include <nrfx_dppi.h> #else #include <nrfx_ppi.h> #endif #include <helpers/nrfx_gppi.h> #include <stddef.h> #include <string.h> #include <nrf_erratas.h> /* Constants */ /* 2 Mb RX wait for acknowledgment time-out value. * Smallest reliable value: 160. */ #define RX_ACK_TIMEOUT_US_2MBPS 160 /* 1 Mb RX wait for acknowledgment time-out value. */ #define RX_ACK_TIMEOUT_US_1MBPS 300 /* 250 Kb RX wait for acknowledgment time-out value. */ #define RX_ACK_TIMEOUT_US_250KBPS 300 /* 1 Mb RX wait for acknowledgment time-out (combined with BLE). */ #define RX_ACK_TIMEOUT_US_1MBPS_BLE 300 /* Minimum retransmit time */ #define RETRANSMIT_DELAY_MIN 435 /* Interrupt flags */ /* Interrupt mask value for TX success. */ #define INT_TX_SUCCESS_MSK 0x01 /* Interrupt mask value for TX failure. */ #define INT_TX_FAILED_MSK 0x02 /* Interrupt mask value for RX_DR. */ #define INT_RX_DATA_RECEIVED_MSK 0x04 /* Mask value to signal updating BASE0 radio address. */ #define ADDR_UPDATE_MASK_BASE0 (1 << 0) /* Mask value to signal updating BASE1 radio address. */ #define ADDR_UPDATE_MASK_BASE1 (1 << 1) /* Mask value to signal updating radio prefixes. */ #define ADDR_UPDATE_MASK_PREFIX (1 << 2) /* The maximum value for PID. */ #define PID_MAX 3 #define BIT_MASK_UINT_8(x) (0xFF >> (8 - (x))) #define RADIO_SHORTS_COMMON \ (RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk | \ RADIO_SHORTS_ADDRESS_RSSISTART_Msk | \ RADIO_SHORTS_DISABLED_RSSISTOP_Msk) #ifdef CONFIG_ESB_SYS_TIMER0 #define ESB_SYS_TIMER NRF_TIMER0 #define ESB_SYS_TIMER_IRQn TIMER0_IRQn #endif #ifdef CONFIG_ESB_SYS_TIMER1 #define ESB_SYS_TIMER NRF_TIMER1 #define ESB_SYS_TIMER_IRQn TIMER1_IRQn #endif #ifdef CONFIG_ESB_SYS_TIMER2 #define ESB_SYS_TIMER NRF_TIMER2 #define ESB_SYS_TIMER_IRQn TIMER2_IRQn #endif #ifdef CONFIG_ESB_SYS_TIMER3 #define ESB_SYS_TIMER NRF_TIMER3 #define ESB_SYS_TIMER_IRQn TIMER3_IRQn #endif #ifdef CONFIG_ESB_SYS_TIMER4 #define ESB_SYS_TIMER NRF_TIMER4 #define ESB_SYS_TIMER_IRQn TIMER4_IRQn #endif int dbg_ready_counter; int dbg_disabled_counter; int dbg_end_counter; bool end_event = false; bool tx_start_event = false; /* Internal Enhanced ShockBurst module state. */ enum esb_state { ESB_STATE_IDLE, /* Idle. */ ESB_STATE_PTX_TX, /* Transmitting without acknowledgment. */ ESB_STATE_PTX_TX_ACK, /* Transmitting with acknowledgment. */ ESB_STATE_PTX_RX_ACK, /* Transmitting with acknowledgment and * reception of payload with the * acknowledgment response. */ ESB_STATE_PRX, /* Receiving packets without ACK. */ ESB_STATE_PRX_SEND_ACK, /* Transmitting ACK in RX mode. */ }; /* Pipe info PID and CRC and acknowledgment payload. */ struct pipe_info { uint16_t crc; /* CRC of the last received packet. * Used to detect retransmits. */ uint8_t pid; /* Packet ID of the last received packet * Used to detect retransmits. */ bool ack_payload; /* State of the transmission of ACK payloads. */ }; /* Structure used by the PRX to organize ACK payloads for multiple pipes. */ struct payload_wrap { /* Pointer to the ACK payload. */ struct esb_payload *p_payload; /* Value used to determine if the current payload pointer is used. */ bool in_use; /* Pointer to the next ACK payload queued on the same pipe. */ struct payload_wrap *p_next; }; /* First-in, first-out queue of payloads to be transmitted. */ struct payload_tx_fifo { /* Payload queue */ struct esb_payload *payload[CONFIG_ESB_TX_FIFO_SIZE]; uint32_t back; /* Back of the queue (last in). */ uint32_t front; /* Front of queue (first out). */ uint32_t count; /* Number of elements in the queue. */ }; /* First-in, first-out queue of received payloads. */ struct payload_rx_fifo { /* Payload queue */ struct esb_payload *payload[CONFIG_ESB_RX_FIFO_SIZE]; uint32_t back; /* Back of the queue (last in). */ uint32_t front; /* Front of queue (first out). */ uint32_t count; /* Number of elements in the queue. */ }; /* Enhanced ShockBurst address. * * Enhanced ShockBurst addresses consist of a base address and a prefix * that is unique for each pipe. See @ref esb_addressing in the ESB user * guide for more information. */ struct esb_address { uint8_t base_addr_p0[4]; /* Base address for pipe 0, in big endian. */ uint8_t base_addr_p1[4]; /* Base address for pipe 1-7, in big endian. */ uint8_t pipe_prefixes[8]; /* Address prefix for 0 to pipe7. */ uint8_t num_pipes; /* Number of pipes available. */ uint8_t addr_length; /* Length of the address plus the prefix. */ uint8_t rx_pipes_enabled; /* Bitfield for enabled pipes. */ uint8_t rf_channel; /* Channel to use (between 0 and 100). */ }; static bool esb_initialized; static struct esb_config esb_cfg; static volatile enum esb_state esb_state = ESB_STATE_IDLE; /* Default address configuration for ESB. * Roughly equal to the nRF24Lxx defaults, except for the number of pipes, * because more pipes are supported. */ __ALIGN(4) /* static struct esb_address esb_addr = { .base_addr_p0 = {0xE7, 0xE7, 0xE7, 0xE7}, .base_addr_p1 = {0xC2, 0xC2, 0xC2, 0xC2}, .pipe_prefixes = {0xE7, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8}, .addr_length = 5, .num_pipes = CONFIG_ESB_PIPE_COUNT, .rf_channel = 2, .rx_pipes_enabled = 0xFF }; */ static struct esb_address esb_addr = { .base_addr_p0 = {0x99, 0x44, 0x99, 0x96}, .base_addr_p1 = {0x00, 0x00, 0x00, 0x00}, .pipe_prefixes = {0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, .addr_length = 4, .num_pipes = 1, .rf_channel = 40, .rx_pipes_enabled = 0xFF }; /*Ajit change*/ static esb_event_handler event_handler; static struct esb_payload *current_payload; /* FIFOs and buffers */ static struct payload_tx_fifo tx_fifo; static struct payload_rx_fifo rx_fifo; #if 0 static uint8_t tx_payload_buffer[CONFIG_ESB_MAX_PAYLOAD_LENGTH + 2]; static uint8_t rx_payload_buffer[CONFIG_ESB_MAX_PAYLOAD_LENGTH + 2]; #endif //Ajit change static uint8_t tx_payload_buffer[CONFIG_ESB_MAX_PAYLOAD_LENGTH]; static uint8_t rx_payload_buffer[CONFIG_ESB_MAX_PAYLOAD_LENGTH]; /* Random access buffer variables for ACK payload handling */ struct payload_wrap ack_pl_wrap[CONFIG_ESB_TX_FIFO_SIZE]; struct payload_wrap *ack_pl_wrap_pipe[CONFIG_ESB_PIPE_COUNT]; /* Run time variables */ static uint8_t pids[CONFIG_ESB_PIPE_COUNT]; static struct pipe_info rx_pipe_info[CONFIG_ESB_PIPE_COUNT]; static volatile uint32_t interrupt_flags; static volatile uint32_t retransmits_remaining; static volatile uint32_t last_tx_attempts; static volatile uint32_t wait_for_ack_timeout_us; static uint32_t radio_shorts_common = RADIO_SHORTS_COMMON; /* PPI or DPPI instances */ #ifdef DPPI_PRESENT typedef uint8_t ppi_channel_t; #else typedef nrf_ppi_channel_t ppi_channel_t; #endif static ppi_channel_t ppi_ch_radio_ready_timer_start; static ppi_channel_t ppi_ch_radio_address_timer_stop; static ppi_channel_t ppi_ch_timer_compare0_radio_disable; static ppi_channel_t ppi_ch_timer_compare1_radio_txen; static ppi_channel_t ppi_ch_timer_compare1_radio_rxen; static uint32_t ppi_all_channels_mask; /* These function pointers are changed dynamically, depending on protocol * configuration and state. Note that they will be 0 initialized. */ static void (*on_radio_disabled)(void); static void (*on_radio_end)(void); static void (*update_rf_payload_format)(uint32_t payload_length); int esb_init_tx(const struct esb_config *config); int esb_init_rx(const struct esb_config *config); /* The following functions are assigned to the function pointers above. */ static void on_radio_disabled_tx_noack(void); static void on_radio_disabled_tx(void); static void on_radio_disabled_tx_wait_for_ack(void); static void on_radio_disabled_rx(void); static void on_radio_disabled_rx_ack(void); void mband_frequency_set(void); /* Function to do bytewise bit-swap on an unsigned 32-bit value */ static uint32_t bytewise_bit_swap(const uint8_t *input) { #if __CORTEX_M == (0x04U) uint32_t inp = (*(uint32_t *)input); return sys_cpu_to_be32((uint32_t)__RBIT(inp)); #else uint32_t inp = sys_cpu_to_le32(*(uint32_t *)input); inp = (inp & 0xF0F0F0F0) >> 4 | (inp & 0x0F0F0F0F) << 4; inp = (inp & 0xCCCCCCCC) >> 2 | (inp & 0x33333333) << 2; inp = (inp & 0xAAAAAAAA) >> 1 | (inp & 0x55555555) << 1; return inp; #endif } /* Convert a base address from nRF24L format to nRF5 format */ static uint32_t addr_conv(const uint8_t *addr) { return __REV(bytewise_bit_swap(addr)); } static inline void apply_errata143_workaround(void) { /* Workaround for Errata 143 * Check if the most significant bytes of address 0 (including * prefix) match those of another address. It's recommended to * use a unique address 0 since this will avoid the 3dBm penalty * incurred from the workaround. */ uint32_t base_address_mask = esb_addr.addr_length == 5 ? 0xFFFF0000 : 0xFF000000; /* Load the two addresses before comparing them to ensure * defined ordering of volatile accesses. */ uint32_t addr0 = NRF_RADIO->BASE0 & base_address_mask; uint32_t addr1 = NRF_RADIO->BASE1 & base_address_mask; if (addr0 == addr1) { uint32_t prefix0 = NRF_RADIO->PREFIX0 & 0x000000FF; uint32_t prefix1 = (NRF_RADIO->PREFIX0 & 0x0000FF00) >> 8; uint32_t prefix2 = (NRF_RADIO->PREFIX0 & 0x00FF0000) >> 16; uint32_t prefix3 = (NRF_RADIO->PREFIX0 & 0xFF000000) >> 24; uint32_t prefix4 = NRF_RADIO->PREFIX1 & 0x000000FF; uint32_t prefix5 = (NRF_RADIO->PREFIX1 & 0x0000FF00) >> 8; uint32_t prefix6 = (NRF_RADIO->PREFIX1 & 0x00FF0000) >> 16; uint32_t prefix7 = (NRF_RADIO->PREFIX1 & 0xFF000000) >> 24; if (prefix0 == prefix1 || prefix0 == prefix2 || prefix0 == prefix3 || prefix0 == prefix4 || prefix0 == prefix5 || prefix0 == prefix6 || prefix0 == prefix7) { /* This will cause a 3dBm sensitivity loss, * avoid using such address combinations if possible. */ *(volatile uint32_t *)0x40001774 = ((*(volatile uint32_t *)0x40001774) & 0xfffffffe) | 0x01000000; } } } void mband_frequency_set(void) { NRF_RADIO->FREQUENCY = (RADIO_FREQUENCY_MAP_Low << RADIO_FREQUENCY_MAP_Pos); } static void update_rf_payload_format_esb_dpl(uint32_t payload_length) { #if (CONFIG_ESB_MAX_PAYLOAD_LENGTH <= 32) /* Using 6 bits for length */ /*Ajit change*/ NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S0LEN_Pos) | (0 << RADIO_PCNF0_LFLEN_Pos) | (0 << RADIO_PCNF0_S1LEN_Pos); #else /* Using 8 bits for length */ NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S0LEN_Pos) | (8 << RADIO_PCNF0_LFLEN_Pos) | (3 << RADIO_PCNF0_S1LEN_Pos); #endif //Ajit change NRF_RADIO->PCNF1 = (RADIO_PCNF1_WHITEEN_Disabled << RADIO_PCNF1_WHITEEN_Pos) | (RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) | ((esb_addr.addr_length - 1) << RADIO_PCNF1_BALEN_Pos) | (CONFIG_ESB_MAX_PAYLOAD_LENGTH << RADIO_PCNF1_STATLEN_Pos) | (CONFIG_ESB_MAX_PAYLOAD_LENGTH << RADIO_PCNF1_MAXLEN_Pos); } static void update_rf_payload_format_esb(uint32_t payload_length) { /*Ajit change*/ NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S0LEN_Pos) | (0 << RADIO_PCNF0_LFLEN_Pos) | (0 << RADIO_PCNF0_S1LEN_Pos); //Ajit Change NRF_RADIO->PCNF1 = (RADIO_PCNF1_WHITEEN_Disabled << RADIO_PCNF1_WHITEEN_Pos) | (RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) | ((esb_addr.addr_length - 1) << RADIO_PCNF1_BALEN_Pos) | (payload_length << RADIO_PCNF1_STATLEN_Pos) | (payload_length << RADIO_PCNF1_MAXLEN_Pos); } static void update_radio_addresses(uint8_t update_mask) { if ((update_mask & ADDR_UPDATE_MASK_BASE0) != 0) { NRF_RADIO->BASE0 = addr_conv(esb_addr.base_addr_p0); } if ((update_mask & ADDR_UPDATE_MASK_BASE1) != 0) { NRF_RADIO->BASE1 = addr_conv(esb_addr.base_addr_p1); } if ((update_mask & ADDR_UPDATE_MASK_PREFIX) != 0) { NRF_RADIO->PREFIX0 = bytewise_bit_swap(&esb_addr.pipe_prefixes[0]); NRF_RADIO->PREFIX1 = bytewise_bit_swap(&esb_addr.pipe_prefixes[4]); } /* Workaround for Errata 143 */ #if NRF52_ERRATA_143_ENABLE_WORKAROUND if (nrf52_errata_143()) { apply_errata143_workaround(); } #endif } static void update_radio_tx_power(void) { NRF_RADIO->TXPOWER = esb_cfg.tx_output_power << RADIO_TXPOWER_TXPOWER_Pos; } static bool update_radio_bitrate(void) { NRF_RADIO->MODE = esb_cfg.bitrate << RADIO_MODE_MODE_Pos; switch (esb_cfg.bitrate) { case ESB_BITRATE_2MBPS: #if defined(CONFIG_SOC_SERIES_NRF52X) || defined(CONFIG_SOC_NRF5340_CPUNET) case ESB_BITRATE_2MBPS_BLE: #endif wait_for_ack_timeout_us = RX_ACK_TIMEOUT_US_2MBPS; break; case ESB_BITRATE_1MBPS: wait_for_ack_timeout_us = RX_ACK_TIMEOUT_US_1MBPS; break; #ifdef CONFIG_SOC_SERIES_NRF51X case ESB_BITRATE_250KBPS: wait_for_ack_timeout_us = RX_ACK_TIMEOUT_US_250KBPS; break; #endif /* CONFIG_SOC_SERIES_NRF51X */ case ESB_BITRATE_1MBPS_BLE: wait_for_ack_timeout_us = RX_ACK_TIMEOUT_US_1MBPS_BLE; break; default: /* Should not be reached */ return false; } return true; } static bool update_radio_protocol(void) { switch (esb_cfg.protocol) { case ESB_PROTOCOL_ESB_DPL: update_rf_payload_format = update_rf_payload_format_esb_dpl; break; case ESB_PROTOCOL_ESB: update_rf_payload_format = update_rf_payload_format_esb; break; default: /* Should not be reached */ return false; } return true; } static bool update_radio_crc(void) { switch (esb_cfg.crc) { case ESB_CRC_16BIT: NRF_RADIO->CRCINIT = 0xFFFFUL; /* Initial value */ NRF_RADIO->CRCPOLY = 0x1021UL; /* CRC poly: x^16+x^12^x^5+1 */ // Ajit change from 0x11021 NRF_RADIO->CRCCNF = ESB_CRC_16BIT << RADIO_CRCCNF_LEN_Pos; //(" ** After setting CRC registers ** NRF_RADIO->CRCCNF: 0x%x \r\n",NRF_RADIO->CRCCNF); break; case ESB_CRC_8BIT: NRF_RADIO->CRCINIT = 0xFFUL; /* Initial value */ NRF_RADIO->CRCPOLY = 0x107UL; /* CRC poly: x^8+x^2^x^1+1 */ NRF_RADIO->CRCCNF = ESB_CRC_8BIT << RADIO_CRCCNF_LEN_Pos; break; case ESB_CRC_OFF: NRF_RADIO->CRCINIT = 0x00UL; NRF_RADIO->CRCPOLY = 0x00UL; NRF_RADIO->CRCCNF = ESB_CRC_OFF << RADIO_CRCCNF_LEN_Pos; break; default: return false; } return true; } static bool update_radio_parameters(void) { bool params_valid = true; update_radio_tx_power(); params_valid &= update_radio_bitrate(); params_valid &= update_radio_protocol(); params_valid &= update_radio_crc(); update_rf_payload_format(esb_cfg.payload_length); params_valid &= (esb_cfg.retransmit_delay >= RETRANSMIT_DELAY_MIN); return params_valid; } static void reset_fifos(void) { tx_fifo.back = 0; tx_fifo.front = 0; tx_fifo.count = 0; rx_fifo.back = 0; rx_fifo.front = 0; rx_fifo.count = 0; } static void initialize_fifos(void) { static struct esb_payload rx_payload[CONFIG_ESB_RX_FIFO_SIZE]; static struct esb_payload tx_payload[CONFIG_ESB_TX_FIFO_SIZE]; reset_fifos(); for (size_t i = 0; i < CONFIG_ESB_TX_FIFO_SIZE; i++) { tx_fifo.payload[i] = &tx_payload[i]; } for (size_t i = 0; i < CONFIG_ESB_RX_FIFO_SIZE; i++) { rx_fifo.payload[i] = &rx_payload[i]; } for (size_t i = 0; i < CONFIG_ESB_TX_FIFO_SIZE; i++) { ack_pl_wrap[i].p_payload = &tx_payload[i]; ack_pl_wrap[i].in_use = false; ack_pl_wrap[i].p_next = 0; } for (size_t i = 0; i < CONFIG_ESB_PIPE_COUNT; i++) { ack_pl_wrap_pipe[i] = 0; } } static void tx_fifo_remove_last(void) { if (tx_fifo.count == 0) { return; } uint32_t key = irq_lock(); tx_fifo.count--; if (++tx_fifo.front >= CONFIG_ESB_TX_FIFO_SIZE) { tx_fifo.front = 0; } irq_unlock(key); } /* Function to push the content of the rx_buffer to the RX FIFO. * * The module will point the register NRF_RADIO->PACKETPTR to a buffer for * receiving packets. After receiving a packet the module will call this * function to copy the received data to the RX FIFO. * * @param pipe Pipe number to set for the packet. * @param pid Packet ID. * * @retval true Operation successful. * @retval false Operation failed. */ static bool rx_fifo_push_rfbuf(uint8_t pipe, uint8_t pid) { if (rx_fifo.count >= CONFIG_ESB_RX_FIFO_SIZE) { return false; } if (esb_cfg.protocol == ESB_PROTOCOL_ESB_DPL) { if (rx_payload_buffer[0] > CONFIG_ESB_MAX_PAYLOAD_LENGTH) { return false; } rx_fifo.payload[rx_fifo.back]->length = rx_payload_buffer[0]; } else if (esb_cfg.mode == ESB_MODE_PTX) { /* Received packet is an acknowledgment */ rx_fifo.payload[rx_fifo.back]->length = 0; } else { rx_fifo.payload[rx_fifo.back]->length = esb_cfg.payload_length; } // memcpy(rx_fifo.payload[rx_fifo.back]->data, &rx_payload_buffer[2], // rx_fifo.payload[rx_fifo.back]->length); memcpy(rx_fifo.payload[rx_fifo.back]->data, &rx_payload_buffer[0], rx_fifo.payload[rx_fifo.back]->length); rx_fifo.payload[rx_fifo.back]->pipe = pipe; rx_fifo.payload[rx_fifo.back]->rssi = NRF_RADIO->RSSISAMPLE; rx_fifo.payload[rx_fifo.back]->pid = pid; rx_fifo.payload[rx_fifo.back]->noack = !(rx_payload_buffer[1] & 0x01); if (++rx_fifo.back >= CONFIG_ESB_RX_FIFO_SIZE) { rx_fifo.back = 0; } rx_fifo.count++; return true; } static void sys_timer_init(void) { /* Configure the system timer with a 1 MHz base frequency */ ESB_SYS_TIMER->PRESCALER = 4; ESB_SYS_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit; ESB_SYS_TIMER->SHORTS = TIMER_SHORTS_COMPARE1_CLEAR_Msk | TIMER_SHORTS_COMPARE1_STOP_Msk; } static void ppi_init(void) { #ifdef DPPI_PRESENT nrfx_dppi_channel_alloc(&ppi_ch_radio_ready_timer_start); nrfx_dppi_channel_alloc(&ppi_ch_radio_address_timer_stop); nrfx_dppi_channel_alloc(&ppi_ch_timer_compare0_radio_disable); nrfx_dppi_channel_alloc(&ppi_ch_timer_compare1_radio_txen); NRF_RADIO->PUBLISH_READY = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_radio_ready_timer_start; ESB_SYS_TIMER->SUBSCRIBE_START = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_radio_ready_timer_start; NRF_RADIO->PUBLISH_ADDRESS = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_radio_address_timer_stop; ESB_SYS_TIMER->SUBSCRIBE_SHUTDOWN = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_radio_address_timer_stop; ESB_SYS_TIMER->PUBLISH_COMPARE[0] = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_timer_compare0_radio_disable; NRF_RADIO->SUBSCRIBE_DISABLE = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_timer_compare0_radio_disable; ESB_SYS_TIMER->PUBLISH_COMPARE[1] = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_timer_compare1_radio_txen; NRF_RADIO->SUBSCRIBE_TXEN = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_timer_compare1_radio_txen; #else nrfx_ppi_channel_alloc(&ppi_ch_radio_ready_timer_start); nrfx_ppi_channel_alloc(&ppi_ch_radio_address_timer_stop); nrfx_ppi_channel_alloc(&ppi_ch_timer_compare0_radio_disable); nrfx_ppi_channel_alloc(&ppi_ch_timer_compare1_radio_txen); nrfx_ppi_channel_alloc(&ppi_ch_timer_compare1_radio_rxen); nrfx_ppi_channel_assign(ppi_ch_radio_ready_timer_start, (uint32_t)&NRF_RADIO->EVENTS_READY, (uint32_t)&ESB_SYS_TIMER->TASKS_START); nrfx_ppi_channel_assign(ppi_ch_radio_address_timer_stop, (uint32_t)&NRF_RADIO->EVENTS_ADDRESS, (uint32_t)&ESB_SYS_TIMER->TASKS_SHUTDOWN); nrfx_ppi_channel_assign(ppi_ch_timer_compare0_radio_disable, (uint32_t)&ESB_SYS_TIMER->EVENTS_COMPARE[0], (uint32_t)&NRF_RADIO->TASKS_DISABLE); nrfx_ppi_channel_assign(ppi_ch_timer_compare1_radio_txen, (uint32_t)&ESB_SYS_TIMER->EVENTS_COMPARE[1], (uint32_t)&NRF_RADIO->TASKS_TXEN); nrfx_ppi_channel_assign(ppi_ch_timer_compare1_radio_rxen, (uint32_t)&ESB_SYS_TIMER->EVENTS_COMPARE[2], (uint32_t)&NRF_RADIO->TASKS_RXEN); #endif ppi_all_channels_mask = (1 << ppi_ch_radio_ready_timer_start) | (1 << ppi_ch_radio_address_timer_stop) | (1 << ppi_ch_timer_compare0_radio_disable) | (1 << ppi_ch_timer_compare1_radio_txen) | (1 << ppi_ch_timer_compare1_radio_rxen); } static void start_tx_transaction(void) { bool ack; last_tx_attempts = 1; /* Prepare the payload */ current_payload = tx_fifo.payload[tx_fifo.front]; switch (esb_cfg.protocol) { case ESB_PROTOCOL_ESB: update_rf_payload_format(current_payload->length); #if 0 tx_payload_buffer[0] = current_payload->pid; tx_payload_buffer[1] = 0; memcpy(&tx_payload_buffer[2], current_payload->data, current_payload->length); #endif //Ajit change memcpy(&tx_payload_buffer, current_payload->data, current_payload->length); #if 0 NRF_RADIO->SHORTS = radio_shorts_common | RADIO_SHORTS_DISABLED_RXEN_Msk; NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk | RADIO_INTENSET_READY_Msk; /* Configure the retransmit counter */ retransmits_remaining = esb_cfg.retransmit_count; on_radio_disabled = on_radio_disabled_tx; esb_state = ESB_STATE_PTX_TX_ACK; #endif #if 0 printk("Printing tx data before transmitting...\r\n"); int i = 0; for(i=0; i < current_payload->length; i++) { printk("tx_payload_buffer[%d] : %x \r\n",i,tx_payload_buffer[i]); } #endif #if 1 // Ajit change for tx to work //NRF_RADIO->SHORTS = radio_shorts_common; NRF_RADIO->SHORTS = radio_shorts_common | RADIO_SHORTS_DISABLED_RXEN_Msk; //NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk | RADIO_INTENSET_READY_Msk; //NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; nrfx_gppi_channels_disable(1 << ppi_ch_timer_compare1_radio_rxen); nrfx_gppi_channels_enable(1 << ppi_ch_timer_compare1_radio_txen); on_radio_disabled = on_radio_disabled_tx_noack; esb_state = ESB_STATE_PTX_TX; #endif break; case ESB_PROTOCOL_ESB_DPL: ack = !current_payload->noack || !esb_cfg.selective_auto_ack; #if 0 tx_payload_buffer[0] = current_payload->length; tx_payload_buffer[1] = current_payload->pid << 1; tx_payload_buffer[1] |= current_payload->noack ? 0x00 : 0x01; memcpy(&tx_payload_buffer[2], current_payload->data, current_payload->length); #endif //Ajit change memcpy(&tx_payload_buffer, current_payload->data, current_payload->length); /* Handling ack if noack is set to false or if * selective auto ack is turned off */ if (ack) { NRF_RADIO->SHORTS = radio_shorts_common | RADIO_SHORTS_DISABLED_RXEN_Msk; NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk | RADIO_INTENSET_READY_Msk; /* Configure the retransmit counter */ retransmits_remaining = esb_cfg.retransmit_count; on_radio_disabled = on_radio_disabled_tx; esb_state = ESB_STATE_PTX_TX_ACK; } else { NRF_RADIO->SHORTS = radio_shorts_common; NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; on_radio_disabled = on_radio_disabled_tx_noack; esb_state = ESB_STATE_PTX_TX; } break; default: /* Should not be reached */ break; } NRF_RADIO->TXADDRESS = current_payload->pipe; NRF_RADIO->RXADDRESSES = 1 << current_payload->pipe; //NRF_RADIO->FREQUENCY = esb_addr.rf_channel; NRF_RADIO->FREQUENCY = esb_addr.rf_channel & RADIO_FREQUENCY_FREQUENCY_Msk; NRF_RADIO->PACKETPTR = (uint32_t)tx_payload_buffer; NVIC_ClearPendingIRQ(RADIO_IRQn); irq_enable(RADIO_IRQn); NRF_RADIO->EVENTS_ADDRESS = 0; NRF_RADIO->EVENTS_PAYLOAD = 0; NRF_RADIO->EVENTS_DISABLED = 0; NRF_RADIO->TASKS_TXEN = 1; tx_start_event = true; } static void on_radio_disabled_tx_noack(void) { interrupt_flags |= INT_TX_SUCCESS_MSK; tx_fifo_remove_last(); if (tx_fifo.count == 0) { esb_state = ESB_STATE_IDLE; NVIC_SetPendingIRQ(ESB_EVT_IRQ); } else { NVIC_SetPendingIRQ(ESB_EVT_IRQ); start_tx_transaction(); } } static void on_radio_disabled_tx(void) { printk("Inside on_radio_disabled_tx \r\n"); /* Remove the DISABLED -> RXEN shortcut, to make sure the radio stays * disabled after the RX window */ NRF_RADIO->SHORTS = radio_shorts_common; /* Make sure the timer is started the next time the radio is ready, * and that it will disable the radio automatically if no packet is * received by the time defined in wait_for_ack_timeout_us */ ESB_SYS_TIMER->CC[0] = wait_for_ack_timeout_us; ESB_SYS_TIMER->CC[1] = esb_cfg.retransmit_delay - 130; ESB_SYS_TIMER->TASKS_CLEAR = 1; ESB_SYS_TIMER->EVENTS_COMPARE[0] = 0; ESB_SYS_TIMER->EVENTS_COMPARE[1] = 0; /* Remove */ ESB_SYS_TIMER->TASKS_START = 1; nrfx_gppi_channels_enable(ppi_all_channels_mask); nrfx_gppi_channels_disable(1 << ppi_ch_timer_compare1_radio_txen); NRF_RADIO->EVENTS_END = 0; if (esb_cfg.protocol == ESB_PROTOCOL_ESB) { update_rf_payload_format(0); } NRF_RADIO->PACKETPTR = (uint32_t)rx_payload_buffer; on_radio_disabled = on_radio_disabled_tx_wait_for_ack; esb_state = ESB_STATE_PTX_RX_ACK; } static void on_radio_disabled_tx_wait_for_ack(void) { /* This marks the completion of a TX_RX sequence (TX with ACK) */ /* Make sure the timer will not deactivate the radio if a packet is * received. */ nrfx_gppi_channels_disable(ppi_all_channels_mask); /* If the radio has received a packet and the CRC status is OK */ if (NRF_RADIO->EVENTS_END && NRF_RADIO->CRCSTATUS != 0) { ESB_SYS_TIMER->TASKS_SHUTDOWN = 1; interrupt_flags |= INT_TX_SUCCESS_MSK; last_tx_attempts = esb_cfg.retransmit_count - retransmits_remaining + 1; tx_fifo_remove_last(); if (esb_cfg.protocol != ESB_PROTOCOL_ESB && rx_payload_buffer[0] > 0) { if (rx_fifo_push_rfbuf((uint8_t)NRF_RADIO->TXADDRESS, rx_payload_buffer[1] >> 1)) { interrupt_flags |= INT_RX_DATA_RECEIVED_MSK; } } if ((tx_fifo.count == 0) || (esb_cfg.tx_mode == ESB_TXMODE_MANUAL)) { esb_state = ESB_STATE_IDLE; NVIC_SetPendingIRQ(ESB_EVT_IRQ); } else { NVIC_SetPendingIRQ(ESB_EVT_IRQ); start_tx_transaction(); } } else { if (retransmits_remaining-- == 0) { ESB_SYS_TIMER->TASKS_SHUTDOWN = 1; /* All retransmits are expended, and the TX operation is * suspended */ last_tx_attempts = esb_cfg.retransmit_count + 1; interrupt_flags |= INT_TX_FAILED_MSK; esb_state = ESB_STATE_IDLE; NVIC_SetPendingIRQ(ESB_EVT_IRQ); } else { /* There are still more retransmits left, TX mode should * be entered again as soon as the system timer reaches * CC[1]. */ NRF_RADIO->SHORTS = radio_shorts_common | RADIO_SHORTS_DISABLED_RXEN_Msk; update_rf_payload_format(current_payload->length); NRF_RADIO->PACKETPTR = (uint32_t)tx_payload_buffer; on_radio_disabled = on_radio_disabled_tx; esb_state = ESB_STATE_PTX_TX_ACK; ESB_SYS_TIMER->TASKS_START = 1; nrfx_gppi_channels_enable(1 << ppi_ch_timer_compare1_radio_txen); if (ESB_SYS_TIMER->EVENTS_COMPARE[1]) { NRF_RADIO->TASKS_TXEN = 1; } } } } static void clear_events_restart_rx(void) { NRF_RADIO->SHORTS = radio_shorts_common; update_rf_payload_format(esb_cfg.payload_length); NRF_RADIO->PACKETPTR = (uint32_t)rx_payload_buffer; NRF_RADIO->EVENTS_DISABLED = 0; NRF_RADIO->TASKS_DISABLE = 1; while (NRF_RADIO->EVENTS_DISABLED == 0) { /* wait for register to settle */ } NRF_RADIO->EVENTS_DISABLED = 0; NRF_RADIO->SHORTS = radio_shorts_common | RADIO_SHORTS_DISABLED_TXEN_Msk; NRF_RADIO->TASKS_RXEN = 1; } static void on_radio_disabled_rx_dpl(bool retransmit_payload, struct pipe_info *pipe_info) { uint32_t pipe = NRF_RADIO->RXMATCH; if (tx_fifo.count > 0 && ack_pl_wrap_pipe[pipe] != 0) { current_payload = ack_pl_wrap_pipe[pipe]->p_payload; /* Pipe stays in ACK with payload until TX FIFO is empty */ /* Do not report TX success on first ack payload or retransmit */ if (pipe_info->ack_payload == true && !retransmit_payload) { ack_pl_wrap_pipe[pipe]->in_use = false; ack_pl_wrap_pipe[pipe] = ack_pl_wrap_pipe[pipe]->p_next; tx_fifo.count--; if (tx_fifo.count > 0 && ack_pl_wrap_pipe[pipe] != 0) { current_payload = ack_pl_wrap_pipe[pipe]->p_payload; } else { current_payload = 0; } /* ACK payloads also require TX_DS */ /* (page 40 of the 'nRF24LE1_Product_Specification_rev1_6.pdf') */ interrupt_flags |= INT_TX_SUCCESS_MSK; } if (current_payload != 0) { pipe_info->ack_payload = true; update_rf_payload_format(current_payload->length); tx_payload_buffer[0] = current_payload->length; memcpy(&tx_payload_buffer[2], current_payload->data, current_payload->length); } else { pipe_info->ack_payload = false; update_rf_payload_format(0); tx_payload_buffer[0] = 0; } } else { pipe_info->ack_payload = false; update_rf_payload_format(0); tx_payload_buffer[0] = 0; } tx_payload_buffer[1] = rx_payload_buffer[1]; } static void on_radio_disabled_rx(void) { bool retransmit_payload = false; bool send_rx_event = true; struct pipe_info *pipe_info; if (NRF_RADIO->CRCSTATUS == 0) { clear_events_restart_rx(); return; } if (rx_fifo.count >= CONFIG_ESB_RX_FIFO_SIZE) { clear_events_restart_rx(); return; } pipe_info = &rx_pipe_info[NRF_RADIO->RXMATCH]; if (NRF_RADIO->RXCRC == pipe_info->crc && (rx_payload_buffer[1] >> 1) == pipe_info->pid) { retransmit_payload = true; send_rx_event = false; } pipe_info->pid = rx_payload_buffer[1] >> 1; pipe_info->crc = NRF_RADIO->RXCRC; //Ajit change// nrfx_gppi_channels_enable(ppi_all_channels_mask); nrfx_gppi_channels_disable(1 << ppi_ch_timer_compare1_radio_rxen); //Ajit change /* Check if an ack should be sent */ if ((esb_cfg.selective_auto_ack == false) || ((rx_payload_buffer[1] & 0x01) == 1)) { NRF_RADIO->SHORTS = radio_shorts_common | RADIO_SHORTS_DISABLED_RXEN_Msk; switch (esb_cfg.protocol) { case ESB_PROTOCOL_ESB_DPL: on_radio_disabled_rx_dpl(retransmit_payload, pipe_info); break; case ESB_PROTOCOL_ESB: update_rf_payload_format(0); tx_payload_buffer[0] = rx_payload_buffer[0]; tx_payload_buffer[1] = 0; break; } esb_state = ESB_STATE_PRX_SEND_ACK; NRF_RADIO->TXADDRESS = NRF_RADIO->RXMATCH; NRF_RADIO->PACKETPTR = (uint32_t)tx_payload_buffer; on_radio_disabled = on_radio_disabled_rx_ack; } else { clear_events_restart_rx(); } if (send_rx_event) { /* Push the new packet to the RX buffer and trigger a received * event if the operation was * successful. */ if (rx_fifo_push_rfbuf(NRF_RADIO->RXMATCH, pipe_info->pid)) { interrupt_flags |= INT_RX_DATA_RECEIVED_MSK; NVIC_SetPendingIRQ(ESB_EVT_IRQ); } } } static void on_radio_disabled_rx_ack(void) { NRF_RADIO->SHORTS = radio_shorts_common | RADIO_SHORTS_DISABLED_TXEN_Msk; update_rf_payload_format(esb_cfg.payload_length); NRF_RADIO->PACKETPTR = (uint32_t)rx_payload_buffer; on_radio_disabled = on_radio_disabled_rx; esb_state = ESB_STATE_PRX; } /* Retrieve interrupt flags and reset them. * * @param[out] interrupts Interrupt flags. */ static void get_and_clear_irqs(uint32_t *interrupts) { __ASSERT_NO_MSG(interrupts != NULL); uint32_t key = irq_lock(); *interrupts = interrupt_flags; interrupt_flags = 0; irq_unlock(key); } static void radio_irq_handler(void) { #if 0 if (NRF_RADIO->EVENTS_TXREADY) { NRF_RADIO->EVENTS_TXREADY = 0; //printk("Inside EVENTS_TXREADY \r\n"); } if (NRF_RADIO->EVENTS_RXREADY) { NRF_RADIO->EVENTS_RXREADY = 0; //printk("Inside EVENTS_RXREADY \r\n"); } #endif if (NRF_RADIO->EVENTS_READY && (NRF_RADIO->INTENSET & RADIO_INTENSET_READY_Msk)) { dbg_ready_counter++; NRF_RADIO->EVENTS_READY = 0; ESB_SYS_TIMER->TASKS_START; } if (NRF_RADIO->EVENTS_END && (NRF_RADIO->INTENSET & RADIO_INTENSET_END_Msk)) { dbg_end_counter++; NRF_RADIO->EVENTS_END = 0; /* Call the correct on_radio_end function, depending on the * current protocol state. */ end_event = true; if (on_radio_end) { on_radio_end(); } } if (NRF_RADIO->EVENTS_DISABLED && (NRF_RADIO->INTENSET & RADIO_INTENSET_DISABLED_Msk)) { dbg_disabled_counter++; NRF_RADIO->EVENTS_DISABLED = 0; /* Call the correct on_radio_disable function, depending on the * current protocol state. */ if (on_radio_disabled) { on_radio_disabled(); } } } static void esb_evt_irq_handler(void) { uint32_t interrupts; struct esb_evt event; event.tx_attempts = last_tx_attempts; get_and_clear_irqs(&interrupts); if (event_handler != NULL) { if (interrupts & INT_TX_SUCCESS_MSK) { event.evt_id = ESB_EVENT_TX_SUCCESS; event_handler(&event); } if (interrupts & INT_TX_FAILED_MSK) { event.evt_id = ESB_EVENT_TX_FAILED; event_handler(&event); } if (interrupts & INT_RX_DATA_RECEIVED_MSK) { event.evt_id = ESB_EVENT_RX_RECEIVED; event_handler(&event); } } } ISR_DIRECT_DECLARE(RADIO_IRQHandler) { radio_irq_handler(); ISR_DIRECT_PM(); return 1; } ISR_DIRECT_DECLARE(ESB_EVT_IRQHandler) { esb_evt_irq_handler(); ISR_DIRECT_PM(); return 1; } ISR_DIRECT_DECLARE(ESB_SYS_TIMER_IRQHandler) { ISR_DIRECT_PM(); return 1; } int esb_init(const struct esb_config *config) { if (config == NULL) { return -EINVAL; } if (esb_initialized) { esb_disable(); } event_handler = config->event_handler; memcpy(&esb_cfg, config, sizeof(esb_cfg)); //separate interrupt_flags = 0; memset(rx_pipe_info, 0, sizeof(rx_pipe_info)); memset(pids, 0, sizeof(pids)); update_radio_parameters(); //separate /* Configure radio address registers according to ESB default values */ NRF_RADIO->BASE0 = 0xE7E7E7E7; NRF_RADIO->BASE1 = 0x43434343; NRF_RADIO->PREFIX0 = 0x23C343E7; NRF_RADIO->PREFIX1 = 0x13E363A3; initialize_fifos(); sys_timer_init(); ppi_init(); IRQ_DIRECT_CONNECT(RADIO_IRQn, CONFIG_ESB_RADIO_IRQ_PRIORITY, RADIO_IRQHandler, 0); IRQ_DIRECT_CONNECT(ESB_EVT_IRQ, CONFIG_ESB_EVENT_IRQ_PRIORITY, ESB_EVT_IRQHandler, 0); IRQ_DIRECT_CONNECT(ESB_SYS_TIMER_IRQn, CONFIG_ESB_EVENT_IRQ_PRIORITY, ESB_SYS_TIMER_IRQHandler, 0); irq_enable(RADIO_IRQn); irq_enable(ESB_EVT_IRQ); irq_enable(ESB_SYS_TIMER_IRQn); esb_state = ESB_STATE_IDLE; //separate esb_initialized = true; //separate #ifdef CONFIG_SOC_NRF52832 if ((NRF_FICR->INFO.VARIANT & 0x0000FF00) == 0x00004500) { /* Check if the device is an nRF52832 Rev. 2. */ /* Workaround for nRF52832 rev 2 errata 182 */ *(volatile uint32_t *)0x4000173C |= (1 << 10); } #endif return 0; } int esb_init_tx(const struct esb_config *config) { if (config == NULL) { return -EINVAL; } memcpy(&esb_cfg, config, sizeof(esb_cfg)); //separate update_radio_parameters(); //separate esb_state = ESB_STATE_IDLE; //separate return 0; } int esb_init_rx(const struct esb_config *config) { if (config == NULL) { return -EINVAL; } memcpy(&esb_cfg, config, sizeof(esb_cfg)); //separate update_radio_parameters(); //separate esb_state = ESB_STATE_IDLE; //separate return 0; } int esb_suspend(void) { if (esb_state != ESB_STATE_IDLE) { return -EBUSY; } /* Clear PPI */ nrfx_gppi_channels_disable(ppi_all_channels_mask); esb_state = ESB_STATE_IDLE; return 0; } void esb_disable(void) { /* Clear PPI */ nrfx_gppi_channels_disable(ppi_all_channels_mask); esb_state = ESB_STATE_IDLE; esb_initialized = false; reset_fifos(); memset(rx_pipe_info, 0, sizeof(rx_pipe_info)); memset(pids, 0, sizeof(pids)); /* Disable the interrupts used by ESB */ irq_disable(RADIO_IRQn); irq_disable(ESB_SYS_TIMER_IRQn); irq_disable(ESB_EVT_IRQ); NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Enabled << RADIO_SHORTS_READY_START_Pos | RADIO_SHORTS_END_DISABLE_Enabled << RADIO_SHORTS_END_DISABLE_Pos; } bool esb_is_idle(void) { return (esb_state == ESB_STATE_IDLE); } static struct payload_wrap *find_free_payload_cont(void) { for (int i = 0; i < CONFIG_ESB_TX_FIFO_SIZE; i++) { if (!ack_pl_wrap[i].in_use) return &ack_pl_wrap[i]; } return 0; } int esb_write_payload(const struct esb_payload *payload) { if (!esb_initialized) { return -EACCES; } if (payload == NULL) { return -EINVAL; } if (payload->length == 0 || payload->length > CONFIG_ESB_MAX_PAYLOAD_LENGTH || (esb_cfg.protocol == ESB_PROTOCOL_ESB && payload->length > esb_cfg.payload_length)) { return -EMSGSIZE; } if (tx_fifo.count >= CONFIG_ESB_TX_FIFO_SIZE) { return -ENOMEM; } if (payload->pipe >= CONFIG_ESB_PIPE_COUNT) { return -EINVAL; } uint32_t key = irq_lock(); if (esb_cfg.mode == ESB_MODE_PTX) { memcpy(tx_fifo.payload[tx_fifo.back], payload, sizeof(struct esb_payload)); pids[payload->pipe] = (pids[payload->pipe] + 1) % (PID_MAX + 1); tx_fifo.payload[tx_fifo.back]->pid = pids[payload->pipe]; if (++tx_fifo.back >= CONFIG_ESB_TX_FIFO_SIZE) { tx_fifo.back = 0; } tx_fifo.count++; } else { struct payload_wrap *new_ack_payload = find_free_payload_cont(); if (new_ack_payload != 0) { new_ack_payload->in_use = true; new_ack_payload->p_next = 0; memcpy(new_ack_payload->p_payload, payload, sizeof(struct esb_payload)); pids[payload->pipe] = (pids[payload->pipe] + 1) % (PID_MAX + 1); new_ack_payload->p_payload->pid = pids[payload->pipe]; if (ack_pl_wrap_pipe[payload->pipe] == 0) { ack_pl_wrap_pipe[payload->pipe] = new_ack_payload; } else { struct payload_wrap *pl = ack_pl_wrap_pipe[payload->pipe]; while (pl->p_next != 0) { pl = (struct payload_wrap *)pl->p_next; } pl->p_next = (struct payload_wrap *)new_ack_payload; } tx_fifo.count++; } } irq_unlock(key); if (esb_cfg.mode == ESB_MODE_PTX && esb_cfg.tx_mode == ESB_TXMODE_AUTO && esb_state == ESB_STATE_IDLE) { start_tx_transaction(); } return 0; } int esb_read_rx_payload(struct esb_payload *payload) { if (!esb_initialized) { return -EACCES; } if (payload == NULL) { return -EINVAL; } if (rx_fifo.count == 0) { return -ENODATA; } uint32_t key = irq_lock(); payload->length = rx_fifo.payload[rx_fifo.front]->length; payload->pipe = rx_fifo.payload[rx_fifo.front]->pipe; payload->rssi = rx_fifo.payload[rx_fifo.front]->rssi; payload->pid = rx_fifo.payload[rx_fifo.front]->pid; payload->noack = rx_fifo.payload[rx_fifo.front]->noack; memcpy(payload->data, rx_fifo.payload[rx_fifo.front]->data, payload->length); if (++rx_fifo.front >= CONFIG_ESB_RX_FIFO_SIZE) { rx_fifo.front = 0; } rx_fifo.count--; irq_unlock(key); return 0; } int esb_start_tx(void) { if (esb_state != ESB_STATE_IDLE) { printk("inside esb_start_tx , esb_state != ESB_STATE_IDLE \r\n"); return -EBUSY; } if (tx_fifo.count == 0) { return -ENODATA; } start_tx_transaction(); return 0; } int esb_start_rx(void) { if (esb_state != ESB_STATE_IDLE) { printk("inside esb_start_rx , esb_state != ESB_STATE_IDLE \r\n"); return -EBUSY; } nrfx_gppi_channels_disable(1 << ppi_ch_timer_compare1_radio_txen); nrfx_gppi_channels_enable(1 << ppi_ch_timer_compare1_radio_rxen); NRF_RADIO->INTENCLR = 0xFFFFFFFF; NRF_RADIO->EVENTS_DISABLED = 0; on_radio_disabled = on_radio_disabled_rx; NRF_RADIO->SHORTS = radio_shorts_common | RADIO_SHORTS_DISABLED_TXEN_Msk; NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; esb_state = ESB_STATE_PRX; NRF_RADIO->RXADDRESSES = esb_addr.rx_pipes_enabled; //NRF_RADIO->FREQUENCY = esb_addr.rf_channel; NRF_RADIO->FREQUENCY = esb_addr.rf_channel & RADIO_FREQUENCY_FREQUENCY_Msk; NRF_RADIO->PACKETPTR = (uint32_t)rx_payload_buffer; NVIC_ClearPendingIRQ(RADIO_IRQn); irq_enable(RADIO_IRQn); NRF_RADIO->EVENTS_ADDRESS = 0; NRF_RADIO->EVENTS_PAYLOAD = 0; NRF_RADIO->EVENTS_DISABLED = 0; NRF_RADIO->TASKS_RXEN = 1; return 0; } int esb_stop_tx(void) { // NRF_RADIO->SHORTS = 0; // NRF_RADIO->EVENTS_DISABLED = 0; //Ajit NRF_RADIO->SHORTS = 0; NRF_RADIO->INTENCLR = 0xFFFFFFFF; // on_radio_disabled = NULL; NRF_RADIO->EVENTS_DISABLED = 0; //ajit NRF_RADIO->TASKS_DISABLE = 1; while (NRF_RADIO->EVENTS_DISABLED == 0) { /* wait for register to settle */ } esb_state = ESB_STATE_IDLE; return 0; } int esb_stop_rx(void) { if (esb_state != ESB_STATE_PRX && esb_state != ESB_STATE_PRX_SEND_ACK) { printk("return invalid from esb_stop_rx \r\n"); return -EINVAL; } NRF_RADIO->SHORTS = 0; NRF_RADIO->INTENCLR = 0xFFFFFFFF; on_radio_disabled = NULL; NRF_RADIO->EVENTS_DISABLED = 0; NRF_RADIO->TASKS_DISABLE = 1; while (NRF_RADIO->EVENTS_DISABLED == 0) { /* wait for register to settle */ } esb_state = ESB_STATE_IDLE; return 0; } int esb_flush_tx(void) { if (!esb_initialized) { return -EACCES; } uint32_t key = irq_lock(); tx_fifo.count = 0; tx_fifo.back = 0; tx_fifo.front = 0; irq_unlock(key); return 0; } int esb_pop_tx(void) { if (!esb_initialized) { return -EACCES; } if (tx_fifo.count == 0) { return -ENODATA; } uint32_t key = irq_lock(); if (++tx_fifo.back >= CONFIG_ESB_TX_FIFO_SIZE) { tx_fifo.back = 0; } tx_fifo.count--; irq_unlock(key); return 0; } int esb_flush_rx(void) { if (!esb_initialized) { return -EACCES; } uint32_t key = irq_lock(); rx_fifo.count = 0; rx_fifo.back = 0; rx_fifo.front = 0; memset(rx_pipe_info, 0, sizeof(rx_pipe_info)); irq_unlock(key); return 0; } int esb_set_address_length(uint8_t length) { if (esb_state != ESB_STATE_IDLE) { return -EBUSY; } if (!(length > 2 && length < 6)) { return -EINVAL; } esb_addr.addr_length = length; update_rf_payload_format(esb_cfg.payload_length); return 0; } int esb_set_base_address_0(const uint8_t *addr) { if (esb_state != ESB_STATE_IDLE) { return -EBUSY; } if (addr == NULL) { return -EINVAL; } memcpy(esb_addr.base_addr_p0, addr, sizeof(esb_addr.base_addr_p0)); update_radio_addresses(ADDR_UPDATE_MASK_BASE0); return 0; } int esb_set_base_address_1(const uint8_t *addr) { if (esb_state != ESB_STATE_IDLE) { return -EBUSY; } if (addr == NULL) { return -EINVAL; } memcpy(esb_addr.base_addr_p1, addr, sizeof(esb_addr.base_addr_p1)); update_radio_addresses(ADDR_UPDATE_MASK_BASE1); return 0; } int esb_set_prefixes(const uint8_t *prefixes, uint8_t num_pipes) { if (esb_state != ESB_STATE_IDLE) { return -EBUSY; } if (prefixes == NULL) { return -EINVAL; } if (!(num_pipes <= CONFIG_ESB_PIPE_COUNT)) { return -EINVAL; } memcpy(esb_addr.pipe_prefixes, prefixes, num_pipes); esb_addr.num_pipes = num_pipes; esb_addr.rx_pipes_enabled = BIT_MASK_UINT_8(num_pipes); update_radio_addresses(ADDR_UPDATE_MASK_PREFIX); return 0; } int esb_update_prefix(uint8_t pipe, uint8_t prefix) { if (esb_state != ESB_STATE_IDLE) { return -EBUSY; } if (pipe >= CONFIG_ESB_PIPE_COUNT) { return -EINVAL; } esb_addr.pipe_prefixes[pipe] = prefix; update_radio_addresses(ADDR_UPDATE_MASK_PREFIX); return 0; } int esb_enable_pipes(uint8_t enable_mask) { if (esb_state != ESB_STATE_IDLE) { return -EBUSY; } if ((enable_mask | BIT_MASK_UINT_8(CONFIG_ESB_PIPE_COUNT)) != BIT_MASK_UINT_8(CONFIG_ESB_PIPE_COUNT)) { return -EINVAL; } esb_addr.rx_pipes_enabled = enable_mask; return 0; } int esb_set_rf_channel(uint32_t channel) { if (esb_state != ESB_STATE_IDLE) { return -EBUSY; } if (channel > 100) { return -EINVAL; } esb_addr.rf_channel = channel; return 0; } int esb_get_rf_channel(uint32_t *channel) { if (channel == NULL) { return -EINVAL; } *channel = esb_addr.rf_channel; return 0; } int esb_set_tx_power(enum esb_tx_power tx_output_power) { if (esb_state != ESB_STATE_IDLE) { return -EBUSY; } if (esb_cfg.tx_output_power != tx_output_power) { esb_cfg.tx_output_power = tx_output_power; update_radio_tx_power(); } return 0; } int esb_set_retransmit_delay(uint16_t delay) { if (esb_state != ESB_STATE_IDLE) { return -EBUSY; } if (delay < RETRANSMIT_DELAY_MIN) { return -EINVAL; } esb_cfg.retransmit_delay = delay; return 0; } int esb_set_retransmit_count(uint16_t count) { if (esb_state != ESB_STATE_IDLE) { return -EBUSY; } esb_cfg.retransmit_count = count; return 0; } int esb_set_bitrate(enum esb_bitrate bitrate) { if (esb_state != ESB_STATE_IDLE) { return -EBUSY; } esb_cfg.bitrate = bitrate; return update_radio_bitrate() ? 0 : -EINVAL; } int esb_reuse_pid(uint8_t pipe) { if (esb_state != ESB_STATE_IDLE) { return -EBUSY; } if (!(pipe < CONFIG_ESB_PIPE_COUNT)) { return -EINVAL; } pids[pipe] = (pids[pipe] + PID_MAX) % (PID_MAX + 1); return 0; }
/* * Copyright (c) 2018 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include <zephyr/kernel.h> #include <cmsis_os.h> #include <string.h> #define ACTIVE 1 #define NOT_ACTIVE 0 static void zephyr_timer_wrapper(struct k_timer *timer); struct timer_obj { struct k_timer ztimer; os_timer_type type; uint32_t status; void (*callback_function)(void const *argument); void *arg; }; K_MEM_SLAB_DEFINE(cmsis_timer_slab, sizeof(struct timer_obj), CONFIG_CMSIS_TIMER_MAX_COUNT, __alignof__(struct timer_obj)); static void zephyr_timer_wrapper(struct k_timer *timer) { struct timer_obj *cm_timer; cm_timer = CONTAINER_OF(timer, struct timer_obj, ztimer); (cm_timer->callback_function)(cm_timer->arg); } /** * @brief Create a Timer */ osTimerId osTimerCreate(const osTimerDef_t *timer_def, os_timer_type type, void *argument) { struct timer_obj *timer; if (timer_def == NULL) { return NULL; } if (type != osTimerOnce && type != osTimerPeriodic) { return NULL; } if (k_mem_slab_alloc(&cmsis_timer_slab, (void **)&timer, K_MSEC(100)) == 0) { (void)memset(timer, 0, sizeof(struct timer_obj)); } else { return NULL; } timer->callback_function = timer_def->ptimer; timer->arg = argument; timer->type = type; timer->status = NOT_ACTIVE; k_timer_init(&timer->ztimer, zephyr_timer_wrapper, NULL); return (osTimerId)timer; } osTimerId osTimerCreate_US(const osTimerDef_t *timer_def, os_timer_type type, void *argument) { struct timer_obj *timer; if (timer_def == NULL) { return NULL; } if (type != osTimerOnce && type != osTimerPeriodic) { return NULL; } if (k_mem_slab_alloc(&cmsis_timer_slab, (void **)&timer, K_USEC(100000)) == 0) { (void)memset(timer, 0, sizeof(struct timer_obj)); } else { return NULL; } timer->callback_function = timer_def->ptimer; timer->arg = argument; timer->type = type; timer->status = NOT_ACTIVE; k_timer_init(&timer->ztimer, zephyr_timer_wrapper, NULL); return (osTimerId)timer; } /** * @brief Start or restart a Timer */ osStatus osTimerStart(osTimerId timer_id, uint32_t millisec) { struct timer_obj *timer = (struct timer_obj *) timer_id; if (timer == NULL) { return osErrorParameter; } if (k_is_in_isr()) { return osErrorISR; } if (timer->type == osTimerOnce) { k_timer_start(&timer->ztimer, K_MSEC(millisec), K_NO_WAIT); } else if (timer->type == osTimerPeriodic) { k_timer_start(&timer->ztimer, K_MSEC(millisec), K_MSEC(millisec)); } timer->status = ACTIVE; return osOK; } osStatus osTimerStart_US(osTimerId timer_id, uint32_t microsec) { struct timer_obj *timer = (struct timer_obj *) timer_id; if (timer == NULL) { return osErrorParameter; } if (k_is_in_isr()) { return osErrorISR; } if (timer->type == osTimerOnce) { k_timer_start(&timer->ztimer, K_USEC(microsec), K_NO_WAIT); } else if (timer->type == osTimerPeriodic) { k_timer_start(&timer->ztimer, K_USEC(microsec), K_USEC(microsec)); } timer->status = ACTIVE; return osOK; } /** * @brief Stop the Timer */ osStatus osTimerStop(osTimerId timer_id) { struct timer_obj *timer = (struct timer_obj *) timer_id; if (timer == NULL) { return osErrorParameter; } if (k_is_in_isr()) { return osErrorISR; } if (timer->status == NOT_ACTIVE) { return osErrorResource; } k_timer_stop(&timer->ztimer); timer->status = NOT_ACTIVE; return osOK; } /** * @brief Delete the timer that was created by osTimerCreate */ osStatus osTimerDelete(osTimerId timer_id) { struct timer_obj *timer = (struct timer_obj *) timer_id; if (timer == NULL) { return osErrorParameter; } if (k_is_in_isr()) { return osErrorISR; } if (timer->status == ACTIVE) { k_timer_stop(&timer->ztimer); timer->status = NOT_ACTIVE; } k_mem_slab_free(&cmsis_timer_slab, (void *) &timer); return osOK; }