/** * \file * \brief This is the implementation of the Nordic Semiconductor nRF24L01+ low level driver. * \author (c) 2013-2024 Erich Styger, http://mcuoneclipse.com/ * \note MIT License (http://opensource.org/licenses/mit-license.html), see 'RNet_License.txt' * * This module deals with the low level functions of the transceiver. */ #include "McuRNetConfig.h" #if McuRNET_CONFIG_IS_ENABLED #include "Radio.h" #include "RadioNRF24.h" #include "McuNRF24L01.h" #include "RMSG.h" #include "RStdIO.h" #include "RPHY.h" #include "McuUtility.h" #include "McuRNet.h" #include "McuLog.h" #if configUSE_SEGGER_SYSTEM_VIEWER_HOOKS #include "McuSystemView.h" #endif #if McuRNet_CREATE_EVENTS /* optional application user events */ extern void McuRNet_OnRadioEvent(McuRNet_RadioEvent event); #endif #define NRF24_DYNAMIC_PAYLOAD 1 /* if set to one, use dynamic payload size */ #define NRF24_AUTO_ACKNOWLEDGE 1 /* if set to one, the transceiver is configured to use auto acknowledge */ #define RADIO_CHANNEL_DEFAULT RNET_CONFIG_TRANSCEIVER_CHANNEL /* default communication channel */ #define RADIO_USE_DTN (1 && McuLib_CONFIG_SDK_USE_FREERTOS) /* use direct task notification for interrupt */ /* macros to configure device either for RX or TX operation */ #define McuNRF24L01_CONFIG_SETTINGS (McuNRF24L01_EN_CRC|McuNRF24L01_CRCO) #define TX_POWERUP() McuNRF24L01_WriteRegister(McuNRF24L01_CONFIG, McuNRF24L01_CONFIG_SETTINGS|McuNRF24L01_PWR_UP|McuNRF24L01_PRIM_TX) /* enable 2 byte CRC, power up and set as PTX */ #define RX_POWERUP() McuNRF24L01_WriteRegister(McuNRF24L01_CONFIG, McuNRF24L01_CONFIG_SETTINGS|McuNRF24L01_PWR_UP|McuNRF24L01_PRIM_RX) /* enable 1 byte CRC, power up and set as PRX */ #define POWERDOWN() McuNRF24L01_WriteRegister(McuNRF24L01_CONFIG, McuNRF24L01_CONFIG_SETTINGS) /* power down */ static bool RADIO_isSniffing = FALSE; #define RADIO_NOF_ADDR_BYTES 5 /* we are using 5 address bytes */ static const uint8_t RADIO_TADDR_P0[RADIO_NOF_ADDR_BYTES] = {0x11, 0x22, 0x33, 0x44, 0x55}; /* device address for pipe 0 */ static const uint8_t RADIO_TADDR_P1[RADIO_NOF_ADDR_BYTES] = {0xB3, 0xB4, 0xB5, 0xB6, 0xF1}; /* device address for pipe 1 */ static const uint8_t RADIO_TADDR_P2[1] = {0xF2}; /* device address for pipe 2 */ static const uint8_t RADIO_TADDR_P3[1] = {0xF3}; /* device address for pipe 3 */ static const uint8_t RADIO_TADDR_P4[1] = {0xF4}; /* device address for pipe 4 */ static const uint8_t RADIO_TADDR_P5[1] = {0xF5}; /* device address for pipe 5 */ #if RNET_CONFIG_SEND_RETRY_CNT>0 static uint8_t RADIO_RetryCnt; static uint8_t TxDataBuffer[RPHY_BUFFER_SIZE]; /*!< global buffer if using retries */ #endif static TaskHandle_t radioTaskHandle; /* used for direct task notification */ /* Radio state definitions */ typedef enum RADIO_AppStatusKind { RADIO_INITIAL_STATE, /* initial state of the state machine */ RADIO_RECEIVER_ALWAYS_ON, /* receiver is in RX mode */ RADIO_TRANSMIT_DATA, /* send data */ RADIO_WAITING_DATA_SENT, /* wait until data is sent */ RADIO_TIMEOUT, RADIO_READY_FOR_TX_RX_DATA, RADIO_CHECK_TX, /* send data if any */ RADIO_POWER_DOWN, /* transceiver powered down */ } RADIO_AppStatusKind; static RADIO_AppStatusKind RADIO_AppStatus = RADIO_INITIAL_STATE; static RPHY_PacketDesc radioRx; static uint8_t radioRxBuf[RPHY_BUFFER_SIZE]; static uint8_t RADIO_CurrChannel = RADIO_CHANNEL_DEFAULT; typedef enum RADIO_State { RADIO_NONE, RADIO_POWERUP, /* powering up */ RADIO_TX_RX, } RADIO_State; static RADIO_State appState = RADIO_NONE; void RADIO_Notify(uint32_t flags) { if (flags!=0) { (void)xTaskNotify(radioTaskHandle, flags, eSetBits); } } void RADIO_NotifyFromInterrupt(uint32_t flags, BaseType_t *pxHigherPriorityTaskWoken) { if (flags!=0) { (void)xTaskNotifyFromISR(radioTaskHandle, flags, eSetBits, pxHigherPriorityTaskWoken); } } /* callback called from radio driver */ void RADIO_OnInterrupt(void) { #if McuNRF24L01_CONFIG_IRQ_PIN_ENABLED /* called from interrupt context */ BaseType_t xHigherPriorityTaskWoken = pdFALSE; RADIO_NotifyFromInterrupt(RADIO_FLAG_INTERRUPT, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); #else RADIO_Notify(RADIO_FLAG_INTERRUPT); #endif } bool RADIO_PollInterruptFlag(void) { uint8_t status = McuNRF24L01_GetStatus(); if (status&(McuNRF24L01_STATUS_RX_DR|McuNRF24L01_STATUS_TX_DS|McuNRF24L01_STATUS_MAX_RT)) { McuLog_trace("IRQ in status"); return true; /* interrupt */ } return false; /* no interrupt */ } static bool CheckAndClearFlag(uint32_t *flags, uint32_t flag) { if ((*flags)&flag) { *flags &= ~flag; /* clear flag */ return true; } return false; /* flag not set */ } #if 0 static bool RADIO_GetInterruptFlag(uint32_t timeoutTicks) { bool hasInterrupt = false; #if 1 uint8_t status = McuNRF24L01_GetStatus(); if (status&(McuNRF24L01_STATUS_RX_DR|McuNRF24L01_STATUS_TX_DS|McuNRF24L01_STATUS_MAX_RT)) { hasInterrupt = true; McuLog_trace("No Radio IRQ, but set in status?"); } #else (void)timeoutTicks; /* not used */ hasInterrupt = RADIO_isrFlag; if (!hasInterrupt) { hasInterrupt = McuNRF24L01_PollInterrupt(); } #endif return hasInterrupt; } #endif uint8_t RADIO_FlushQueues(void) { uint8_t res = ERR_OK; if (RPHY_FlushRxQueue()!=ERR_OK) { res = ERR_FAILED; } if (RPHY_FlushTxQueue()!=ERR_OK) { res = ERR_FAILED; } return res; } static uint8_t RADIO_Flush(void) { McuNRF24L01_Write(McuNRF24L01_FLUSH_RX); /* flush old data */ McuNRF24L01_Write(McuNRF24L01_FLUSH_TX); /* flush old data */ return ERR_OK; } bool RADIO_CanDoPowerDown(uint32_t notifcationValue) { if (notifcationValue&RADIO_FLAG_INTERRUPT) { return FALSE; /* interrupt pending */ } switch(RADIO_AppStatus) { case RADIO_TRANSMIT_DATA: case RADIO_WAITING_DATA_SENT: case RADIO_TIMEOUT: return FALSE; /* sending/receiving data, cannot power down */ case RADIO_INITIAL_STATE: case RADIO_RECEIVER_ALWAYS_ON: case RADIO_READY_FOR_TX_RX_DATA: case RADIO_CHECK_TX: case RADIO_POWER_DOWN: break; /* check other conditions */ default: break; } /* switch */ if (RMSG_RxQueueNofItems()!=0) { return FALSE; /* items received, cannot power down */ } if (RMSG_TxQueueNofItems()!=0) { return FALSE; /* items to be sent, cannot power down */ } return TRUE; /* ok to power down */ } uint8_t RADIO_PowerDown(void) { uint8_t res; res = RADIO_Flush(); POWERDOWN(); return res; } static uint8_t CheckTx(void) { RPHY_PacketDesc packet; #if RNET_CONFIG_SEND_RETRY_CNT==0 uint8_t TxDataBuffer[RPHY_BUFFER_SIZE]; /* local tx buffer if not using retries */ #endif RPHY_FlagsType flags; if (RMSG_GetTxMsg(TxDataBuffer, sizeof(TxDataBuffer))==ERR_OK) { flags = RPHY_BUF_FLAGS(TxDataBuffer); if (flags&RPHY_PACKET_FLAGS_POWER_DOWN) { /* special request */ (void)RADIO_PowerDown(); return ERR_DISABLED; /* no more data, pipes flushed */ } McuNRF24L01_StopRxTx(); /* CE low */ TX_POWERUP(); /* set up packet structure */ packet.phyData = &TxDataBuffer[0]; packet.flags = flags; packet.phySize = sizeof(TxDataBuffer); #if NRF24_DYNAMIC_PAYLOAD packet.rxtx = RPHY_BUF_PAYLOAD_START(packet.phyData); #else packet.rxtx = &RPHY_BUF_SIZE(packet.phyData); /* we transmit the data size too */ #endif if (RADIO_isSniffing) { RPHY_SniffPacket(&packet, TRUE); /* sniff outgoing packet */ } #if NRF24_DYNAMIC_PAYLOAD if (flags&RPHY_PACKET_FLAGS_NO_ACK) { McuNRF24L01_TxPayloadNoAck(packet.rxtx, RPHY_BUF_SIZE(packet.phyData)); /* send data, using dynamic payload size, without ack request */ } else { McuNRF24L01_TxPayload(packet.rxtx, RPHY_BUF_SIZE(packet.phyData)); /* send data, using dynamic payload size, with ack request */ } #else McuNRF24L01_TxPayload(packet.rxtx, RPHY_PAYLOAD_SIZE); /* send data, using fixed payload size */ #endif return ERR_OK; } return ERR_NOTAVAIL; /* no data to send? */ } /* called to check if we have something in the RX payload FIFO. If so, we queue it to the RX queue. */ static uint8_t ReadRxPayload(void) { #if NRF24_DYNAMIC_PAYLOAD uint8_t payloadSize; #endif uint8_t res = ERR_OK; uint8_t RxDataBuffer[RPHY_BUFFER_SIZE]; uint8_t status; RPHY_PacketDesc packet; bool hasRxData, hasRx; hasRxData = FALSE; packet.flags = RPHY_PACKET_FLAGS_NONE; packet.phyData = &RxDataBuffer[0]; packet.phySize = sizeof(RxDataBuffer); #if NRF24_DYNAMIC_PAYLOAD packet.rxtx = RPHY_BUF_PAYLOAD_START(packet.phyData); #else packet.rxtx = &RPHY_BUF_SIZE(packet.phyData); /* we transmit the data size too */ #endif status = McuNRF24L01_GetStatusClrIRQ(); hasRx = (status&McuNRF24L01_STATUS_RX_DR)!=0; if (hasRx) { /* data received interrupt */ hasRxData = TRUE; #if NRF24_DYNAMIC_PAYLOAD (void)McuNRF24L01_ReadNofRxPayload(&payloadSize); if (payloadSize==0 || payloadSize>32) { /* packet with error? */ McuNRF24L01_Write(McuNRF24L01_FLUSH_RX); /* flush old data */ return ERR_FAILED; } else { memset(packet.rxtx, 0xab, payloadSize); /* have defined data for the SPI transfer */ McuNRF24L01_RxPayload(packet.rxtx, payloadSize); /* get payload: note that we transmit as payload! */ RPHY_BUF_SIZE(packet.phyData) = payloadSize; } #else McuNRF24L01_RxPayload(packet.rxtx, RPHY_PAYLOAD_SIZE); /* get payload: note that we transmit as payload! */ #endif } else { McuNRF24L01_Write(McuNRF24L01_FLUSH_RX); /* flush old data */ } if (hasRxData) { /* put message into Rx queue */ #if McuRNet_CREATE_EVENTS /*lint -save -e522 function lacks side effect */ McuRNet_OnRadioEvent(McuRNet_RADIO_MSG_RECEIVED); /*lint -restore */ #endif res = RMSG_QueueRxMsg(packet.phyData, packet.phySize, RPHY_BUF_SIZE(packet.phyData), packet.flags); if (res!=ERR_OK) { if (res==ERR_OVERFLOW) { McuLog_error("Rx queue overflow!"); } else { McuLog_error("Rx Queue full?"); } } } else { res = ERR_RXEMPTY; /* no data */ } return res; } static void WaitRandomTime(void) { if (configTICK_RATE_HZ<=100) { /* slower tick rate */ vTaskDelay(10+(xTaskGetTickCount()%16)); } else { /* higher tick rate: wait between 10 and 10+32 ticks */ vTaskDelay(10+(xTaskGetTickCount()%32)); } } static void RADIO_HandleStateMachine(uint32_t notifcationValue) { #if RNET_RADIO_WAITNG_TIMEOUT_MS>0 static TickType_t sentTimeTickCntr = 0; /* used for timeout */ #endif uint8_t status, res; bool interrupt; for(;;) { /* will break/return */ switch (RADIO_AppStatus) { case RADIO_INITIAL_STATE: McuNRF24L01_StopRxTx(); /* will send/receive data later */ RADIO_AppStatus = RADIO_RECEIVER_ALWAYS_ON; /* turn receive on */ break; /* process switch again */ case RADIO_RECEIVER_ALWAYS_ON: /* turn receive on */ RX_POWERUP(); McuNRF24L01_StartRxTx(); /* Listening for packets */ RADIO_AppStatus = RADIO_READY_FOR_TX_RX_DATA; break; /* process switch again */ case RADIO_READY_FOR_TX_RX_DATA: /* we are ready to receive/send data data */ /* check first if we have incoming data: */ interrupt = CheckAndClearFlag(¬ifcationValue, RADIO_FLAG_INTERRUPT); if (interrupt) { /* Rx data? */ int cntr = 0; while(ReadRxPayload()==ERR_OK) { /* continoue reading messages from transceiver */ cntr++; if (cntr>2) { McuLog_trace("cntr: %d", cntr); } } RADIO_AppStatus = RADIO_RECEIVER_ALWAYS_ON; /* continue listening */ break; /* process switch again */ #if 1 } else { uint8_t val; (void)McuNRF24L01_GetFifoStatus(&val); if (!(val&McuNRF24L01_FIFO_STATUS_RX_EMPTY)) { /* */ McuLog_fatal("rx fifo not empty"); } #endif } #if RNET_CONFIG_SEND_RETRY_CNT>0 RADIO_RetryCnt = 0; #endif RADIO_AppStatus = RADIO_CHECK_TX; /* check if we can send something */ break; /* process switch again */ case RADIO_CHECK_TX: if (CheckAndClearFlag(¬ifcationValue, RADIO_FLAG_TX_REQUEST)) { res = CheckTx(); if (res==ERR_OK) { /* there was data and it has been sent */ #if RNET_RADIO_WAITNG_TIMEOUT_MS>0 sentTimeTickCntr = xTaskGetTickCount(); /* remember time when it was sent, used for timeout */ #endif RADIO_AppStatus = RADIO_WAITING_DATA_SENT; break; /* process switch again */ } else if (res==ERR_DISABLED) { /* special message to power down transceiver */ RADIO_AppStatus = RADIO_POWER_DOWN; } else if (res==ERR_NOTAVAIL) { /* no data available to be sent */ RADIO_AppStatus = RADIO_READY_FOR_TX_RX_DATA; } else { McuLog_fatal("wrong return code %d", res); } } else { RADIO_AppStatus = RADIO_READY_FOR_TX_RX_DATA; } return; case RADIO_POWER_DOWN: return; case RADIO_WAITING_DATA_SENT: interrupt = CheckAndClearFlag(¬ifcationValue, RADIO_FLAG_INTERRUPT); if (interrupt) { /* check if we have received an interrupt: this is either timeout or low level ack */ status = McuNRF24L01_GetStatusClrIRQ(); if (status&McuNRF24L01_STATUS_MAX_RT) { /* retry timeout interrupt */ McuNRF24L01_Write(McuNRF24L01_FLUSH_TX); /* flush old data */ RADIO_AppStatus = RADIO_TIMEOUT; /* timeout */ WaitRandomTime(); } else { #if McuRNet_CREATE_EVENTS /*lint -save -e522 function lacks side effect */ McuRNet_OnRadioEvent(McuRNet_RADIO_MSG_SENT); /*lint -restore */ #endif RADIO_AppStatus = RADIO_RECEIVER_ALWAYS_ON; /* turn receive on */ } break; /* process switch again */ } #if RNET_RADIO_WAITNG_TIMEOUT_MS>0 if (pdMS_TO_TICKS((xTaskGetTickCount()-sentTimeTickCntr))>pdMS_TO_TICKS(RNET_RADIO_WAITNG_TIMEOUT_MS)) { RADIO_AppStatus = RADIO_TIMEOUT; /* timeout */ } #endif return; case RADIO_TIMEOUT: #if RNET_CONFIG_SEND_RETRY_CNT>0 if (RADIO_RetryCnt0) { //McuLog_trace("Tx again: nof: %d", nof); RADIO_Notify(RADIO_FLAG_TX_REQUEST); /* trigger again */ } } for(i=0;i10) { /* limit frequency of checks */ if (RADIO_IsSane()!=ERR_OK) { McuLog_error("Radio not ready, going to re-initialize"); RADIO_Notify(RADIO_FLAG_REQUEST_INIT); nofRadioInit++; } checkCntr = 0; } if (nofRadioInit>5) { /* limit number of retry */ for(;;) { McuLog_fatal("Radio not ready, to many attempts"); vTaskDelay(pdMS_TO_TICKS(30*1000)); } } #endif notifcationValue = 0; res = xTaskNotifyWait(0UL, RADIO_FLAG_INTERRUPT|RADIO_FLAG_TX_REQUEST|RADIO_FLAG_REQUEST_INIT, ¬ifcationValue, portMAX_DELAY); /* check flags */ #if RNet_App_CONFIG_DO_SANITY_CHECK checkCntr++; #endif if (res==pdPASS) { if (notifcationValue&RADIO_FLAG_INTERRUPT) { //McuLog_trace("DTN: ISR"); #if configUSE_SEGGER_SYSTEM_VIEWER_HOOKS McuSystemView_Print("DTN: ISR"); #endif } if (notifcationValue&RADIO_FLAG_TX_REQUEST) { //McuLog_trace("DTN: Tx"); #if configUSE_SEGGER_SYSTEM_VIEWER_HOOKS McuSystemView_Print("DTN: Tx"); #endif } if (notifcationValue&RADIO_FLAG_REQUEST_INIT) { //McuLog_trace("DTN: Init"); #if configUSE_SEGGER_SYSTEM_VIEWER_HOOKS McuSystemView_Print("DTN: Init"); #endif appState = RADIO_NONE; /* re-initialize and power up transceiver */ } Process(notifcationValue); /* process radio in/out queues */ } } } static const unsigned char *RadioStateStr(RADIO_AppStatusKind state) { switch(state) { case RADIO_INITIAL_STATE: return (const unsigned char*)"INITIAL"; case RADIO_RECEIVER_ALWAYS_ON: return (const unsigned char*)"ALWAYS_ON"; case RADIO_TRANSMIT_DATA: return (const unsigned char*)"TRANSMIT_DATA"; case RADIO_WAITING_DATA_SENT: return (const unsigned char*)"WAITING_DATA_SENT"; case RADIO_READY_FOR_TX_RX_DATA: return (const unsigned char*)"READY_TX_RX"; case RADIO_CHECK_TX: return (const unsigned char*)"CHECK_TX"; case RADIO_POWER_DOWN: return (const unsigned char*)"POWER_DOWN"; default: break; } return (const unsigned char*)"UNKNOWN"; } static void RADIO_PrintHelp(const McuShell_StdIOType *io) { McuShell_SendHelpStr((unsigned char*)"radio", (unsigned char*)"Group of radio commands\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows radio help or status\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" init", (unsigned char*)"Initialize radio\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" channel ", (unsigned char*)"Switches to the given channel (0..127)\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" datarate ", (unsigned char*)"Changes the data rate (250, 1000, 2000)\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" txaddr ", (unsigned char*)"Set TX address, of up to 5 hex bytes, separated by space\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" rxaddr ", (unsigned char*)"Set RX pipe address for pipe (0-5), of up to 5 hex bytes, separated by space\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" power ", (unsigned char*)"Changes output power (0, -10, -12, -18)\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" sniff on|off", (unsigned char*)"Turns sniffing on or off\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" writereg 0xReg 0xVal", (unsigned char*)"Write a transceiver register\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" readreg 0xReg", (unsigned char*)"Read a transceiver register\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" printregs", (unsigned char*)"Print the radio registers\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" flush", (unsigned char*)"Empty all queues\r\n", io->stdOut); } static void RadioPrintRegisters(McuShell_ConstStdIOType *io) { int i; uint8_t val; uint8_t bufidx[16], buf[16]; for(i=0;i<=0x1D;i++) { val = McuNRF24L01_ReadRegister(i); McuUtility_strcpy(bufidx, sizeof(bufidx), (unsigned char*)" addr 0x"); McuUtility_strcatNum8Hex(bufidx, sizeof(bufidx), i); buf[0] = '\0'; McuUtility_strcatNum8Hex(buf, sizeof(buf), val); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); McuShell_SendStatusStr(bufidx, buf, io->stdOut); } } static void RADIO_PrintStatus(const McuShell_StdIOType *io) { uint8_t buf[48]; uint8_t val0, val1; int8_t val; uint16_t dataRate; McuShell_SendStatusStr((unsigned char*)"Radio", (unsigned char*)"\r\n", io->stdOut); McuShell_SendStatusStr((unsigned char*)" state", RadioStateStr(RADIO_AppStatus), io->stdOut); McuShell_SendStr((unsigned char*)"\r\n", io->stdOut); McuShell_SendStatusStr((unsigned char*)" sniff", RADIO_isSniffing?(unsigned char*)"yes\r\n":(unsigned char*)"no\r\n", io->stdOut); McuShell_SendStatusStr((unsigned char*)" sniff stdio", McuShell_GetStdio()==NULL?(unsigned char*)"NULL\r\n":(unsigned char*)"Shell default standard I/O\r\n", io->stdOut); (void)McuNRF24L01_GetChannel(&val0); McuUtility_Num8uToStr(buf, sizeof(buf), val0); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" (HW), "); McuUtility_strcatNum8u(buf, sizeof(buf), RADIO_CurrChannel); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" (SW)\r\n"); McuShell_SendStatusStr((unsigned char*)" channel", buf, io->stdOut); (void)McuNRF24L01_GetAddressWidth(&val0); McuUtility_Num8uToStr(buf, sizeof(buf), val0); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" bytes\r\n"); McuShell_SendStatusStr((unsigned char*)" addr width", buf, io->stdOut); { int i, pipe; /* Pipes 0 to 5 */ uint8_t pipeAddr[RADIO_NOF_ADDR_BYTES]; uint8_t str[sizeof(" RX_ADDR_Px")]; for(i=0;istdOut); for (pipe=0;pipe<6;pipe++) { /* pipes 0 to 5 */ /* RX_ADDR_Px */ (void)McuNRF24L01_GetRxAddress(pipe, &pipeAddr[0], sizeof(pipeAddr)); buf[0] = '\0'; McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"0x"); for(i=0;istdOut); } } (void)McuNRF24L01_GetOutputPower(&val); McuUtility_Num8sToStr(buf, sizeof(buf), val); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" dBm\r\n"); McuShell_SendStatusStr((unsigned char*)" power", buf, io->stdOut); (void)McuNRF24L01_GetDataRate(&dataRate); McuUtility_Num16uToStr(buf, sizeof(buf), dataRate); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" kbps\r\n"); McuShell_SendStatusStr((unsigned char*)" data rate", buf, io->stdOut); val0 = McuNRF24L01_GetStatus(); McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"0x"); McuUtility_strcatNum8Hex(buf, sizeof(buf), val0); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)": "); if (val0&McuNRF24L01_STATUS_RX_DR) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"RX_DR "); } if (val0&McuNRF24L01_STATUS_TX_DS) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"TX_DS "); } if (val0&McuNRF24L01_STATUS_MAX_RT) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"MAX_RT "); } if ((val0&McuNRF24L01_STATUS_RX_P_NO) == McuNRF24L01_STATUS_RX_P_NO_RX_FIFO_EMPTY) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"RxFifoEmpty "); } if ((val0&McuNRF24L01_STATUS_RX_P_NO) == McuNRF24L01_STATUS_RX_P_NO_UNUSED) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"RxUnused "); } if ((val0&McuNRF24L01_STATUS_RX_P_NO) == McuNRF24L01_STATUS_RX_P_NO_5) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"RxP#5 "); } if ((val0&McuNRF24L01_STATUS_RX_P_NO) == McuNRF24L01_STATUS_RX_P_NO_4) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"RxP#4 "); } if ((val0&McuNRF24L01_STATUS_RX_P_NO) == McuNRF24L01_STATUS_RX_P_NO_3) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"RxP#3 "); } if ((val0&McuNRF24L01_STATUS_RX_P_NO) == McuNRF24L01_STATUS_RX_P_NO_2) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"RxP#2 "); } if ((val0&McuNRF24L01_STATUS_RX_P_NO) == McuNRF24L01_STATUS_RX_P_NO_1) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"RxP#1 "); } if ((val0&McuNRF24L01_STATUS_RX_P_NO) == McuNRF24L01_STATUS_RX_P_NO_0) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"RxP#0 "); } if (val0&McuNRF24L01_STATUS_TX_FULL) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"TX_FULL "); } McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); McuShell_SendStatusStr((unsigned char*)" STATUS", buf, io->stdOut); (void)McuNRF24L01_GetFifoStatus(&val0); McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"0x"); McuUtility_strcatNum8Hex(buf, sizeof(buf), val0); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)": "); if (val0&McuNRF24L01_FIFO_STATUS_TX_REUSE) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"REUSE "); } if (val0&McuNRF24L01_FIFO_STATUS_TX_FULL) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"TX_FULL "); } if (val0&McuNRF24L01_FIFO_STATUS_TX_EMPTY) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"TX_EMPTY "); } if (val0&McuNRF24L01_FIFO_STATUS_RX_FULL) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"RX_FULL "); } if (val0&McuNRF24L01_FIFO_STATUS_RX_EMPTY) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"RX_EMPTY "); } McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); McuShell_SendStatusStr((unsigned char*)" FIFO_STATUS", buf, io->stdOut); (void)McuNRF24L01_ReadObserveTxRegister(&val0, &val1); McuUtility_Num8uToStr(buf, sizeof(buf), val0); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" lost, "); McuUtility_strcatNum8u(buf, sizeof(buf), val1); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" retry\r\n"); McuShell_SendStatusStr((unsigned char*)" OBSERVE_TX", buf, io->stdOut); #if 0 /* The RPD status will get reset very fast by another (e.g. WLAN) packet. So this is not really a useful feature :-( */ (void)McuNRF24L01_ReadReceivedPowerDetector(&val0); /*! \todo only works in RX mode, but somehow this still does not work? */ if (val0&1) { McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"1, > -64 dBm\r\n"); } else { McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"0, < -64 dBm\r\n"); } McuShell_SendStatusStr((unsigned char*)" RPD", buf, io->stdOut); #endif McuUtility_Num16uToStr(buf, sizeof(buf), RNET_CONFIG_SEND_RETRY_CNT); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); McuShell_SendStatusStr((unsigned char*)" Max Retries", buf, io->stdOut); if (McuNRF24L01_ReadFeature(&val0)==ERR_OK) { McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"0x"); McuUtility_strcatNum8Hex(buf, sizeof(buf), val0); if (val0&McuNRF24L01_FEATURE_EN_DPL) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" EN_DPL"); } if (val0&McuNRF24L01_FEATURE_EN_ACK_PAY) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" EN_ACK_PAY"); } if (val0&McuNRF24L01_FEATURE_EN_DYN_PAY) { McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" EN_DYN_ACK"); } McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); } else { McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"ERROR\r\n"); } McuShell_SendStatusStr((unsigned char*)" Feature", buf, io->stdOut); } uint8_t RADIO_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io) { uint8_t res = ERR_OK; const unsigned char *p; uint8_t val; int8_t vals; uint8_t addr[RADIO_NOF_ADDR_BYTES]; int i; if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"radio help")==0) { RADIO_PrintHelp(io); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"radio status")==0) { RADIO_PrintStatus(io); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)"radio init")==0) { *handled = TRUE; RADIO_Notify(RADIO_FLAG_REQUEST_INIT); return ERR_OK; } else if (McuUtility_strcmp((char*)cmd, (char*)"radio sniff on")==0) { RADIO_isSniffing = TRUE; *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)"radio sniff off")==0) { RADIO_isSniffing = FALSE; *handled = TRUE; } else if (McuUtility_strncmp((char*)cmd, (char*)"radio channel", sizeof("radio channel")-1)==0) { p = cmd+sizeof("radio channel"); if (McuUtility_ScanDecimal8uNumber(&p, &val)==ERR_OK && val<=0x7F) { res = RADIO_SetChannel(val); *handled = TRUE; } else { McuShell_SendStr((unsigned char*)"Wrong argument, must be in the range 0..128\r\n", io->stdErr); res = ERR_FAILED; } } else if (McuUtility_strncmp((char*)cmd, (char*)"radio power", sizeof("radio power")-1)==0) { p = cmd+sizeof("radio power"); if (McuUtility_ScanDecimal8sNumber(&p, &vals)==ERR_OK && (vals==0 || vals==-10 || vals==-12 || vals==-18)) { (void)McuNRF24L01_SetOutputPower(vals); *handled = TRUE; } else { McuShell_SendStr((unsigned char*)"Wrong argument, must be 0, -10, -12 or -18\r\n", io->stdErr); res = ERR_FAILED; } } else if (McuUtility_strncmp((char*)cmd, (char*)"radio writereg", sizeof("radio writereg")-1)==0) { uint8_t reg; p = cmd+sizeof("radio writereg"); if (McuUtility_ScanHex8uNumber(&p, ®)==ERR_OK && McuUtility_ScanHex8uNumber(&p, &val)==ERR_OK) { McuNRF24L01_WriteRegister(reg, val); *handled = TRUE; } else { McuShell_SendStr((unsigned char*)"Wrong arguments\r\n", io->stdErr); res = ERR_FAILED; } } else if (McuUtility_strncmp((char*)cmd, (char*)"radio readreg", sizeof("radio readreg")-1)==0) { uint8_t reg; uint8_t buf[16]; p = cmd+sizeof("radio readreg"); if (McuUtility_ScanHex8uNumber(&p, ®)==ERR_OK) { val = McuNRF24L01_ReadRegister(reg); buf[0] = '\0'; McuUtility_strcpy(buf, sizeof(buf), (uint8_t*)"0x"); McuUtility_strcatNum8Hex(buf, sizeof(buf), val); McuUtility_strcat(buf, sizeof(buf), (uint8_t*)"\r\n"); McuShell_SendStr(buf, io->stdOut); *handled = TRUE; } else { McuShell_SendStr((unsigned char*)"Wrong arguments\r\n", io->stdErr); res = ERR_FAILED; } } else if (McuUtility_strcmp((char*)cmd, (char*)"radio flush")==0) { *handled = TRUE; if (RADIO_Flush()!=ERR_OK) { McuShell_SendStr((unsigned char*)"Flushing failed!\r\n", io->stdErr); res = ERR_FAILED; } if (RADIO_FlushQueues()!=ERR_OK) { McuShell_SendStr((unsigned char*)"Flushing queues failed!\r\n", io->stdErr); res = ERR_FAILED; } } else if (McuUtility_strcmp((char*)cmd, (char*)"radio printregs")==0) { RadioPrintRegisters(io); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)"radio datarate 250")==0) { (void)McuNRF24L01_SetDataRate(250); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)"radio datarate 1000")==0) { (void)McuNRF24L01_SetDataRate(1000); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)"radio datarate 2000")==0) { (void)McuNRF24L01_SetDataRate(2000); *handled = TRUE; } else if (McuUtility_strncmp((char*)cmd, (char*)"radio txaddr ", sizeof("radio txaddr ")-1)==0) { p = cmd+sizeof("radio txaddr ")-1; for(i=0;istdErr); res = ERR_FAILED; break; /* break for loop */ } } /* for */ if (McuNRF24L01_SetTxAddress(addr, sizeof(addr))!=ERR_OK) { McuShell_SendStr((unsigned char*)"Error setting TX address!\r\n", io->stdErr); res = ERR_FAILED; } *handled = TRUE; } else if (McuUtility_strncmp((char*)cmd, (char*)"radio rxaddr ", sizeof("radio rxaddr ")-1)==0) { uint8_t pipe; p = cmd+sizeof("radio rxaddr ")-1; if (McuUtility_ScanDecimal8uNumber(&p, &pipe)==ERR_OK) { for(i=0;istdErr); res = ERR_FAILED; break; /* break for loop */ } } /* for */ if (McuNRF24L01_SetRxAddress(pipe, addr, sizeof(addr))!=ERR_OK) { McuShell_SendStr((unsigned char*)"Error setting RX address!\r\n", io->stdErr); res = ERR_FAILED; } } *handled = TRUE; } return res; } #if McuNRF24L01_CONFIG_IRQ_PIN_ENABLED #include "McuGPIO.h" #include "RadioNRF24.h" #if McuLib_CONFIG_CPU_IS_KINETIS #include "fsl_port.h" #endif static McuGPIO_Handle_t irqPin; #if McuLib_CONFIG_CPU_IS_RPxxxx static void gpio_IsrCallback(uint gpio, uint32_t events) { switch(gpio) { case McuNRF24L01_CONFIG_IRQ_PIN_NUMBER: RADIO_OnInterrupt(); break; default: break; } } #endif /* McuLib_CONFIG_CPU_IS_RPxxxx */ #if McuLib_CONFIG_CPU_IS_KINETIS void PORTB_IRQHandler(void) { /* interrupt handler for PORTB */ uint32_t flags; flags = GPIO_PortGetInterruptFlags(McuNRF24L01_CONFIG_IRQ_PIN_GPIO); if (flags&(1U<