2024年4月18日发(作者:)

课程设计说明书

题 目:

课 程:

院 (部):

专 业:

班 级:

学生姓名:

学 号:

指导教师:

完成日期:

基于STM32的无线通信系统设计

课程设计

计算机科学与技术学院

计算机科学与技术专业

I

ARM

目录

课程设计说明书 ............................................................................................................................................................. I

课程设计任务书 ............................................................................................................................................................ 2

1.

课程设计题目 ................................................................................................................................................................ 3

2.

课程设计目的 ................................................................................................................................................................ 3

3.

课程设计内容 ............................................................................................................................................................ 3

3.1

硬件资源 ................................................................................................................................................................. 3

3.2软件资源 .................................................................................................................................................................. 8

3.3

调试环境准备与使用 ............................................................................................................................................ 11

3.4

系统设计步骤 ....................................................................................................................................................... 12

3.4.1需求分析 ................................................................................................................................................... 12

3.4.2概要设计 ................................................................................................................................................... 12

3.4.3详细设计 ................................................................................................................................................... 16

3.4.4

系统实现及调试 ...................................................................................................................................... 20

3.4.5

功能测试 .................................................................................................................................................. 40

3.4.6

系统评价(结果分析) ......................................................................................................................... 41

3.5.结论(体会) ..................................................................................................................................................... 42

3.6.参考文献 ............................................................................................................................................................. 42

课程设计指导教师评语 .................................................................................................................................................. 43

II

山东建筑大学计算机科学与技术学院

1

课程设计任务书

设计题目

班 级

基于STM32的无线通信系统设计

学 号

指导教师

已知技术

参数和设

计要求

技术参数:

基于Cortex-M3内核的奋斗STM32开发板,无线射频收发器

nRF24L01P工作于2.4GHz频段,STM32和nRF24L01P之间采用SPI

接口方式,嵌入式操作系统平台采用uC/OS-II。

设计要求:

用STM32开发板和nRF24L01扩展板设计一个基于uC/OS-II的

无线通信系统,能够实现两个无线节点间的数据收发。

设计内容:

1. 编写STM32和nRF24L01P的初始化程序。

2. 将uC/OS-II移植至 STM32。

3. 设计简单的无线通信协议,编写无线通信任务和射频收发

中断服务子程序。

设计步骤:

1.uC/OS-II任务划分及概要设计,ISR的功能设计。

2.编写 STM32和nRF24L01P的初始化程序,调试STM32的片

内定时器模块,编写基于nRF24L01P模块的数据收发ISR。

3.编写与移植相关的几个函数,将uC/OS-II移植至 STM32。

4.拟定通信协议,编写无线通信任务。

5.利用两套STM32开发板和nRF24L01扩展板调试上述功能,

总结分析,撰写课程设计说明书。

1、奋斗STM32开发版资源及应用:10学时

2、《Cortex M3权威指南》、《STM32F10X参考手册》、《STM32固

件库手册》:20学时

设计内

容与步

设计工作计

3、MDK安装及使用:5学时

划与进度安

4、概要设计:15学时

5、

uC/OS-II

移植及所用外设的驱动程序编写:10学时

6、无线通信任务编程及调试:15学时

7、撰写课程设计说明书:15学时

设计考核

要求

1、考勤20%

2、课程设计说明书50%。

3、成果演示30%

2

1. 课程设计题目

基于STM32的无线通信系统设计

2. 课程设计目的

《ARM课程设计》是计算机科学与技术专业的专业限定选修实践课程,是学习《嵌入式

系统设计》课程后必要的实践教学环节。通过本课程设计使学生加深理解、巩固课堂教学和

平时实验内容,使学生初步具备嵌入式应用系统分析、系统设计、系统实现与测试的实际能

力,强化学生的实践意识、提高动手能力,发挥学生的想象力和创新能力,从而实现课程教

学目标。提高综合运用所学知识进行系统分析、设计的能力。加深对嵌入式软件开发流程以

及项目开发步逐的认识,进一步熟悉UC/OS-II的一直与使用,进一步熟悉UCGUI的使用,

提高嵌入式软件开发所必须的技能。

本课程设计主要培养学生在嵌入式系统设计方面的能力。通过本课程的学习和实践,学

生应能在嵌入式系统组成形式、构造方法、设计流程以及基于集成开发环境调试嵌入式系统

的方法等方面得到锻炼,在硬件系统设计(整合)、操作系统移植、应用程序编写等方面得到

全面训练。

3. 课程设计内容

3.1 硬件资源

基于奋斗STM32开发板,完成<基于STM32的无线通信系统设计>的设计及调试。系统涉及

的硬件资源主要有:

(1) 电源模块

AMS1117-3.3(N1)输入+5V,提供3.3V 的固定电压输出,为了降低电磁干扰,C1-C5 为

CPU 提供BANK 电源(VCC:P50、P75、P100、P28、P11 GND:P49、P74、P99、P27、P10)

滤波。CPU 的模拟输入电源供电脚VDDA(P22)通过L1 22uH 的电感与+3.3V VDD 电压连接,

CPU 的模拟地VSSA(P19)及VREF-(P20)通过R1 0 欧电阻与GND 连接。VREF+(P21)采用

3

VDDA(P22)电源基准。 RT9166-2.5(N2)和RT9166-2.8(N3)输入+5V,提供2.5V 及2.8V 的

固定电压输出,为MP3 电路VS1003 提供所需的电压。为RTC 的备份电源采用V1 3.3V 锂离

子片状电池,如图3.1。

图3.1

(2) 复位时钟模块

外部晶体/陶瓷谐振器(HSE)(P12、P13):B1:8MHz 晶体谐振器,C8,C9 谐振电容选

择10P。系统的时钟经过PLL 模块将时钟提高到72MHz。

低速外部时钟源(LSE)(P8、P9):B2: 32.768KHz 晶体谐振器。C10,C11 谐振电容选

择10P。注意: 根据 ST 公司的推荐, B2 要采用电容负载为6P 的晶振,否则有可能会出

现停振的现象,时钟模块如图3.2所示。

4

图3.2

(3) 主控芯片

采用STM32F103VET6 作为开发板的MCU 平台。这个MCU 是STM32F103里的高容量芯

片, 具有512K 字节的内部FLASH,64K 字节的SRAM, 外设资源有全速USB Device,SDIO,

SPI,I2C,I2S,FSMC,定时器,USART,ADC,DAC,CAN 等接口,如图3.3所示。

图3.3

(4) LCD液晶显示模块

5

LCD显示模块采用STM32 的FSMC 接口模式。显示速度更快。3 寸屏, 分辨率240X400,

64K 色,数据接口16 位,背光源是4 LED 并联模式, 背光驱动采用白光驱动器提供背光用

的横流源, 使背光更加均匀,背光明暗控制采用TTL 电平或者PWM 模式控制。屏上带电阻

式触摸屏, 模块板上带SPI 控制方式的触摸屏控制电路,如图3.4所示。

图3.4

(5) 串行接口

拥有 1 路RS-232 接口,CPU 的PA9-US1-TX(P68)、PA10-US1-RX(P69)通过MAX3232

实现1 路RS-232 接口,分别连接在XS5 和XS17 接口上。 USART1 在系统存储区启动模式

下,可以通过该口通过PC 对板上的CPU 进行ISP,该口也可作为普通串口功能使用, XS6

接口作为TTL 异步通信接口USART2 的接口,在一些应用的调试上有作用,比如通过XS6连

接GPS OEM 板, 可以接收GPS 的协议数据。串行接口如图3.5所示。

USART1 地址:0x4001 3800 - 0x4001 3BFF

USART2 地址:0x4000 4400 - 0x4000 47FF

图3.5

6

(6) NRF24L01 模块简介

本实验采用的无线模块芯片型号为 NRF24L01+,是工作在 2.4~2.5GHz频段的,具备自

动重发功能,6 个数据传输通道,最大无线传输速率为2Mbits。MCU 可与该芯片通过 SPI 接

口访问芯片的寄存器进行配置,模块规格如图3.6所示。

图3.6

SPI写操作

图3.7

SPI读操作

图3.8

7

3.2软件资源

(1) 操作系统

A. 操作系统介绍

本设计所使用的UC/OS-II操作系统版本号为2.85,是一种可移植的,可植入

ROM的,可裁剪的,抢占式的,实时多任务操作系统内核。它被广泛应用于微处理

器、微控制器和数字信号处理器μC/OS-II是一种可移植的,可植入ROM的,可裁

剪的,抢占式的,实时多任务操作系统内核。它被广泛应用于微处理器、微控制器

和数字信号处理器;最小编译内核可达到2KB,结构精简,硬件要求低。

B. 目录结构

(a) UC-OS-II/Port

在此目录下包含三个文件,OS_CPU_C.C,OS_DBG.C,OS_CPU_;

1. 在OS_CPU_C.C中,定义了系统初始化,系统滴答,系统堆栈初始化等钩

子函数函数,其中,除了系统堆栈初始化是“可重入的”,其他函数都是

不可冲入函数,在执行期间必须关闭中断,否则系统将会出现不可预料

的错误。

2. 在OS_DBG.C中,声明了调试相关的数据结构,以及全局的数据结构配置

声明数据信息,以及系统调试初始化函数;

3. 在OS_CPU_中,使用arm汇编程序完成了全局中断的保存于回复,

最高就绪态的执行,系统上下文切换,系统异常挂起以及进入异常的堆

栈操作等函数;

(b) UC-OS-II/CPU

在此目录下仅有CPU_这一个文件,通篇使用arm汇编完成;主要操作

有中断的使能与清除;临界区操作(进出临界区);

(c) UC-OS-II/Source

在此目录下是操作系统的平台无关性源码,保存了系统核心代码、邮箱,内

存管理,信号量,消息队列,临界资源控制,时间控制等操作系统的各种高

级应用API接口函数,是操作系统的主要功能实现部分;

C. 修改内容

8

在通过以上的了解之后,UC-OS-II系统的源码结构十分清晰,在此只需要修

改UC-OS-II/Port,UC-OS-II/CPU这两个文件中的少量代码即可(细节将在详细设

计中介绍)。

(2) 固件库

stm32f10x_stdperiph_lib,版本为3.5.0,系统库的结构如图3.9所示:

图3.9

CMSCS文件夹内包含的内容与cpu内核和cpu启动相关的文件,

stm32F10x_stdperiph_driver文件内包含了stm32f10x系列所有外设的驱动库;

stm32f10x_stdperiph_example内包含了多个关于本系列芯片的一些例程;

stm32f10x_stdperiph_templete内包含了多种开发平台的模板;最下面的chm文件为关于固

件库的使用说明;

A. MSICS文件,如图3.10所示:

图3.10

core_m3.c为内核相关源码;start_up_.s为stm32f10x高容量系列的启动

文件;system_stm32f10x.c为系统时钟和cpu设置相关配置的源码;

B. stm32F10x_stdperiph_driver

这个文件夹内容如图3.11,包含两个部分,为库的外设驱动源码文件夹,inc中保存头

文件,src保存相关外设的源文件。

9

图3.11

图3.12所示的是外设驱动库的头文件的内容:

图3.12

图3.13所示的是外设驱动库的C文件内容:

图3.13

在本设计中仅使用FSMC,GPIO,RCC,MSIC,EXTI,SPI六个部分;因此只需添加这三个原

文件即可;其中FSMC用来做液晶显示驱动;RCC为操作系统提供systick;MSIC提供了中断

相关的函数;GPIO用来做USB的开关控制;EXTI使用外部中断;SPI提供SPI总线操作,为

NRF24C01和触摸屏提供支持;因此以上部分必须添加;头文件在工程设置中C/C++现象卡中

的includePATH里面选择;

10

(3) UCGUI

uC/GUI是Micrium公司研发的通用的嵌入式用户图像界面软件。他给任何使用图像LCD

的应用程序提供单独于处理器和LCD控制器之外的有效的图形用户接口。能够应用于单一任

务环境,也能够应用于多任务环境中。uC/GUI能够应用于任何LCD控制器和CPU的任何尺寸

的物理显示或模拟显示中。

在此,使用的UCGUI已经封装成库文件,所有的调用接口可以在GUI.h等头文件里面看

到。

用户应用程序只需描述关于窗口的数据结构,GUI显示初始化函数,回调函数,以及用

户界面任务函数四个部分;进行显示任务设计时可以借助UCGUIBulider,通过图形界面设计

产生比较准确的界面布局数据,在本设计中,由于涉及到较多的按键,因此UCGUIBulider只

能编辑到BUTTON9,需要注意的是GUI.H里面定义了用户自定义ID,可以借助这个ID在基础

上增加数字实现 大范围ID定义。

3.3 调试环境准备与使用

(1) MDK编译调试环境安装

MDK安装:首先安装MDK,是常规安装,next,agree。。。。最后选择不安装ULINK

等;安装完毕后,以管理员身份运行keil,在file-》lisenceManagement拷给CID,然后打

开破解软件,拷贝CID,generation拷贝lisence码至keil的lisenceManagement内的license

栏,add添加lisence看到2020年的使用期限则破解成功;

(2) JLINK驱动安装

JLINK安装与常规软件安装无异,最后可以不建立桌面快捷方式和菜单

启动选项;

(3) MDK建立工程

(4) MDK项目属性设置

(5) 使用MDK调试工程

(6) 使用MDK下载运行

11

3.4 系统设计步骤

3.4.1需求分析

(1) 本设计需要实现功能:

编写STM32和nRF24L01P的初始化程序。

将uC/OS-II移植至 STM32。

设计简单的无线通信协议,编写无线通信任务和射频收发中断服务子程序。

(2) 性能价格要求:

A. 在开发板固有硬件资源上尽量不增加硬件资源;选择免费开源嵌入式

操作系统;

B. 使用操作系统,提高任务调度,资源管理,系统稳定性;使用中断提

高响应速度。

(3) 热设计要求:

开发板功耗相对较低,发热元器件分布为分散,不需要其他措施来提高散热能力;

(4) 信息安全要求:

本设计为实验产品,暂不考虑在PC机与开发板通信过程中增加加密模块;

如果是在工程项目中,有必要开率增加通信加密模块(AES或者LBLock都可考虑)。

3.4.2概要设计

(1)软件结构图

图形输入输出界面和无线收发任务

uCOSII实时操作系统

ST库 NRF驱动

uCGUI库文件

LCD驱动

图3.14

(2)程序流程图

12

开始

底层驱动初始化

系统时钟、中断向量、LED指示灯、串口、

uCOS操作系统、uCGUI、触摸屏初始化

检测无线模块连接情况

绘制搜索无线模块界面

NRF模块是否连接

初始化NRF无线模块

配置NRF为接收模式

绘制输入输出界面

是否有输入

接收数据,并将数据

显示在液晶屏上

是否有发送请求

13

NRF切换成发送模式

发送数据

发送完成后是

否接到响应

是否达到最大

发送次数

NRF切换至接收模式

14

(2)任务和ISR描述

A.任务描述

1

2

3

4

任务名称

开始任务

用户界面

触摸输入

空闲

英文简称

App_TaskStart

AppTaskUserIF

AppTaskKbd

Idle

优先

2

5

4

10

堆栈容量

(BYTE)

128

256

512

16

任务描述

创建其他子任务

创建输入输出窗体

检测触摸屏输入

空闲任务

B. ISR描述

编号

1

2

ISR名称

复位

英文简称

RST_ISR

优先级

1

2

ISR描述

上电复位,看门狗复位,按键复位

系统时钟

SysTickHandler

外部中断0

EXTI0_IRQHandl

er

系统时钟中断

3

组优先级0,(NRF24L01中断)

次优先级1

(3)接口设计

A. 用户接口HMI

硬件:TFT3.2寸液晶屏

控制器ILI9341

TFT触摸控制器TSC2046

软件:uCGUI窗体

文本编辑框控件,显示要发送的内容

TXT文本显示控件,显示接收到的内容

按键控件,send clear发送和清空按键,大小写转换按键和数字和字母组合按键。

B. 内部接口

UART接口:

用于串口调试。

Uart1管脚配置

管脚名称

Uart_TX

Uart_RX

Uart1模式配置

波特率:115200

数据位:8

停止位:1

校验位:无

管脚名称

GPIOA GPIO_Pin_9

GPIOA GPIO_Pin_10

输入输出模式

复用推完输出模式

浮空输入模式

15

SPI接口:

NRF24L01模块通信接口SPI2管脚配置

管脚名称 对应管脚管脚

SCK GPIOB GPIO_Pin_13

MISO GPIOB GPIO_Pin_14

MOSI GPIOB GPIO_Pin_15

CE GPIOC GPIO_Pin_6

CS GPIOB GPIO_Pin_0

IRQ GPIOA GPIO_Pin_0

NRF24L01模块通信接口SPI2管脚模式配置

双线全双工

主模式

数据大小8位

上升沿采样

高位在前

输入输出模式

复用推挽输出模式

通用推挽输出模式

上拉输入模式

3.4.3详细设计

(1)数据存储空间分配,包括每种数据的名称、作用域、数据类型、占用物理空间大小、涉

及的任务或ISR

数据名称

Rx_Succ

描述 作用域 数据类型

unsigned char

大小

1B

涉及的任务或ISR

AppTaskUserIF任务

和EXTI0_IRQn中断

unsigned char

unsigned char

unsigned char

unsigned char

接收成功标全局

TX_ADDRESS0-5 通道地址0-5 全局

rx_buf

tx_buf

status_buf

接收缓存区

发射缓存区

状态缓冲区

全局

全局

全局

5B

32B

32B

32B

EXTI0_IRQn中断

AppTaskUserIF任务

AppTaskUserIF任务

AppTaskUserIF任务

和EXTI0_IRQn中断

nrf_baud

nrf_Pipe

波特设置 全局 unsigned char

unsigned char

1B

1B

AppTaskUserIF任务

AppTaskUserIF任务 发射通道选全局

nrf_Pipe_r 接收通道选全局

unsigned char 1B EXTI0_IRQn中断

(2)主要任务及中断服务子程序的流程图3.14:

16

执行main()

系统时钟的设置

RCC_Configuration

()

GPIO_Configur

ation()

NVIC_Configur

ation()

tp_config()

显示器接口

FSMC进行配置

外设初始化BSP_Init()

ucosII内核初始化

OSInit()

禁止CPU的中断

-CPU_IntDis()

图3.14

建立主任务,该任务是为了在内核启动后,建立另外2个用户任务, 并清0节拍计数器, 启

动ucOSII内核。主任务的任务名为App_TaskStart, 主任务有自己的堆栈, 堆栈尺寸为

APP_TASK_START_STK_SIZE*4(字节), 然后执行ucosII内部函数OSTimeSet(0),将节拍计

数器清0,节拍计数器范围是0-4294967295,对于节拍频率100hz时, 每隔497天就重新计

数, 调用内部函数OSStart(),启动ucosII内核, 此时ucosII内核开始运行。对任务表

进行监视,主任务因为已经处于就绪状态,于是开始执行主任务App_TaskStart(),uCOSII

的任务结构规定必须为无返回的结构,也就是无限循环模式如3.15所示

SPI2_NRF24L01_Init(void)

使能SPI2外设时钟

配置 SPI2 引脚

配置SPI2 NRF24L01+片选

SPI2

配置NRF24L01+ 模式选择

配置NRF24L01+ 中断信号产生

连接到 PA0

17

禁止SPI2 NRF24L01+的片选。

SPI2 配置

使能SPI2

图3.15

通过SPI2 发送一个字节的数据:

返回读出的数据

接收缓冲区是否

是空

通过SPI2外设发出数据

发送缓冲区是否

是空

开 始

图3.16

void EXTI0_IRQHandler(void)为 NRF24L01 发送及接收中断响应程序:

判断是否产生了

EXTI0中断

恢复全局中断标志

保存全局中断

标志,关总中断

开 始

18

结 束

执行一次任务切换

清除07寄存器标志 进入接收模式

读取状态寄存其来判断数据

判断是否接收到

数据

发射达到最大复

发次数

清除发送缓冲区

判断是否是PA0

线变低

清除EXTI0上的中断标志

图3.17

(3)液晶显示器界面含有发送数据、接收数据、键盘,通过点击按键显示所输入内容,点击

发送后,显示器接收数据内显示所发送内容,另外一块液晶显示器接收数据后,显示所发送

内容,实现无线通信功能。 其中所涉及的函数有GUI_WIDGET_CREATE_INFO(定义了对

话框资源列表)、GUI_WIDGET_CREATE_INFO(定义了对话框资源列表)、

_cbCallback(WM_MESSAGE * pMsg)(ucgui回调函数,是作为对话框动作响应的函数)、

Fun(void)(显示及处理界面)。

19

3.4.4 系统实现及调试

(1)主程序

int main(void)

{

// CPU_INT08U os_err;

/* 禁止所有中断 */

CPU_IntDis();

/* ucosII 初始化 */

OSInit();

/* 硬件平台初始化 */

BSP_Init();

//建立主任务, 优先级最高 建立这个任务另外一个用途是为了以后使用统计任务

// os_err =

OSTaskCreate((void (*) (void *)) App_TaskStart, //指向

任务代码的指针

(void *) 0, //

任务开始执行时,传递给任务的参数的指针

(OS_STK *) &App_TaskStartStk[APP_TASK_START_STK_SIZE -

1], //分配给任务的堆栈的栈顶指针 从顶向下递减

(INT8U) APP_TASK_START_PRIO);

//分配给任务的优先级

OSTimeSet(0); //ucosII的节拍计数器清0 节拍计数器是0-4294967295

OSStart(); //启动ucosII内核

return (0);

}

(2)任务设计

/****************************************************************************

* 名 称:static void App_TaskStart(void* p_arg)

* 功 能:开始任务建立

* 入口参数:无

* 出口参数:无

* 说 明:

* 调用方法:无

****************************************************************************/

static void App_TaskStart(void* p_arg)

{

(void) p_arg;

//初始化ucosII时钟节拍

OS_CPU_SysTickInit();

//使能ucos 的统计任务

20

#if (OS_TASK_STAT_EN > 0)

OSStatInit(); //----统计任务初始化函数

#endif

App_TaskCreate(); //建立其他的任务

while (1)

{

/* 100ms间隔LED闪烁 */

Led_ON();

OSTimeDlyHMSM(0, 0, 0, 100);

Led_OFF();

OSTimeDlyHMSM(0, 0, 0, 100);

}

}

/****************************************************************************

* 名 称:static void App_TaskCreate(void)

* 功 能:建立其余任务的函数

* 入口参数:无

* 出口参数:无

* 说 明:

* 调用方法:无

****************************************************************************/

static void App_TaskCreate(void)

{

/* 建立用户界面任务 */

OSTaskCreateExt(AppTaskUserIF, //指向

任务代码的指针

(void *)0, //任务开始

执行时,传递给任务的参数的指针

(OS_STK *)&AppTaskUserIFStk[APP_TASK_USER_IF_STK_SIZE-1], //分

配给任务的堆栈的栈顶指针 从顶向下递减

APP_TASK_USER_IF_PRIO, //分配

给任务的优先级

APP_TASK_USER_IF_PRIO, //预备

给以后版本的特殊标识符,在现行版本同任务优先级

(OS_STK *)&AppTaskUserIFStk[0], //指向

任务堆栈栈底的指针,用于堆栈的检验

APP_TASK_USER_IF_STK_SIZE, //

指定堆栈的容量,用于堆栈的检验

(void *)0, //指向

用户附加的数据域的指针,用来扩展任务的任务控制块

OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //

选项,指定是否允许堆栈检验,是否将堆栈清0,任务是否要

//进行浮点运算等等。

21

/* 建立触摸驱动任务 */

OSTaskCreateExt(AppTaskKbd,

(void *)0,

(OS_STK *)&AppTaskKbdStk[APP_TASK_KBD_STK_SIZE-1],

APP_TASK_KBD_PRIO,

APP_TASK_KBD_PRIO,

(OS_STK *)&AppTaskKbdStk[0],

APP_TASK_KBD_STK_SIZE,

(void *)0,

OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);

}

/****************************************************************************

* 名 称:static void AppTaskUserIF (void *p_arg)

* 功 能:用户界面任务

* 入口参数:无

* 出口参数:无

* 说 明:

* 调用方法:无

****************************************************************************/

static void AppTaskUserIF (void *p_arg)

{

(void)p_arg;

GUI_Init(); //ucgui初始化

while(1)

{

Fun(); //界面主程序

}

}

/****************************************************************************

* 名 称:static void AppTaskKbd (void *p_arg)

* 功 能:触摸屏坐标获取

* 入口参数:无

* 出口参数:无

* 说 明:

* 调用方法:无

****************************************************************************/

static void AppTaskKbd (void *p_arg)

{

(void)p_arg;

while(1)

{

/* 延时10ms会读取一次触摸坐标 */

OSTimeDlyHMSM(0,0,0,10);

22

GUI_TOUCH_Exec();

}

}

(3)中断服务子程序

void SysTickHandler(void)

{

OS_CPU_SR cpu_sr;

OS_ENTER_CRITICAL(); //保存全局中断标志,关总中断/* Tell uC/OS-II that we are

starting an ISR*/

OSIntNesting++; //OSSemPost(NMEA_MBOX);

OS_EXIT_CRITICAL(); //恢复全局中断标志

OSTimeTick(); /* Call uC/OS-II's OSTimeTick(),在os_core.c文件里定义,主要

判断延时的任务是否计时到*/

OSIntExit(); //在os_core.c文件里定义,如果有更高优先级的任务就绪了,则执行一

次任务切换

}

void EXTI0_IRQHandler(void)

{

unsigned char status;

OS_CPU_SR cpu_sr;

OS_ENTER_CRITICAL(); //保存全局中断标志,关总中断 Tell uC/OS-II that we are

starting an ISR

OSIntNesting++;

OS_EXIT_CRITICAL(); //恢复全局中断标志

if(EXTI_GetITStatus(EXTI_Line0) != RESET) //判断是否产生了EXTI0中断

{

if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==0){ //判断是否是PA0线变低

status = SPI_Read(READ_REG1+STATUS); // 读取状态寄存其来判断数据接

收状况

if(status & 0x40) // 判断是否接收到数据

{

SPI_Read_Buf(RD_RX_PLOAD,rx_buf,TX_PLOAD_WIDTH); //从接收缓冲区里读

出数据

if((status & 0x0e) <= 0x0a)

{

nrf_Pipe_r=(status&0x0e)>>1; //读出是在哪个通道接收

}

else

{

nrf_Pipe_r=0;

}

23

Rx_Succ = 1; //读取数据完成标志

/* 根据读出的接收通道号,将相应信息写入状态文本缓冲区 */

if(nrf_Pipe_r==0) memcpy(status_buf, "Pipe 0 Recive OK! ", 20);

else if(nrf_Pipe_r==1) memcpy(status_buf, "Pipe 1 Recive OK! ", 20);

else if(nrf_Pipe_r==2) memcpy(status_buf, "Pipe 2 Recive OK! ", 20);

else if(nrf_Pipe_r==3) memcpy(status_buf, "Pipe 3 Recive OK! ", 20);

else if(nrf_Pipe_r==4) memcpy(status_buf, "Pipe 4 Recive OK! ", 20);

else if(nrf_Pipe_r==5) memcpy(status_buf, "Pipe 5 Recive OK! ", 20);

}

else if((status &0x10) > 0)

{ //发射达到最大复发次数

SPI_RW_Reg(0xe1,0); //清除发送缓冲区

RX_Mode(); //进入接收模式

Rx_Succ=1;

/* 根据发送通道,将相应信息写入状态文本缓冲区 */

if(nrf_Pipe==0) memcpy(status_buf, "Pipe 0 NO ACK! ", 20);

else if(nrf_Pipe==1) memcpy(status_buf, "Pipe 1 NO ACK! ", 20);

else if(nrf_Pipe==2) memcpy(status_buf, "Pipe 2 NO ACK! ", 20);

else if(nrf_Pipe==3) memcpy(status_buf, "Pipe 3 NO ACK! ", 20);

else if(nrf_Pipe==4) memcpy(status_buf, "Pipe 4 NO ACK! ", 20);

else if(nrf_Pipe==5) memcpy(status_buf, "Pipe 5 NO ACK! ", 20);

}

else if((status &0x20) > 0)

{ //发射后收到应答

SPI_RW_Reg(0xe1,0); //清除发送缓冲区

RX_Mode(); //进入接收模式

Rx_Succ=1;

/* 根据发送通道,将相应信息写入状态文本缓冲区 */

if(nrf_Pipe==0) memcpy(status_buf, "Pipe 0 Send OK! ", 20);

else if(nrf_Pipe==1) memcpy(status_buf, "Pipe 1 Send OK! ", 20);

else if(nrf_Pipe==2) memcpy(status_buf, "Pipe 2 Send OK! ", 20);

else if(nrf_Pipe==3) memcpy(status_buf, "Pipe 3 Send OK! ", 20);

else if(nrf_Pipe==4) memcpy(status_buf, "Pipe 4 Send OK! ", 20);

else if(nrf_Pipe==5) memcpy(status_buf, "Pipe 5 Send OK! ", 20);

}

SPI_RW_Reg(WRITE_REG1+STATUS, status); //清除07寄存器标志

}

EXTI_ClearITPendingBit(EXTI_Line0); //清除EXTI0上的中断标志

}

OSIntExit(); //在os_core.c文件里定义,如果有更高优先级的任务就绪了,

则执行一次任务切换

}

(4)界面实现设计

24

void Fun(void);

extern void RX_Mode(void);

extern void TX_Mode(void);

extern void NRF24L01_TXBUF(uint8_t* data_buffer, uint8_t Nb_bytes);

int8_t Shift = 0;

unsigned char text_buf[32] = "";

/* ucgui类型定义*/

WM_HWIN hWin;

WM_HWIN hListBox[8];

WM_HWIN text1,text2,text3,bt[33],edit1,edit2;

GUI_COLOR DesktopColorOld;

const GUI_FONT* pFont = &GUI_Font8x13_1;

const GUI_FONT* pFont18 = &GUI_FontComic18B_1;

/* 定义了对话框资源列表 */

static const GUI_WIDGET_CREATE_INFO aDialogCreate[] = {

{ FRAMEWIN_CreateIndirect, "NRF24L01P", 0, 0, 0, 240, 400,

FRAMEWIN_CF_ACTIVE },

{ BUTTON_CreateIndirect, "SEND", GUI_ID_BUTTON0, 0, 316, 120, 55 },

{ BUTTON_CreateIndirect, "CLEAR", GUI_ID_BUTTON1, 120, 316, 120, 55 },

{ EDIT_CreateIndirect, "", GUI_ID_EDIT1, 0, 25, 230, 35,

EDIT_CF_LEFT, 50 },

{ EDIT_CreateIndirect, "", GUI_ID_EDIT2, 0, 85, 230, 35,

EDIT_CF_LEFT, 50 },

{ TEXT_CreateIndirect, "Send Text Area", GUI_ID_TEXT0, 1, 2, 230,

25, TEXT_CF_LEFT },

{ TEXT_CreateIndirect, "Receive Text Area ", GUI_ID_TEXT1, 1, 62, 230,

25, TEXT_CF_LEFT },

{ BUTTON_CreateIndirect, "Q", GUI_ID_BUTTON2, 0, 150,24, 30, 0,0},

{ BUTTON_CreateIndirect, "W", GUI_ID_BUTTON3, 24, 150,24,30, 0,0},

25

{ BUTTON_CreateIndirect, "P", GUI_ID_BUTTON11, 216,150,24, 30,

0,0},

{ BUTTON_CreateIndirect, "O", GUI_ID_BUTTON10, 192,150,24, 30,

0,0},

{ BUTTON_CreateIndirect, "I", GUI_ID_BUTTON9, 168,150,24, 30,

0,0},

{ BUTTON_CreateIndirect, "Y", GUI_ID_BUTTON7, 120,150,24, 30,

0,0},

{ BUTTON_CreateIndirect, "U", GUI_ID_BUTTON8, 144,150,24, 30,

0,0},

{ BUTTON_CreateIndirect, "T", GUI_ID_BUTTON6, 96, 150,24, 30,

0,0},

{ BUTTON_CreateIndirect, "R", GUI_ID_BUTTON5, 72, 150,24, 30,

0,0},

{ BUTTON_CreateIndirect, "E", GUI_ID_BUTTON4, 48, 150,24, 30,

0,0},

{ BUTTON_CreateIndirect, "A", GUI_ID_BUTTON12, 10, 182,24, 30,

0,0},

{ BUTTON_CreateIndirect, "S", GUI_ID_BUTTON13, 34, 182,24, 30,

0,0},

{ BUTTON_CreateIndirect, "D", GUI_ID_BUTTON14, 58, 182,24, 30,

0,0},

{ BUTTON_CreateIndirect, "F", GUI_ID_BUTTON15, 82, 182,24, 30,

0,0},

{ BUTTON_CreateIndirect, "G", GUI_ID_BUTTON16, 106, 182,24, 30,

0,0},

{ BUTTON_CreateIndirect, "H", GUI_ID_BUTTON17, 130, 182,24, 30,

0,0},

26

{ BUTTON_CreateIndirect, "J", GUI_ID_BUTTON18, 154, 182,24, 30,

0,0},

{ BUTTON_CreateIndirect, "K", GUI_ID_BUTTON19, 178, 182,24, 30,

0,0},

{ BUTTON_CreateIndirect, "L", GUI_ID_BUTTON20, 202, 182,24, 30,

0,0},

{ BUTTON_CreateIndirect, "Z", GUI_ID_BUTTON21, 34, 214,24, 30,

0,0 },

{ BUTTON_CreateIndirect, "X", GUI_ID_BUTTON22, 58, 214,24, 30,

0,0},

{ BUTTON_CreateIndirect, "C", GUI_ID_BUTTON23, 82, 214,24, 30,

0,0},

{ BUTTON_CreateIndirect, "V", GUI_ID_BUTTON24, 106, 214,24, 30,

0,0},

{ BUTTON_CreateIndirect, "B", GUI_ID_BUTTON25, 130, 214,24, 30,

0,0},

{ BUTTON_CreateIndirect, "N", GUI_ID_BUTTON26, 154, 214,24, 30,

0,0},

{ BUTTON_CreateIndirect, "M", GUI_ID_BUTTON27, 178, 214,24, 30,

0,0},

{ BUTTON_CreateIndirect, "Shift", GUI_ID_BUTTON28, 0, 214,34, 30,

0,0},

{ BUTTON_CreateIndirect, "1", GUI_ID_BUTTON29, 0, 246,24, 30,

0,0},

{ BUTTON_CreateIndirect, "2", GUI_ID_BUTTON30, 24, 246,24, 30,

0,0},

{ BUTTON_CreateIndirect, "3", GUI_ID_BUTTON31, 48, 246,24, 30,

0,0},

27

{ BUTTON_CreateIndirect, "4", GUI_ID_BUTTON32, 72, 246,24, 30,

0,0},

{ BUTTON_CreateIndirect, "5", GUI_ID_BUTTON33, 96, 246,24, 30,

0,0},

{ BUTTON_CreateIndirect, "6", GUI_ID_BUTTON34, 120, 246,24, 30,

0,0},

{ BUTTON_CreateIndirect, "7", GUI_ID_BUTTON35, 144, 246,24, 30,

0,0},

{ BUTTON_CreateIndirect, "8", GUI_ID_BUTTON36, 168, 246,24, 30,

0,0},

{ BUTTON_CreateIndirect, "9", GUI_ID_BUTTON37, 192, 246,24, 30,

0,0},

{ TEXT_CreateIndirect, "status", GUI_ID_TEXT2, 1, 120, 240, 25,

TEXT_CF_HCENTER },

};

/****************************************************************************

* 名 称:static void _cbCallback(WM_MESSAGE * pMsg)

* 功 能:ucgui回调函数,是作为对话框动作响应的函数

* 入口参数:无

* 出口参数:无

* 说 明:

* 调用方法:

****************************************************************************/

static void _cbCallback(WM_MESSAGE * pMsg)

{

unsigned char text_buf[1] = "";

int NCode, Id;

switch (pMsg->MsgId)

{

case WM_NOTIFY_PARENT: //通知父窗口有事件在窗口部件上发生

Id = WM_GetId(pMsg->hWinSrc); //获得对话框窗口里发生事件的部

件的ID

NCode = pMsg->Data.v; //通知代码

switch (NCode)

{

case WM_NOTIFICATION_RELEASED: //窗体部件动作被释放

if (Id == GUI_ID_BUTTON1)

28

{ //按键CLEAR被松开

memcpy(status_buf, "", 20); //清空状态文本缓冲区

memcpy(rx_buf, "", 32); //清空接收文本缓冲区

TEXT_SetText(text3,(const char *)status_buf); //清空状态文本框

EDIT_SetText(edit2,(const char *)rx_buf); //清空接收字符编辑框

memcpy(tx_buf, "", 32); //清空发送文本缓冲区

NRF24L01_TXBUF(tx_buf,32); //将发送字符缓冲区的字符通过

NRF24L01发送出去

EDIT_SetText(edit1,(const char *)tx_buf); //清空接收字符编辑框

}

else if (Id == GUI_ID_BUTTON0)

{ //按键SEND 被松开

// memcpy(tx_buf, "1234567890abcdefghij!@#$%^&*()-=", 32); //将32字节

的文本拷贝到发送文本缓冲区

// memcpy(tx_buf, "", 32); //清空发送文本缓冲区

memcpy(rx_buf, "", 32); //清空接收文本缓冲区

memcpy(status_buf, "", 20); //清空状态文本缓冲区

EDIT_SetText(edit2,(const char *)rx_buf); //清空接收字符编辑框

NRF24L01_TXBUF(tx_buf,32); //将发送字符缓冲区的字符通过

NRF24L01发送出去

memcpy(tx_buf, "", 32); //清空发送文本缓冲区

EDIT_SetText(edit1,(const char *)tx_buf); //清空接收字符编辑框

TEXT_SetText(text3,(const char *)status_buf); //清空状态文本框

}

else if(Id == GUI_ID_BUTTON28)

{

if(Shift==0) Shift = 1;

else Shift = 0;

}

else if(Id == GUI_ID_BUTTON2)

{

if(Shift==0) memcpy(text_buf,"q", 1);

else memcpy(text_buf,"Q", 1);

strcat((char *)tx_buf,(const char *)text_buf);

EDIT_SetText(edit1,(const char *)tx_buf);

}

。。。。。。

}

else if(Id == GUI_ID_BUTTON29)

{

memcpy(text_buf,"1", 1);

strcat((char *)tx_buf,(const char *)text_buf);

29

EDIT_SetText(edit1,(const char *)tx_buf);

}

。。。。。。

break;

default: break;

}

default:

WM_DefaultProc(pMsg); //默认程序来处理消息

break;

}

}

/****************************************************************************

* 名 称:void Fun(void)

* 功 能:显示及处理界面

* 入口参数:无

* 出口参数:无

* 说 明:

* 调用方法:无

****************************************************************************/

void Fun(void)

{

GUI_CURSOR_Show(); //打开鼠标图形显示

/* 建立对话框时,包含了资源列表,资源数目, 并且指定了用于动作响应的回调函数 */

hWin = GUI_CreateDialogBox(aDialogCreate, GUI_COUNTOF(aDialogCreate),

_cbCallback, 0, 0, 0);

FRAMEWIN_SetFont(hWin, &GUI_FontComic18B_1); //对话框字体设置

FRAMEWIN_SetClientColor(hWin, GUI_WHITE); //对话框的窗体颜色是

黑色

// memcpy(tx_buf, "1234567890abcdefghij!@#$%^&*()-=", 32); //将长度为32字节的

发送字符串拷贝到发送缓冲区

memcpy(tx_buf, "", 32);

memcpy(rx_buf, "", 32); //将接收缓存区清空

text1 = WM_GetDialogItem(hWin, GUI_ID_TEXT0); //获得对话框里

GUI_ID_TEXT0项目(文本框Send Text Area)的句柄

text2 = WM_GetDialogItem(hWin, GUI_ID_TEXT1); //获得对话框里

GUI_ID_TEXT1项目(文本框Receive Text Area)的句柄

text3 = WM_GetDialogItem(hWin, GUI_ID_TEXT2); //获得对话框里

GUI_ID_TEXT2项目(状态字符文本框)的句柄

TEXT_SetFont(text1,pFont); //设置对话框里文本框

Send Text Area的字体

30

TEXT_SetFont(text2,pFont);

Receive Text Area的字体

TEXT_SetFont(text3,pFont);

本框的字体

TEXT_SetTextColor(text1,GUI_GREEN);

Text Area的字体颜色

TEXT_SetTextColor(text2,GUI_GREEN);

Receive Text Area的字体颜色

TEXT_SetTextColor(text3,GUI_RED);

符文本框的字体颜色

edit1 = WM_GetDialogItem(hWin, GUI_ID_EDIT1);

GUI_ID_EDIT1项目(编辑框 发送字符串显示区)的句柄

EDIT_SetFont(edit1,pFont18);

送字符串显示区的字体

EDIT_SetText(edit1,(const char *)tx_buf);

框里编辑框 发送字符串显示区的字符串

edit2 = WM_GetDialogItem(hWin, GUI_ID_EDIT2);

GUI_ID_EDIT2项目(编辑框 接收字符串显示区)的句柄

EDIT_SetFont(edit2,pFont18);

收字符串显示区的字体

EDIT_SetText(edit2,(const char *)rx_buf);

框里编辑框 接收字符串显示区的字符串

bt[0]=WM_GetDialogItem(hWin,GUI_ID_BUTTON0);

GUI_ID_BUTTON0项目(按键SEND)的句柄

bt[1]=WM_GetDialogItem(hWin,GUI_ID_BUTTON1);

GUI_ID_BUTTON0项目(按键CLEAR)的句柄

BUTTON_SetFont(bt[0],pFont);

的字体

BUTTON_SetFont(bt[1],pFont);

的字体

BUTTON_SetTextColor(bt[0],0,GUI_WHITE);

SEND未被按下的字体颜色

BUTTON_SetTextColor(bt[1],0,GUI_WHITE);

未被按下的字体颜色

nrf_Pipe = 0;

置为0

nrf_baud = 0;

2MPS

RX_Mode();

while (1)

{

if(Rx_Succ==1){ //

有效数据

31

//设置对话框里文本框

//设置对话框里状态字符文

//设置对话框里文本框Send

//设置对话框里文本框

//设置对话框里状态字

//获得对话框里

//设置对话框里编辑框 发

//设置对话

//获得对话框里

//设置对话框里编辑框 接

//设置对话

//获得对话框里

//获得对话框里

//设置对话框里按键SEND

//设置对话框里按键CLEAR

//设置对话框里按键

//设置对话框里按键CLEAR

//NRF24L01初始发射通道设

//NRF24L01速率 初始为

//NRF24L01进入接收模式

当NRF24L01接收到

EDIT_SetText(edit2,(const char *)rx_buf); //将接收缓冲区的字符写

入到接收字符编辑框内

TEXT_SetText(text3,(const char *)status_buf); //将状态文本缓冲区的字符

写入到状态文本框内

Rx_Succ = 0;

}

WM_Exec(); //刷新屏幕

}

}

(5)NRF24L01驱动设计

/****************************************************************************

* Copyright (C), 2013 wccWorkRoot,

*

* 文件名: NRF24L01.c

* 内容简述:

* 2.4G 通信模块NRF24L01+的驱动部分

*

* 文件历史:

* 版本号 日期 作者 说明

* v0.1 2015-10-25 wcc 创建该文件

*

****************************************************************************/

#define NRF_GLOBALS

#include "NRF24L01.h"

#include "stm32f10x_gpio.h"

#include "stm32f10x_spi.h"

#include "globals.h"

void MODE_CE(BYTE a);

void SPI2_NRF24L01_Init(void);

BYTE SPI2_NRF_SendByte(BYTE byte);

BYTE SPI_RW_Reg(BYTE data1, BYTE data2);

BYTE SPI_Write_Buf(BYTE reg, BYTE *pBuf, BYTE bytes);

BYTE SPI_Read(BYTE reg);

BYTE SPI_Read_Buf(BYTE reg, BYTE *pBuf, BYTE bytes);

void RX_Mode(void);

void TX_Mode(void);

void NRF24L01_TXBUF(uint8_t* data_buffer, uint8_t Nb_bytes);

/****************************************************************************

* 名 称:void MODE_CE(BYTE a)

* 功 能:NRF24L01 收/发模式有效选择

* 入口参数:a: 1:NRF24L01 收/发有效 0:关

* 出口参数:无

* 说 明:

32

* 调用方法:MODE_CE(1);

****************************************************************************/

void MODE_CE(BYTE a){ //NRF24L01 MODE-CE

if(a==1) GPIO_SetBits(GPIOC, GPIO_Pin_6); //On

else GPIO_ResetBits(GPIOC, GPIO_Pin_6); //Off

}

/****************************************************************************

* 名 称:void SPI2_NRF24L01_Init(void)

* 功 能:NRF24L01 SPI2接口初始化

* 入口参数:无

* 出口参数:无

* 说 明:

* 调用方法:SPI2_NRF24L01_Init();

****************************************************************************/

void SPI2_NRF24L01_Init(void)

{

SPI_InitTypeDef SPI_InitStructure;

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2 ,ENABLE); //使能SPI2外设时钟

/* 配置 SPI2 引脚: SCK, MISO and MOSI(PB13, PB14, PB15) */

GPIO__Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;

GPIO__Speed = GPIO_Speed_50MHz;

GPIO__Mode = GPIO_Mode_AF_PP; //复用功能(推挽)输出

SPI2

GPIO_Init(GPIOB, &GPIO_InitStructure);

/* 配置SPI2 NRF24L01+片选 PB0 */

GPIO__Pin = GPIO_Pin_0;

GPIO__Speed = GPIO_Speed_50MHz; //输出模式最大速度

50MHz

GPIO__Mode = GPIO_Mode_Out_PP; //通用推挽输出模式

GPIO_Init(GPIOB, &GPIO_InitStructure);

/* 配置NRF24L01+ 模式选择 PC6 */

GPIO__Pin = GPIO_Pin_6; //NRF24L01 MODE-CE

GPIO__Speed = GPIO_Speed_50MHz; //输出模式最大速度

50MHz

GPIO__Mode = GPIO_Mode_Out_PP; //通用推挽输出模式

GPIO_Init(GPIOC, &GPIO_InitStructure);

/* 配置NRF24L01+ 中断信号产生连接到 PA0 */

GPIO__Pin = GPIO_Pin_0; //NRF24L01 IRQ

33

GPIO__Mode = GPIO_Mode_IPU; //上拉输入模式

GPIO_Init(GPIOA, &GPIO_InitStructure);

//禁止SPI2 NRF24L01+的片选。

NotSelect_NRF();

/* SPI2 配置 */

SPI__Direction = SPI_Direction_2Lines_FullDuplex; //全双工

SPI__Mode = SPI_Mode_Master; //主模式

SPI__DataSize = SPI_DataSize_8b; //8位

SPI__CPOL = SPI_CPOL_Low; //时钟极性 空

闲状态时,SCK保持低电平

SPI__CPHA = SPI_CPHA_1Edge; //时钟相位

数据采样从第一个时钟边沿开始

SPI__NSS = SPI_NSS_Soft; //软件产生

NSS

SPI__BaudRatePrescaler = SPI_BaudRatePrescaler_16; //波特率控

制 SYSCLK/16

SPI__FirstBit = SPI_FirstBit_MSB; //数据高位在前

SPI__CRCPolynomial = 7; //CRC多项

式寄存器初始值为7

SPI_Init(SPI2, &SPI_InitStructure);

/* 使能SPI2 */

SPI_Cmd(SPI2, ENABLE); //SPI_Cmd()函数定义在 #include "stm32f10x_spi.h"

}

/****************************************************************************

* 名 称:BYTE SPI2_NRF_SendByte(BYTE byte)

* 功 能:通过SPI2 发送一个字节的数据。

* 入口参数:byte: 发送的数据

* 出口参数:接收到的字节

* 说 明:

* 调用方法:SPI2_NRF_SendByte(data1);

****************************************************************************/

BYTE SPI2_NRF_SendByte(BYTE byte)

{

/* 循环检测发送缓冲区是否是空 */

while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);

/* 通过SPI2外设发出数据 */

SPI_I2S_SendData(SPI2, byte);

/* 等待接收数据,循环检查接收数据缓冲区 */

while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);

/* 返回读出的数据 */

return SPI_I2S_ReceiveData(SPI2);

}

/****************************************************************************

34

* 名 称:unsigned char SPI_RW_Reg(unsigned char data1,unsigned char data2)

* 功 能:通过SPI2 将单字节写入到NRF24L01+指定的寄存器里。

* 入口参数:data1: NRF24L01寄存器

data2: 单字节数据

* 出口参数:接收到的字节

* 说 明:

* 调用方法:SPI_RW_Reg(WRITE_REG1 + EN_AA, 0x3f);

****************************************************************************/

BYTE SPI_RW_Reg(BYTE data1,BYTE data2)

{

BYTE Data = 0;

Select_NRF(); //选择NRF24L01片选

Data = SPI2_NRF_SendByte(data1); //指定NRF24L01寄存器

SPI2_NRF_SendByte(data2); //写入数据

NotSelect_NRF(); //禁止NRF24L01片选

return(Data); //返回NRF24L01 写寄存器的状态信息

}

/****************************************************************************

* 名 称:unsigned char SPI_Write_Buf(BYTE reg, BYTE *pBuf, BYTE bytes)

* 功 能:通过SPI2 将数组里的数据写入到NRF24L01+指定的寄存器里。

* 入口参数:reg: NRF24L01寄存器

pBuf: 数组

bytes: 写入的字节数

* 出口参数:接收到的字节

* 说 明:

* 调用方法:SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P0, TX_ADDRESS0, TX_ADR_WIDTH);

****************************************************************************/

BYTE SPI_Write_Buf(BYTE reg, BYTE *pBuf, BYTE bytes)

{

BYTE status,byte_ctr;

Select_NRF(); //选择NRF24L01片选

status = SPI2_NRF_SendByte(reg); //指定NRF24L01寄存器

for(byte_ctr=0; byte_ctr

{

SPI2_NRF_SendByte(*pBuf++);

}

NotSelect_NRF(); //禁止NRF24L01片选

return(status); //返回NRF24L01 写寄存器的状态信息

}

/****************************************************************************

* 名 称:unsigned char SPI_Read(BYTE reg)

35

* 功 能:通过SPI2 将NRF24L01+指定的寄存器里读出一个字节。

* 入口参数:reg: NRF24L01寄存器

* 出口参数:指定NRF24L01寄存器的状态信息

* 说 明:

* 调用方法:status=SPI_Read(READ_REG1+STATUS);

****************************************************************************/

BYTE SPI_Read(BYTE reg)

{

BYTE Data;

Select_NRF(); //选择NRF24L01片选

SPI2_NRF_SendByte(reg); //指定NRF24L01寄存器

Data = SPI2_NRF_SendByte(0); //读出数据

NotSelect_NRF(); //禁止NRF24L01片选

return (Data);

}

/****************************************************************************

* 名 称:unsigned char SPI_Read_Buf(BYTE reg, BYTE *pBuf, BYTE bytes)

* 功 能:通过SPI2 将NRF24L01+指定的寄存器里的数据读出指定长度到指定的数组里。

* 入口参数:reg: NRF24L01寄存器

pBuf: 数组

bytes: 长度

* 出口参数:指定NRF24L01寄存器的状态信息

* 说 明:

* 调用方法:SPI_Read_Buf(RD_RX_PLOAD,rx_buf,TX_PLOAD_WIDTH);

****************************************************************************/

BYTE SPI_Read_Buf(BYTE reg, BYTE *pBuf, BYTE bytes)

{

BYTE status,i;

Select_NRF(); //选择NRF24L01片选

status=SPI2_NRF_SendByte(reg); //读出指定NRF24L01寄存器的状态信息

for(i=0; i

{

pBuf[i]=SPI2_NRF_SendByte(0);

}

NotSelect_NRF(); //禁止NRF24L01片选

return(status); //返回指定NRF24L01寄存器的状态信息

}

/****************************************************************************

* 名 称:RX_Mode(void)

* 功 能:设置NRF24L01+的接收模式

* 入口参数:无

* 出口参数:无

* 说 明:设置了6个接收通道地址,数据宽度32、接收自动应答、6个接收通道使能、

36

* 射频频道0、16位CRC、收发中断、增益0dB等等

* 调用方法:RX_Mode();

****************************************************************************/

void RX_Mode(void)

{

MODE_CE(0);

SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P0, TX_ADDRESS0, TX_ADR_WIDTH); //数据通道

0接收地址,最大5个字节, 此处接收地址和发送地址相同

SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P1, TX_ADDRESS1, TX_ADR_WIDTH); //数据通道

1接收地址,最大5个字节, 此处接收地址和发送地址相同

// SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P2, TX_ADDRESS2, TX_ADR_WIDTH); //数据通道

2接收地址,最大5个字节, 此处接收地址和发送地址相同

// SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P3, TX_ADDRESS3, TX_ADR_WIDTH); //数据通道

3接收地址,最大5个字节, 此处接收地址和发送地址相同

// SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P4, TX_ADDRESS4, TX_ADR_WIDTH); //数据通道

4接收地址,最大5个字节, 此处接收地址和发送地址相同

// SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P5, TX_ADDRESS5, TX_ADR_WIDTH); //数据通道

5接收地址,最大5个字节, 此处接收地址和发送地址相同

SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P2, TX_ADDRESS2, 1); //数据通道2接收地址,

5个字节, 高字节与TX_ADDRESS1[39:8]相同,低字节同TX_ADDRESS2[0]

SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P3, TX_ADDRESS3, 1); //数据通道3接收地址,

5个字节, 高字节与TX_ADDRESS1[39:8]相同,低字节同TX_ADDRESS3[0]

SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P4, TX_ADDRESS4, 1); //数据通道4接收地址,

5个字节, 高字节与TX_ADDRESS1[39:8]相同,低字节同TX_ADDRESS4[0]

SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P5, TX_ADDRESS5, 1); //数据通道5接收地址,

5个字节, 高字节与TX_ADDRESS1[39:8]相同,低字节同TX_ADDRESS5[0]

SPI_RW_Reg(WRITE_REG1 + RX_PW_P0, TX_PLOAD_WIDTH); // 接收数据通道0有效数据

宽度32 范围1-32

SPI_RW_Reg(WRITE_REG1 + RX_PW_P1, TX_PLOAD_WIDTH); // 接收数据通道1有效数据

宽度32 范围1-32

SPI_RW_Reg(WRITE_REG1 + RX_PW_P2, TX_PLOAD_WIDTH); // 接收数据通道2有效数据

宽度32 范围1-32

SPI_RW_Reg(WRITE_REG1 + RX_PW_P3, TX_PLOAD_WIDTH); // 接收数据通道3有效数据

宽度32 范围1-32

SPI_RW_Reg(WRITE_REG1 + RX_PW_P4, TX_PLOAD_WIDTH); // 接收数据通道4有效数据

宽度32 范围1-32

SPI_RW_Reg(WRITE_REG1 + RX_PW_P5, TX_PLOAD_WIDTH); // 接收数据通道5有效数据

宽度32 范围1-32

SPI_RW_Reg(WRITE_REG1 + EN_AA, 0x3f); // 使能通道0-通道5接收自动应答

SPI_RW_Reg(WRITE_REG1 + EN_RXADDR, 0x3f); // 接收通道0-5 使能

SPI_RW_Reg(WRITE_REG1 + RF_CH, 0); // 选择射频工作频道0 范围0-127

if(nrf_baud==0) SPI_RW_Reg(WRITE_REG1 + RF_SETUP, 0x0f); // 0db, 2M BPS 射

频寄存器 无线速率bit5:bit3 发射功率bit2-bit1

37

//

00: 1M BPS 00:-18dB

//

01: 2M BPS 01:-12dB

//

10: 250K BPS 10:-6dB

//

11:保留 11:0dB

else if(nrf_baud==1) SPI_RW_Reg(WRITE_REG1 + RF_SETUP, 0x07); // 0db, 1M BPS

else SPI_RW_Reg(WRITE_REG1 + RF_SETUP, 0x27); // 0db, 250K BPS

SPI_RW_Reg(WRITE_REG1 + CONFIG, 0x0f); // bit6 接收中断产生时,IRQ引脚产

生低电平

// bit5 发送中断产生时,IRQ引脚产

生低电平

// bit4 最大重复发送次数完成时 IRQ引脚

产生低电平

// bit3 CRC校验允许

// bit2 16位CRC

// bit1 上电

// bit0 接收模式

MODE_CE(1); // 使能接收模式

}

/****************************************************************************

* 名 称:TX_Mode(void)

* 功 能:设置NRF24L01+的发送模式

* 入口参数:无

* 出口参数:无

* 说 明:设置了6个发射通道地址、射频频道0、16位CRC、收发中断、增益0dB等等

* 调用方法:TX_Mode();

****************************************************************************/

void TX_Mode(void)

{

MODE_CE(0);

SPI_RW_Reg(WRITE_REG1 + SETUP_RETR, 0x1a); // 自动重发延时500us + 86us, 自动

重发计数10次

if(nrf_Pipe==0) SPI_Write_Buf(WRITE_REG1 + TX_ADDR, TX_ADDRESS0, TX_ADR_WIDTH);

//数据通道0发送地址,最大5个字节

else if(nrf_Pipe==1) SPI_Write_Buf(WRITE_REG1 + TX_ADDR, TX_ADDRESS1,

TX_ADR_WIDTH); //数据通道1发送地址,最大5个字节

else if(nrf_Pipe==2) SPI_Write_Buf(WRITE_REG1 + TX_ADDR, TX_ADDRESS2,

TX_ADR_WIDTH); //数据通道2发送地址,最大5个字节

else if(nrf_Pipe==3) SPI_Write_Buf(WRITE_REG1 + TX_ADDR, TX_ADDRESS3,

TX_ADR_WIDTH); //数据通道3发送地址,最大5个字节

38

else if(nrf_Pipe==4) SPI_Write_Buf(WRITE_REG1 + TX_ADDR, TX_ADDRESS4,

TX_ADR_WIDTH); //数据通道4发送地址,最大5个字节

else if(nrf_Pipe==5) SPI_Write_Buf(WRITE_REG1 + TX_ADDR, TX_ADDRESS5,

TX_ADR_WIDTH); //数据通道5发送地址,最大5个字节

if(nrf_Pipe==0) SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P0, TX_ADDRESS0,

TX_ADR_WIDTH); // 将0通道的接收地址设置为 0通道的发射地址

else if(nrf_Pipe==1) SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P0, TX_ADDRESS1,

TX_ADR_WIDTH); // 将0通道的接收地址设置为 1通道的发射地址

else if(nrf_Pipe==2) SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P0, TX_ADDRESS2,

TX_ADR_WIDTH); // 将0通道的接收地址设置为 2通道的发射地址

else if(nrf_Pipe==3) SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P0, TX_ADDRESS3,

TX_ADR_WIDTH); // 将0通道的接收地址设置为 3通道的发射地址

else if(nrf_Pipe==4) SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P0, TX_ADDRESS4,

TX_ADR_WIDTH); // 将0通道的接收地址设置为 4通道的发射地址

else if(nrf_Pipe==5) SPI_Write_Buf(WRITE_REG1 + RX_ADDR_P0, TX_ADDRESS5,

TX_ADR_WIDTH); // 将0通道的接收地址设置为 5通道的发射地址

SPI_RW_Reg(WRITE_REG1 + CONFIG, 0x0e); // bit6 接收中断产生时,IRQ引脚产

生低电平

// bit5 发送中断产生时,IRQ引脚产

生低电平

// bit4 最大重复发送次数完成时 IRQ引脚

产生低电平

// bit3 CRC校验允许

// bit2 16位CRC

// bit1 上电

// bit0 发送模式

MODE_CE(1); // 使能发送模式

}

/****************************************************************************

* 名 称:delay_ms(unsigned int x)

* 功 能:延时基数为1毫秒程序

* 入口参数:x 延时的毫秒数

* 出口参数:无

* 说 明:无

* 调用方法:delay_ms(1);

****************************************************************************/

void delay_ms(unsigned int x)

{

unsigned int i,j;

i=0;

for(i=0;i

{

j=108;

while(j--);

39

}

}

/****************************************************************************

* 名 称:USB_To_NRF_Send_Data(uint8_t* data_buffer, uint8_t Nb_bytes)

* 功 能:将保存在USB接收缓存区的32字节的数据通过NRF24L01+发送出去

* 入口参数:data_buffer USB接收缓存区

Nb_bytes USB缓存接收到的字节数

* 出口参数:无

* 说 明:当接收到的USB虚拟串口数据小于32,把有效数据外的空间用0填满

* 调用方法:RX_Mode();

****************************************************************************/

void NRF24L01_TXBUF(uint8_t* data_buffer, uint8_t Nb_bytes)

{

unsigned char i = 0;

MODE_CE(0); //NRF 模式控制

SPI_RW_Reg(WRITE_REG1+STATUS,0xff); //设置状态寄存器初始化

SPI_RW_Reg(FLUSH_TX,0); //清除TX FIFO寄存器

SPI_RW_Reg(FLUSH_RX,0); //清除RX FIFO寄存器

TX_Mode(); //设置为发送模式

delay_ms(10);

if(Nb_bytes<32)

{ //当发送的数据长度小于32,把有效数据外的空间用0填满

for(i=Nb_bytes; i<32; i++)

data_buffer[i] = 0;

}

SPI_Write_Buf(WR_TX_PLOAD, data_buffer, TX_PLOAD_WIDTH); //发送32字节

的缓存区数据到NRF24L01

MODE_CE(1); //保持10us以上,将数据发送出去

}

3.4.5 功能测试

测试数据

发送消息

接收消息

发送编码

123

HELLO

接受编码

123

HELLO

发送板直接输入123后,然后点击SEND后如图 3.18:

接收板接收到后如图3.19:

40

图3.18 图3.19

3.4.6 系统评价(结果分析)

基于Cortex-M3内核的奋斗STM32开发板,无线射频收发器nRF24L01P工作于2.4GM频

段,STM32和nRF24L01P之间采用SPI接口方式。

用STM32开发板和nRF24L01扩展板设计一个基于uC/OS-II的无线通信系统,并基于

UCGUI实现良好的人机界面,能够实现两个无线节点间的数据收发,实现俩个开发板互相收

发数据,可以输入自定义的数据,然后发送并对方接收,并能够将接收到的数据显示在液晶

屏上,可以清空显示屏接收的数据。可以用小键盘手动输入数据,实现大小写英文字母的转

换(shift键),实现良好的人机界面。双方可以互发数据,实现良好的通讯。

41

3.5.结论(体会)

通过这次课程设计,我学会了如何用STM32开发板和nRF24L01扩展板设计一个基于

uC/OS-II的无线通信系统,最后终于完成了课程设计的基本要求。在3到10周的实践与探

索中,我们遇到了一些问题,我们就请教老师和其他同学,将这些困难一一解决了,小组中的

每个成员都很团结,努力的配合组长所分配的任务,并且积极完成任务。在此次的课程设计

中,我们不仅巩固了之前学过的专业知识,也增强了动手能力,也明白了团队凝聚力也是很

重要的。

3.6.参考文献

1.

邵贝贝译,嵌入式实时操作系统

µC/OS-II(第二版)[M]. 北京:北京航空航天大学出版社,

2003.

2.

魏洪兴等,嵌入式系统设计师教程

[M].北京:清华大学出版社,2006.

3. 王田苗,魏洪兴,嵌入式系统设计与实例开发(第三版[M].北京:清华大学出版社,2008.

4. 李宁,基于MDK的STM32处理器开发应用北京:北京航空航天大学出版社,2008.

5. 刘波文,ARM Cortex-M3应用开发实例详解 [M].北京:电子工业出版社,2011.

6. 刘波文,黎胜容,ARM嵌入式项目开发三位一体实战精讲[M]. 北京:北京航空航天大学出版社,2011.

7. ST 0008 Reference Manual. 2011.

8.

STMicroelectronics. STM32F10X参考手册.

9. STMicroelectronics. STM32固件库手册.

10.

a-Si TFT LCD Single Chip Driver ——ILI9341数据手册

山东建筑大学计算机科学与技术学院

42

课程设计指导教师评语

班级: 学生姓名: 学号:

指导教师评语(包括工作态度,遵守纪律;基本理论、知识、技能;独立工作能

力和分析解决问题的能力;完成任务情况及水平):

学生成绩(百分制):

指导教师签名: 年

43

月 日