之前有写一篇学习SPI通讯协议的文章,这次就通过NRF24L01+这款2.4G无线芯片来实际应用一下SPI协议,主控芯片用的STC15系列。
初始化
首先根据NRF24L01P的芯片手册对其进行初始化
void NRF_RC_Init(void)
{
NRF_WriteRegister(NRF_REG_CONFIG,(RX_IRQ_ENABLE | TX_IRQ_ENABLE | MAX_RT_IRQ_ENABLE | CRC_ON | CRC_1bytes | PWR_DOWN | RX_ON)); //寄存器初始化,使能这些寄存器
NRF_WriteRegister(NRF_REG_EN_AA, ENAA_ALL_DISABLE); //使能自动应答(不使用自动应答)
NRF_WriteRegister(NRF_REG_EN_RXADDR, ERX_P0); //ENABLE P0 RX 使能pipe0作为RX通道
NRF_WriteRegister(NRF_REG_SETUP_RETR, ARC_RE_Transmit_Diable); //设置自动重传,自动重传0
NRF_WriteRegister(NRF_REG_RF_SETUP,RF_DR_2Mbps | RF_PWR_N0dBm); //设置频率和功率,频率为2Mbps,设置射频输出频率为0dBm
NRF_WriteRegister(NRF_REG_RX_PW_P0, NRF_BUFFER_SIZE); //数据管0中RX有效负载的字节数,设为19
NRF_RX_address(RC_Address); //读取地址,地址五个字节,可以自己设置
NRF_TX_address(RC_Address); //接收地址,地址五个字节,可以自己设置
NRF_WriteRegister(NRF_REG_RF_CH, NRF_RC_DEFUAL_CHANNEL); //设置NRF运行频率(通道)
NRF_WriteRegister(NRF_REG_STATUS, CLEAR_RX_IRQ | CLEAR_TX_IRQ | CLEAR_MAX_RT_IRQ); //清除RX、TX、RT状态寄存器
NRF_TX_mode(); //进入TX模式
printf("\nNRF_RC_Init_OK\n");
}
有一些封装好的函数这里贴一下:
// 写寄存器函数
void NRF_WriteRegister(u8 address, u8 command)
{
CS=0;
soft_SPI_RW_MODE00(W_REGISTER | address);
soft_SPI_RW_MODE00(command);
CS=1;
}
//读寄存器函数
uint8_t NRF_ReadRegister(u8 address)
{
u8 read_data;
CS=0;
soft_SPI_RW_MODE00(R_REGISTER | address);
read_data = soft_SPI_RW_MODE00(NOP);
CS=1;
return read_data;
}
//TX地址
void NRF_TX_address(uint8_t address[])
{
uint8_t i;
CS=0;
soft_SPI_RW_MODE00(W_REGISTER + NRF_REG_TX_ADDR); //将地址写入TX_ADDR寄存器中
for (i = 0; i < 5; i++)
{
soft_SPI_RW_MODE00(address[i]);
}
CS=1;
}
//RX地址
void NRF_RX_address(uint8_t address[])
{
uint8_t i;
CS=0;
soft_SPI_RW_MODE00(W_REGISTER + NRF_REG_RX_ADDR_P0);//将地址写入RX_ADDR pipe0中
for (i = 0; i < 5; i++)
{
soft_SPI_RW_MODE00(address[i]);
}
CS=1;
}
在想要对NRF进行寄存器写入操作时,需要先写入一个指令:soft_SPI_RW_MODE00(W_REGISTER | address),再进行写入的操作;读指令同理:soft_SPI_RW_MODE00(R_REGISTER | address)。
在想要进行读操作时,可以写入NOP(0XFF)。
测试主控与NRF的SPI通讯是否正常
这里就不贴代码了,说一下大概思路。把NRF所有被配置过的寄存器的值全部通过NRF_ReadRegister函数读出来,在与写入的值进行比对,如果值相同说明没有问题,如果某个值出错了就输出错误就好了。我这里测出来的通讯是正常的所以直接进行下一步了,让两个NRF芯片通过无线进行通讯。
无线通讯
根据图中的步骤配置好NRF的寄存器。
准备两个NRF芯片,一个作为发送端,另一个作为接收端,从而实现2.4G无线单向通讯。说一下大概思路。发送端在配置好寄存器后,需要进入TX_MODE才能作为发送端使用,接收端需要进入RX_MODE。
如果想要进入TX或RX模式,需要先进入stand by模式,即power_up置1,然后CE管脚先拉低,再去对切换TX、RX模式的寄存器的对应位进行写操作。
//TX模式
void NRF_RX_mode(void)
{
CE=0;
NRF_WriteRegister(NRF_REG_CONFIG, RX_IRQ_ENABLE | TX_IRQ_ENABLE | MAX_RT_IRQ_ENABLE | CRC_ON | CRC_1bytes | PWR_UP | RX_ON);
CE=1;
}
//RX模式
void NRF_TX_mode(void)
{
CE=0;
NRF_WriteRegister(NRF_REG_CONFIG, RX_IRQ_ENABLE | TX_IRQ_ENABLE | MAX_RT_IRQ_ENABLE | CRC_ON | CRC_1bytes | PWR_UP | TX_ON);
CE=1;
}
进入TX_MODE之后就可以发送数据了,需要发送的数据会从TX_FIFO寄存器中发送出去。这里需要一个函数将数据写入FIFO中(可能表述不太准确)。
void NRF_FIFO_WRITE(uint8_t *d,uint8_t len)
{
uint8_t i=0;
CS=0; //片选拉低
soft_SPI_RW_MODE00(W_TX_PAYLOAD);
for(i=0;i<len;i++)
soft_SPI_RW_MODE00(d[i]);
CS=1;
}
在写入需要发送的数据之前,需要先写入一个指令:W_TX_PAYLOAD
完整的程序如下,这里做一个简单的应用,发送端每隔2ms发送一次系统运行时间:
void main(void)
{
Timer0Init();
ES = 1;
ET0 = 1;
EA = 1;
NRF_RC_Init(); //初始化NRF
nrf_check_all_reg_status(); //检测NRF寄存器的值
NRF_TX_mode(); //进入TX模式
if(millis()>=(RC_WRITE_HANDLE_PERIOD+RC_WRITE_HANDLE_TIME))
{
RC_WRITE_HANDLE_TIME=millis();//计时
RED_LED_ON();
rc_txdata_handle(); //数据处理函数
NRF_FIFO_WRITE(nrf_tx_buffer,NRF_BUFFER_SIZE);//发送数据
NRF_CLEAR_TX_IRQ(); //发送完后清掉中断标志位
}
}
有一段数据处理函数如下:
//数据处理函数
void rc_txdata_handle(void)
{
trans_data.time = millis();
nrf_tx_buffer[0]=trans_data.read_byte[0];
nrf_tx_buffer[1]=trans_data.read_byte[1];
nrf_tx_buffer[2]=trans_data.read_byte[2];
nrf_tx_buffer[3]=trans_data.read_byte[3];
printf("trans_data.time=%ld\n",(long)trans_data.time);
// printf("nrf_tx_buffer[0]=%x\n",(int)nrf_tx_buffer[0]);
// printf("nrf_tx_buffer[1]=%x\n",(int)nrf_tx_buffer[1]);
// printf("nrf_tx_buffer[2]=%x\n",(int)nrf_tx_buffer[2]);
// printf("nrf_tx_buffer[3]=%x\n",(int)nrf_tx_buffer[3]);
}
这里用到了之前学过的联合体union来处理数据。
接收端的代码如下:
while(1)
{
NRF_RX_mode();
while (IRQ) //接收标志位,如果在发送或接收过程中为高电平,结束后为低电平
{
static uint16_t wait; //等待接收完成
if(++wait>1000)
{
wait=0;
break;
}
}
if(!IRQ) //接收完成后读取接收到的数据并处理
{
uint8_t status = NRF_ReadRegister(NRF_REG_STATUS);
if (status & CLEAR_RX_IRQ) //若该位为0则为空,为1说明有数据传入
{
RED_LED_ON();
NRF_WriteRegister(NRF_REG_STATUS, CLEAR_RX_IRQ);
NRF_FIFO_READ(nrf_rx_buffer, NRF_BUFFER_SIZE);
rc_rxdata_handle();
NRF_FIFO_FLUSH(FLUSH_RX); //清除RX_FIFO中的数据 准备下一次数据接收。
}
}
}
通过上面的几段代码就可以实现两片NRF芯片的单向通讯了。