参考教程:https://www.bilibili.com/video/BV16bN1zZES6/?spm_id_from=333.1387.favlist.content.click

一、NRF24L01无线通信模块工作原理

1、NRF24L01简介

(1)NRF24L01是Nordic Semiconductor公司开发的一款单芯片2.4GHz无线收发器。

(2)可配置工作频率:2.400GHz~2.525GHz(2.400~2.4835GHz为全球ISM频段,该频段内无需授权即可大范围使用)。

(3)可配置数据传输速率:250kbps(仅+版本支持),1Mbps,2Mbps。

(4)可配置3~5字节地址宽度,可配置1~32字节有效载荷宽度。

(5)可配置发射功率:0dBm,-6dBm,-12dBm,-18dBm。

(6)带有1个发送通道、6个接收通道,支持一对多通信。

(7)运用Enhanced ShockBurst™技术,支持自动数据包组装和定时、自动应答、自动重传。

(8)支持其它高级功能,如动态包长、应答附加载荷、动态应答。

2、芯片框图

(1)NRF24L01+的框图如下图所示。

(2)NRF24L01+主要分为三部分:

①Baseband负责基带信号处理,基带信号可简单理解为二进制数据流。

        通过SPI可以实现模块与单片机通信,收到单片机的信号后,通过内部的总线线路,SPI可以将单片机传来的信息传递到各子模块

        通过SPI,单片机可以读写Register map(寄存器表,内含电路参数、发送地址、接收地址等重要数据)

        TX FIFOs(发送队列)和RX FIFOs(接收队列)各有3个数据包空间,也即可以有3个数据包在队列中排队,等待被发送或被接收,FIFO可通过数据总线与SPI交互,进而实现FIFO与单片机的数据交互

        在TX FIFOs中排队的数据会依次送入RF Transmitter模块,由RF Transmitter模块处理后直接发送

        RF Receiver接收到的有效数据会依次送入RX FIFOs,待STM32有读命令请求时,RX FIFOs中的数据会通过SPI发送给单片机

        Enhanced ShockBurst Baseband Engine和Radio Control负责控制整个数据发送、接收等功能的执行

②RF Transmitter负责射频发射,即把基带的二进制数据流调制成适合天线发送的高频信号,这样就能将信号通过天线发射。

        收到TX FIFOs的二进制数据流后,先对其进行GFSK调制,将数据流调制到2.4GHz的工作频率下,再通过发送滤波器处理,再经过PA功率放大器处理,然后将信号输出至天线

③RF Receiver负责射频接收,即从天线接收高频信号,并解调为二进制数据流,将其传入Baseband部分进行处理。

        从天线接收到信号后,先使其经过LNA低噪声放大器处理,再经过接收滤波器处理,再经GFSK解调,即可将天线信号还原为基带信号,将其送入RX FIFOs中

(3)模块引脚定义:

①单片机交互引脚:

[1]CSN是SPI的片选,低电平有效。

[2]SCK、MISO和MOSI是SPI的通信引脚,该外设与单片机使用SPI协议进行通信。

[3]IRQ是中断信号输出,当收到数据、发送成功或发送失败等事件发生时,IRQ会输出低电平跳变,可配合单片机的外部中断来实现中断式收发数据,也可供单片机监测IRQ引脚电平以查询是否有事件发生。

[4]CE引脚负责控制新芯片的工作模式,置为高电平时进入收发模式,置低电平时不工作。

②电源相关引脚即框图下方的五个引脚,从左到右依次为电源负极、电源正极、参考电流、内部数字电源输出和功率放大器的电源输出,根据数据手册中的说明连接电路即可。

③XC1和XC2是连接晶振的引脚,ANT1和ANT2是连接天线的引脚。

3、硬件外围电路设计

(1)NRF24L01+的封装如下图所示。

(2)NRF24L01+数据手册提供的参考电路如下图所示,手册中还提供了元器件选型表和PCB布局参考,绘制PCB时可直接参考数据手册。

4、主要工作流程

(1)发送数据流程:

①单片机通过SPI写入待发送数据及发送地址,芯片会根据单片机指定的发送数据和发送地址进行数据包的组装,再增加一些校验位、前导码(用于发送方和接收方同步,确认数据流边界)等信息,然后存至TX FIFOs。

②单片机控制芯片进入发送模式。

③芯片对TX FIFOs中的二进制数据流进行GFSK调制,然后通过天线发送。

(2)接收数据流程:

①天线收到信号后对其进行GFSK解调,得到二进制数据流。

②拆解数据包,比对其中的接收地址和寄存器表中的接收地址是否一致,如果一致,有效数据内容会存至RX FIFOs,同时置标志位表示有数据接收。

③单片机根据标志位判断是否有数据接收,如有,可通过SPI读取RX FIFOs中的数据。

④增强型ShockBurst支持自动应答,接收数据后,接收方和发送发角色互换,接收方发送一个空数据包进行应答,这个过程由芯片自主完成。

5、数据包格式

(1)数据包的组装由芯片自主完成,一个数据包中包含了数据、地址等信息。

(2)ShockBurst数据包格式(旧版):

①从左到右依次为1字节的前导码,3~5字节的地址,1~32字节的有效载荷数据,1~2字节的CRC校验码。

②字节数浮动的段,需要收发双方事先约定好使用的位数,否则协议冲突,无法正常通信。

(3)Enhanced ShockBurst数据包格式:

①从左到右依次为1字节的前导码,3~5字节的地址,6bit的有效载荷长度(可理解为数据段长度,用于服务动态包长,这样数据段的长度就可以不固定了),2bit的包标识符(用于支持自动应答和自动重发),1bit的不要应答标志位,0~32字节的有效载荷数据,1~2字节的CRC校验码。

②字节数浮动的段,除有效载荷数据段外,需要收发双方事先约定好使用的位数,否则协议冲突,无法正常通信。

③通过配置寄存器,可以使Enhanced ShockBurst数据包格式兼容ShockBurst数据包格式。

6、状态转移图与模式控制

(1)下图所示的是芯片各个工作模式的切换示意图。

(2)各状态释义:

状态

释义

Undefined(未定义状态)

初始化状态

Power Down(掉电模式)

芯片不工作,处于最低功耗状态

Standby-1(待机模式1)

芯片成功起振,准备就绪,比掉电模式功耗高

Standby-2(待机模式2)

芯片欲进入发送模式,但无数据可发,暂时待机

RX Mode(接收模式)

芯片处于接收数据模式

TX Mode(发送模式)

芯片处于发送数据模式

(3)状态转移条件:

状态转移

条件

Undefined(未定义状态)→Power on reset 100ms→Power Down(掉电模式)

VDD电压大于1.9V后,经过100ms的上电复位,即芯片供电电压大于1.9V后,芯片才开始工作

Undefined(未定义状态)→Crystal oscillator start up Tpd2stby→Standby-1(待机模式1)

通过SPI置寄存器标志位PWR_UP为1,芯片经过Tpd2stby这段时间(这段时间用于晶振起振)后,进入待机模式1

Standby-1(待机模式1)→Power Down(掉电模式)

通过SPI置寄存器标志位PWR_UP为0,马上回到掉电模式

Standby-1(待机模式1)→Rx Setting 130us→RX Mode(接收模式)

通过SPI置寄存器标志位PRIM_RX为1,并置CE引脚为高电平,芯片经过130us的RX设置后,进入接收模式

RX Mode(接收模式)→Standby-1(待机模式1)

置CE引脚为低电平,进入待机模式1

Standby-1(待机模式1)→Tx Setting 130us→TX Mode(发送模式)

通过SPI置寄存器标志位PRIM_RX为0,并置CE引脚为高电平持续至少10us,且发送队列非空,则芯片经过130us的RX设置后,进入发送模式

TX Mode(发送模式)→Standby-1(待机模式1)

置CE引脚为低电平,且一个数据包发送完毕(这意味着即使数据发送过程中CE跳0,也会发送一个完整的数据包),进入待机模式1

Standby-1(待机模式1)→Standby-2(待机模式2)

通过SPI置寄存器标志位PRIM_RX为0,并置CE引脚为高电平,且发送队列为空,芯片进入待机模式2

Standby-2(待机模式2)→Tx Setting 130us→TX Mode(发送模式)

置CE引脚为高电平,且发送队列非空,则芯片经过130us的RX设置后,进入发送模式

TX Mode(发送模式)→TX Mode(发送模式)

置CE引脚为高电平,且发送队列非空,芯片不退出发送模式

TX Mode(发送模式)→Standby-2(待机模式2)

置CE引脚为高电平,且发送队列为空,芯片进入待机模式2

……

其余转移线较易理解,此处不再过多赘述,具体可见数据手册……

(4)模式控制方式总结:

        掉电模式:   PWR_UP = 0

        待机模式I: PWR_UP = 1,CE = 0

        接收模式:   PWR_UP = 1,CE = 1,PRIM_RX = 1

        发送模式:   PWR_UP = 1,CE = 1,PRIM_RX = 0

        待机模式II: PWR_UP = 1,CE = 1,PRIM_RX = 0,TX FIFO为空

7、一对多通信

(1)每个设备配置有1个发送通道和6个接收通道,这意味着它可以实现一对多通信。

(2)带有应答的传输过程:

①PTX(主发送)设备发出数据包,PRX(主接收)设备地址匹配的接收通道收到数据包。

②PRX(主接收)设备以相同地址发出应答包,PTX(主发送)设备接收通道0收到应答包。

(3)6个接收通道分别是通道0~通道5,每个通道的地址长度是5个字节,其中只有通道0和通道1的地址可任意配置,通道2~通道5的高4字节与通道1保持一致,所以通道0~通道1的地址寄存器长度为5字节,而通道2~通道5的地址寄存器长度为1字节。

8、自动应答和自动重传

(1)常规的传输和应答过程:

①MCU PTX进行UL,即发送方单片机通过SPI写入要发送的数据。

②PTX设备开始生成发送波形,包的PID为1,其作用为避免自动重传时接收方重复收到数据包;同时,另一个PRX设备开始接收这个波形。

③一个数据包传输完成,PRX设备的IRQ引脚输出低电平跳变,同时产生RX_DR中断,表示有新数据接收完成。

④MCU PRX进行DL,即接收方单片机通过SPI读取接收到的数据。

⑤一个数据包传输完成后等待130us,PRX设备进入发送模式,PTX设备进入接收模式,PRX设备会自动向PTX设备再发送一个应答包,格式与数据包类似,包含有地址信息;同时PTX设备在接收通道0接收这个应答包,收到应答包后产生TX_DS中断,表示数据发送成功。

(2)传输过程中数据包丢包的处理过程:

①PTX设备第一次发送PID=1的数据包,如果该数据包在传输过程中丢失,接收方收不到数据,也就不会回复应答信号。

②PTX设备转入接收模式后,如果收不到应答信号,接收状态会自动结束以保证能耗。

③在ARD时段(自动重传时间可配置)过去后,PTX设备开始重传数据包,PID仍为1,表示是上一个数据包的重传。

④如果发送的数据包持续丢失,PTX设备不会一直重传,可配置最大重传次数,超过此次数后,PTX设备会回到待机状态,并置MAT_RT为1。

(3)传输过程中应答包丢包的处理过程:

①一个数据包传输完成后等待130us,PRX设备进入发送模式,PTX设备进入接收模式,PRX设备会自动向PTX设备发送一个应答包,如果应答包在传输过程中丢失,PTX设备没收到应答信号,会认为发送失败。

②在ARD时段(自动重传时间可配置)过去后,PTX设备开始重传数据包,PID仍为1,表示是上一个数据包的重传;同时PRX设备开始接收数据包,检测到这个数据包PID仍为1,说明是上一个数据包的重复(如果是新的数据包,PTX设备在发送前会置PID自增1),直接丢弃该数据包,但还是要回复应答信号。

9、NRF24L01指令传输和读写操作

(1)SPI协议中四根通信线的作用:SS/CS选中从机,在SCK时钟的同步下,MOSI传输主机至从机的数据流,MISO传输从机至主机的数据流,一次传输会发送同时接收一个字节。

(2)NRF24L01的SPI通信过程:CSN(SS/CS)下降沿通信开始,第一个发送的字节表示指令,根据指令的不同,后续可以跟写操作、读操作或者不执行操作,最后CSN(SS/CS)上升沿通信结束。

①读操作SPI时序:

②写操作SPI时序:

(3)NRF24L01指令集:

①读寄存器指令的英文名为R_REGISTER,其指令码为“0b000x xxxx”(二进制),高3位表示读命令,低5位指定读取的寄存器地址。芯片中有几个寄存器比较特殊,如发送地址寄存器、接收通道0/1地址寄存器各自有5个字节,如果需要读它们,该指令后面可以跟多个SPI交换数据的操作。

②写寄存器指令的英文名为W_REGISTER,其指令码为“0b001x xxxx”(二进制),高3位表示写命令,低5位指定写入的寄存器地址,该指令只能在掉电或待机模式下执行。芯片中有几个寄存器比较特殊,如发送地址寄存器、接收通道0/1地址寄存器各自有5个字节,如果需要写它们,该指令后面可以跟多个SPI交换数据的操作。

③读接收有效载荷指令(即读RX FIFOs)的英文名为R_RX_PAYLOAD,其指令码为“0x61”(十六进制),该指令后可以跟1~32字节的SPI交换数据操作,RX FIFOs中的有效载荷被读取后将直接删除,该指令只能在接收模式下使用。

④写发送有效载荷指令(即写TX FIFOs)的英文名为W_TX_PAYLOAD,其指令码为“0xA0”(十六进制),该指令后可以跟1~32字节的SPI交换数据操作,写入的数据会进入TX FIFOs中,该指令在发送时使用。

⑤FLUSH_TX指令用于清空TX FIFOs中的所有数据,其指令码为“0xE1”(十六进制),这是单独指令,后面不跟任何写/读操作。

⑥FLUSH_RX指令用于清空RX FIFOs中的所有数据,其指令码为“0xE2”(十六进制),这是单独指令,后面不跟任何写/读操作,该指令在传输应答时不应被执行。

⑦REUSE_TX_PL指令用于重新发送上次传输的有效载荷,其指令码为“0xE3”(十六进制),这是单独指令,后面不跟任何写/读操作,该指令用于PTX设备。

⑧NOP指令是空指令,从机不执行任何操作,其指令码为“0xFF”(十六进制),可以使用这个指令读取状态寄存器,主机可以从发空指令交换回的数据得到状态寄存器的内容(实际上,当主机发送任何SPI命令时,SPI都会将从机状态寄存器的值换回给主机)。

(4)NRF24L01寄存器定义表(概览,具体可参考芯片手册):

地址

名称

释义

地址

名称

释义

00

CONFIG

配置寄存器

0D

RX_ADDR_P3

接收通道3地址

01

EN_AA

使能自动应答

0E

RX_ADDR_P4

接收通道4地址

02

EN_RXADDR

使能接收通道

0F

RX_ADDR_P5

接收通道5地址

03

SETUP_AW

设置地址宽度

10

TX_ADDR

发送地址5字节

04

SETUP_RETR

设置自动重传

11

RX_PW_P0

接收通道0宽度

05

RF_CH

射频通道

12

RX_PW_P1

接收通道1宽度

06

RF_SETUP

射频相关参数设置

13

RX_PW_P2

接收通道2宽度

07

STATUS

状态寄存器

14

RX_PW_P3

接收通道3宽度

08

OBSERVE_TX

发送观察寄存器

15

RX_PW_P4

接收通道4宽度

09

RPD

接收功率检测

16

RX_PW_P5

接收通道5宽度

0A

RX_ADDR_P0

接收通道0地址5字节

17

FIFO_STATUS

FIFO状态

0B

RX_ADDR_P1

接收通道1地址5字节

1C

DYNPD

使能动态包长

0C

RX_ADDR_P2

接收通道2地址

1D

FEATURE

使能高级特征

(5)状态寄存器定义:

助记符

重置值

类型

备注

Reserved

7

0b0

R/W

预留

RX_DR

6

0b0

R/W

RX FIFO数据就绪中断,当新数据到达RX FIFO时该位置1

写1表示清除此状态位

TX_DS

5

0b0

R/W

数据已发送TX FIFO中断,当数据包成功发送时该位置1,若激活自动应答,则需收到应答信号才置1

写1表示清除此状态位

MAX_RT

4

0b0

R/W

最大TX重传中断,事件触发时置1

写1表示清除此状态位

如果MAX_RT为1,则必须清除状态位,否则无法通信

RX_P_NO

3:1

0b111

R

RX FIFO读取有效负载的通道号

000-101:通道号

110:不定义

111:RX FIFO为空

TX_FULL

0

0b0

R

1:TX FIFO满

0:TX FIFO有空位

(6)其它常用寄存器定义:

        配置寄存器CONFIG:

助记符

重置值

类型

备注

Reserved

7

0b0

R/W

预留

MASK_RX_DR

6

0b0

R/W

收到数据中断配置

1:不开启此中断

0:事件触发时IRQ输出低电平

MASK_TX_DS

5

0b0

R/W

成功发出数据中断配置

1:不开启此中断

0:事件触发时IRQ输出低电平

MASK_MAX_RT

4

0b0

R/W

达到最大重传次数中断配置

1:不开启此中断

0:事件触发时IRQ输出低电平

EN_CRC

3

0b1

R/W

使能CRC功能,如果开启了自动应答功能,此位必须为1

CRCO

2

0b0

R/W

控制CRC校验使用的字节数

0:1字节

1:2字节

PWM_UP

1

0b0

R/W

1:POWER UP,上电

0:POWER DOWN,掉电

PRIM_RX

0

0b0

R/W

用于RX/TX的控制

1:PRX,主接收模式

0:PTX,主发送模式

        使能自动应答功能寄存器EN_AA:

助记符

重置值

类型

备注

Reserved

7:6

0b00

R/W

预留

ENAA_P5

5

0b1

R/W

1:使能接收通道5的自动应答

0:失能接收通道5的自动应答

ENAA_P4

4

0b1

R/W

1:使能接收通道4的自动应答

0:失能接收通道4的自动应答

ENAA_P3

3

0b1

R/W

1:使能接收通道3的自动应答

0:失能接收通道3的自动应答

ENAA_P2

2

0b1

R/W

1:使能接收通道2的自动应答

0:失能接收通道2的自动应答

ENAA_P1

1

0b1

R/W

1:使能接收通道1的自动应答

0:失能接收通道1的自动应答

ENAA_P0

0

0b1

R/W

1:使能接收通道0的自动应答

0:失能接收通道0的自动应答

        使能接收地址寄存器EN_RXADDR:

助记符

重置值

类型

备注

Reserved

7:6

0b00

R/W

预留

EPX_P5

5

0b0

R/W

1:使能接收通道5

0:失能接收通道5

EPX_P4

4

0b0

R/W

1:使能接收通道4

0:失能接收通道4

EPX_P3

3

0b0

R/W

1:使能接收通道3

0:失能接收通道3

EPX_P2

2

0b0

R/W

1:使能接收通道2

0:失能接收通道2

EPX_P1

1

0b1

R/W

1:使能接收通道1

0:失能接收通道1

EPX_P0

0

0b1

R/W

1:使能接收通道0

0:失能接收通道0

        地址宽度设置寄存器SETUP_AW:

助记符

重置值

类型

备注

Reserved

7:2

0b000000

R/W

预留

AW

1:0

0b11

R/W

00:非法值

01:地址长度为3字节

10:地址长度为4字节

11:地址长度为5字节

        自动重传设置寄存器SETUP_RETR:

助记符

重置值

类型

备注

ARDa

7:4

0b0000

R/W

设置自动重传的延时

0000:延时设置为250us

0001:延时设置为500us

……(等差数列关系,公差为250us)

1111:延时设置为4000us

ARC

3:0

0b0011

R/W

设置重传次数,ARC的值转换为十进制即为最大允许重传次数

        射频通道寄存器RF_CH:

助记符

重置值

类型

备注

Reserved

7

0b0

R/W

预留

RF_CH

6:0

0b0000010

R/W

配置射频的工作频率为(2400+RF_CH)MHz,默认值为2.402GHz

        射频参数配置寄存器RF_SETUP:

助记符

重置值

类型

备注

CONT_WAVE

7

0b0

R/W

可不关注

Reserved

6

0b0

R/W

预留

RF_DR_LOW

5

0b0

R/W

配置数据传输速率,与RF_DR_HIGH共同作用,具体见RF_DR_HIGH备注

PLL_LOCK

4

0b0

R/W

可不关注

RF_DR_HIGH

3

0b1

R/W

配置数据传输速率,与RF_DR_LOW共同作用

[RF_DR_LOW, RF_DR_HIGH]:

00:配置传输速率为1Mbps

01:配置传输速率为2Mbps

10:配置传输速率为250kbps

11:预留

RF_PWR

2:1

0b11

R/W

配置发送功率

00:配置发送功率为-18dBm

01:配置发送功率为-12dBm

10:配置发送功率为-6dBm

11:配置发送功率为0dBm

Obsolete

0

/

/

不必关注

        发送观察寄存器OBSERVE_TX:

助记符

重置值

类型

备注

PLOS_CNT

7:4

0b0000

R

记录在最大重传数之后没有通过的报文数

ARC_CNT

3:0

0b0011

R

记录自动重传成功时重传的数据包个数,也就是重传了多少次才成功

        FIFO_STATUS队列状态寄存器:

助记符

重置值

类型

备注

Reserved

7

0b0

R/W

可不关注

TX_REUSE

6

0b0

R

1:TX FIFO为空

0:TX FIFO非空

TX_FULL

5

0b0

R

1:TX FIFO已满

0:TX FIFO未满

RX_EMPTY

4

0b0

R

可不关注

Reserved

3:2

0b00

R/W

预留

RX_FULL

1

0b0

R

1:RX FIFO已满

0:RX FIFO未满

RX_EMPTY

0

0b1

R

1:RX FIFO为空

0:RX FIFO非空

10、读写操作示例

(1)写寄存器,给SETUP_RETR寄存器写入0x55。

(2)读寄存器,读出RF_CH寄存器的值。

(3)写寄存器,给TX_ADDR寄存器写入0x11, 0x22, 0x33, 0x44, 0x55。

(4)读寄存器,读出TX_ADDR寄存器的值,读5字节。

(5)写发送有效载荷,给TX_PAYLOAD写入0x01, 0x02, 0x03, 0x04。

(6)读接收有效载荷,读出RX_PAYLOAD的值,读4字节。

(7)清空发送FIFO。

(8)快速读状态寄存器。

二、NRF24L01驱动代码实现

1、准备工作

(1)按下图所示接好电路,同样的电路需准备两份,设计两个单片机通过NRF24L01进行通信(说明:本实验程序适用于NRF24L01、NRF24L01+和Si24R1)。

(2)拷贝一份STM32教程中“使用OLED屏进行显示”的工程文件夹,并更名为“NRF24L01驱动代码实现”,然后新建三个文件并添加至工程中,分别命名为NRF24L01.c、NRF24L01.h和NRF24L01_Define.h,将它们存放在Hardware文件夹中。

2、模块编写

(1)NRF24L01.c文件负责实现模块的主要功能。

①宏定义和全局变量部分:

#include "stm32f10x.h"                  // Device header
#include "NRF24L01_Define.h"

/* 发送部分全局变量 */
uint8_t NRF24L01_TxAddress[5] = {0x11, 0x22, 0x33, 0x44, 0x55};	//发送地址,固定5字节
#define NRF24L01_TX_PACKET_WIDTH		4	//发送数据包宽度,范围:1~32字节
uint8_t NRF24L01_TxPacket[NRF24L01_TX_PACKET_WIDTH];		//发送数据包
/* 接收部分全局变量 */
uint8_t NRF24L01_RxAddress[5] = {0x11, 0x22, 0x33, 0x44, 0x55};	//接收通道0地址,固定5字节
#define NRF24L01_RX_PACKET_WIDTH		4	//接收通道0数据包宽度,范围:1~32字节
uint8_t NRF24L01_RxPacket[NRF24L01_RX_PACKET_WIDTH];		//接收数据包

②引脚配置部分(对照电路图和状态转移图编写):

/* 引脚配置部分 */
void NRF24L01_W_CE(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_0,(BitAction)BitValue);
	//置PA0引脚(连接CE)为高/低电平,高or低取决于函数参数值
}

void NRF24L01_W_CSN(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_1,(BitAction)BitValue);
	//置PA1引脚(连接CSN)为高/低电平,高or低取决于函数参数值
}

void NRF24L01_W_SCK(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_2,(BitAction)BitValue);
	//置PA2引脚(连接SCK)为高/低电平,高or低取决于函数参数值
}

void NRF24L01_W_MOSI(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_3,(BitAction)BitValue);
	//置PA3引脚(连接MOSI)为高/低电平,高or低取决于函数参数值
}

uint8_t NRF24L01_R_MISO(void)
{
	return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4);
	//读取PA4引脚(连接MISO)的电平并返回
}

void NRF24L01_GPIO_Init(void)  //初始化NRF24L01模块使用的GPIO口
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);  //CE、CSN、SCK、MOSI引脚初始化为推挽输出模式

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);  //MISO引脚初始化为上拉输入模式
	
	NRF24L01_W_CE(0);  	//CE默认输出低电平(默认退出收发模式)
	NRF24L01_W_CSN(1); 	//CSN默认输出高电平(不选中从机)
	NRF24L01_W_SCK(0); 	//SCK默认输出低电平(对应SPI模式0)
	NRF24L01_W_MOSI(0);	//MOSI默认输出低电平(也可默认输出高电平)
}

③SPI通信实现部分(对照读/写操作时序图编写):

/* SPI通信实现部分 */
uint8_t NRF24L01_SPI_SwapByte(uint8_t Byte)  //一次字节数据交换
{
	//主机把要发送的Byte从最高位开始逐位移到MOSI引脚输出
	//同时将从机在MISO引脚返回的数据从最高位开始逐位从Byte的低位移入
	for(int i = 0; i < 8; i++)
	{
		//从高位移出数据
		if(Byte & 0x80)
		{
			NRF24L01_W_MOSI(1);
		}
		else
		{
			NRF24L01_W_MOSI(0);
		}
		
		Byte <<= 1;   //Byte左移,高位移出,低位补0
		
		NRF24L01_W_SCK(1);  //SCK置高电平
		
		//移入数据至低位
		if(NRF24L01_R_MISO())
		{
			Byte |= 0x01;
		}
		else
		{
			Byte &= ~0x01;
		}
		
		NRF24L01_W_SCK(0);  //SCK置低电平
	}
	
	return Byte;
}

④指令时序实现部分(对照指令定义编写):

/* 指令时序实现部分 */
void NRF24L01_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	//在指定(单字节)寄存器地址RegAddress中写入Data
	NRF24L01_W_CSN(0);            //SPI通信起始
	NRF24L01_SPI_SwapByte(NRF24L01_W_REGISTER | RegAddress);  //发送写指令码,同时发送寄存器地址
	NRF24L01_SPI_SwapByte(Data);  //发送写入寄存器的数据
	NRF24L01_W_CSN(1);            //SPI通信结束
}

uint8_t NRF24L01_ReadReg(uint8_t RegAddress)
{
	//读取指定地址RegAddress(单字节)寄存器中的数据
	uint8_t Data;
	NRF24L01_W_CSN(0);            //SPI通信起始
	NRF24L01_SPI_SwapByte(NRF24L01_R_REGISTER | RegAddress);  //发送读指令码,同时发送寄存器地址
	Data = NRF24L01_SPI_SwapByte(NRF24L01_NOP);  //接收寄存器中的数据,用空指令做交换
	NRF24L01_W_CSN(1);            //SPI通信结束
	
	return Data;
}

void NRF24L01_WriteRegs(uint8_t RegAddress, uint8_t* DataArray, uint8_t Count)
{
	//在指定(多字节)寄存器地址RegAddress中写入Data
	NRF24L01_W_CSN(0);            //SPI通信起始
	NRF24L01_SPI_SwapByte(NRF24L01_W_REGISTER | RegAddress);  //发送写指令码,同时发送寄存器地址
	for(int i  = 0; i < Count; i++)
	{
		NRF24L01_SPI_SwapByte(DataArray[i]);  //发送写入寄存器的1字节数据,寄存器有几个字节就发几次
	}
	NRF24L01_W_CSN(1);            //SPI通信结束
}

void NRF24L01_ReadRegs(uint8_t RegAddress, uint8_t* DataArray, uint8_t Count)
{
	//读取指定地址RegAddress(多字节)寄存器中的数据
	uint8_t Data;
	NRF24L01_W_CSN(0);            //SPI通信起始
	NRF24L01_SPI_SwapByte(NRF24L01_R_REGISTER | RegAddress);  //发送读指令码,同时发送寄存器地址
	for(int i  = 0; i < Count; i++)
	{
		DataArray[i] = NRF24L01_SPI_SwapByte(NRF24L01_NOP);  //接收寄存器中的1字节数据,寄存器有几个字节就收几次
	}
	NRF24L01_W_CSN(1);            //SPI通信结束
}

void NRF24L01_WriteTxPayload(uint8_t* DataArray, uint8_t Count)
{
	//写发送有效载荷至TX FIFO
	NRF24L01_W_CSN(0);            //SPI通信起始
	NRF24L01_SPI_SwapByte(NRF24L01_W_TX_PAYLOAD);  //发送写FIFO指令码
	for(int i  = 0; i < Count; i++)
	{
		NRF24L01_SPI_SwapByte(DataArray[i]);  //发送写入TX FIFO的1字节数据,数据段有几个字节就发几次
	}
	NRF24L01_W_CSN(1);            //SPI通信结束
}

void NRF24L01_ReadRxPayload(uint8_t* DataArray, uint8_t Count)
{
	//从RX FIFO中读取有效载荷
	NRF24L01_W_CSN(0);            //SPI通信起始
	NRF24L01_SPI_SwapByte(NRF24L01_R_RX_PAYLOAD);  //发送读FIFO指令码
	for(int i  = 0; i < Count; i++)
	{
		DataArray[i] = NRF24L01_SPI_SwapByte(NRF24L01_NOP);  //接收RX FIFO中的1字节数据,数据段有几个字节就收几次
	}
	NRF24L01_W_CSN(1);            //SPI通信结束
}

void NRF24L01_FlushTx(void)
{
	//清空TX FIFO
	NRF24L01_W_CSN(0);            //SPI通信起始
	NRF24L01_SPI_SwapByte(NRF24L01_FLUSH_TX);  //发送清空TX FIFO指令码
	NRF24L01_W_CSN(1);            //SPI通信结束
}

void NRF24L01_FlushRx(void)
{
	//清空RX FIFO
	NRF24L01_W_CSN(0);            //SPI通信起始
	NRF24L01_SPI_SwapByte(NRF24L01_FLUSH_RX);  //发送清空RX FIFO指令码
	NRF24L01_W_CSN(1);            //SPI通信结束
}

uint8_t NRF24L01_ReadStatus(void)
{
	//读取状态寄存器
	uint8_t Status;
	NRF24L01_W_CSN(0);            //SPI通信起始
	Status = NRF24L01_SPI_SwapByte(NRF24L01_NOP); //发送空指令,接收状态寄存器的值
	NRF24L01_W_CSN(1);            //SPI通信结束
	
	return Status;
}

⑤功能实现部分(根据状态转移图和寄存器定义表编写):

[1]模式控制函数:

void NRF24L01_PowerDown(void)  //控制NRF24L01进入掉电模式
{
	uint8_t Config;
	/* CE置为0 */
	NRF24L01_W_CE(0);
	
	/* PWR_UP置为0 */
	Config = NRF24L01_ReadReg(NRF24L01_CONFIG); //获取配置寄存器的值
	if (Config == 0xFF) {return;}					//配置寄存器全为1,出错,退出函数
	Config &= ~0x02;
	NRF24L01_WriteReg(NRF24L01_CONFIG, Config); //置配置寄存器第1位为0,其它位保持原值
}

void NRF24L01_StandbyI(void)  //控制NRF24L01进入待机模式1
{
	uint8_t Config;
	
	/* CE置为0 */
	NRF24L01_W_CE(0);

	/* PWR_UP置为1 */
	Config = NRF24L01_ReadReg(NRF24L01_CONFIG); //获取配置寄存器的值
	if (Config == 0xFF) {return;}					//配置寄存器全为1,出错,退出函数
	Config |= 0x02;
	NRF24L01_WriteReg(NRF24L01_CONFIG, Config); //置配置寄存器第1位为1,其它位保持原值
}

void NRF24L01_RxMode(void)  //控制NRF24L01进入接收模式
{
	uint8_t Config;
	/* CE置为0 */
	NRF24L01_W_CE(0);
	
	/* PWR_UP置为1,PRIM_RX置为1 */
	Config = NRF24L01_ReadReg(NRF24L01_CONFIG); //获取配置寄存器的值
	if (Config == 0xFF) {return;}					//配置寄存器全为1,出错,退出函数
	Config |= 0x03;
	NRF24L01_WriteReg(NRF24L01_CONFIG, Config); //置配置寄存器第0位和第1位为1,其它位保持原值
	
	/* CE置为1 */
	NRF24L01_W_CE(1);
}

void NRF24L01_TxMode(void)  //控制NRF24L01进入发送模式
{
	uint8_t Config;
	/* CE置为0 */
	NRF24L01_W_CE(0);
	
	/* PWR_UP置为1,PRIM_RX置为0 */
	Config = NRF24L01_ReadReg(NRF24L01_CONFIG); //获取配置寄存器的值
	if (Config == 0xFF) {return;}					//配置寄存器全为1,出错,退出函数
	Config |= 0x02;
	Config &= ~0x01;
	NRF24L01_WriteReg(NRF24L01_CONFIG, Config); //置配置寄存器第0位为0、第1位为1,其它位保持原值
	
	/* CE置为1 */
	NRF24L01_W_CE(1);
}

[2]模块初始化函数:

void NRF24L01_Init(void)
{
	/* 模块引脚初始化 */
	NRF24L01_GPIO_Init();
	
	/* 寄存器初始化,配置初始参数 */
	NRF24L01_WriteReg(NRF24L01_CONFIG, 0x08);      //CONFIG寄存器初始化
	NRF24L01_WriteReg(NRF24L01_EN_AA, 0x3F);       //EN_AA寄存器初始化
	NRF24L01_WriteReg(NRF24L01_EN_RXADDR, 0x01);   //EN_RXADDR寄存器初始化,只开启接收通道0
	NRF24L01_WriteReg(NRF24L01_SETUP_AW, 0x03);    //SETUP_AW寄存器初始化,配置地址宽度为5
	NRF24L01_WriteReg(NRF24L01_SETUP_RETR, 0x03);  //SETUP_RETR寄存器初始化,配置地址宽度为5
	NRF24L01_WriteReg(NRF24L01_RF_CH, 0x02);   //RF_CH寄存器初始化,配置工作频率
	NRF24L01_WriteReg(NRF24L01_RF_SETUP, 0x0E);    //RF_SETUP寄存器初始化
	
	NRF24L01_WriteRegs(NRF24L01_RX_ADDR_P0, NRF24L01_RxAddress, 5);    //接收通道0地址配置
	
	NRF24L01_WriteReg(NRF24L01_RX_PW_P0, NRF24L01_RX_PACKET_WIDTH);    //NRF24L01_RX_PW_P0寄存器初始化,配置接收通道0接收数据包宽度

	/* 默认进入接收模式 */
	NRF24L01_RxMode();
}

[3]发送函数:

uint8_t NRF24L01_Send(void)
{
	uint8_t Status;
	uint8_t SendFlag;
	uint32_t Timeout;
	
	/* 写入发送通道地址 */
	NRF24L01_WriteRegs(NRF24L01_TX_ADDR, NRF24L01_TxAddress, 5);
	
	/* 写入发送数据 */
	NRF24L01_WriteTxPayload(NRF24L01_TxPacket, NRF24L01_TX_PACKET_WIDTH);
	
	/* 写入接收通道0的地址(开启自动应答后,需与发送通道地址一致,以接收应答包) */
	NRF24L01_WriteRegs(NRF24L01_RX_ADDR_P0, NRF24L01_TxAddress, 5);
	/* 进入发送模式 */
	NRF24L01_TxMode();
	
	/* 指定超时时间,即循环读取状态寄存器的次数 */
	Timeout = 10000;
	
	while(1)
	{
		/* 读取状态寄存器 */
		Status = NRF24L01_ReadStatus();
		
		Timeout --;                 //超时计次
		if(Timeout == 0)
		{
			SendFlag = 4;           //认为发送超时,置标志位为4
			NRF24L01_Init();        //重新初始化设备
			break;                //跳出循环
		}
		
		/* 根据状态寄存器的值,判断发送状态 */
		if((Status & 0x30) == 0x30)    //MAX_RT和TX_DS同时为1
		{
			SendFlag = 3;           //认为状态寄存器的值不合法,置标志位为3
			NRF24L01_Init();        //重新初始化设备
			break;                //跳出循环
		}
		else if((Status & 0x10) == 0x10)  //MAX_RT为1
		{
			SendFlag = 2;           //达到了最大重发次数仍未收到应答,置标志位为2
			NRF24L01_Init();        //重新初始化设备
			break;                //跳出循环
		}
		else if((Status & 0x20) == 0x20)  //TX_DS为1
		{
			SendFlag = 1;            //发送成功,无错误,置标志位为1
			break;                  //跳出循环
		}
	}
	
	/* 给状态寄存器的位4(MAX_RT)和位5(TX_DS)写1,清标志位 */
	NRF24L01_WriteReg(NRF24L01_STATUS, 0x30);

	/* 清空Tx FIFO的所有数据 */
	NRF24L01_FlushTx();

	/* 发送完成后,恢复接收通道0原来的地址 */
	//如果发送地址和接收通道0地址设置相同,则可不执行这一句
	NRF24L01_WriteRegs(NRF24L01_RX_ADDR_P0, NRF24L01_RxAddress, 5);
	
	/* 发送完成,芯片恢复为接收模式 */
	NRF24L01_RxMode();
	
	/* 返回发送标志位 */
	return SendFlag;
}

[4]接收函数:

uint8_t NRF24L01_Receive(void)
{
	uint8_t Status, Config;
	uint8_t ReceiveFlag;
	
	/* 读取状态寄存器 */
	Status = NRF24L01_ReadStatus();
	
	/* 读取配置寄存器 */
	Config = NRF24L01_ReadReg(NRF24L01_CONFIG);
	
	/* 根据配置寄存器和状态寄存器的值,判断接收状态 */
	if ((Config & 0x02) == 0x00)            //PWR_UP为0
	{
		ReceiveFlag = 3;                 //设备仍处于掉电模式,置标志位为3
		NRF24L01_Init();                //重新初始化设备
	}
	else if ((Status & 0x30) == 0x30)        //MAX_RT和TX_DS同时为1
	{
		ReceiveFlag = 2;                 //认为状态寄存器的值不合法,置标志位为2
		NRF24L01_Init();                //重新初始化设备
	}
	else if ((Status & 0x40) == 0x40)   	  //RX_DR为1
	{
		ReceiveFlag = 1;                 //接收到数据,置标志位为1
		
		/* 读接收有效载荷 */
		NRF24L01_ReadRxPayload(NRF24L01_RxPacket, NRF24L01_RX_PACKET_WIDTH);
		
		/* 给状态寄存器的位6(RX_DR)写1,清标志位 */
		NRF24L01_WriteReg(NRF24L01_STATUS, 0x40);

		/* 清空Rx FIFO的所有数据 */
		NRF24L01_FlushRx();
	}
	else
	{
		ReceiveFlag = 0;				//未接收到数据,置标志位为0
	}
	
	/* 返回接收标志位 */
	return ReceiveFlag;
}

(2)NRF24L01.h文件负责声明模块新增的函数和外部可能需要调用的全局变量。

#ifndef __NRF24L01_H
#define __NRF24L01_H

#include "NRF24L01_Define.h"

extern uint8_t NRF24L01_TxAddress[];
extern uint8_t NRF24L01_TxPacket[];
extern uint8_t NRF24L01_RxAddress[];
extern uint8_t NRF24L01_RxPacket[];

uint8_t NRF24L01_ReadReg(uint8_t RegAddress);
void NRF24L01_ReadRegs(uint8_t RegAddress, uint8_t *DataArray, uint8_t Count);
void NRF24L01_WriteReg(uint8_t RegAddress, uint8_t Data);
void NRF24L01_WriteRegs(uint8_t RegAddress, uint8_t *DataArray, uint8_t Count);
void NRF24L01_ReadRxPayload(uint8_t *DataArray, uint8_t Count);
void NRF24L01_WriteTxPayload(uint8_t *DataArray, uint8_t Count);
void NRF24L01_FlushTx(void);
void NRF24L01_FlushRx(void);
uint8_t NRF24L01_ReadStatus(void);
void NRF24L01_PowerDown(void);
void NRF24L01_StandbyI(void);
void NRF24L01_RxMode(void);
void NRF24L01_TxMode(void);
void NRF24L01_Init(void);
uint8_t NRF24L01_Send(void);
uint8_t NRF24L01_Receive(void);

#endif

(3)NRF24L01_Define.h文件负责将指令码和寄存器地址用宏替换。

#ifndef __NRF24L01_DEFINE_H
#define __NRF24L01_DEFINE_H

/*NRF24L01指令宏定义*/
#define NRF24L01_R_REGISTER			0x00		//读寄存器,高3位为指令码,低5位为寄存器地址,后续跟1~5字节读数据
#define NRF24L01_W_REGISTER			0x20		//写寄存器,高3位为指令码,低5位为寄存器地址,后续跟1~5字节写数据
#define NRF24L01_R_RX_PAYLOAD		0x61		//读Rx有效载荷,后续跟1~32字节读数据
#define NRF24L01_W_TX_PAYLOAD		0xA0	//写Tx有效载荷,后续跟1~32字节写数据
#define NRF24L01_FLUSH_TX				0xE1		//清空Tx FIFO所有数据,单独指令
#define NRF24L01_FLUSH_RX			0xE2		//清空Rx FIFO所有数据,单独指令
#define NRF24L01_REUSE_TX_PL			0xE3		//重新使用最后一次发送的有效载荷,单独指令
#define NRF24L01_R_RX_PL_WID			0x60		//读取Rx FIFO最前面一个数据包的宽度,后续跟1字节读数据,仅适用于动态包长模式
#define NRF24L01_W_ACK_PAYLOAD		0xA8	//写应答附带的有效载荷,高5位为指令码,低3位为通道号,后续跟1~32字节写数据,仅适用于应答附带载荷模式
#define NRF24L01_W_TX_PAYLOAD_NOACK	0xB0		//写Tx有效载荷,不要求应答,后续跟1~32字节写数据,仅适用于不要求应答模式
#define NRF24L01_NOP					0xFF		//空操作,单独指令,可以用读取状态寄存器

/*NRF24L01寄存器地址宏定义*/
#define NRF24L01_CONFIG				0x00		//配置寄存器,1字节
#define NRF24L01_EN_AA				0x01		//使能自动应答,1字节
#define NRF24L01_EN_RXADDR			0x02		//使能接收通道,1字节
#define NRF24L01_SETUP_AW			0x03		//设置地址宽度,1字节
#define NRF24L01_SETUP_RETR			0x04		//设置自动重传,1字节
#define NRF24L01_RF_CH				0x05		//射频通道,1字节
#define NRF24L01_RF_SETUP				0x06		//射频相关参数设置,1字节
#define NRF24L01_STATUS				0x07		//状态寄存器,1字节
#define NRF24L01_OBSERVE_TX			0x08		//发送观察寄存器,1字节
#define NRF24L01_RPD					0x09		//接收功率检测,1字节
#define NRF24L01_RX_ADDR_P0			0x0A	//接收通道0地址,5字节
#define NRF24L01_RX_ADDR_P1			0x0B		//接收通道1地址,5字节
#define NRF24L01_RX_ADDR_P2			0x0C		//接收通道2地址,1字节,高位地址与接收通道1相同
#define NRF24L01_RX_ADDR_P3			0x0D	//接收通道3地址,1字节,高位地址与接收通道1相同
#define NRF24L01_RX_ADDR_P4			0x0E		//接收通道4地址,1字节,高位地址与接收通道1相同
#define NRF24L01_RX_ADDR_P5			0x0F		//接收通道5地址,1字节,高位地址与接收通道1相同
#define NRF24L01_TX_ADDR				0x10		//发送地址,5字节
#define NRF24L01_RX_PW_P0			0x11		//接收通道0有效载荷数据宽度,1字节
#define NRF24L01_RX_PW_P1			0x12	//接收通道1有效载荷的数据宽度,1字节
#define NRF24L01_RX_PW_P2			0x13	//接收通道2有效载荷的数据宽度,1字节
#define NRF24L01_RX_PW_P3			0x14	//接收通道3有效载荷的数据宽度,1字节
#define NRF24L01_RX_PW_P4			0x15	//接收通道4有效载荷的数据宽度,1字节
#define NRF24L01_RX_PW_P5			0x16	//接收通道5有效载荷的数据宽度,1字节
#define NRF24L01_FIFO_STATUS		0x17	//发送和接收FIFO状态,1字节
#define NRF24L01_DYNPD				0x1C	//使能接收通道的动态包长模式,1字节
#define NRF24L01_FEATURE			0x1D	//使能高级功能,1字节

#endif

3、main.c文件编写与程序调试

(1)包含相关模块头文件,并定义全局变量。

#include "stm32f10x.h"                  // Device header
#include "Key.h"
#include "OLED.h"
#include "NRF24L01.h"
#include "Delay.h"

uint8_t KeyNum;

uint8_t SendFlag;								//发送标志位
uint8_t SendSuccessCount, SendFailedCount;		//发送成功计次,发送失败计次

uint8_t ReceiveFlag;							//接收标志位
uint8_t ReceiveSuccessCount, ReceiveFailedCount;   //接收成功计次,接收失败计次

(2)主函数中初始化各模块后,编写测试代码。

int main(void)
{
	/* 模块初始化 */
	OLED_Init();		//OLED初始化
	Key_Init();         //按键初始化
	NRF24L01_Init();    //NRF24L01初始化
	
	/* OLED显示静态字符串 */
	OLED_ShowString(1, 1, "T:000-000-0");	//格式为:T:发送成功计次-发送失败计次-发送标志位
	OLED_ShowString(3, 1, "R:000-000-0");	//格式为:R:接收成功计次-接收失败计次-接收标志位
	
	/* 初始化发送数据 */
	NRF24L01_TxPacket[0] = 0x00;
	NRF24L01_TxPacket[1] = 0x01;
	NRF24L01_TxPacket[2] = 0x02;
	NRF24L01_TxPacket[3] = 0x03;
	
	while (1)
	{
		KeyNum = Key_GetNum();		//读取按键,获取键码
		
		if (KeyNum == 1)				//按键按下
		{
			/* 变换测试数据,便于观察实验现象 */
			NRF24L01_TxPacket[0]++;
			NRF24L01_TxPacket[1]++;
			NRF24L01_TxPacket[2]++;
			NRF24L01_TxPacket[3]++;
			
			/* 调用NRF24L01_Send函数,发送数据,同时返回发送标志位 */
			SendFlag = NRF24L01_Send();
			
			if (SendFlag == 1){			//发送标志位为1,表示发送成功
				SendSuccessCount++;	//发送成功计次变量自增
			}
			else{						//发送标志位不为1,即2/3/4,表示发送不成功
				SendFailedCount++;	//发送失败计次变量自增
			}
			
			OLED_ShowNum(1, 3, SendSuccessCount, 3);	//显示发送成功次数
			OLED_ShowNum(1, 7, SendFailedCount, 3);		//显示发送失败次数
			OLED_ShowNum(1, 11, SendFlag, 1);			//显示最近一次的发送标志位
			
			/* OLED显示发送数据 */
			OLED_ShowHexNum(2, 1, NRF24L01_TxPacket[0], 2);
			OLED_ShowHexNum(2, 4, NRF24L01_TxPacket[1], 2);
			OLED_ShowHexNum(2, 7, NRF24L01_TxPacket[2], 2);
			OLED_ShowHexNum(2, 10, NRF24L01_TxPacket[3], 2);
			
			/* TX字符串闪烁一次,表明发送了一次数据 */
			OLED_ShowString(1, 15, "TX");
			Delay_ms(100);
			OLED_ShowString(1, 15, "  ");
		}
		
		/* 调用NRF24L01_Receive函数,接收数据,同时返回接收标志位 */
		ReceiveFlag = NRF24L01_Receive();
		
		if (ReceiveFlag)				//接收标志位不为0,表示收到了一个数据包
		{
			if (ReceiveFlag == 1){		//接收标志位为1,表示接收成功
				ReceiveSuccessCount++;	//接收成功计次变量自增
			}
			else{		//接收标志位不为0也不为1,即2/3,表示此次接收产生了错误
				ReceiveFailedCount++;	//接收失败计次变量自增
			}
			
			OLED_ShowNum(3, 3, ReceiveSuccessCount, 3);	//显示接收成功次数
			OLED_ShowNum(3, 7, ReceiveFailedCount, 3);	//显示接收失败次数
			OLED_ShowNum(3, 11, ReceiveFlag, 1);		//显示最近一次的接收标志位
			
			/* OLED显示接收数据 */
			OLED_ShowHexNum(4, 1, NRF24L01_RxPacket[0], 2);
			OLED_ShowHexNum(4, 4, NRF24L01_RxPacket[1], 2);
			OLED_ShowHexNum(4, 7, NRF24L01_RxPacket[2], 2);
			OLED_ShowHexNum(4, 10, NRF24L01_RxPacket[3], 2);
			
			/* RX字符串闪烁一次,表明接收到了一次数据 */
			OLED_ShowString(3, 15, "RX");
			Delay_ms(100);
			OLED_ShowString(3, 15, "  ");
		}
	}
}

(3)将程序编译,然后分别下载到两个单片机中,按照注释进行调试,测试双方能否互相正常收发数据。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐