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 页-