0、友情提示 
《零死角玩转 STM32》系列教程由初级篇、中级篇、高级篇、系统篇、
四个部分组成,根据野火 STM32 开发板旧版教程升级而来,且经过重新深入编
写,重新排版,更适合初学者,步步为营,从入门到精通,从裸奔到系统,让
您零死角玩转 STM32。M3 的世界,与野火同行,乐意惬无边。 
另外,野火团队历时一年精心打造的《STM32 库开发实战指南》将于今
年 10 月份由机械工业出版社出版,该书的排版更适于纸质书本阅读以及更有利
于查阅资料。内容上会给你带来更多的惊喜。是一本学习 STM32 必备的工具
书。敬请期待! 
                                                 -第 2 页-                                   
                                                                                           
 
9、2.4G 无线(NRF24L01+) 
9.1 实验描述及工程文件清单 
实验描述 
利用 NRF24L01+无线模块,使两块 STM32 开发板实现无线 
传输数据。用串口输出实验结果到 pc。 
硬件连接 
PA4-SPI1-NSS  : W25X16-CS   
PA5-SPI1-SCK  : W25X16-CLK  
PA6-SPI1-MISO : W25X16-DO   
PA7-SPI1-MOSI : W25X16-DIO 
用到的库文件 
startup/start_stm32f10x_hd.c 
CMSIS/core_cm3.c 
CMSIS/system_stm32f10x.c 
FWlib/stm32f10x_gpio.c 
FWlib/stm32f10x_rcc.c 
FWlib/stm32f10x_usart.c 
FWlib/stm32f10x_spi.c 
用户编写的文件  USER/main.c 
USER/stm32f10x_it.c 
USER/usart1.c 
SPI_NRF.c 
野火 STM32 开发板 2.4G 无线模块接口图: 
 
                                                 -第 3 页-                                   
                                                                                           
 
9.2 NRF24L01 模块简介 
本实验采用的无线模块芯片型号为 NRF24L01+,是工作在 2.4~2.5GHz
频段的,具备自动重发功能,6 个数据传输通道,最大无线传输速率为
2Mbits。MCU 可与该芯片通过 SPI 接口访问芯片的寄存器进行配置。 
以下是该模块的硬件电路: 
截图来自《NRF24l01 模块说明书.pdf》,page3 
注意:这个模块的工作电压为 3.3V,实验时请把 vcc 接到板上的 3v3 接
 
口,超过 3.6v 该模块会烧坏! 
引脚说明及本实验中与开发板的连接: 
Pin 
Name 
Description 
与开发板相连 
 Chip Enable Activates RX or TX mode 
排针 P5 的 PA2 
CE 
CSN 
SCK 
 SPI Chip Select  
 SPI Clock 
1 
2 
3  
4 
5  
6 
7  
排针 P3 的 PA1 
排针 P5 的 PA5 
排针 P5 的 PA7 
MOSI 
 SPI Slave Data Input 
MISO 
 SPI Slave Data Output, with tri-state option  排针 P5 的 PA6 
IRQ 
VDD 
 Maskable interrupt pin. Active low 
排针 P5 的 PA3 
 Power Supply (+1.9V - +3.6V DC) 
电源 3v3 
                                                 -第 4 页-                                   
                                                                                           
 
8 
VSS 
 Ground (0V) 
电源 GND 接口 
这个例程采用的是 STM32 的 SPI1 接口,但其中的硬件 SPI1-CSN 端口
(用于片选)已经在 2M-FLASH 上采用,所以本实验用一个空闲端口 PA1 用
作无线模块的片选,由软件产生片选信号。 
请注意区分这个模块的 CSN 片选信号与 CE 使能信号的功能。 
CSN 端口是 SPI 通讯协议中的片选端。多个 SPI 设备可以共用 STM32 的
SCK,MISO,MOSI 端口,不同的设备间就是用 CSN 来区分。 
    CE 实际是 NRF24L01 的芯片使能端,通过配置 CE 可以使 NRF24L01
进入不同的状态。如下图示: 
截图来自:《nRF24L01P(新版无线模块控制 IC).PDF》,page24. 
 
9.3 代码分析 
这个实验用到两个代码,主机和从机的代码驱动是一样的,区别只是 main
中函数调用的流程不一样,从机接收模式的时候,相应的主机在发送模式。 
附从机流程图: 
                                                 -第 5 页-                                   
                                                                                           
 
下面以的从机的代码为例进行分析。 
首先要添加用的库文件,在工程文件夹下 Fwlib 下我们需添加以下库文
 
件: 
1.  stm32f10x_gpio.c    
2.  stm32f10x_rcc.c    
3.  stm32f10x_usart.c    
4.  stm32f10x_spi.c   
还要在 stm32f10x_conf.h 中把相应的头文件添加进来: 
1. 
2. 
3. 
4. 
#include "stm32f10x_gpio.h"       
#include "stm32f10x_spi.h"    
#include "stm32f10x_rcc.h"      
#include "stm32f10x_usart.h"     
                                                 -第 6 页-                                   
                                                                                           
 
进入 main 函数,边看代码边了解程序的流程: 
1.  int main(void)    
2.  {          
3.   /* 串口 1 初始化 */   
4.      USART1_Config();     
5.     
6.  /*SPI 接口初始化*/       
7.     SPI_NRF_Init();     
8.     
9.      printf("\r\n 这是一个 NRF24L01 无线传输实验 \r\n");    
10.     printf("\r\n 这是无线传输 从机端 的反馈信息\r\n");    
11.     printf("\r\n   正在检测 NRF 与 MCU 是否正常连接。。。\r\n");    
12.          
13.      /*检测 NRF 模块与 MCU 的连接*/   
14.     status = NRF_Check();               
15.        if(status == SUCCESS)           
16.              printf("\r\n      NRF 与 MCU 连接成功\r\n");      
17.        else       
18.              printf("\r\n   正在检测 NRF 与 MCU 是否正常连接。。。\r\n");    
19.                   
20. while(1)    
21.     {               
22.     printf("\r\n 从机端 进入接收模式\r\n");     
23.     NRF_RX_Mode();    
24.          
25.     /*等待接收数据*/   
26.     status = NRF_Rx_Dat(rxbuf);    
27.    
28.        /*判断接收状态*/   
29.     if(status == RX_DR)    
30.     {    
31.      for(i=0;i<4;i++)    
32.      {      
33.         printf("\r\n 从机端 接收到 主机端 发送的数据
为:%d \r\n",rxbuf[i]);     
34.         /*把接收的数据+1 后发送给主机*/   
35.         rxbuf[i]+=1;          
36.         txbuf[i] = rxbuf[i];     
37.         }    
38.     }       
39.         printf("\r\n 从机端 进入自应答发送模式\r\n");     
40.         NRF_TX_Mode();    
41.    
42.         /*不断重发,直至发送成功*/       
43.      do   
44.        {          
45.         status = NRF_Tx_Dat(txbuf);    
46.         }while(status == MAX_RT);    
47.     }     
48. }   
 报告野火,这个代码错了,没有调用 SystemInit()函数来设置时钟!是的,
大家熟悉的 SystemInit()函数不见了,但这样并没有出错,原因是这个例程的库
是 3.5 版本的!在 3.5 版本的库中 SystemInit()函数在启动文件
startup_stm32f10x_hd.d 中已用汇编语句调用了,设置的时钟为默认的 72M。
所以在 main 函数就不需要再调用啦,当然,再调用一次也是没问题的。 
                                                 -第 7 页-                                   
                                                                                           
 
关于 USART1_Config()函数,是用来配置串口的,关于这两个函数的具体讲解
可以参考前面的教程,这里不再详述。 
接着进入   SPI_NRF_Init()函数是怎样配置 STM32 的 SPI 接口的: 
1.  void SPI_NRF_Init(void)    
2.  {    
3.    SPI_InitTypeDef  SPI_InitStructure;    
4.    GPIO_InitTypeDef GPIO_InitStructure;    
5.        
6.   /*使能 GPIOB,GPIOD,复用功能时钟*/   
7.    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE|RCC
_APB2Periph_AFIO, ENABLE);    
8.     
9.   /*使能 SPI1 时钟*/   
10.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);    
11.    
12.    /*配置 SPI_NRF_SPI 的 SCK,MISO,MOSI 引脚,GPIOA^5,GPIOA^6,GPIOA^7 */   
13.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;    
14.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;    
15.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用功能    
16.   GPIO_Init(GPIOA, &GPIO_InitStructure);      
17.    
18.   /*配置 SPI_NRF_SPI 的 CE 引脚,GPIOA^2 和 SPI_NRF_SPI 的 CSN 引
脚: NSS GPIOA^1*/   
19.    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_1;    
20.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;    
21.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    
22.   GPIO_Init(GPIOA, &GPIO_InitStructure);     
23.    
24.    /*配置 SPI_NRF_SPI 的 IRQ 引脚,GPIOA^3*/   
25.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;    
26.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;    
27.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;  //上拉输入    
28.   GPIO_Init(GPIOA, &GPIO_InitStructure);     
29.               
30.   /* 这是自定义的宏,用于拉高 csn 引脚,NRF 进入空闲状态 */   
31.   NRF_CSN_HIGH();     
32.      
33.   SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; /
/双线全双工    
34.   SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                     //
主模式    
35.   SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                 //
数据大小 8 位    
36.   SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                        //
时钟极性,空闲时为低    
37.   SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                      //
第 1 个边沿有效,上升沿为采样时刻    
38.   SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                         //
NSS 信号由软件产生    
39.   SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;  
//8 分频,9MHz    
40.   SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                //
高位在前    
41.   SPI_InitStructure.SPI_CRCPolynomial = 7;    
42.   SPI_Init(SPI1, &SPI_InitStructure);    
43.    
44.   /* Enable SPI1  */   
45.   SPI_Cmd(SPI1, ENABLE);    
46. }   
                                                 -第 8 页-