这个函数充分利用了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); // 开始写入
非阻塞的快速写入(发送), 这里最后一个参数默认值就是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队列或确保发送之间保持足够的时间间隔.
读取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 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;
}
为了避免干扰, 注释掉了串口输出部分, 使用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再做测试.
Update 2021-09-17: 今天在两块STM32F401CCU6的最小板上测试了, 使用的是STM32Cube F4 的 HAL 库, 发送是fastWrite, 关闭中断, 接收是基于中断的接收.
接收和发送都是25MHz的外部振荡源
以下情况, 发送端都是循环无间隔的发送
接收端使用printf输出
.
号, 发送端错误率接近0接收端仅开启LED闪灯, 发送端错误率接近0
如果接收端通过UART输出完整内容, 发送端在发送间隔小于10ms后, 总是有10-15/256比例的错误
如果接stc8收端仅仅输出tx_ds, max_rt, rx_dr, pipe_num这4个标志位的内容, 发送端即使无间隔, 错误率也几乎为0
如果改成STM32F401CCU6接收, STC8A8K64S4A12发送, 在无间隔发送时错误率约为1/16
如果接收端通过UART输出完整内容, 10ms及以上间隔错误率为0, 在发送间隔小于10ms后, 错误率迅速上升, 在间隔为0时错误率为50%
如果接收端仅开启LED闪灯, 发送端无间隔发送时错误率几乎为0
以下都是不带内容输出的测试结果
发送间隔1ms时发送速率最高, 能达到720~748个package每秒, 每个package是32字节, 差不多23K字节每秒
发送间隔为0ms时速度下降明显, 只有1ms间隔时的80%
基于TX FIFO进行发送的优化方式是有效的
对MCU的性能要求较高, 需要接收端有很高的中断响应处理速度, 接收端的速度对发送成功率影响很大
在速率要求较高的场合, STC12及以下的单片机不太合适
STC8A的性能做接收没有问题, 发送还需要调优
收发都使用STC8H时, 理论上可以用于传输单声道16K采样的音频, 或者双声道8K采样的音频
手机扫一扫
移动阅读更方便
你可能感兴趣的文章