在学习完STM32一些标准外设后,我开始尝试一些不同的模块使用,NRF24L01算是第一个真正自己根据手册来编写的完整代码的模块,还是挺有纪念意义的。整个项目分为两个板块,SPI通信层和NRF24L01驱动层,我将TX和RX的初始化都统一放在了NRF24L01的驱动层,这样使用起来更方便,需要发送就初始化TX,需要接收就初始化RX即可。
实验现象如下

进入正题,首先说说参考的资料吧,如只需要参考代码,可以直接跳到代码部分
在功能文档中,详细给出了RX,TX的初始化步骤,如下

然后在模块说明书中你可以找到这个模块的引脚分布,我在接线时就参考这张图

除此之外你还需要到中文说明书中找到寄存器地址

以及指令操作

还有工作模式的选择

通过上面这些资料,基本上就能完成NRF24L01模块最简单的通信了,接下来就详细介绍代码部分
首先是SPI通信层代码,因为只需要将两个模块进行通信,对传输速率可以说没有什么要求,所以我选择使用软件模拟SPI,具体代码如下:
引脚的电平操作:
void MySPI_W_CSN(uint8_t BitValue)
{
GPIO_WriteBit(CSN_Port, CSN_Pin, (BitAction)BitValue);
}
void MySPI_W_SCK(uint8_t BitValue)
{
GPIO_WriteBit(SCK_Port, SCK_Pin, (BitAction)BitValue);
}
void MySPI_W_MOSI(uint8_t BitValue)
{
GPIO_WriteBit(MOSI_Port, MOSI_Pin, (BitAction)BitValue);
}
uint8_t MySPI_R_MISO(void)
{
return GPIO_ReadInputDataBit(MISO_Port, MISO_Pin);
}
初始化SPI,其实也就是初始化I/O口:
void MySPI_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//输出引脚配置为推挽输出
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = MOSI_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(MOSI_Port, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = SCK_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SCK_Port, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = CSN_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(CSN_Port, &GPIO_InitStructure);
//输入引脚配置为上拉输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = MISO_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(MISO_Port, &GPIO_InitStructure);
MySPI_W_CSN(1);
MySPI_W_SCK(0);
}
然后就是最重要的部分,SPI的三个基本时序:
void MySPI_Start(void)
{
MySPI_W_CSN(0);
}
void MySPI_Stop(void)
{
MySPI_W_CSN(1);
}
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
uint8_t i, ByteReceive = 0x00;
for (i = 0; i < 8; i ++)
{
MySPI_W_MOSI(ByteSend & (0x80 >> i));
MySPI_W_SCK(1);
if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}
MySPI_W_SCK(0);
}
return ByteReceive;
}
接下来就是NRF24L01的驱动层代码了
收发地址的配置以及NRF24L01引脚电平操作
//收发地址
uint8_t T_ADDR[]={0x01,0x01,0x01,0x01,0x01};
uint8_t R_ADDR[]={0x01,0x01,0x01,0x01,0x01};
//NRF24L01的引脚电平操作
void NRF24L01_CE(uint8_t Value)
{
GPIO_WriteBit(CE_Port,CE_Pin,(BitAction)Value);
}
uint8_t NRF24L01_IRQ(void)
{
return GPIO_ReadInputDataBit(IRQ_Port,IRQ_Pin);
}
NRF24L01的引脚初始化
void NRF24L01_IO_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = CE_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(CE_Port, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = IRQ_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IRQ_Port, &GPIO_InitStructure);
MySPI_Init();
}
四种对NRF24L01寄存器的读写操作
uint8_t NRF24L01_Write_Reg(uint8_t Reg,uint8_t Value)
{
uint8_t Status;
MySPI_Start();
Status = MySPI_SwapByte(Reg);
MySPI_SwapByte(Value);
MySPI_Stop();
return(Status);
}
uint8_t NRF24L01_Read_Reg(uint8_t Reg)
{
uint8_t Reg_Val;
MySPI_Start();
MySPI_SwapByte(Reg);
Reg_Val=MySPI_SwapByte(NOP);
MySPI_Stop();
return(Reg_Val);
}
uint8_t NRF24L01_Read_Buf(uint8_t Reg,uint8_t *Buf,uint8_t Len)
{
uint8_t Status,uint8_t_ctr;
MySPI_Start();
Status=MySPI_SwapByte(Reg);
for(uint8_t_ctr=0;uint8_t_ctr<Len;uint8_t_ctr++)
{
Buf[uint8_t_ctr]=MySPI_SwapByte(NOP);
}
MySPI_Stop();
return Status;
}
uint8_t NRF24L01_Write_Buf(uint8_t Reg, uint8_t *Buf, uint8_t Len)
{
uint8_t Status,uint8_t_ctr;
MySPI_Start();
Status = MySPI_SwapByte(Reg);
for(uint8_t_ctr=0; uint8_t_ctr<Len; uint8_t_ctr++) {
MySPI_SwapByte(Buf[uint8_t_ctr]);
}
MySPI_Stop();
return Status;
}
TX模式初始化
void NRF24L01_TX_Init(void)
{
NRF24L01_CE(0);
//NRF24L01引脚初始化
NRF24L01_IO_Init();
//写TX节点的地址
NRF24L01_Write_Buf(W_REGISTER+TX_ADDR,T_ADDR,5);
//写RX节点的地址
NRF24L01_Write_Buf(W_REGISTER+RX_ADDR_P0,R_ADDR,5);
//使能AUTO ACK(自动应答)
NRF24L01_Write_Reg(W_REGISTER+EN_AA,0x01);
//使能PIPE O(通道0)
NRF24L01_Write_Reg(W_REGISTER+EN_RXADDR,0x01);
//配置自动重发次数
NRF24L01_Write_Reg(W_REGISTER+SETUP_RETR,0x1A);
//选择通信频率
NRF24L01_Write_Reg(W_REGISTER+RF_CH,0x00);//2.4GHz
//配置NRF24L01的基本参数以及切换工作模式
NRF24L01_Write_Reg(W_REGISTER+CONFIG,0x0E);
NRF24L01_CE(1);
}
RX模式初始化
void NRF24L01_RX_Init(void)
{
NRF24L01_CE(0);
NRF24L01_IO_Init();
//写RX节点的地址
NRF24L01_Write_Buf(W_REGISTER+RX_ADDR_P0,R_ADDR,5);
//使能AUTO ACK(自动应答)
NRF24L01_Write_Reg(W_REGISTER+EN_AA,0x01);
//使能PIPE 0(通道0)
NRF24L01_Write_Reg(W_REGISTER+EN_RXADDR,0x01);
//选择通信频率
NRF24L01_Write_Reg(W_REGISTER+RF_CH,0x00);//2.4GHz
//选择通道0有效数据宽度
NRF24L01_Write_Reg(W_REGISTER+RX_PW_P0,32);//32字节
//配置NRF24L01的基本参数以及切换工作模式
NRF24L01_Write_Reg(W_REGISTER+CONFIG,0x0F);
NRF24L01_CE(1);
}
NRF24L01接收数据
uint8_t NRF24L01_ReceiveDate(uint8_t* Buf)
{
uint8_t Flag;
Flag=NRF24L01_Read_Reg(R_REGISTER+STATUS);
NRF24L01_Write_Reg(W_REGISTER+STATUS,Flag);
if(Flag&RX_Flag)
{
NRF24L01_Read_Buf(R_RX_PAYLOAD,Buf,32);
NRF24L01_Write_Reg(FLUSH_RX,NOP);
Delay_us(150);
return 0;
}
return 1;//没收到任何数据
}
NRF24L01发送数据
uint8_t NRF24L01_SendDate(uint8_t* Buf)
{
uint8_t Flag;
NRF24L01_CE(0);
NRF24L01_Write_Reg(W_REGISTER+CONFIG,0x0E);
NRF24L01_Write_Buf(W_TX_PAYLOAD,Buf,32);
NRF24L01_CE(1);
while(NRF24L01_IRQ()==1);
Flag=NRF24L01_Read_Reg(R_REGISTER+STATUS);
NRF24L01_Write_Reg(W_REGISTER+STATUS,Flag);
if(Flag&MAX_TX)
{
NRF24L01_Write_Reg(FLUSH_TX,NOP);
return MAX_TX;
}
if(Flag&TX_Flag)
{
return TX_Flag;
}
return NOP;//其他原因发送失败
}
监测NRF24L01是否存在(来自demo)
uint8_t NRF24L01_Check(void)
{
uint8_t Buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};
uint8_t i;
NRF24L01_Write_Buf(W_REGISTER+TX_ADDR,Buf,5);//写入5个字节的地址
NRF24L01_Read_Buf(TX_ADDR,Buf,5); //读出写入的地址
for(i=0;i<5;i++)
{
if(Buf[i]!=0XA5)
break;
}
if(i!=5)
return 1;//检测24L01错误
return 0; //检测到24L01
}
到这里,整个NRF24L01的功能就编写完成了,如果你也想像我一样进行简单验证的话,这里也有发送端和接收端的主函数代码
发送端
int main(void)
{
uint8_t Date[32]={0x1A,0x2B,0x3C,0x4D,0x5E,0x6F};
OLED_Init();
NRF24L01_TX_Init();
while (1)
{
for(uint8_t i=0;i<100;i++)
{
Date[i]++;
NRF24L01_SendDate(Date);
}
}
}
接收端
int main(void)
{
uint8_t Date[32]={0};
OLED_Init();
NRF24L01_RX_Init();
while (1)
{
if(NRF24L01_IRQ()==0)
{
NRF24L01_ReceiveDate(Date);
}
OLED_ShowHexNum(2,1,Date[0],2);
OLED_ShowHexNum(2,4,Date[1],2);
OLED_ShowHexNum(3,1,Date[2],2);
OLED_ShowHexNum(3,4,Date[3],2);
OLED_ShowHexNum(4,1,Date[4],2);
OLED_ShowHexNum(4,4,Date[5],2);
}
}
到这里,这篇文章就结束了,感谢各位的观看,如果觉得文章对你有帮助,可以点赞,如果有什么问题,大家也可以留言一起探讨,但请营造一个和谐友好的交流环境。当然,如果发现了什么问题也欢迎各位指正,多谢!
2万+





