本帖最后由 deru_qq 于 2015-12-21 08:42 编辑
3 基于超级终端的外置键盘 一般开发板只有有限的几个按键,有的甚至没有,想扩展键盘比较麻烦,使用也不方便,但是串口大多开发板都是有的,这部分就介绍如何使用串口配合PC的超级终端来外扩一个外置的键盘,有了这个键盘,几乎能输入电脑标准键盘上的所有按键,并且扩展非常方便,“即插即用”。在介绍这个模块之前,先做些铺垫。
3.1 MCU串行通信处理方法先介绍一下自己经常用的一个串口通信处理方法和一个自定义的printf函数,这些都是从日本人chaN(FatFs的作者)那里得来的,使用非常方便。 要介绍的这种串口通信方法使用了软件FIFO 发送时:应用程序写要发送的数据到发送FIFO,TX中断函数查询发送FIFO,如果不为空就从里面读数据发送出去 接收时: RX中断函数将接收到的数据写入接收FIFO,应用程序查询接收FIFO,如果不为空,就可以将数据读出来。 使用这种处理,应用程序编程变得很方便;但是移植时,发送中断的处理需要结合MCU的中断触发机制。后面会结合STM32和51单片机讲一下怎样移植这个模块。
3.1.1 软件FIFO操作 软件FIFO只需要提供MCU的开关中断的方法即可 关中断:FIFO_ENTER_CRITICAL() 开中断:FIFO_EXIT_CRITICAL()
3.1.2移植方法串口初始化 串口的初始化不同的平台都不一样,但是初始化之后要开接收中断,发送中断要不要开根据平台决定,下面例子有说。 中断触发条件的设置 对于接收中断,一般设为只要有数据接收到就触发中断,大部分MCU都支持这个功能。要注意的是有些MCU的串口接收具有硬件FIFO功能,接收中断的触发条件可以设为FIFO满或者FIFO不空,这时要设为不空,否则会有一些接收数据存留在FIFO中。 对于发送中断,有的MCU是发送寄存器空触发,有的是发送完成触发,这两个本质是不一样的,下面分别根据51和STM32来介绍。 STM32 初始化:初始化之后开接收中断,发送中断先不要开,接收中断触发条件是RXNE。 Uart_Putc()函数:(阻塞) void Uart_Putc(unsigned char c) { Fifo_Write(&TxFifo, c); __HAL_UART_ENABLE_IT(&UartHandle, UART_IT_TXE); } 将要发送的字节写入FIFO,开发送中断,发送中断的触发条件为TXE,即发送寄存器空触发中断,这时如果没有数据在发送,会立刻进入中断
Uart_Getc()函数(阻塞) unsigned char Uart_Getc(void) { returnFifo_Read(&RxFifo); } 不用移植,注意这个函数是阻塞函数,如果需要非阻塞函数,可以先查询一下FIFO是否为空,如果为空,读取失败返回。
中断处理函数: void USART3_IRQHandler(void) { unsigned charc;
if(__HAL_UART_GET_FLAG(&UartHandle, UART_FLAG_RXNE)==1) { c =(unsigned char)((UartHandle.Instance)->DR); if(!Fifo_Full(&RxFifo)) Fifo_Write(&RxFifo, c); }
if(__HAL_UART_GET_FLAG(&UartHandle, UART_FLAG_TXE)==1) { if(!Fifo_Empty(&TxFifo)) { c =Fifo_Read(&TxFifo); (UartHandle.Instance)->DR = (unsigned short)c; } else { __HAL_UART_DISABLE_IT(&UartHandle, UART_IT_TXE); } } } 中断处理函数,对于接收中断,将接收到的数据写入接收FIFO即可,但是如果FIFO已满,接收到的数据将被丢弃;对于发送中断,如果FIFO不为空,读出数据发送,如果FIFO为空,注意要禁用发送中断,否则会一直进入中断。
51单片机 初始化: 初始化之后,开中断,51的接收和发送是一个中断,只能同时使能或禁用 Uart_Putc()函数: void Uart_Putc(unsigned char c) { Fifo_Write(&TxFifo, c); EA= 0; if(!TxRun) { TxRun= 1; TI= 1; } EA= 1; } 先写数据到FIFO,51的发送中断是发送完成触发,并且可以软件置位标识位触发,这里就是将TI置1来触发软件中断;这里有一个TxRun变量,如果有数据要发送,要将这个变量置1,中断程序会在FIFO中所有数据接收完成之后将其清零,注意修改TxRun时要先关中断。 void UartISR(void) interrupt 4 { if(RI){ Fifo_Write(&RxFifo,SBUF); RI= 0; } else { TI= 0; //清除发送中断标志 if(!Fifo_Empty(&TxFifo){ SBUF = Fifo_Read(&TxFifo); } else { TxRun= 0; } } }
3.1.3 非阻塞函数 前面的getc和putc函数都是阻塞函数,但有时需要非阻塞函数,这时可以使用FIFO_Full()和FIFO_Empty()函数先判断一下,如果可以写或者读,就执行,然后返回成功,否则返回失败,这个功能配合定时器就可以实现带超时的非阻塞函数。 3.2 xprintf模块 在串口输出时,如果将putc重定位,可以使用C标准库的printf函数,这对于调试意义重大,但重定向到标准的printf函数好像比较麻烦,编译出来的代码也会增加,RAM小的情况下也比较容易出问题(我自己没试过),所以一直以来都没用过,一直在用的是chaN的xprintf模块,这个模块基本上具备了printf的所有功能(不支持浮点数打印),还有sprintf,等等,代码编译之后大概2K,实现只需要stdarg.h头文件,这个头文件中都是一些宏操作,没什么消耗,它有几个好处: 1. 使用方便:不需要移植,通过一个函数调用完成重定向,也可以将输出函数作为参数来实现临时的重定向 2. 适合调试用:模块提供了xgetc, xgets,xatoi等函数,具有回显功能,可以方便从串口输入数据,完成字符串到整数的转换等,后面Demo程序会看到相关的用法。 模块使用 这个模块不用移植,可以直接使用,只需要调用两个函数: xdev_out(Uart_Putc); xdev_in(Uart_Getc); 这样就完成了重定向,虽说一般用于串口,其实这个模块可以用于任何字符输出设备,比如液晶。 chaN的网站上有很多值得学习的地方,电子爱好者不要错过 见附件文档 3.3 基于超级终端的外置键盘3.3.1设计思路: 在PC的超级终端下,按下一个按键,会(不是所有按键都会)通过串口发送一个字符,有些功能键按下之后会发送3个字符,可以利用这个特性来用MCU的串口扩展一个外接键盘。 键盘扫描: 根据前面的介绍,串口接收到的字符都存放在接收FIFO中,这样隔一段时间去查询一下FIFO,如果有数据,将数据读出,就知道那个按键按下,然后执行相应的处理,而这个FIFO相当于键盘的缓冲区(本模块使用了单独的按键缓冲FIFO),有些功能键按下会一下发送3个字符,这三个字符如果分别读出会被误判为其他按键按下,如:UP键对应的键盘码为1B 5B 41,如果顺序读出来,会认为ESC(1B),“[”和“A”按下了,对于这种情况,可以用一个状态机来处理,如果接收到的字符是1B,那么延时5ms,再扫描,如果5ms之内又接收到了数据,认为新接收到的数据和将要接收到的数据是同一个按键的码值,如果5ms之内没接收到,说明按下的就是ESC键。 本模块在定时器中断中执行案件扫描,占用CPU时间很少,并且这个外接键盘有一个好处,就是免去了判断按键键值的麻烦,接收到的字符既是按键键值。 超级终端的高级用途: 超级终端除了可以按顺序输出打印的字符外,还可以在指定位置显示字符,改变背景颜色,字符颜色和显示模式等,完全可以当做一个简单的监控界面来使用,在要介绍的模块中,实现了几个简单的函数,想了解超级终端高级用途的可以参考这里: 见附件文档
3.3.2 模块移植 这个模块的移植需要fifo.c,其他只需要提供一个非阻塞的串口接收函数 #define ht_getc_unblock(c) Uart_Getc_Unblock(c) 如果接收FIFO为空,这个函数返回0,如果接收FIFO不为空,从接收FIFO中读出一个字节,将这个字节赋值给*c 3.3.3 接口函数
KeyBuf_Proc():这个函数要放在1ms定时器中断服务函数中执行 HT_GetKey():从键盘缓冲中读取一个按键,如果读到的值为0,说明没有按键按下。 HT_KeyScan_Enable(),HT_KeyScan_Disable():键盘的扫描可以使能或禁用,使能键盘扫描时,串口接收FIFO中的数据会在KeyBuf_Proc()函数中读入键盘缓冲;禁用扫描时,键盘缓冲会被清除,KeyBuf_Proc()函数也不会执行按键扫描,超级终端可以当做普通的串口终端来使用。 下面这几个函数属于超级终端的高级用法了 HT_ClearScreen():清除当前显示 HT_SetStyle():设置显示风格,可以是高亮,闪烁,带下划线等 HT_SetBackground():设置字符背景色 HT_SetForeground():设置字符前景色 HT_SetXY():设置光标位置(注意光标的原点位置坐标是(1,1)不是(0,0) 在Demo程序中会看到相应的用法。
4. 综合DEMO程序 前面讲了这么多,看得都没劲了,还是来个Demo程序来演示一下吧 硬件准备: 将STM32F469I-DISCO通过STlink的USB接口连接到电脑,保证可以下载程序,并且虚拟串口可以使用(这个虚拟串口的驱动相当难装,我自己折腾了一个下午才装成功,后面会附上这个方法),下载程序。 超级终端演示:
程序默认将输出函数定位到了串口上,超级终端会打印Hello World,并有“>”来提示输入命令,这个Demo程序支持几个命令,用于超级终端演示的有 “dir2lcd”:定位输出函数到液晶,如果输入这个命令,显示将会从超级终端切换到液晶。 “ht_test”:进入外置键盘的测试程序,进入之后的界面是这样的:
按下一个按键,将会在屏幕固定位置打印出按下的按键及其键值,按ESC键会退出测试模式。 液晶和CUI演示 在超级终端中输入“dir2lcd”命令将输出重定向到LCD,这时要看LCD屏幕了,在LCD屏幕上可以有以下命令: “dir2ht”:切换输出到超级终端 “opt_enable”:使能CUI的DMA2D硬件加速,上电默认是使能的 “opt_disable”:禁用CUI的DMA2D硬件加速 “cui_test”:输入这个命令会打印出来几行字符,多输入几次写满整个屏幕,可以看滚屏的效果,并且可以对比开优化和不开优化的效果。
附录:关于附件中的DEMO工程 cui_demo工程文件和STM32CUBEF4软件包的位置关系如下:
工程所需要的文件都在cui_demo/src文件夹下,串口驱动的HAL库太乱了,自己写了一个,比较简洁。
STM32CUBEF4软件包太大了就不上传了
|