一、引脚定义
CE:使能控制,当CE=0,芯片处于待机模式,当CE=1时,芯片处于工作模式(发射模式、接收模式),只有当CE=0时才能切换模式,CE=1不能切换工作模式。(根据程序接A2)。
CSN:SPI片选功能,相当于SPI的CS,在给芯片写入命令时,需要先将CSN置0。(根据代码接A4)。
SCK:SPI通信的时钟线。(根据程序接A5)
MOSI:(Master Out Slave In的简写),主机输出从机输入,对于STM32来说是输出,对于NFR24L01来说是输入。(根据程序接A7)
MISO:主机输入从机输出,对于STM32来说是输入,对于NFR24L01来说是输出,STM32通过这个引脚可以读取接收到的数据。(根据程序接A6)
IRQ:中断引脚,当接收到数据或者应答信号后,IRQ会置0。(根据程序接A2)
二、工作模式
1、掉电模式:无论在哪种工作模式,只要将寄存器的PWR_UP置0,都会切换成掉电模式。
2、待机模式:PWR_UP置1,同时将CE引脚置0,接收模式或者发送模式就会切换成待机模式。
3、发送模式:CE=1且PRIM_RX=0,从待机模式切换成发射模式。
4、接收模式:CE=0且PRIM_RX=1,待机模式切换成接收模式。
三、主要命令
- 每次通讯由两部分内容组成:指令格式+数据
- 下图为指令格式(命令+寄存器地址)
-
四、重要的寄存器
-
4.1 Config寄存器
-
PWR_UP和PRIM_RX用来配置工作模式,这在工作模式中已经讲过
-
4.2 自动应答寄存器
-
在后面的代码部分,由于用的是通道0,所以配置成0x01。
-
4.3 状态寄存器
-
4.4 6个接收通道地址
-
NFR24L01有6个接收通道和一个发送通道。
-
4.5 发送地址
-
必须要跟接收通道地址配置成相同
-
4.6 工作频率寄存器
-
工作频率的设置范围是0~128,计算公式如下:
-
F0的最高频率为:2400+128MHz/1024 = 2.52GHz,跟手册上的范围一致。
-
五、数据帧的发送和接收流
-
5.1 发送地址和接收地址的配置,一定要配置成一样的,地址宽度通常为5个字节,这里为了展示方便,就画了一个字节的地址,代码里用了5个字节的0xF0。
5.2 TX_FIFO里的数据是0x66,这是要发送的数据。
5.3 CONFIG配置的意思(发送数据、接收数据、达到最大重复发送次数都会产生中断,使用16位CRC校验,左边是发送模式,右边是接收模式)
5.4 STATUS配置中的3个1表示RX_FIFO为空。
5.5 EN_AA配置表示开启了通道0的应答。
5.6 发送端:当配置好相应的寄存器后,将TX_FIFO里的数据发送出去,同时置IQR=0,告诉MCU数据已经发送出去,同时置STATUS寄存器中的TX_DS为1,同时,NFR24L01从发射模式变为接收模式,用来接收应答信号,接收到应答信号后又会变成发送模式,这中间模式的切换都要在CE=0时切换。
5.7 接收端:当配置好相应的寄存器后,接收从发送端发送过来的数据,这时,RX_FIFO的数据就会变成0x66,同时,NFR24L01从接收模式切换为发送模式,发送应答信号,接收到数据后也会将IRQ置为0,告诉MCU已经接收到信号,可以将数据搬运走。状态寄存器STATUS中的RX_DS置1。状态寄存器中的标志位需要重新写入1(而不是写0,这是手册中的规定),才能变成0。
-
六、代码
- MySPI.c
-
#include "stm32f10x.h" // Device header /* 引脚定义 */ #define SPI_CLK GPIO_Pin_5 #define SPI_MISO GPIO_Pin_6 //主机输入、从机输出 #define SPI_MOSI GPIO_Pin_7 //void MySPI_W_CS(uint8_t BitValue) //{ // GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue); //} void MySPI_Init() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //这里一定要初始化为推挽输出 // GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //这里一定要初始化为复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = SPI_CLK | SPI_MOSI; //SCK和MOSI要配置成复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = SPI_MISO; GPIO_Init(GPIOA, &GPIO_InitStructure); SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; //选择波特率 SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //第一个变化沿采样数据 SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //默认低电平,Low和1Edge配合起来就是模式0 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC校验位,给默认值 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //发送数据的位数,一般选择8位 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线全双工模式 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位先行 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //SMT32作为主机 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //SS通过软件模拟的方式来实现 SPI_Init(SPI1,&SPI_InitStructure); SPI_Cmd(SPI1,ENABLE); // MySPI_W_CS(1); }
MySPI.h
-
#ifndef __MYSPI_H #define __MYSPI_H void MySPI_Init(void); //void MySPI_Start(void); //void MySPI_Stop(void); uint8_t MySPI_SwapData(uint8_t ByteSend); #endif
NFR24L01.c
-
void NFR24L01_Init() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入模式 GPIO_InitStructure.GPIO_Pin = NFR24L01_IRQ; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出模式 GPIO_InitStructure.GPIO_Pin = NFR24L01_CE | NFR24L01_CS; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_SetBits(GPIOA,NFR24L01_CE | NFR24L01_CS | NFR24L01_IRQ); MySPI_Init(); } void NFR24L01_Write_CS(uint8_t BitValue) //控制从设备是否被选中 { GPIO_WriteBit(GPIOA, NFR24L01_CS, (BitAction)BitValue); } void NFR24L01_Write_CE(uint8_t BitValue) //使能,当写入0则为空闲模式,当写入1则为发射或接收模式 { GPIO_WriteBit(GPIOA, NFR24L01_CE, (BitAction)BitValue); } uint8_t NFR24L01_Read_IQR() //当读到0时说明发送成功或接收成功 { return GPIO_ReadInputDataBit(GPIOA,NFR24L01_IRQ); } uint8_t NFR24L01_Write(uint8_t Command,uint8_t Data) { uint8_t status; NFR24L01_Write_CS(0); //start status = MySPI_SwapData(Command); // 发送命令 MySPI_SwapData(Data); //发送数据 NFR24L01_Write_CS(1); //stop return status; } uint8_t NFR24L01_Read(uint8_t Command) { uint8_t Data; NFR24L01_Write_CS(0); //start MySPI_SwapData(Command); // 发送命令 Data = MySPI_SwapData(0xFF); //交换数据 NFR24L01_Write_CS(1); //stop return Data; } uint8_t NFR24L01_Write_Buffer(uint8_t Command,uint8_t *buf,uint8_t length) { uint8_t status,i; NFR24L01_Write_CS(0); //start status = MySPI_SwapData(Command); //写命令 for(i=0;i<length;i++) { MySPI_SwapData(buf[i]); //写数据 } NFR24L01_Write_CS(1); //stop return status; } uint8_t NFR24L01_Read_Buffer(uint8_t Command,uint8_t *buf,uint8_t length) { uint8_t status,i; NFR24L01_Write_CS(0); //start status = MySPI_SwapData(Command); for(i=0;i<length;i++) { buf[i] = MySPI_SwapData(0xFF); } NFR24L01_Write_CS(1); //stop return status; } void NFR24L01_Set_Mode(uint8_t Mode) { NFR24L01_Write_CE(0); //在配置之前都要将CE置0,否则无法更改配置 NFR24L01_Write_Buffer(SPI_WRITE_REG+TX_ADDR,T_Addr,5); //写TX节点地址 NFR24L01_Write_Buffer(SPI_WRITE_REG+RX_ADDR_P0,R_Addr,5); //写RX通道0的地址 NFR24L01_Write(SPI_WRITE_REG+EN_AA,0x01); //开启通道0的自动应答 NFR24L01_Write(SPI_WRITE_REG+EN_RXADDR,0x01); //开启通道0的接收地址 NFR24L01_Write(SPI_WRITE_REG+SETUP_RETR,0x25); //设置自动重发间隔时间:750us+86us ;最大自动重发次数:5次 NFR24L01_Write(SPI_WRITE_REG+RF_CH,0x00); //配置频率 NFR24L01_Write(SPI_WRITE_REG+RX_PW_P0,32); //配置通道0的接收字节宽度 NFR24L01_Write(SPI_WRITE_REG+CONFIG,Mode); //配置成发送模式 NFR24L01_Write_CE(1); } uint8_t NFR24L01_Send(uint8_t *buf) { uint8_t state; NFR24L01_Write_CE(0); NFR24L01_Write_Buffer(WR_TX_PLOAD,buf,32); NFR24L01_Write_CE(1); //配置成发送模式 while(NFR24L01_Read_IQR() == SET); //等待发送发送完成,发送完成后会产生中断信号,IQR由1变成0 state = NFR24L01_Read(SPI_READ_REG+STATUS); //读取状态寄存器 NFR24L01_Write(SPI_WRITE_REG+STATUS,state); if(state & MAX_TX) //如果达到最多重发次数 { NFR24L01_Write(FLUSH_TX,0xFF); return MAX_TX; } if(state & TX_OK) { return TX_OK; } return 0xFF; //发送失败 } uint8_t NFR24L01_Receive(uint8_t *buf) { uint8_t state; NFR24L01_Write_CE(1); while(NFR24L01_IRQ == SET); NFR24L01_Write_CE(0); //等待接收数据 state = NFR24L01_Read(SPI_READ_REG+STATUS); NFR24L01_Write(SPI_WRITE_REG+STATUS,state); //清楚标记位 if(state & RX_OK) { NFR24L01_Read_Buffer(RD_RX_PLOAD,buf,32); NFR24L01_Write(FLUSH_RX,0xFF); return RX_OK; } return 0; //接收失败 }
NFR24L01.h
-
#ifndef __NFR24L01_H #define __NFR24L01_H #include "stm32f10x.h" // Device header /* 引脚定义 */ #define NFR24L01_CE GPIO_Pin_3 //CE引脚,使能控制 #define NFR24L01_CS GPIO_Pin_4 //片选引脚 #define NFR24L01_IRQ GPIO_Pin_2 //中断引脚 /* NFR24L01寄存器命令 */ #define SPI_READ_REG 0x00 //读配置寄存器,低5位为寄存器地址 #define SPI_WRITE_REG 0x20 //写配置寄存器,低5位为寄存器地址 #define RD_RX_PLOAD 0x61 //读RX有效数据(低字节先出),1~32字节 #define WR_TX_PLOAD 0xA0 //写TX有效数据,1~32字节 #define FLUSH_TX 0xE1 //清除TX FIFO寄存器.发射模式下用 #define FLUSH_RX 0xE2 //清除RX FIFO寄存器.接收模式下用 #define REUSE_TX_PL 0xE3 //重新使用上一包数据,CE为高,数据包被不断发送 #define RD_RX_PL_WID 0x60 //读RX有效数据(高字节先出),1~32字节 #define W_ACK_PLOAD 0xA0 //发送寄存器,写入数据可发送出去 #define W_TX_PLOAD_NACK 0xB0 //发送寄存器,写入数据可发送出去,但不应答 #define RF_NOP 0xFF //空操作,可以用来读状态寄存器 /* 寄存器地址 */ #define CONFIG 0x00 //配置寄存器地址;bit0:1接收模式,0发射模式;bit1:电选择;bit2:CRC模式;bit3:CRC使能; //bit4:中断MAX_RT(达到最大重发次数中断)使能;bit5:中断TX_DS使能;bit6:中断RX_DR使能 #define EN_AA 0x01 //使能自动应答功能 bit0~5,对应通道0~5 #define EN_RXADDR 0x02 //接收地址允许,bit0~5,对应通道0~5 #define SETUP_AW 0x03 //设置地址宽度(所有数据通道):bit1,0:00,3字节;01,4字节;02,5字节; #define SETUP_RETR 0x04 //建立自动重发;bit3:0,自动重发计数器;bit7:4,自动重发延时 250*x+86us #define RF_CH 0x05 //RF通道,bit6:0,工作通道频率; #define RF_SETUP 0x06 //RF寄存器;bit5,bit3:传输速率(00:1Mbps,01:2Mbps,10:250Kbps);bit2:1,发射功率;bit0:低噪声放大器增益 #define STATUS 0x07 //状态寄存器;bit0:TX FIFO满标志;bit3:1,接收数据通道号(最大:6);bit4,达到最多次重发 //bit5:数据发送完成中断;bit6:接收数据中断; #define OBSERVE_TX 0x08 //发送检测寄存器,bit7:4,数据包丢失计数器;bit3:0,重发计数器 #define CD 0x09 //载波检测寄存器,bit0,载波检测; #define RX_ADDR_P0 0x0A //数据通道0接收地址,最大长度5个字节,低字节在前 #define RX_ADDR_P1 0x0B //数据通道1接收地址,最大长度5个字节,低字节在前 #define RX_ADDR_P2 0x0C //数据通道2接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; #define RX_ADDR_P3 0x0D //数据通道3接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; #define RX_ADDR_P4 0x0E //数据通道4接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; #define RX_ADDR_P5 0x0F //数据通道5接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; #define TX_ADDR 0x10 //发送地址(低字节在前),ShockBurstTM模式下,RX_ADDR_P0与此地址相等 #define RX_PW_P0 0x11 //接收数据通道0有效数据宽度(1~32字节),设置为0则非法 #define RX_PW_P1 0x12 //接收数据通道1有效数据宽度(1~32字节),设置为0则非法 #define RX_PW_P2 0x13 //接收数据通道2有效数据宽度(1~32字节),设置为0则非法 #define RX_PW_P3 0x14 //接收数据通道3有效数据宽度(1~32字节),设置为0则非法 #define RX_PW_P4 0x15 //接收数据通道4有效数据宽度(1~32字节),设置为0则非法 #define RX_PW_P5 0x16 //接收数据通道5有效数据宽度(1~32字节),设置为0则非法 #define FIFO_STATUS 0x17 //FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RX FIFO满标志;bit2,3,保留 //bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,循环发送上一数据包.0,不循环; /* 无线收发中断标志 */ #define MAX_TX 0x10 //达到最大发送次数中断 #define TX_OK 0x20 //TX发送完成中断 #define RX_OK 0x40 //接收到数据中断 void NFR24L01_Init(void); uint8_t NFR24L01_Write_Buffer(uint8_t Command,uint8_t *buf,uint8_t length); uint8_t NFR24L01_Read_Buffer(uint8_t Command,uint8_t *buf,uint8_t length); void NFR24L01_Set_Mode(uint8_t Mode); uint8_t NFR24L01_Send(uint8_t *buf); uint8_t NFR24L01_Receive(uint8_t *buf); uint8_t NFR24L01_Write(uint8_t Command,uint8_t Data); uint8_t NFR24L01_Read(uint8_t Command); uint8_t NFR24L01_Read_IQR(void); #endif
发送端main.c
-
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "NFR24L01.h" uint8_t data_buffer[32] = {0xA5,0x66,0x99,0xFF,0x54,0x32}; int main() { OLED_Init(); NFR24L01_Init(); NFR24L01_Set_Mode(0x0E); OLED_ShowString(1,1,"Init OK"); while(1) { NFR24L01_Send(data_buffer); } }
接收端main.c
-
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "NFR24L01.h" uint8_t data[32] = {0}; int main() { OLED_Init(); NFR24L01_Init(); NFR24L01_Set_Mode(0x0F); OLED_ShowString(1,1,"Init OK"); while(1) { if(NFR24L01_Read_IQR() == 0) { NFR24L01_Receive(data); } OLED_ShowHexNum(2,1,data[0],2); OLED_ShowHexNum(2,4,data[1],2); OLED_ShowHexNum(2,7,data[2],2); OLED_ShowHexNum(3,1,data[3],2); OLED_ShowHexNum(3,4,data[4],2); OLED_ShowHexNum(3,7,data[5],2); OLED_ShowHexNum(4,1,data[6],2); OLED_ShowHexNum(4,4,data[7],2); OLED_ShowHexNum(4,7,data[8],2); } }