RF24项目代码分析

函数 bool writeFast(const void* buf, uint8_t len)

这个函数充分利用了nrf24带有3x32字节的FIFO发送队列, 在队列未满之前持续写入.

使用这个函数时需要屏蔽IRQ的外部中断, 它不需要中断信号也不会去读取IRQ状态, 因为它是通过读取TX_FULL标志位来判断是否继续写入的

// 仅当 FIFO 已满时阻塞, 如果FIFO满, 会在这里阻塞直到TX成功或者失败
while ((get_status() & (_BV(TX_FULL)))) {
if (status & _BV(MAX_RT)) {
return 0; // 如果状态MAX_RT标志置位, 说明之前的数据发送失败, 返回0, 上级调用得到0的结果后可以启用重新发送
}
//...
}
startFastWrite(buf, len, multicast); // 开始写入

函数 void startFastWrite(const void* buf, uint8_t len, const bool multicast, bool startTx = 1)

非阻塞的快速写入(发送), 这里最后一个参数默认值就是1, 所以默认情况下ce(HIGH)会一直被执行.

这个函数会使CE一直保持高电平, 这时候NRF24会一直保持在TX或STANDBY-II状态, 直到调用txStandBy()命令.

这个函数可以用于一次性调用NRF24发送多组数据.

// 根据文档, 如果要在非接收模式下设置PTX模式, 需要做的就是写入数据并设置CE为高电平, 在这个模式下, 如果持续往 FIFO buffers 里写入
// 数据包会立即被发送, 而不需要等待130us的间隔, 否则会进入 Standby-II 模式, 这样依然比待机模式快, 避免了每次发送时修改 config 寄存器并等待150us
void RF24::startFastWrite(const void* buf, uint8_t len, const bool multicast, bool startTx)
{ //TMRh20
write_payload(buf, len, multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD);
if (startTx) {
ce(HIGH);
}
}

注意: 不能让NRF24在TX模式下保持FIFO队列满状态超过4ms. 启用了自动重发(auto retransmit)和自动应答(autoAck)后, NRF24保持TX模式的时间长度依然满足这个规则, 这样允许调用txStandBy() 清空FIFO队列或确保发送之间保持足够的时间间隔.

函数 bool txStandBy();

读取FIFO_STATUS寄存器, 直至TX_EMPTY标志位置位.

  • 如果队列已空, 则停止发送ce(LOW), 进入STANDBY-I模式
  • 在循环中检查status中的MAX_RT标志位, 如果被置位也停止发送, 清空队列, 返回0
bool RF24::txStandBy()
{
while (!(read_register(FIFO_STATUS) & _BV(TX_EMPTY))) {
if (status & _BV(MAX_RT)) {
write_register(NRF_STATUS, _BV(MAX_RT));
ce(LOW);
flush_tx(); //Non blocking, flush the data
return 0;
}
}
ce(LOW); //Set STANDBY-I mode
return 1;
}

函数 void reUseTX()

void RF24::reUseTX()
{
write_register(NRF_STATUS, _BV(MAX_RT)); //Clear max retry flag
write_register(REUSE_TX_PL, RF24_NOP, true);
ce(LOW); //Re-Transfer packet
ce(HIGH);
}

读取过程

  • 根据手册 The STATUS register is serially shifted out on the MISO pin simultaneously to the SPI command word shift-ing to the MOSI pin 在每次读取或者写入操作时, 在发出指令的同时, 一边是上位机通过MOSI口往NRF24写, 一边NRF24会把STATUS寄存器的值通过MISO口往上位机写, 所以在任何读写寄存器的操作时, 都能交换得到STATUS寄存器的值
  • 根据手册The 3 bit pipe information in the STATUS register is updated during the IRQ pin high to low transition. The pipe information is unreliable if the STATUS register is read during an IRQ pin high to low transition. 当IRQ从高电平转低电平时才会更新STATUS寄存器中3-bit的pipe信息, 当电平还在转换过程中时读取的STATUS寄存器值是不可靠的. 要先清除状态标志位, 再读取STATUS值.

读取时一般使用以下的步骤, 启用中断, 中断时先检查是什么标志位, 然后读取有数据的pipe编号

void setup() {
pinMode(IRQ_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(IRQ_PIN), isrCallbackFunction, FALLING);
} void isrCallbackFunction() {
bool tx_ds, tx_df, rx_dr;
radio.whatHappened(tx_ds, tx_df, rx_dr); // resets the IRQ pin to HIGH
if (radio.available()) { // is there a payload
radio.read(&buffer, SIZE); // fetch payload from FIFO
//...
}
}

读取status寄存器判断标志位和清除标志位是在一个指令中完成的

void RF24::whatHappened(bool& tx_ok, bool& tx_fail, bool& rx_ready)
{
// Read the status & reset the status in one easy call
// Or is that such a good idea?
write_register(NRF_STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT)); // Report to the user what happened
tx_ok = status & _BV(TX_DS);
tx_fail = status & _BV(MAX_RT);
rx_ready = status & _BV(RX_DR);
}

再次读取status寄存器, 用于判断pipe编号

bool RF24::available(uint8_t* pipe_num)
{
// get implied RX FIFO empty flag from status byte
uint8_t pipe = (get_status() >> RX_P_NO) & 0x07;
if (pipe > 5)
return 0; // If the caller wants the pipe number, include that
if (pipe_num)
*pipe_num = pipe; return 1;
}

在STC12C5A60S2上的测试

发送端

为了避免干扰, 注释掉了串口输出部分, 使用MAX7219的8x8LED作为输出显示

#include "hml/hml.h"
//#include <stdio.h>
#include "max7219.h" /********** SPI(nRF24L01) commands ***********/
//
#define NRF24_CMD_R_REGISTER 0x00 // [000A AAAA] Register read
#define NRF24_CMD_W_REGISTER 0x20 // [001A AAAA] Register write
#define NRF24_CMD_R_RX_PAYLOAD 0x61 // Read RX payload
#define NRF24_CMD_W_TX_PAYLOAD 0xA0 // Write TX payload
#define NRF24_CMD_FLUSH_TX 0xE1 // Flush TX FIFO
#define NRF24_CMD_FLUSH_RX 0xE2 // Flush RX FIFO
#define NRF24_CMD_REUSE_TX_PL 0xE3 // Reuse TX payload
#define NRF24_CMD_R_RX_PL_WID 0x60 // Read RX-payload width for the top R_RX_PAYLOAD in the RX FIFO.
#define NRF24_CMD_W_ACK_PAYLOAD 0xA8 // [1010 1PPP] Write ACK Payload to be with ACK packet on PIPE PPP
#define NRF24_CMD_W_TX_PAYLOAD_NOACK 0xB0 //Write TX payload and disable AUTOACK
#define NRF24_CMD_NOP 0xFF // No operation (used for reading status register) #define NRF24_CMD_ACTIVATE 0x50 // (De)Activates R_RX_PL_WID, W_ACK_PAYLOAD, W_TX_PAYLOAD_NOACK features
#define NRF24_CMD_LOCK_UNLOCK 0x50 // Lock/unlock exclusive features // SPI(nRF24L01) register address definitions
#define NRF24_REG_CONFIG 0x00 // Configuration register
#define NRF24_REG_EN_AA 0x01 // Enable "Auto acknowledgment"
#define NRF24_REG_EN_RXADDR 0x02 // Enable RX addresses
#define NRF24_REG_SETUP_AW 0x03 // Setup of address widths
#define NRF24_REG_SETUP_RETR 0x04 // Setup of automatic re-transmit
#define NRF24_REG_RF_CH 0x05 // RF channel
#define NRF24_REG_RF_SETUP 0x06 // RF setup
#define NRF24_REG_STATUS 0x07 // Status register
#define NRF24_REG_OBSERVE_TX 0x08 // Transmit observe register
#define NRF24_REG_RPD 0x09 // Received power detector
#define NRF24_REG_RX_ADDR_P0 0x0A // Receive address data pipe 0
#define NRF24_REG_RX_ADDR_P1 0x0B // Receive address data pipe 1
#define NRF24_REG_RX_ADDR_P2 0x0C // Receive address data pipe 2
#define NRF24_REG_RX_ADDR_P3 0x0D // Receive address data pipe 3
#define NRF24_REG_RX_ADDR_P4 0x0E // Receive address data pipe 4
#define NRF24_REG_RX_ADDR_P5 0x0F // Receive address data pipe 5
#define NRF24_REG_TX_ADDR 0x10 // Transmit address
#define NRF24_REG_RX_PW_P0 0x11 // Number of bytes in RX payload in data pipe 0
#define NRF24_REG_RX_PW_P1 0x12 // Number of bytes in RX payload in data pipe 1
#define NRF24_REG_RX_PW_P2 0x13 // Number of bytes in RX payload in data pipe 2
#define NRF24_REG_RX_PW_P3 0x14 // Number of bytes in RX payload in data pipe 3
#define NRF24_REG_RX_PW_P4 0x15 // Number of bytes in RX payload in data pipe 4
#define NRF24_REG_RX_PW_P5 0x16 // Number of bytes in RX payload in data pipe 5
#define NRF24_REG_FIFO_STATUS 0x17 // FIFO status register
#define NRF24_REG_DYNPD 0x1C // Enable dynamic payload length
#define NRF24_REG_FEATURE 0x1D // Feature register // Register bits definitions
#define NRF24_CONFIG_PRIM_RX 0x01 // PRIM_RX bit in CONFIG register
#define NRF24_CONFIG_PWR_UP 0x02 // PWR_UP bit in CONFIG register
#define NRF24_FEATURE_EN_DYN_ACK 0x01 // EN_DYN_ACK bit in FEATURE register
#define NRF24_FEATURE_EN_ACK_PAY 0x02 // EN_ACK_PAY bit in FEATURE register
#define NRF24_FEATURE_EN_DPL 0x04 // EN_DPL bit in FEATURE register
#define NRF24_FLAG_RX_DREADY 0x40 // RX_DR bit (data ready RX FIFO interrupt)
#define NRF24_FLAG_TX_DSENT 0x20 // TX_DS bit (data sent TX FIFO interrupt)
#define NRF24_FLAG_MAX_RT 0x10 // MAX_RT bit (maximum number of TX re-transmits interrupt)
#define NRF24_FLAG_TX_FULL 0x01 // 1:TX FIFO full // Register masks definitions
#define NRF24_MASK_REG_MAP 0x1F // Mask bits[4:0] for CMD_RREG and CMD_WREG commands
#define NRF24_MASK_CRC 0x0C // Mask for CRC bits [3:2] in CONFIG register
#define NRF24_MASK_STATUS_IRQ 0x70 // Mask for all IRQ bits in STATUS register
#define NRF24_MASK_RF_PWR 0x06 // Mask RF_PWR[2:1] bits in RF_SETUP register
#define NRF24_MASK_RX_P_NO 0x0E // Mask RX_P_NO[3:1] bits in STATUS register
#define NRF24_MASK_DATARATE 0x28 // Mask RD_DR_[5,3] bits in RF_SETUP register
#define NRF24_MASK_EN_RX 0x3F // Mask ERX_P[5:0] bits in EN_RXADDR register
#define NRF24_MASK_RX_PW 0x3F // Mask [5:0] bits in RX_PW_Px register
#define NRF24_MASK_RETR_ARD 0xF0 // Mask for ARD[7:4] bits in SETUP_RETR register
#define NRF24_MASK_RETR_ARC 0x0F // Mask for ARC[3:0] bits in SETUP_RETR register
#define NRF24_MASK_RXFIFO 0x03 // Mask for RX FIFO status bits [1:0] in FIFO_STATUS register
#define NRF24_MASK_TXFIFO 0x30 // Mask for TX FIFO status bits [5:4] in FIFO_STATUS register
#define NRF24_MASK_PLOS_CNT 0xF0 // Mask for PLOS_CNT[7:4] bits in OBSERVE_TX register
#define NRF24_MASK_ARC_CNT 0x0F // Mask for ARC_CNT[3:0] bits in OBSERVE_TX register #define NRF24_ADDR_WIDTH 5 // RX/TX address width
#define NRF24_PLOAD_WIDTH 32 // Payload width
#define NRF24_TEST_ADDR "nRF24" uint8_t nrf24_state; typedef enum
{
NRF24_MODE_RX = 0x00,
NRF24_MODE_TX = 0x01
} NRF24_MODE; typedef enum
{
NRF24_SCEN_RX = 0x00,
NRF24_SCEN_TX = 0x01,
NRF24_SCEN_HALF_DUPLEX = 0x02
} NRF24_SCEN; __xdata uint8_t xbuf[NRF24_PLOAD_WIDTH + 1];
const uint8_t TX_ADDRESS[NRF24_ADDR_WIDTH] = {0x32,0x4E,0x6F,0x64,0x65};
const uint8_t RX_ADDRESS[NRF24_ADDR_WIDTH] = {0x32,0x4E,0x6F,0x64,0x22};
const NRF24_SCEN CURRENT_SCEN = NRF24_SCEN_TX; #define NRF_CSN P1_4
#define NRF_MOSI P1_5
#define NRF_MISO P1_6
#define NRF_SCK P1_7
#define NRF_IRQ P3_2
#define NRF_CE P3_7 void NRF24L01_writeReg(uint8_t reg, uint8_t value)
{
NRF_CSN = 0;
nrf24_state = SPI_RW(reg);
SPI_RW(value);
NRF_CSN = 1;
} uint8_t NRF24L01_readReg(uint8_t reg)
{
uint8_t value;
NRF_CSN = 0;
nrf24_state = SPI_RW(reg);
value = SPI_RW(NRF24_CMD_NOP);
NRF_CSN = 1;
return value;
} void NRF24L01_readToBuf(uint8_t reg, uint8_t *pBuf, uint8_t len)
{
uint8_t u8_ctr;
NRF_CSN = 0;
nrf24_state = SPI_RW(reg);
for (u8_ctr = 0; u8_ctr < len; u8_ctr++)
pBuf[u8_ctr] = SPI_RW(NRF24_CMD_NOP);
NRF_CSN = 1;
} void NRF24L01_writeFromBuf(uint8_t reg, const uint8_t *pBuf, uint8_t len)
{
uint8_t u8_ctr;
NRF_CSN = 0;
nrf24_state = SPI_RW(reg);
for (u8_ctr = 0; u8_ctr < len; u8_ctr++)
SPI_RW(*pBuf++);
NRF_CSN = 1;
} void NRF24L01_printBuf(uint8_t *buf)
{
for (uint8_t i = 0; i < NRF24_PLOAD_WIDTH; i++)
{
//printf_tiny("%x ", buf[i]);
}
//printf_tiny("\r\n");
} /**
* Flush the RX FIFO
*/
void NRF24L01_flushRX(void)
{
NRF24L01_writeReg(NRF24_CMD_FLUSH_RX, NRF24_CMD_NOP);
} /**
* Flush the TX FIFO
*/
void NRF24L01_flushTX(void)
{
NRF24L01_writeReg(NRF24_CMD_FLUSH_TX, NRF24_CMD_NOP);
} void NRF24L01_checkFlag(uint8_t *tx_ds, uint8_t *max_rt, uint8_t *rx_dr)
{
// Read the status & reset the status in one easy call
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_STATUS, NRF24_FLAG_RX_DREADY|NRF24_FLAG_TX_DSENT|NRF24_FLAG_MAX_RT);
// Report to the user what happened
*tx_ds = nrf24_state & NRF24_FLAG_TX_DSENT;
*max_rt = nrf24_state & NRF24_FLAG_MAX_RT;
*rx_dr = nrf24_state & NRF24_FLAG_RX_DREADY;
} bool NRF24L01_rxAvailable(uint8_t* pipe_num)
{
nrf24_state = NRF24L01_readReg(NRF24_REG_STATUS);
uint8_t pipe = (nrf24_state >> 1) & 0x07;
if (pipe > 5)
return false;
// If the caller wants the pipe number, include that
if (pipe_num)
*pipe_num = pipe; return true;
} void NRF24L01_handelIrqFlag(uint8_t *buf)
{
int8_t tx_ds, max_rt, rx_dr, pipe_num;
NRF24L01_checkFlag(&tx_ds, &max_rt, &rx_dr);
if (NRF24L01_rxAvailable(&pipe_num)) {
NRF24L01_readToBuf(NRF24_CMD_R_RX_PAYLOAD, buf, NRF24_PLOAD_WIDTH);
}
//printf_tiny("TX_DS:%x, MAX_RT:%x, RX_DR:%x, PIPE:%x\r\n", tx_ds, max_rt, rx_dr, pipe_num);
//printf_tiny("TX_DS:%x\r\n", tx_ds);
//NRF24L01_printBuf(xbuf);
} void NRF24L01_tx(uint8_t *txbuf)
{
NRF_CE = 0;
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_CONFIG, 0x0E);
NRF24L01_writeFromBuf(NRF24_CMD_W_TX_PAYLOAD, txbuf, NRF24_PLOAD_WIDTH);
NRF_CE = 1;
sleep(5); // 4ms+ for reliable DS state when SETUP_RETR is 0x13
NRF_CE = 0;
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_CONFIG, 0x0F);
NRF_CE = 1;
} void NRF24L01_startFastWrite(const void* txbuf)
{
NRF24L01_writeFromBuf(NRF24_CMD_W_TX_PAYLOAD, txbuf, NRF24_PLOAD_WIDTH);
NRF_CE = 1;
} bool NRF24L01_writeFast(const void* txbuf)
{
//Blocking only if FIFO is full. This will loop and block until TX is successful or fail
while ((NRF24L01_readReg(NRF24_REG_STATUS) & NRF24_FLAG_TX_FULL)) {
//printf_tiny(">STATE:%x\r\n", nrf24_state);
if (nrf24_state & NRF24_FLAG_MAX_RT) {
return false;
}
}
//printf_tiny("STATE:%x\r\n", nrf24_state);
NRF24L01_startFastWrite(txbuf);
return true;
} void NRF24L01_reUseTX(void)
{
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_STATUS, NRF24_FLAG_MAX_RT);//Clear max retry flag
NRF_CE = 0;
NRF_CE = 1;
} uint8_t NRF24L01_check(void)
{
uint8_t i;
uint8_t *ptr = (uint8_t *)NRF24_TEST_ADDR;
NRF24L01_writeFromBuf(NRF24_CMD_W_REGISTER | NRF24_REG_TX_ADDR, ptr, NRF24_ADDR_WIDTH);
NRF24L01_readToBuf(NRF24_CMD_R_REGISTER | NRF24_REG_TX_ADDR, xbuf, NRF24_ADDR_WIDTH);
for (i = 0; i < NRF24_ADDR_WIDTH; i++) {
if (xbuf[i] != *ptr++) return 1;
}
return 0;
} void NRF24L01_init(NRF24_MODE mode)
{
NRF_CE = 0;
NRF24L01_writeFromBuf(NRF24_CMD_W_REGISTER + NRF24_REG_TX_ADDR, (uint8_t *)TX_ADDRESS, NRF24_ADDR_WIDTH);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_RX_PW_P0, NRF24_PLOAD_WIDTH);
NRF24L01_writeFromBuf(NRF24_CMD_W_REGISTER + NRF24_REG_RX_ADDR_P0, (uint8_t *)TX_ADDRESS, NRF24_ADDR_WIDTH);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_RX_PW_P1, NRF24_PLOAD_WIDTH);
NRF24L01_writeFromBuf(NRF24_CMD_W_REGISTER + NRF24_REG_RX_ADDR_P1, (uint8_t *)RX_ADDRESS, NRF24_ADDR_WIDTH);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_EN_AA, 0x3f);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_EN_RXADDR, 0x3f);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_SETUP_RETR, 0x13);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_RF_CH, 40);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_RF_SETUP, 0x07);
switch (mode)
{
case NRF24_MODE_TX:
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_CONFIG, 0x7E);
break;
case NRF24_MODE_RX:
default:
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_CONFIG, 0x0F);
break;
}
NRF_CE = 1;
} void EXTI0_irqHandler(void) __interrupt (IE0_VECTOR)
{
NRF24L01_handelIrqFlag(xbuf);
} void EXTI_init(void)
{
EXTI_configTypeDef ec;
ec.mode = EXTI_mode_lowLevel;
ec.priority = IntPriority_High;
EXTI_config(PERIPH_EXTI_0, &ec);
EXTI_cmd(PERIPH_EXTI_0, ENABLE);
UTIL_setInterrupts(ENABLE);
} void SPI_init(void)
{
SPI_configTypeDef sc;
sc.baudRatePrescaler = SPI_BaudRatePrescaler_4;
sc.cpha = SPI_CPHA_1Edge;
sc.cpol = SPI_CPOL_low;
sc.firstBit = SPI_FirstBit_MSB;
sc.pinmap = SPI_pinmap_P1;
sc.nss = SPI_NSS_Soft;
sc.mode = SPI_Mode_Master;
SPI_config(&sc);
SPI_cmd(ENABLE);
} void main(void)
{
const uint8_t tmp[] = {
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F}; //UTIL_enablePrintf();
SPI_init(); while (NRF24L01_check())
{
//printf_tiny("Check failed\r\n");
sleep(500);
} switch (CURRENT_SCEN)
{
case NRF24_SCEN_TX:
MAX7219_init(1, 0x00, 0x07, 0x00);
NRF24L01_init(NRF24_MODE_TX);
//EXTI_init();
//printf_tiny("Initialized\r\n");
uint8_t pos = 0, failures = 0, i = 0, j = 1;
for (i = 0; i < 7; i++) {
MAX7219_singeWrite(0, i+1, 0x00);
}
while (true)
{
if (!NRF24L01_writeFast(tmp + pos))
{
failures++;
NRF24L01_reUseTX();
}
else
{
i++;
}
pos++;
if (pos > 0x0F) pos = 0x00; if (i == 0xFF || failures == 0xFF)
{
//printf_tiny("[x]F/S = %x/%x\r\n", failures, i);
MAX7219_singeWrite(0, j, failures);
MAX7219_singeWrite(0, j+1, i);
i = 0x00;
failures = 0x00;
j += 2;
if (j > 7) j = 1;
}
//sleep(1);// 20:err(40%~50%), 10:err(60%-70%)
}
break; case NRF24_SCEN_RX:
NRF24L01_init(NRF24_MODE_RX);
EXTI_init();
while(true);
break; case NRF24_SCEN_HALF_DUPLEX:
NRF24L01_init(NRF24_MODE_RX);
EXTI_init();
while (true)
{
NRF24L01_tx((uint8_t *)tmp);
sleep(500);
}
break; default:
//printf_tiny("Unknown scen\r\n");
break;
}
}

接收端

接收端也注释了串口输出

#include "hml/hml.h"
//#include <stdio.h> /********** SPI(nRF24L01) commands ***********/
//
#define NRF24_CMD_R_REGISTER 0x00 // [000A AAAA] Register read
#define NRF24_CMD_W_REGISTER 0x20 // [001A AAAA] Register write
#define NRF24_CMD_R_RX_PAYLOAD 0x61 // Read RX payload
#define NRF24_CMD_W_TX_PAYLOAD 0xA0 // Write TX payload
#define NRF24_CMD_FLUSH_TX 0xE1 // Flush TX FIFO
#define NRF24_CMD_FLUSH_RX 0xE2 // Flush RX FIFO
#define NRF24_CMD_REUSE_TX_PL 0xE3 // Reuse TX payload
#define NRF24_CMD_R_RX_PL_WID 0x60 // Read RX-payload width for the top R_RX_PAYLOAD in the RX FIFO.
#define NRF24_CMD_W_ACK_PAYLOAD 0xA8 // [1010 1PPP] Write ACK Payload to be with ACK packet on PIPE PPP
#define NRF24_CMD_W_TX_PAYLOAD_NOACK 0xB0 //Write TX payload and disable AUTOACK
#define NRF24_CMD_NOP 0xFF // No operation (used for reading status register) #define NRF24_CMD_ACTIVATE 0x50 // (De)Activates R_RX_PL_WID, W_ACK_PAYLOAD, W_TX_PAYLOAD_NOACK features
#define NRF24_CMD_LOCK_UNLOCK 0x50 // Lock/unlock exclusive features // SPI(nRF24L01) register address definitions
#define NRF24_REG_CONFIG 0x00 // Configuration register
#define NRF24_REG_EN_AA 0x01 // Enable "Auto acknowledgment"
#define NRF24_REG_EN_RXADDR 0x02 // Enable RX addresses
#define NRF24_REG_SETUP_AW 0x03 // Setup of address widths
#define NRF24_REG_SETUP_RETR 0x04 // Setup of automatic re-transmit
#define NRF24_REG_RF_CH 0x05 // RF channel
#define NRF24_REG_RF_SETUP 0x06 // RF setup
#define NRF24_REG_STATUS 0x07 // Status register
#define NRF24_REG_OBSERVE_TX 0x08 // Transmit observe register
#define NRF24_REG_RPD 0x09 // Received power detector
#define NRF24_REG_RX_ADDR_P0 0x0A // Receive address data pipe 0
#define NRF24_REG_RX_ADDR_P1 0x0B // Receive address data pipe 1
#define NRF24_REG_RX_ADDR_P2 0x0C // Receive address data pipe 2
#define NRF24_REG_RX_ADDR_P3 0x0D // Receive address data pipe 3
#define NRF24_REG_RX_ADDR_P4 0x0E // Receive address data pipe 4
#define NRF24_REG_RX_ADDR_P5 0x0F // Receive address data pipe 5
#define NRF24_REG_TX_ADDR 0x10 // Transmit address
#define NRF24_REG_RX_PW_P0 0x11 // Number of bytes in RX payload in data pipe 0
#define NRF24_REG_RX_PW_P1 0x12 // Number of bytes in RX payload in data pipe 1
#define NRF24_REG_RX_PW_P2 0x13 // Number of bytes in RX payload in data pipe 2
#define NRF24_REG_RX_PW_P3 0x14 // Number of bytes in RX payload in data pipe 3
#define NRF24_REG_RX_PW_P4 0x15 // Number of bytes in RX payload in data pipe 4
#define NRF24_REG_RX_PW_P5 0x16 // Number of bytes in RX payload in data pipe 5
#define NRF24_REG_FIFO_STATUS 0x17 // FIFO status register
#define NRF24_REG_DYNPD 0x1C // Enable dynamic payload length
#define NRF24_REG_FEATURE 0x1D // Feature register // Register bits definitions
#define NRF24_CONFIG_PRIM_RX 0x01 // PRIM_RX bit in CONFIG register
#define NRF24_CONFIG_PWR_UP 0x02 // PWR_UP bit in CONFIG register
#define NRF24_FEATURE_EN_DYN_ACK 0x01 // EN_DYN_ACK bit in FEATURE register
#define NRF24_FEATURE_EN_ACK_PAY 0x02 // EN_ACK_PAY bit in FEATURE register
#define NRF24_FEATURE_EN_DPL 0x04 // EN_DPL bit in FEATURE register
#define NRF24_FLAG_RX_DREADY 0x40 // RX_DR bit (data ready RX FIFO interrupt)
#define NRF24_FLAG_TX_DSENT 0x20 // TX_DS bit (data sent TX FIFO interrupt)
#define NRF24_FLAG_MAX_RT 0x10 // MAX_RT bit (maximum number of TX re-transmits interrupt)
#define NRF24_FLAG_TX_FULL 0x01 // 1:TX FIFO full // Register masks definitions
#define NRF24_MASK_REG_MAP 0x1F // Mask bits[4:0] for CMD_RREG and CMD_WREG commands
#define NRF24_MASK_CRC 0x0C // Mask for CRC bits [3:2] in CONFIG register
#define NRF24_MASK_STATUS_IRQ 0x70 // Mask for all IRQ bits in STATUS register
#define NRF24_MASK_RF_PWR 0x06 // Mask RF_PWR[2:1] bits in RF_SETUP register
#define NRF24_MASK_RX_P_NO 0x0E // Mask RX_P_NO[3:1] bits in STATUS register
#define NRF24_MASK_DATARATE 0x28 // Mask RD_DR_[5,3] bits in RF_SETUP register
#define NRF24_MASK_EN_RX 0x3F // Mask ERX_P[5:0] bits in EN_RXADDR register
#define NRF24_MASK_RX_PW 0x3F // Mask [5:0] bits in RX_PW_Px register
#define NRF24_MASK_RETR_ARD 0xF0 // Mask for ARD[7:4] bits in SETUP_RETR register
#define NRF24_MASK_RETR_ARC 0x0F // Mask for ARC[3:0] bits in SETUP_RETR register
#define NRF24_MASK_RXFIFO 0x03 // Mask for RX FIFO status bits [1:0] in FIFO_STATUS register
#define NRF24_MASK_TXFIFO 0x30 // Mask for TX FIFO status bits [5:4] in FIFO_STATUS register
#define NRF24_MASK_PLOS_CNT 0xF0 // Mask for PLOS_CNT[7:4] bits in OBSERVE_TX register
#define NRF24_MASK_ARC_CNT 0x0F // Mask for ARC_CNT[3:0] bits in OBSERVE_TX register #define NRF24_ADDR_WIDTH 5 // RX/TX address width
#define NRF24_PLOAD_WIDTH 32 // Payload width
#define NRF24_TEST_ADDR "nRF24" uint8_t nrf24_state; typedef enum
{
NRF24_MODE_RX = 0x00,
NRF24_MODE_TX = 0x01
} NRF24_MODE; typedef enum
{
NRF24_SCEN_RX = 0x00,
NRF24_SCEN_TX = 0x01,
NRF24_SCEN_HALF_DUPLEX = 0x02
} NRF24_SCEN; uint8_t xbuf[NRF24_PLOAD_WIDTH + 1];
const uint8_t RX_ADDRESS[NRF24_ADDR_WIDTH] = {0x32,0x4E,0x6F,0x64,0x65};
const uint8_t TX_ADDRESS[NRF24_ADDR_WIDTH] = {0x32,0x4E,0x6F,0x64,0x22};
const NRF24_SCEN CURRENT_SCEN = NRF24_SCEN_RX; #define NRF_CSN P1_4
#define NRF_MOSI P1_5
#define NRF_MISO P1_6
#define NRF_SCK P1_7
#define NRF_IRQ P3_2
#define NRF_CE P3_7 void NRF24L01_writeReg(uint8_t reg, uint8_t value)
{
NRF_CSN = 0;
nrf24_state = SPI_RW(reg);
SPI_RW(value);
NRF_CSN = 1;
} uint8_t NRF24L01_readReg(uint8_t reg)
{
uint8_t value;
NRF_CSN = 0;
nrf24_state = SPI_RW(reg);
value = SPI_RW(NRF24_CMD_NOP);
NRF_CSN = 1;
return value;
} void NRF24L01_readToBuf(uint8_t reg, uint8_t *pBuf, uint8_t len)
{
uint8_t u8_ctr;
NRF_CSN = 0;
nrf24_state = SPI_RW(reg);
for (u8_ctr = 0; u8_ctr < len; u8_ctr++)
pBuf[u8_ctr] = SPI_RW(NRF24_CMD_NOP);
NRF_CSN = 1;
} void NRF24L01_writeFromBuf(uint8_t reg, const uint8_t *pBuf, uint8_t len)
{
uint8_t u8_ctr;
NRF_CSN = 0;
nrf24_state = SPI_RW(reg);
for (u8_ctr = 0; u8_ctr < len; u8_ctr++)
SPI_RW(*pBuf++);
NRF_CSN = 1;
} void NRF24L01_printBuf(uint8_t *buf)
{
for (uint8_t i = 0; i < NRF24_PLOAD_WIDTH; i++)
{
//printf_tiny("%x ", buf[i]);
}
//printf_tiny("\r\n");
} /**
* Flush the RX FIFO
*/
void NRF24L01_flushRX(void)
{
NRF24L01_writeReg(NRF24_CMD_FLUSH_RX, NRF24_CMD_NOP);
} /**
* Flush the TX FIFO
*/
void NRF24L01_flushTX(void)
{
NRF24L01_writeReg(NRF24_CMD_FLUSH_TX, NRF24_CMD_NOP);
} void NRF24L01_checkFlag(uint8_t *tx_ds, uint8_t *max_rt, uint8_t *rx_dr)
{
// Read the status & reset the status in one easy call
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_STATUS, NRF24_FLAG_RX_DREADY|NRF24_FLAG_TX_DSENT|NRF24_FLAG_MAX_RT);
// Report to the user what happened
*tx_ds = nrf24_state & NRF24_FLAG_TX_DSENT;
*max_rt = nrf24_state & NRF24_FLAG_MAX_RT;
*rx_dr = nrf24_state & NRF24_FLAG_RX_DREADY;
} bool NRF24L01_rxAvailable(uint8_t* pipe_num)
{
nrf24_state = NRF24L01_readReg(NRF24_REG_STATUS);
uint8_t pipe = (nrf24_state >> 1) & 0x07;
if (pipe > 5)
return false;
// If the caller wants the pipe number, include that
if (pipe_num)
*pipe_num = pipe; return true;
} void NRF24L01_handelIrqFlag(uint8_t *buf)
{
int8_t tx_ds, max_rt, rx_dr, pipe_num;
NRF24L01_checkFlag(&tx_ds, &max_rt, &rx_dr);
if (NRF24L01_rxAvailable(&pipe_num)) {
NRF24L01_readToBuf(NRF24_CMD_R_RX_PAYLOAD, buf, NRF24_PLOAD_WIDTH);
}
//printf_tiny("RX:%x\r\n", buf[0]);
//printf_tiny("TX_DS:%x, MAX_RT:%x, RX_DR:%x, PIPE:%x, RX:%x\r\n", tx_ds, max_rt, rx_dr, pipe_num, buf[0]);
//NRF24L01_printBuf(xbuf);
} void NRF24L01_tx(uint8_t *txbuf)
{
NRF_CE = 0;
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_CONFIG, 0x0E);
NRF24L01_writeFromBuf(NRF24_CMD_W_TX_PAYLOAD, txbuf, NRF24_PLOAD_WIDTH);
NRF_CE = 1;
sleep(4); // 4ms+ for reliable DS state when SETUP_RETR is 0x13
NRF_CE = 0;
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_CONFIG, 0x0F);
NRF_CE = 1;
} uint8_t NRF24L01_check(void)
{
uint8_t i;
uint8_t *ptr = (uint8_t *)NRF24_TEST_ADDR;
NRF24L01_writeFromBuf(NRF24_CMD_W_REGISTER | NRF24_REG_TX_ADDR, ptr, NRF24_ADDR_WIDTH);
NRF24L01_readToBuf(NRF24_CMD_R_REGISTER | NRF24_REG_TX_ADDR, xbuf, NRF24_ADDR_WIDTH);
for (i = 0; i < NRF24_ADDR_WIDTH; i++) {
if (xbuf[i] != *ptr++) return 1;
}
return 0;
} void NRF24L01_init(NRF24_MODE mode)
{
NRF_CE = 0;
NRF24L01_writeFromBuf(NRF24_CMD_W_REGISTER + NRF24_REG_TX_ADDR, (uint8_t *)TX_ADDRESS, NRF24_ADDR_WIDTH);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_RX_PW_P0, NRF24_PLOAD_WIDTH);
NRF24L01_writeFromBuf(NRF24_CMD_W_REGISTER + NRF24_REG_RX_ADDR_P0, (uint8_t *)TX_ADDRESS, NRF24_ADDR_WIDTH);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_RX_PW_P1, NRF24_PLOAD_WIDTH);
NRF24L01_writeFromBuf(NRF24_CMD_W_REGISTER + NRF24_REG_RX_ADDR_P1, (uint8_t *)RX_ADDRESS, NRF24_ADDR_WIDTH);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_EN_AA, 0x3f);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_EN_RXADDR, 0x3f);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_SETUP_RETR, 0x13);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_RF_CH, 40);
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_RF_SETUP, 0x07);
switch (mode)
{
case NRF24_MODE_TX:
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_CONFIG, 0x0E);
break;
case NRF24_MODE_RX:
default:
NRF24L01_writeReg(NRF24_CMD_W_REGISTER + NRF24_REG_CONFIG, 0x0F);
break;
}
NRF_CE = 1;
} void EXTI0_irqHandler(void) __interrupt (IE0_VECTOR)
{
NRF24L01_handelIrqFlag(xbuf);
} void EXTI_init(void)
{
EXTI_configTypeDef ec;
ec.mode = EXTI_mode_lowLevel;
ec.priority = IntPriority_High;
EXTI_config(PERIPH_EXTI_0, &ec);
EXTI_cmd(PERIPH_EXTI_0, ENABLE);
UTIL_setInterrupts(ENABLE);
} void SPI_init(void)
{
SPI_configTypeDef sc;
sc.baudRatePrescaler = SPI_BaudRatePrescaler_4;
sc.cpha = SPI_CPHA_1Edge;
sc.cpol = SPI_CPOL_low;
sc.firstBit = SPI_FirstBit_MSB;
sc.pinmap = SPI_pinmap_P1;
sc.nss = SPI_NSS_Soft;
sc.mode = SPI_Mode_Master;
SPI_config(&sc);
SPI_cmd(ENABLE);
} void main(void)
{
uint8_t sta;
uint8_t tmp[] = {
0x1F, 0x80, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x21, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x28,
0x31, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x38,
0x41, 0x12, 0x13, 0x14, 0x15, 0x16, 0x37, 0x48}; //UTIL_enablePrintf();
SPI_init(); while (NRF24L01_check())
{
//printf_tiny("Check failed\r\n");
sleep(500);
}
//printf_tiny("Check succeeded\r\n"); switch (CURRENT_SCEN)
{
case NRF24_SCEN_TX:
NRF24L01_init(NRF24_MODE_TX);
while (true)
{
// sta = NRF24L01_blockingTx(tmp);
// tmp[1] = sta;
sleep(500);
}
break; case NRF24_SCEN_RX:
NRF24L01_init(NRF24_MODE_RX);
EXTI_init();
while(true);
break; case NRF24_SCEN_HALF_DUPLEX:
NRF24L01_init(NRF24_MODE_RX);
EXTI_init();
while (true)
{
NRF24L01_tx(tmp);
sleep(500);
}
break; default:
//printf_tiny("Unknown scen\r\n");
break;
}
}

在STC12C5A60S2上的测试结果

在STC12C5A60S2上测试并未达到nRF24L01的最大速度, 在发送端间隔小于10ms后, 错误率明显增大, 特别是打开串口输出对错误的影响很大, 猜测是MCU本身的性能约束.需要用STM32再做测试.

在STM32F401CCU6上测试

Update 2021-09-17: 今天在两块STM32F401CCU6的最小板上测试了, 使用的是STM32Cube F4 的 HAL 库, 发送是fastWrite, 关闭中断, 接收是基于中断的接收.

  • 接收和发送都是25MHz的外部振荡源
  • 以下情况, 发送端都是循环无间隔的发送
  • 接收端使用printf输出
    • 如果每次都需要输出32个字节的内容, 发送端的错误率占比接近55%
    • 如果每次只输出一个.号, 发送端错误率接近0
  • 接收端仅开启LED闪灯, 发送端错误率接近0

Update 2021-11-13: 使用STM32F401CCU6 fastwrite发送, STC8A8K64S4A12使用中断接收

  • 如果接收端通过UART输出完整内容, 发送端在发送间隔小于10ms后, 总是有10-15/256比例的错误
  • 如果接stc8收端仅仅输出tx_ds, max_rt, rx_dr, pipe_num这4个标志位的内容, 发送端即使无间隔, 错误率也几乎为0
  • 如果改成STM32F401CCU6接收, STC8A8K64S4A12发送, 在无间隔发送时错误率约为1/16

Update 2021-12-05: 使用W806 fastwrite发送, 使用W806中断接收

  • 如果接收端通过UART输出完整内容, 10ms及以上间隔错误率为0, 在发送间隔小于10ms后, 错误率迅速上升, 在间隔为0时错误率为50%
  • 如果接收端仅开启LED闪灯, 发送端无间隔发送时错误率几乎为0

Update 2022-01-05: 使用 STC8H1K08 fastwrite发送, STC8H3K32S2 使用中断接收

  • 以下都是不带内容输出的测试结果
  • 发送间隔1ms时发送速率最高, 能达到720~748个package每秒, 每个package是32字节, 差不多23K字节每秒
  • 发送间隔为0ms时速度下降明显, 只有1ms间隔时的80%

总结

  • 基于TX FIFO进行发送的优化方式是有效的
  • 对MCU的性能要求较高, 需要接收端有很高的中断响应处理速度, 接收端的速度对发送成功率影响很大
  • 在速率要求较高的场合, STC12及以下的单片机不太合适
  • STC8A的性能做接收没有问题, 发送还需要调优
  • 收发都使用STC8H时, 理论上可以用于传输单声道16K采样的音频, 或者双声道8K采样的音频

参考

nRF24L01基于FIFO TX队列的发送性能优化的更多相关文章

  1. Tair LDB基于Prefixkey中期范围内查找性能优化项目总结

    "Tair LDB基于Prefixkey该范围内查找性能优化"该项目是仅一个月.这个月主要是熟悉项目..以下从几个方面总结下个人在该项目上所做的工作及自己的个人所得所感. 项目工作 ...

  2. 基于SSD固态硬盘的数据库性能优化

    基于SSD固态硬盘的数据库性能优化 2010-11-08 00:0051cto佚名   关键字:固态硬盘 数据库管理 SSD 企业软件热点文章 Java内存结构与模型结构分析 Oracle触发器的语法 ...

  3. Tair LDB基于Prefixkey找到如何提取一系列性能优化项目key的prefix_size

    眼下项目已快截止,编码任务也基本完毕.如今主要是性能測试. 项目是依照"Tair LDB基于Prefixkey的范围查找性能优化项目提议方案"的步骤一步步完毕的,首先先介绍第一个关 ...

  4. 嵌入式流媒体音视频服务器EasyIPCamera中live555发送性能优化点

    EasyIPCamera流媒体服务器 今年EasyDarwin团队在给国内某最大的金融安防公司做技术咨询的时候,开发了一款适用于嵌入式IPCamera.NVR的RTSP流媒体服务器:EasyIPCam ...

  5. 基于AngularJS/Ionic框架开发的性能优化

    AngularJS作为强大的前端MVVM框架,虽然已经做了很多的性能优化,但是我们开发过程中的不当使用还是会对性能产生巨大影响. 下面提出几点优化的方法: 1. 使用单次绑定符号{{::value}} ...

  6. 基于mongoDB的capped collection的性能优化

    MonitorLogging改造(消息接入) 改造前架构: 可以看出原来的流程中,大量业务分析,业务接入耦合在web服务层.大量操作,导致线程线性的挂起线程. 改造后: 将业务通讯抽象成为Monito ...

  7. 深入剖析 iOS 性能优化

    问题种类 时间复杂度 在集合里数据量小的情况下时间复杂度对于性能的影响看起来微乎其微.但如果某个开发的功能是一个公共功能,无法预料调用者传入数据的量时,这个复杂度的优化显得非常重要了.上图列出了各种情 ...

  8. 分布式学习(一)——基于ZooKeeper的队列爬虫

    zookeeper 一直琢磨着分布式的东西怎么搞,公司也没有相关的项目能够参与,所以还是回归自己的专长来吧--基于ZooKeeper的分布式队列爬虫,由于没什么人能够一起沟通分布式的相关知识,下面的小 ...

  9. 转 Kafka、RabbitMQ、RocketMQ等消息中间件的对比 —— 消息发送性能和优势

    Kafka.RabbitMQ.RocketMQ等消息中间件的对比 —— 消息发送性能和优势 引言 分布式系统中,我们广泛运用消息中间件进行系统间的数据交换,便于异步解耦.现在开源的消息中间件有很多,前 ...

随机推荐

  1. LuoguP6553 Strings of Monody 题解

    Content 给定一个长度为 \(n\) 的字符串 \(s\)(仅包含 \(1,4,5\) 三种字符,\(n\) 在本题中无需输入),有 \(m\) 个操作,每次操作给定两个整数 \(l,r\),再 ...

  2. 我的邮箱客户端程序Popmail

    05年的时候写了一个邮箱客户端程序.当时主要目的是研究POP3和SMTP协议,同时锻炼自己的网络编程能力.当然了,如果自己写的邮箱客户端能够满足自身的日常工作需要,而不是频繁的登录不同的网页邮箱,那就 ...

  3. 实时&离线皮肤渲染技术(Real-time&Offline Skin Rendering)

    目录 现实皮肤模型 BSSRDF 渲染模型 [2001] Diffusion Profile(扩散剖面)[2001] 偶极子 [2002] 高斯和 [2007] Burley Normalized D ...

  4. IDEA配置连接(自建Maven仓库)私服并打包上传

    maven的setting.xml文件配置 在servers标签里配置 <server> <id>privete_maven</id> <!--账号密码需要与 ...

  5. 【LeetCode】238. Product of Array Except Self 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 解题方法 两次遍历 日期 题目地址:https://leetcode.c ...

  6. git安装与使用,未完待续... ...

    ​ 目录 一.git概念 二.git简史 三.git的安装 四.git结构 五.代码托管中心-本地库和远程库的交互方式 六.初始化本地仓库 七.git常用命令 1.add和commit命令 2.sta ...

  7. 《Head First设计模式》读书笔记

    前言:本文是记录我在阅读<Head First设计模式>这本书时,做得相关笔记,相关示例代码地址:design-patterns.由于本书不是将设计原则和设计模式分开讲述的,而是在讲一个设 ...

  8. Attention Is All You Need

    目录 概 主要内容 Positional Encoding auto_regressive 额外的细节 代码 Vaswani A., Shazeer N., Parmar N., Uszkoreit ...

  9. CS5265完美替代CH7211|Type-C转HDMI2.0单转方案|CH7211替代方案

    龙迅Chrontel的CH7211是一款Type-C转HDMI2.0半导体设备,可通过USB Type-C连接器将DisplayPort信号转换为HDMI/DVI.这款创新的基于USB Type-C的 ...

  10. 高效位运算 __builtin_系列函数

    •int __builtin_ffs (unsigned int x) 返回x的最后一位1的是从后向前第几位,比如7368(1110011001000)返回4. •int __builtin_clz ...