前言
一般进行远程监控时,2.4G无线通信是充当远程数据传输的一种方法。这时就需要在现场部分具备无线数据发送装置,而在上位机部分由于一般只有串口,所以将采集到的数据送到电脑里又要在上位机端设计一个数据接收的适配器。这里基于stm32分别设计了现场部分和适配器部分,这里只是基本通信功能实现的讲解,一些复杂的技术比如加密、可靠等要根据具体的应用来设计~

总体说明
这里采用stm32作为MCU,采用nRF24L01作为2.4G通信模块。其中适配器中仅仅采用了USART和NRF24L01两个主要部分,负责将下位机通过2.4G发送过来的数据通过串口发送给上位机,或者将上位机的通过串口传来的数据通过2.4G发送给下位机来实现远程监控(没有采用uc-os操作系统,也没有界面,要用串口和上位机相连);其中下位机比较复杂,因为一般下位机是一个集成的系统,包括从各种传感器收集数据、向各种类型的驱动电路发送控制命令、将数据输给打印机或显示器、和无线通信或有线通信设备进行互相通信来实现数据传输等,这里的下位机比较简单:采用uc-os实时操作系统+uc-gui负责界面显示,外接7寸TFT液晶显示屏,和适配器类似也包括USART和NRF24L01通信部分,但是因为有了操作系统和可视化交互界面,所以也有点不同,接下来开始介绍。
适配器部分
这里介绍的流程是以main函数为基准,广度拓宽知识点,最后main函数说完,整个工程的细节也就大致能了解了~
<a></a>
第4行RCC初始化主要是系统时钟和外设时钟配置,这里注意要使能RCC_APB2Periph_USART1,当时忘了使能这个结果串口出现异常,我还以为是初始化和中断向量什么的弄错了呢,浪费了很长时间。
第7行中断向量初始化设置,主要是设置串口接收中断和NRF24L01中断的,这样设置好了之后当串口中断被触发时其对应的中断子程序将被执行(这个科班的大概都知道这里就不多说了),所以我们就要在stm32f10x_it.c里实现他们各自的中断子程序了(这个一会再详细介绍,咱们先把整个框架了解下)。另外说一句,这里的的优先级组将影响主优先级和子优先级数量具体请参考stm32f10X_的固件库的NVIC.
第11行的GPIO初始化,主要是对通用IO口的属性设置和初始化,这里一定要对串口所需的A9和A10配置好!
第12行的SPI2_NRF24L01_Init();主要是驱动NRF24L01的接口初始化,因为NRF24L01采用的是SPI通信,所以这里免不了SPI的设置和相关操作了,不过幸好都封装好了~像以前在51上做SPI就得自己模拟SPI,没有示波器调试起来甚是坑~此外这里我已经把NRF24L01的整个驱动都封装在NRF24L01.c这个文件里了,当想用的时候只要在中断向量里设置其中断接收函数,并在it.c里实现其接收函数;一般主函数里用到的是其初始化函数SPI2_NRF24L01_Init();和 RX_Mode();,当在过程中想利用NRF24L01向外发数据时只要调用函数void NRF_Send_Data(uint8_t* data_buffer, uint8_t Nb_bytes):
第13行是USART初始化,包括波特率、数据位、停止位等~
同样的类似于NRF24L01一旦初始化之后,其数据接收一般采用中断方式、数据发送一般采用直接发送的方式。所以在中断向量里也要设置,也要在it.c中实现其接收中断子函数。其发送直接调用stm32f10的固件库函数(这里我稍加封装了下):其实就是发送一个data之后要监听是否发送完成才能进行下次发送~
接下来进入while循环,不断进行监听看是否有串口接收标志位置1或者无线模块接收标志位置1,如果有表明相应的有数据从该通道传送过来。当是从串口传来的数据表明数据是从上位机发送来的数据,并且想把该数据通过2.4G发送出去,所以这里调用:NRF_Send_Data(TxBufferRF,sizeof(TxBufferRF));将数据发送出去;当数据是从2.4G通道中传过来的,表明数据是从下位机传送过来的想给上位机,于是调用串口发送函数将数据发送给上位机:USART_SendChar(USART1,TxBufferUSART[i]);
看了上图适配器端的数据交换过程就明白了串口中断和无线中断大致要干的事了,这里我就不多介绍,看看下面的代码就明白了(在stm32f10x_it.c中),要再次提醒的是无论是串口还是无线其接收都是采用中断,而发送采用循环直接发送,他们的中断和中断向量有关并要在stm32f10x_it.c里实现相应的中断子程序~
串口和无线接收中断子程序
下位机部分
上面说过一般具有远程通信能力的嵌入式系统其下位机部分往往要干很多事,这里我们采用stm32作为MCU并搭载uc-OS实时操作系统负责任务调度,同时采用7寸TFT彩屏和uc-GUI设计可视化人机交互界面。其中任务包括主任务、界面任务和触摸任务,主任务负责建立其他任务,界面任务中将包含整个人机交互界面的界面刷新逻辑,触摸任务负责获取触摸位置数据获取~
这里我们还是得从main函数先说起:首先在main函数中进行相关初始化,然后建立主任务并启动uc-OS内核;接着在主任务中调用App_TaskCreate(); 分别建立界面任务和触摸任务(如下每个任务的建立类似,要给出指向任务代码的指针、任务执行时传递给任务的参数的指针,分配给这个任务的栈信息,任务优先级等)。这样当任务建立好之后,其执行权就由操作系统调度了~
这里以界面任务为例:因为我们在建立界面任务时已经指定其任务代码指针AppTaskUserIF,所以这里来写其对应的函数(也就是说这里是界面任务的入口)。从下面的代码可以看出进入界面任务时首先对uc-GUI进行初始化,然后进入死循环不断执行Fun()函数(有人会疑惑:这里while死循环不就只能死在这里吗?怎么执行其他任务呢?哈哈,这就是具有操作系统和不具有操作系统的不同啦~虽然这里是while死循环,但是当OS要把CPU占有权分给其他任务时就会把当前执行的任务的信息压入其对应的栈空间,当再次要把CPU分配给该任务时,则把栈里保存的上次执行的情况拿出来继续执行,从而实现抢占与多任务的效果!)
所以接下来我们主要看Fun.c里的Fun函数:虽然代码有点长,但是很好理解,其核心思路就是建立整个界面并对界面中的每个控件进行相关设置同时获得其句柄,在最后又进入了while死循环,在循环中不断检测2.4G是否接受到数据(和适配器端类似也是中断子程序中收数据然后置接收标志为1的),然后根据从2.4G收到的数据来刷新文本显示区;下面一个if判断speed_change_flag是否有效来向串口发送相应的数据。那么我们的问题又来了:这个speed_change_flag是在哪里被改变的呢?这个我们就要参看窗口回调函数了!这里的窗口回调函数是窗口动作响应函数(就像安卓开发里的按钮监听或MFC里的按钮点击事件等),一旦窗口里的控件有相应的触发动作就会调用该函数,并把事件类型封装在WM_MESSAGE里传过来,在该函数里对该消息进行解析并作出相应的动作即可(非常像Win32!!!我怀疑做这个uc-GUI的人有copy微软的嫌疑,(^∇^*)随便猜测,如有雷同,纯属巧合)。这样我们就很容易找到send按钮的监听用于将数据通过NRF24L01发送出去的相关操作,也就明白了滑动条监听用来改变speed1~5.上面说了这么多,少了介绍整个界面是怎么建立的了~其实整个窗体的布局都要放在一个结构体里,然后在fun()函数里调用hWin = GUI_CreateDialogBox(aDialogCreate, GUI_COUNTOF(aDialogCreate), _cbCallback, 0, 0, 0);根据定义的窗口资源和回调函数进行窗体的建立~这样我们就圆满地理解了stm32基于uc-OS并搭载uc-GUI的运行逻辑啦!
Fun()
窗口回调函数
还要回过头说说我们的USART和NRF24L01,他们的初始化要看main函数中的BSP_Init();函数,该函数负相关硬件的初始化设置(中文意思是板级支持包初始化函数,因为uc-OS可以并不只限于stm32单片机,所以这里要根据不同平台进行相应的设置)。该函数位于bsp.c函数中,其作用相当于将以前我们在main函数中进行的相关硬件初始化单独拿出来封装成一个函数而已~但是,串口和无线对应的中断接收程序却有点不一样,因为这里是操作系统,所以在每个中断子程序前要调用OS_ENTER_CRITICAL();保存当前的全局中断标志,然后OSIntNesting++;中断嵌套深度加1,最后调用OS_EXIT_CRITICAL();恢复全局中断标志进入正常的中断处理,此外在中断响应函数最后要调用OSIntExit(); 检测如果有更高优先级的任务就绪了,则执行一次任务切换。
串口和无线中断子程序
最后说明
对于纯玩软件的小伙伴,这里涉及的东西有点多,不必细究,看看了解即可。但是对于初学stm32,尤其是还在为stm32控制NRF24L01不通的同学,这个还是挺有用滴~下面有工程的链接,里面有些注释不规范,一切以我博客里说的为准哦~
链接
本文转自beautifulzzzz博客园博客,原文链接:http://www.cnblogs.com/zjutlitao/p/4242734.html,如需转载请自行联系原作者