一、NRF24L01模块介绍
引脚介绍
NRF24L01所使用的通讯协议为SPI,SPI又可分为软件SPI和硬件SPI。
硬件SPI与软件SPI相比,硬件SPI是靠硬件上面的SPI控制器,所有的时钟边缘采样,时钟发生,还有时序控制,都是由硬件完成的。它降低了CPU的使用率,提高了运行速度。软件SPI就是用代码控制IO输出高低电平,模拟SPI的时序,这种方法通信速度较慢,且不可靠。
想要使用硬件SPI驱动,需要确定使用的引脚是否有SPI外设功能。可以通过用户手册146页进行查看。
当前使用的是硬件SPI接口,而NRF24L01我们需要与它发送数据也需要接收数据,故使用的是4线的SPI,使用到了时钟线SCK、主机输出从机输入线MOSI、主机输入从机输出线MISO和软件控制的片选线NSS。所以除了这些引脚需要使用硬件SPI功能的引脚外,其他引脚都可以使用开发板上其他的GPIO。这里选择使用PB13/PB14/PB15的SPI复用功能 。其他对应接入的引脚请按照你的需要。这里选择的引脚见下表。
模块在开发板上的接线为:
GND ->GND
VCC ->3.3V
CSN ->PA10
CE ->PA9
MOSI->PB15
MISO->PB14
SCK ->PB13
IRQ ->PB8
模块接口原理图
在我们小车主控板中,NRF24L01接口如上,注意不要插反。
二、工程代码
在NRF24L01.H文件中宏定义引脚以及寄存器
//通信引脚#define IRQ_Pin GPIO_PIN_8#define CE_Pin GPIO_PIN_9#define CS_Pin GPIO_PIN_10#define SCK_Pin GPIO_PIN_13#define MISO_Pin GPIO_PIN_14#define MOSI_Pin GPIO_PIN_15//寄存器地址代码#define CONFIG 0x00 // 配置寄存器#define EN_AA 0x01 // 自动应答功能使能寄存器#define EN_RXADDR 0x02 // 接收地址使能寄存器#define SETUP_AW 0x03 // 设置地址宽度寄存器#define SETUP_RETR 0x04 // 设置重发寄存器#define RF_CH 0x05 // 射频通道寄存器#define RF_SETUP 0x06 // 射频设置寄存器#define STATUS 0x07 // 状态寄存器#define OBSERVE_TX 0x08 // 发送观察寄存器#define CD 0x09 // 载波检测寄存器#define RX_ADDR_P0 0x0A // 接收地址数据通道0#define RX_ADDR_P1 0x0B // 接收地址数据通道1#define RX_ADDR_P2 0x0C // 接收地址数据通道2#define RX_ADDR_P3 0x0D // 接收地址数据通道3#define RX_ADDR_P4 0x0E // 接收地址数据通道4#define RX_ADDR_P5 0x0F // 接收地址数据通道5#define TX_ADDR 0x10 // 发送地址寄存器#define RX_PW_P0 0x11 // 接收数据通道0有效数据宽度#define RX_PW_P1 0x12 // 接收数据通道1有效数据宽度#define RX_PW_P2 0x13 // 接收数据通道2有效数据宽度#define RX_PW_P3 0x14 // 接收数据通道3有效数据宽度#define RX_PW_P4 0x15 // 接收数据通道4有效数据宽度#define RX_PW_P5 0x16 // 接收数据通道5有效数据宽度#define FIFO_STATUS 0x17 // FIFO状态寄存器#define DYNPD 0x1C // 动态有效数据长度寄存器#define FEATURE 0x1D // 特性寄存器//操作指令代码#define R_REGISTER 0x00 // 读寄存器命令#define W_REGISTER 0x20 // 写寄存器命令#define R_RX_PAYLOAD 0x61 // 读取接收有效数据命令#define W_TX_PAYLOAD 0xA0 // 写入发送有效数据命令#define FLUSH_TX 0xE1 // 清空发送FIFO命令#define FLUSH_RX 0xE2 // 清空接收FIFO命令#define NOP 0xFF // 无操作命令//状态#define RX_OK 0X40#define TX_OK 0X20#define MAX_OK 0X10
然后在NRF24L01.C文件中先配置SPI,随后配置NRF24L01寄存器
void NRF24L01_GPIO_Init(void){RCC_APBPeriphClk_Enable1(RCC_APB1_PERIPH_SPI2,ENABLE);__RCC_GPIOA_CLK_ENABLE();__RCC_GPIOB_CLK_ENABLE();PB13_AFx_SPI2SCK();//开启引脚复用功能PB14_AFx_SPI2MISO();PB15_AFx_SPI2MOSI();GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.IT=GPIO_IT_NONE;GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pins=MOSI_Pin | SCK_Pin;GPIO_InitStruct.Speed=GPIO_SPEED_HIGH;GPIO_Init(CW_GPIOB, &GPIO_InitStruct);GPIO_InitStruct.IT=GPIO_IT_NONE;GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pins=CS_Pin | CE_Pin;GPIO_InitStruct.Speed=GPIO_SPEED_HIGH;GPIO_Init(CW_GPIOA, &GPIO_InitStruct);GPIO_InitStruct.IT=GPIO_IT_NONE;GPIO_InitStruct.Mode=GPIO_MODE_INPUT_PULLUP;GPIO_InitStruct.Pins=MISO_Pin | IRQ_Pin;GPIO_InitStruct.Speed=GPIO_SPEED_HIGH;GPIO_Init(CW_GPIOB, &GPIO_InitStruct);W_CS(1);SPI_InitTypeDef SPI_InitStructure; // SPI 初始化结构体SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 双线全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 主机模式SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 帧数据长度为8bitSPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // 时钟空闲电平为低SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // 第1个边沿采样SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 片选信号由SSI寄存器控制SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; // 波特率为PCLK的8分频SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 最高有效位 MSB 收发在前SPI_InitStructure.SPI_Speed = SPI_Speed_Low; // 低速SPISPI_Init(CW_SPI2, &SPI_InitStructure); // 初始化SPI_Cmd(CW_SPI2, ENABLE); // 使能SPI1}uint8_t SPI_SwapByte(uint8_t TxByte){uint16_t l_Data = 0;while(SPI_GetFlagStatus(CW_SPI2, SPI_FLAG_TXE) == RESET);SPI_SendData(CW_SPI2, TxByte);while(SPI_GetFlagStatus(CW_SPI2, SPI_FLAG_RXNE) == RESET);l_Data = SPI_ReceiveData(CW_SPI2);//读取接收数据return l_Data; //返回}void W_Reg(uint8_t Reg,uint8_t value)//写字节{W_CS(0);SPI_SwapByte(Reg);SPI_SwapByte(value);W_CS(1);}uint8_t R_Reg(uint8_t Reg)//读字节{uint8_t value;W_CS(0);SPI_SwapByte(Reg);value=SPI_SwapByte(NOP);W_CS(1);return value;}void W_Buf(uint8_t Reg, uint8_t* Buf, uint8_t Len)//连续写{uint8_t i;W_CS(0);SPI_SwapByte(Reg);for(i=0;i<Len;i++){SPI_SwapByte(Buf[i]);}W_CS(1);}void R_Buf(uint8_t Reg, uint8_t* Buf, uint8_t Len)//连续读{uint8_t i;W_CS(0);SPI_SwapByte(Reg);for(i=0;i<Len;i++){Buf[i]=SPI_SwapByte(NOP);}W_CS(1);}void NRF24L01_Init(void)//初始化{NRF24L01_GPIO_Init();W_CE(0);W_Buf(W_REGISTER+TX_ADDR,T_ADDR,5);W_Buf(W_REGISTER+RX_ADDR_P0,R_ADDR,5);W_Reg(W_REGISTER+CONFIG,0x0F);W_Reg(W_REGISTER+EN_AA,0x01);W_Reg(W_REGISTER+RF_CH,0x00);W_Reg(W_REGISTER+RX_PW_P0,32);W_Reg(W_REGISTER+EN_RXADDR,0X01);W_Reg(W_REGISTER+SETUP_RETR,0X1A);W_Reg(FLUSH_RX,NOP);W_CE(1);}void Receive(uint8_t *Buf)//数据接收{uint8_t Status;Status=R_Reg(R_REGISTER+STATUS);if(Status & RX_OK){R_Buf(R_RX_PAYLOAD,Buf,32);W_Reg(FLUSH_RX,NOP);W_Reg(W_REGISTER+STATUS,Status);RX_Flag=1;Delay_us(150);}}uint8_t Send(uint8_t *Buf)//数据发送{uint8_t Status;W_Buf(W_TX_PAYLOAD,Buf,32);W_CE(0);W_Reg(W_REGISTER+CONFIG,0x0E);W_CE(1);while(R_IRQ==1);Status=R_Reg(R_REGISTER+STATUS);if(Status & MAX_OK){W_Reg(FLUSH_TX,NOP);W_Reg(W_REGISTER+STATUS,Status);return MAX_OK;}if(Status & TX_OK){W_Reg(W_REGISTER+STATUS,Status);return TX_OK;}}uint8_t Receive_Flag(void)//接收标志位{if(RX_Flag==1){RX_Flag=0;return 1;}else{return 0;}}
三、实验现象
在发送端main函数中写入如下代码
int main(void){uint8_t Buf[32]={0x00,0x67,0x68,0x69,0x70},num=0;OLED_Init();//初始化NRF24L01_Init();OLED_ShowString(1,1,"Send ok");while(1){num++;Buf[0]=num;Send(Buf);Delay_s(1);}}
在接收端main函数中写入如下代码
int main(void){uint8_t Buf[32]={0};OLED_Init();//初始化NRF24L01_Init();OLED_ShowString(1,1,"Receive ok");while(1){Receive(Buf);if(Receive_Flag()==1){OLED_ShowNum(3,1,Buf[0],3);OLED_ShowHexNum(2,4,Buf[1],2);OLED_ShowHexNum(2,7,Buf[2],2);OLED_ShowHexNum(2,10,Buf[3],2);OLED_ShowHexNum(2,13,Buf[4],2);}}}
然后现象如下
发送端发送四个十六进制数据以及一个递增的十进制数据,OLED屏幕显示 Send ok
接收端第一行显示Receive OK,第二行显示接收到的固定十六进制数据,第三行显示接收到的十进制数据
扫码加入QQ群,3群| 610403240
212