STM32控制NRF24L01无线模块进行通信

一.NRF2401无线模块

1.模块介绍

功能介绍

(1)2.4Ghz 全球开放ISM 频段免许可证使
2) 最高工作速率2Mbps,高效GFSK调制,抗干扰能力强,特别适合工业控制场合
(3)126 频道,满足多点通信和跳频通信需要
(4) 内置硬件CRC 检错和点对多点通信地址控制
(5) 低功耗1.9-3.6V 工作,待机模式下状态为22uA;掉电模
式下为900nA
(6) 内置2.4Ghz 天线,体积小巧 15mmX29mm
(7) 模块可软件设地址,只有收到本机地址时才会输出数据(提
供中断指示),可直接接各种单片机使用,软件编程非常方便
(8) 内置专门稳压电路,使用各种电源包括DC/DC 开关电源均有
很好的通信效果
(9)2.54MM间距接口, DIP封装
(10)工作于EnhancedShockBurst 具有Automaticpacket
handling,Auto packettransactionhandling,具有可选的内置包
应答机制,极大的降低丢包率。

2.硬件与接口

nRF24L01芯片

STM32控制NRF24L01无线模块进行通信_第1张图片

nRF24L01模块

STM32控制NRF24L01无线模块进行通信_第2张图片

(1) VCC脚接电压范围为1.9V~3.6V之间,不能在这个区间之外,超
过3.6V将会烧毁模块。推荐电压3.3V左右。
(2) 除电源VCC和接地端,其余脚都可以直接和普通的5V单片机IO口直接相连,无需电平转换。当然对3V左右的单片机更加适用了。
(3) 硬件上面没有SPI的单片机也可以控制本模块,用普通单片机IO口模拟SPI也可以

各管脚位置和定义:

8.IRQ 7MISO
6.MOSI 5.SCK
4.CSN 3.CE
2.VCC 1.GND

具体说明:

1.vcc:接1.9V~3.6V间的电压
2.GND:接地
3.CE:芯片的模式控制线。在 CSN 为低的情况下,CE 协同NRF24L01 的CONFIG 寄存器共同决定NRF24L01 的状态                
4.CSN:为芯片的片选线  CSN 为低电平芯片工作
5.SCK:为芯片控制的时钟线(SPI时钟)
6.MOSI:为芯片控制数据线,主设备数据输出  从设备数据输入
7.MISO:芯片控制数据线,主设备数据输入  从设备数据输出
8.IRQ:中断信号引脚。中断时变为低电平,即NRF24L01内部发生中断时IRQ引脚从高电平变为低电平。引脚会在以下三种情况变低:(1)Tx FIFO 发完并且收到ACK(使能ACK情况下);(2)Rx FIFO 收到数据;(3)达到最大重发次数。

3.工作模式

工作模式设定

模式 PWR_UP PRIM_RX CE FIFO寄存器状态
接收模式 1 1 1 -
发送模式 1 0 1 数据在TX FIFO寄存器中
发送模式 1 0 1->0 停留在发送模式,直到数据发送完
待机模式2 1 0 1 TX FIFO为空
待机模式1 1 - 0 无数据传输
掉电模式 0 - - -

PWR_UP: 上电
PRIM_RX: 掉电
CE: 芯片使能
PWR_UP和PRIM_RX 在配置寄存器(CONFIG)中设置位0和位1:

地址 参数 复位值 类型 描述
00 寄存器 配置寄存器
- 保留(未用) 7 0 R/W 默认位“0”
- MASK_RX_DR 6 0 R/W 可屏蔽中断RX_RD;1:IRQ引脚不显示RX_RD 中断;0:RX_RD 中断产生时IRQ 引脚电平为低
- MASK_TX_DS 5 0 R/W 可屏蔽中断TX_DS;1:IRQ引脚不显示TX_DS 中断;0:TX_DS中断产生时IRQ 引脚电平为低
- MASK_MAX_RT 4 0 R/W 可屏蔽中断MAX_RT;1:IRQ 引脚不显示TX_DS 中断;0:MAX_RT 中断产生时IRQ 引脚电平为低
- EN_CRC 3 0 R/W CRC使能;如果EN_AA中任意一位为高EN_CRC 强迫为高
- CRCO 2 0 R/W CRC模式;‘0’-8 位CRC 校验‘1’-16 位CRC 校验
- PWR_UP 1 0 R/W NRF24L01上电掉电模式设置位;1:上电;0:掉电
- PRIM_RX 0 0 R/W NRF24L01接收、发射模式设置位;1:接收模式 0:发射模式

3.1 收发模式

收发模式有Enhanced(增强型) ShockBurstTM收发模式、ShockBurstTM收发模式和直接收发模式三种。

3.1.1 ShockBurstTM 模式

ShockBurstTM收发模式下,使用片内的先入先出堆栈区,数据低速从微控制器送入,但高速(1Mbps)
发射,这样可以尽量节能,因此,使用低速的微控制器也能得到很高的射频数据发射速率。与射频协议相
关的所有高速信号处理都在片内进行,这种做法有三大好处:尽量节能;低的系统费用(低速微处理器也能
进行高速射频发射);数据在空中停留时间短,抗干扰性高。nRF2401 的 ShockBurstTM 技术同时也减小
了整个系统的平均工作电流。
在 ShockBurstTM 收发模式下,nRF2401 自动处理字头和 CRC 校验码。在接收数据时,自动把字头和
CRC 校验码移去。在发送数据时,自动加上字头和 CRC 校验码,当发送过程完成后,数据准备好引脚通
知微处理器数据发射完毕。

ShockBurstTM收发模式可以与Nrf2401a,02,E1及E2兼容

3.1.2 Enhanced(增强型) ShockBurstTM 模式

增强型ShockBurst TM 典型的双链方式为:发送方要求终端设备在接收到数据后有应答信号,以便发送方检测有无数据丢失,一旦丢失则重发数据。重发数据设置在地址为 04 的数据重发设置寄存器 用于设置其重发次数及设置在未收到应答信号后等待重发的时间。

数据重发设置寄存器(SETUP_RETR):

地址 参数 复位值 类型 描述
04 寄存器 数据重发设置寄存器
- ARD [7:4] 0000 R/W 自动重发延时: ‘0000’-等待250+86us; ‘0001’-等待500+86us; ‘0010’-等待750+86us; …… ‘1111’-等待4000+86us; (延时时间是指一包数据发送完成到下一包数据开始发射之间的时间间隔)
- ARC [3:0] 0011 R/W 自动重发计数: ‘0000’-禁止自动重发; ‘0001’-自动重发一次; …… ‘1111’-自动重发15次

nRF24L01 在接收模式下可以接收6 路不同通道的数据。
每一个数据通道使用不同的地址,但是共用相同的频道。

也就是说6 个不同的nRF24L01 设置为发送模式后可以与同一个设置为接收模式的nRF24L01 进行通讯,而设置为接收模式的nRF24L01 可以对这6 个发射端进行识别。

数据通道0 是唯一
的一个可以配置为40 位自身地址的数据通道。1~5 数据通道都为8 位自身地址和32 位公用地址。所有的
数据通道都可以设置为增强型ShockBurst 模式。

NRF24L01在确认收到数据后记录地址,并以此地址为目标地址发送应答信号,在发送端,数据通道0被用作接收应答信号,因此属通道0 的接收地址要与发送地址端地址相等,以确保接收到正确的应答信号。

STM32控制NRF24L01无线模块进行通信_第3张图片

当MCU控制NRF24L01发送数据时,NRF24L01就会启动发送数据,发送完后NRF24L01就会转到接收模式并等待终端的应答信号。

如果没有收到应答信号,NRF24L01就会重发数据包,直到收到应答信号,或达到重发次数寄存器设定的最大值为止,如果重发次数超过了设定值则产生MAX_RT(最大重发次数中断)(应该在该中断没有被屏蔽的情况下的时候才会发生)

只要收到确认信号,nRF24L01 就认为最后一包数据已经发送成功(接收方已经收到数据),把TX FIFO
中的数据清除掉并产生TX_DS中断(数据发送完中断)(IRQ 引脚置高)。

3.1.2.1 Enhanced ShockBurstTM发射流程
1.配置CONFIG寄存器位PRIM_RX 为低,进入发送模式
2.当MCU 有数据要发送时,接收节点地址(TX_ADDR)和有效数据(TX_PLD)通过SPI 接口写入
nRF24L01。发送数据的长度以字节计数从MCU 写入TX FIFO。当CSN 为低时数据被不断的写入。
发送端发送完数据后,将通道0 设置为接收模式来接收应答信号,其接收地址(RX_ADDR_P0)与接
收端地址(TX_ADDR)相同。
例:在上图 中数据通道5 的发送端(TX5)及接收端(RX)地址设置如下:
TX5:TX_ADDR=0xB3B4B5B605
TX5:RX_ADDR_P0=0xB3B4B5B605
RX:RX_ADDR_P5=0xB3B4B5B605
3.把CE置高,最小时间为10us,激发nRF24L01进行Enhanced ShockBurstTM 发射
4.nRF24L01 Enhanced ShockBurstTM 模式:
(1) 无线系统上电
(2) 频数据打包(加字头、 CRC校验码)
(3) 高速发射数据包(由MCU 设定为1Mbps 或2Mbps)
5、 如果启动了自动应答模式(自动重发计数器不等于0,ENAA_P0=1),无线芯片立即进入接收模式。
如果在有效应答时间范围内收到应答信号,则认为数据成功发送到了接收端,此时状态寄存器的
TX_DS 位置高并把数据从TX FIFO 中清除掉。如果在设定时间范围内没有接收到应答信号,则重
新发送数据。如果自动重发计数器(ARC_CNT)溢出(超过了编程设定的值),则状态寄存器的
MAX_RT 位置高。不清除TX FIFO 中的数据。当MAX_RT 或TX_DS 为高电平时IRQ 引脚产生中
断。IRQ 中断通过写状态寄存器来复位(见中断章节)。如果重发次数在达到设定的最大重发次数
时还没有收到应答信号的话,在MAX_RX 中断清除之前不会重发数据包。数据包丢失计数器
(PLOS_CNT)在每次产生MAX_RT 中断后加一。也就是说:重发计数器ARC_CNT 计算重发数据
包次数,PLOS_CNT 计算在达到最大允许重发次数时仍没有发送成功的数据包个数。
6、 如果CE 置低,则系统进入待机模式I。如果不设置CE 为低,则系统会发送TX FIFO 寄存器中下
一包数据。如果TX FIFO 寄存器为空并且CE 为高则系统进入待机模式II.
7、 如果系统在待机模式II,当CE 置低后系统立即进入待机模式I.
相关寄存器:

自动应答寄存器:

地址 参数 复位值 类型 描述
01 EN_AA Enhanced ShockBurstTM - - - 使能”自动应答”功能; 此功能禁止后可与nRF2401通讯
- 保留(未用) [7:6] 00 R/W 默认为00
- ENAA_P5 5 1 R/W 数据通道5自动应答允许
- ENAA_P4 4 1 R/W 数据通道4自动应答允许
- ENAA_P3 3 1 R/W 数据通道3自动应答允许
- ENAA_P2 2 1 R/W 数据通道2自动应答允许
- ENAA_P1 1 1 R/W 数据通道1自动应答允许
- ENAA_P0 0 1 R/W 数据通道0自动应答允许

状态寄存器:

地址 参数 复位值 类型 描述
07 STATUS - - - 状态寄存器
- 保留(未用) 7 0 R/W 默认为0
- RX_DR 6 0 R/W 接收数据中断; 当接收到有效数据后置一; 写”1”清除中断
- TX_DS 5 0 R/W 数据发送完成中断; 当数据发送完成后产生中断; 如果工作在自动应答模式下,只有当接收到应答信号后此位置一; 写”1”清除中断
- MAX_RT 4 0 R/W 达到最多次重发中断; 写”1”清除中断; 如果MAX_RT中断产生则必须清楚后系统才能进行通讯
- RX_P_NO [3:1] 111 R 接收数据通道号: 000-101:数据通道号; 110:未使用; 111:RX FIFO 寄存器为空
- TX_FULL 0 0 R TX FIFO寄存器满标志; 1:TX FIFO寄存器满; 0:TX FIFO寄存器未满,有可用空间

发送检测寄存器:

地址 参数 复位值 类型 描述
08 OBSERVE_TX - - - 发送检测寄存器
- PLOS_CNT [7:4] 0 R 数据包丢失计数器; 当写RF_CH寄存器时此寄存器复位; 当丢失15个数据包后此寄存器重启
- ARC_CNT [3:0] 0 R 重发计数器; 发送新数据包时此寄存器复位
地址 参数 复位值 类型 描述
10 TX_ADDR [39:0] 0xE7E7E7E7E7 R/W 发送地址(先写低字节); 在增强型ShockBurstTM模式下RX_ADDR_P0与此地址相等
N/A TX_PLD [255:0] **- W -**
3.1.2.2 Enhanced ShockBurstTM接收流程
1.配置CONFIG寄存器位PRIM_RX 为高,进入接收模式准备接收数据的通道必须
被使能(EN_RXADDR 寄存器),所有工作在增强型ShockBurstTM 模式下的数据通道的自动应答功
能是由(EN_AA 寄存器)来使能的,有效数据宽度是由RX_PW_Px 寄存器来设置的。地址的建立过
程见增强型ShockBurstTM 发送章节。
2、 接收模式由设置CE 为高来启动。
3、 130us 后nRF24L01 开始检测空中信息。
4、 接收到有效的数据包后(地址匹配、CRC 检验正确),数据存储在RX_FIFO 中,同时RX_DR 位
置高,并产生中断。状态寄存器中RX_P_NO 位显示数据是由哪个通道接收到的。
5、 如果使能自动确认信号,则发送确认信号。
6、 MCU 设置CE 脚为低,进入待机模式I(低功耗模式)。
7、 MCU 将数据以合适的速率通过SPI 口将数据读出。
8、 芯片准备好进入发送模式、接收模式或掉电模式。
相关寄存器:

接收数据使能寄存器:

地址 参数 复位值 类型 描述
02 EN_RXADDR - - - 接收地址允许
- 保留(未用) [7:6] 00 R/W 默认为00
- ERX_P5 5 0 R/W 接收数据通道5允许
- ERX_P4 4 0 R/W 接收数据通道4允许
- ERX_P3 3 0 R/W 接收数据通道3允许
- ERX_P2 2 0 R/W 接收数据通道2允许
- ERX_P1 1 0 R/W 接收数据通道1允许
- ERX_P0 0 0 R/W 接收数据通道0允许

接收数据通道有效数据宽度设置寄存器:

地址 参数 复位值 类型 描述
11 PX_PW_P0 - - - 接收数据通道0有效数据寄存器
- 保留(未用) [7:6] 00 R/W 默认为00
- PX_PW_P0 [5:0] 0 R/W 接收数据通道0有效数据宽度(1到32字节); 0:设置不合法; 1:1字节有效数据宽度; ……; 32:32字节有效数据宽度
12 PX_PW_P1 - - - 接收数据通道1有效数据寄存器
- 保留(未用) [7:6] 00 R/W 默认为00
- PX_PW_P1 [5:0] 0 R/W 接收数据通道1有效数据宽度(1到32字节); 0:设置不合法; 1:1字节有效数据宽度; ……; 32:32字节有效数据宽度
…… …… …… …… …… ……
16 PX_PW_P5 - - - 接收数据通道5有效数据寄存器
- 保留(未用) [7:6] 00 R/W 默认为00
- PX_PW_P5 [5:0] 0 R/W 接收数据通道5有效数据宽度(1到32字节); 0:设置不合法; 1:1字节有效数据宽度; ……; 32:32字节有效数据宽度

3.1.3两种数据双方向的通讯方式:

如果想要数据在双方向上通讯,PRIM_RX 寄存器必须紧随芯片工作模式的变化而变化。

处理器必须保证PTX 和PRX 端的同步性。在RX_FIFO 和TX_FIFO 寄存器中可能同时存有数据。

3.1.4数据包识别和CRC校验应用于增强型ShockBurstTM模式下:

每一包数据都包括两位的PID(数据包识别)来识别接收的数据是新数据包还是重发的数据包。

PID识别可以防止接收端同一数据包多次送入MCU。

在发送方每从MCU取得一包新数据后PID值加一。

PID和CRC校验应用在接收方识别接收的数据是重发的数据包还是新数据包。

如果链接中有一些数据丢失了,则PID值与上一包数据的PID值相同。

接收方对新接收的数据包进行比较:如果一包数据拥有与上一包数据相同的PID值,nRF24L01将对两包数据的CRC值进行比较。如果CRC值也相同的话就认为后面一包是前一包的重发数据包而被舍弃。

3.1.5 数据通道:

nRF24L01 配置为接收模式时可以接收==6== 路不同地址相同频率的数据。每个数据通道拥有自己的地址并且可以通过寄存器来进行分别配置。

数据通道是通过寄存器==EN_RXADDR== 来设置的,默认状态下只有数据通道==0== 和数据通道==1== 是开启状态的。

每一个数据通道的地址是通过寄存器==RX_ADDR_Px== 来配置的。通常情况下不允许不同的数据通道设
置完全相同的地址。
数据通道==0====40== 位可配置地址。数据通道==1-5== 的地址为:==32== 位共用地址+各自的地址(最低字节)。

下图所示的是数据通道==1-5== 的地址设置方法举例。所有数据通道可以设置为多达==40== 位,但是==1-5== 数据通道的最低位必须不同。

**当从一个数据通道接收到数据,并且此数据通道设置为应答方式的话,则nRF24L01 在收到数据后
产生应答信号,此应答信号的目标地址为接收通道地址**

STM32控制NRF24L01无线模块进行通信_第4张图片

3.2待机模式

待机模式1在保证快速启动的同时减少系统平均消耗电流。

在待机模式1下,晶振正常工作。

在待机模式2下部分时钟缓冲器处在工作模式。

当发送端TX FIFO寄存器位空并且CE为高电平时进入待机模式2。

在待机模式期间,寄存器配置字内容保持不变。

3.3掉电模式

在掉电模式下,nRF24L01各功能关闭,保持电流消耗最小。

进入掉电模式后,nRF24L01停止工作,但寄存器内容保持不变。

掉电模式由寄存器中PWR_UP位来控制。

二、SPI介绍

SPI 是英语 Serial Peripheral interface 的缩写串行外围设备接口。

SPI,是一种高速的,全双工,
同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为 PCB 的布局
上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信
协议, STM32 也有 SPI 接口。

==SPI 接口一般使用 4 条线通信:==

MISO 主设备数据输入,从设备数据输出。
MOSI 主设备数据输出,从设备数据输入。
SCLK 时钟信号,由主设备产生。
CS 从设备片选信号,由主设备控制。

SPI内部结构简明图:

STM32控制NRF24L01无线模块进行通信_第5张图片

SPI 总线四种工作方式 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,

时钟极性(CPOL)配置串行同步时钟空闲状态:

如果CPOL=0,串行同步时钟的空闲状态为低电平;

如果CPOL=1,串行同步时钟的空闲状态为高电
平。

时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输:

如果 CPHA=0,
在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;

如果 CPHA=1,在串行同步时钟
的第二个跳变沿(上升或下降)数据被采样。

==SPI 主模块和与之通信的外设备时钟相位和极性应该一致==

不同时钟相位下的总线数据传输时序如图:

STM32控制NRF24L01无线模块进行通信_第6张图片

三、STM32开发板控制NRF24L01

ALIENTKEMiniSTM32 开发板带有一个 2.4G 无线模块(NRF24L01 模块)通信接口,采用
8 脚插针方式与开发板连接,用两块板子,一块接收,一块发送,实现无线传输。

1.宏定义

nRF24L01 SPI指令格式

STM32控制NRF24L01无线模块进行通信_第7张图片
由nRF24L01的SPI指令格式和寄存器地址进行相关操作和寄存器地址的宏定义

相关操作和地址宏定义:

//NRF24L01寄存器操作命令
#define NRF_READ_REG    0x00  //读配置寄存器,低5位为寄存器地址
#define NRF_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 NOP             0xFF  //空操作,可以用来读状态寄存器   
//SPI(NRF24L01)寄存器地址
#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寄存器;bit3:传输速率(0:1Mbps,1:2Mbps);bit2:1,发射功率;bit0:低噪声放大器增益
#define STATUS          0x07  //状态寄存器;bit0:TX FIFO满标志;bit3:1,接收数据通道号(最大:6);bit4,达到最多次重发
                              //bit5:数据发送完成中断;bit6:接收数据中断;
#define MAX_TX          0x10  //达到最大发送次数中断
#define TX_OK           0x20  //TX发送完成中断
#define RX_OK           0x40  //接收到数据中断

#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 NRF_FIFO_STATUS 0x17  //FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RX FIFO满标志;bit2,3,保留
                              //bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,循环发送上一数据包.0,不循环;
//////////////////////////////////////////////////////////////////////////////////////////////////////////
//24L01操作线
#define NRF24L01_CE   PAout(4) //24L01片选信号
#define NRF24L01_CSN  PCout(4) //SPI片选信号       
#define NRF24L01_IRQ  PAin(1)  //IRQ主机数据输入
//24L01发送接收数据宽度定义
#define TX_ADR_WIDTH    5       //5字节的地址宽度
#define RX_ADR_WIDTH    5       //5字节的地址宽度
#define TX_PLOAD_WIDTH  32      //32字节的用户数据宽度
#define RX_PLOAD_WIDTH  32      //32字节的用户数据宽度

2.初始化

NRF24L01的SPI通信时序:
STM32控制NRF24L01无线模块进行通信_第8张图片

从图中可以看出, SCK 空
闲的时候是低电平的,而数据在 SCK 的上升沿被读写。所以,我们需要设置 SPI 的 CPOL 和 CPHA均为 0,来满足 NRF24L01 对 SPI 操作的要求。

NRF24L01使用STM32的SPI1,和W25Q64以及SD卡共用一个SPI接口,所以在使用的时候,他们分时复用SPI1,需要把SD卡和W25Q64的片选信号置高,以防这两个器件对NRF24L01的通信造成干扰。

NRF24L01的SPI接口是标准的SPI接口,其最大的数据传输率为10Mbps。

STM32的SPI与nRF2401初始化代码:

SPI_InitTypeDef  SPI_InitStructure; //全局变量SPI结构体
void NRF24L01_Init(void)
{  
    GPIO_InitTypeDef GPIO_InitStructure; 

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC|RCC_APB2Periph_SPI1, ENABLE );    
                                                                                                    //  使能GPIOA,GPIOC,SPI1时钟

    //PA5.SPI1_SCK; PA6.SPI_MISO; PA7.SPI_MOSI
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);//初始化SPI1的GPIO口

    //PA2.CE W25Q4片选信号; PA3.CE SD卡片选信号; PA4.CE 24L01片选信号
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    //PC4.CSN SPI片选信号
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure); 
    GPIO_SetBits(GPIOC,GPIO_Pin_4);

    //PA1.IRQ IRQ主机数据输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU  ;   //上拉输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);  

    //PA1(中断信号引脚)置高,中断时变为低电平
    //PA2(W25Q4片选信号)与PA3(SD卡片选信号)置高,以防这两个器件对NRF24L01造成干扰
    //PA4(24L01片选信号)置高
    GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4);

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;       //设置SPI工作模式:设置为主SPI
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;       //设置SPI的数据大小:SPI发送接收8位帧结构
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;      //选择了串行时钟的稳态:时钟悬空低电平
    //即(CPOL=0)
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;    //数据捕获于第一个时钟沿
    //即(CPHA=0)
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;       //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;      //定义波特率预分频的值:波特率预分频值为8
    //spi速度为9Mhz(24L01的最大SPI时钟为10Mhz) 
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
    SPI_InitStructure.SPI_CRCPolynomial = 7;    //CRC值计算的多项式
    SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
    SPI_Cmd(SPI1,ENABLE);//使能SPI1

    //CE低电平 待机模式
    NRF24L01_CE=0;  //使能24L01
    //CSN为低电平芯片工作
    NRF24L01_CSN=1; //SPI片选取消       
}

3.功能函数

3.1 SPI1的发送与接收

//通过SPI1发送或者接收一个字节的数据
u8 SPI1_ReadWriteByte(u8 TxData)
{       
    u8 retry=0;                 
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查发送缓存空标志位设置与否
        {
        retry++;
        if(retry>200)return 0;
        }             
    SPI_I2S_SendData(SPI1, TxData); //通过外设SPI1发送一个数据
    retry=0;

    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//检查接受缓存非空标志位设置与否
        {
        retry++;
        if(retry>200)return 0;
        }                               
    return SPI_I2S_ReceiveData(SPI1); //返回通过SPI1最近接收的数据                     
}

3.1 向nRF24L01指定寄存器写入或者读取

//SPI写寄存器
//入口参数: reg:指定寄存器地址; value:写入的值
u8 NRF24L01_Write_Reg(u8 reg,u8 value)
{
    u8 status;  
    NRF24L01_CSN=0;                 //使能SPI传输
    status =SPI1_ReadWriteByte(reg);//发送寄存器号 
    SPI1_ReadWriteByte(value);      //写入寄存器的值
    NRF24L01_CSN=1;                 //禁止SPI传输      
    return(status);                 //返回状态值
}
//读取SPI寄存器值
//入口参数:reg:要读的寄存器
u8 NRF24L01_Read_Reg(u8 reg)
{
    u8 reg_val;     
    NRF24L01_CSN = 0;          //使能SPI传输        
    SPI1_ReadWriteByte(reg);   //发送寄存器号
    reg_val=SPI1_ReadWriteByte(0XFF);//读取寄存器内容
    NRF24L01_CSN = 1;          //禁止SPI传输            
    return(reg_val);           //返回状态值
}   

3.3 初始化nRF24L01的接收(TX)和发送(RX)模式

const u8 TX_ADDRESS[TX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址
const u8 RX_ADDRESS[RX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //接收地址
//该函数初始化NRF24L01到RX模式
//设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR
//当CE变高后,即进入RX模式,并可以接收数据了          
void NRF24L01_RX_Mode(void)
{
    NRF24L01_CE=0;    
  NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);
    //写RX节点地址  
  NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答    
  NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址     
  NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40);           //设置RF通信频率        
  NRF24L01_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
    //即32个字节
  NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);  //设置TX发射参数,0db增益,2Mbps,低噪声增益开启   
  NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG, 0x0f);       //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式 
  //向CONFIG(00)写入0000 1111;PRIM_RX=1 接收模式; PWR_UP=1 上电; CRCO=1 16位CRC校验; EN_CRC =1 CRC使能
    NRF24L01_CE = 1; //CE为高,进入接收模式 
}                        
//该函数初始化NRF24L01到TX模式
//设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,选择RF频道,波特率和LNA HCURR
//PWR_UP,CRC使能
//当CE变高后,即进入RX模式,并可以接收数据了          
//CE为高大于10us,则启动发送.  
void NRF24L01_TX_Mode(void)
{                                                        
    NRF24L01_CE=0;      
  NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);
    //写TX节点地址 
  NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH); 
    //设置TX节点地址,主要为了使能ACK      
    NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答    
  NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址  
  NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次
  NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40);       //设置RF通道为40
  NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);  //设置TX发射参数,0db增益,2Mbps,低噪声增益开启   
  NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x0e);    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断
    //向CONFIG(00)写入0000 1111;PRIM_RX=0 发射模式; PWR_UP=1 上电; CRCO=1 16位CRC校验; EN_CRC   =1 CRC使能
    NRF24L01_CE=1;//CE为高,10us后启动发送
}

3.4 向nRF24L01指定位置写入和读出指定长度数据

//在指定位置读出指定长度的数据
//入口参数:reg:寄存器(位置); *pBuf:数据指针; len:数据长度
//返回值,此次读到的状态寄存器值 
u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len)
{
    u8 status,u8_ctr;          
  NRF24L01_CSN = 0;           //使能SPI传输
  status=SPI1_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值       
    for(u8_ctr=0;u8_ctr0XFF);//读出数据
  NRF24L01_CSN=1;       //关闭SPI传输
  return status;        //返回读到的状态值
}
//在指定位置写指定长度的数据
//入口参数:reg:寄存器(位置); *pBuf:数据指针; len:数据长度
//返回值,此次读到的状态寄存器值
u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len)
{
    u8 status,u8_ctr;       
    NRF24L01_CSN = 0;          //使能SPI传输
  status = SPI1_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值
    for(u8_ctr=0; u8_ctr//写入数据  
  NRF24L01_CSN = 1;       //关闭SPI传输
  return status;          //返回读到的状态值
}                  

3.5 启动NRF24L01接收和发送一次数据

//启动NRF24L01发送一次数据
//txbuf:待发送数据首地址
//返回值:发送完成状况
u8 NRF24L01_TxPacket(u8 *txbuf)
{
    u8 sta;  
    NRF24L01_CE=0;
    NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF  32个字节
    NRF24L01_CE=1;//启动发送       
    while(NRF24L01_IRQ!=0);//等待发送完成
    sta=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值    
    NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志
    if(sta&MAX_TX)//达到最大重发次数
    {
        NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器 
        return MAX_TX; 
    }
    if(sta&TX_OK)//发送完成
    {
        return TX_OK;
    }
    return 0xff;//其他原因发送失败
}
//启动NRF24L01接收一次数据
//txbuf:待发送数据首地址
//返回值:0,接收完成;其他,错误代码
u8 NRF24L01_RxPacket(u8 *rxbuf)
{
    u8 sta;                                           
    sta=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值      
    NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志
    if(sta&RX_OK)//接收到数据
    {
        NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
        NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器 
        return 0; 
    }      
    return 1;//没收到任何数据
}           

3.6 检测nRF24L01是否存在

//检测24L01是否存在
//返回值:0,成功;1,失败 
u8 NRF24L01_Check(void)
{
    u8 buf[5]={0XAA,0XAA,0XAA,0XAA,0XAA};//写入5个0XAA字节
    u8 i;    
    NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,buf,5);//写入5个字节的地址.    
    NRF24L01_Read_Buf(TX_ADDR,buf,5); //读出写入的地址  
    for(i=0;i<5;i++)
        if(buf[i]!=0XAA)
            break;                                 
    if(i!=5)return 1;//检测24L01错误    
    return 0;        //检测到24L01
}        

你可能感兴趣的:(嵌入式)