其他分享
首页 > 其他分享> > nRF24L01的发送性能优化

nRF24L01的发送性能优化

作者:互联网

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标志位置位.

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

读取过程

读取时一般使用以下的步骤, 启用中断, 中断时先检查是什么标志位, 然后读取有数据的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上测试并未达到nRF24L01的最大速度, 在发送端间隔小于10ms后, 错误率明显增大, 特别是打开串口输出对错误的影响很大, 猜测是MCU本身的性能约束.需要用STM32再做测试.

参考

标签:TX,NRF24L01,RX,发送,nRF24L01,NRF24,优化,REG,define
来源: https://www.cnblogs.com/milton/p/15259485.html