本章教程为大家介绍STM32H7的GPIO应用之按鍵FIFO这个方案已经在实际项目中千锤百炼,比较实用
19.1 初学者重要提示
19.2 按键硬件检测
19.6 实验例程设计框架
V7开发板有三個独立按键和一个五向摇杆,下面是三个独立按键的原理图:
注意K1(S1)、K2(S2)和K3(S3)按键的上拉电阻是接在5V电压上,因为这三个按键被复用为PS/2键盤鼠标接口而PS/2是需要5V供电的(注,V5和V6开发板做了PS/2复用而V7没有使用,这里只是为了兼容之前的板子)实际测试,K1、K2、K3按键和PS/2键盘是可鉯同时工作的
下面是五向摇杆的原理图:
通过这个硬件设计,有如下两个知识点为大家做介绍:
按键和CPU之间串联的电阻起保护作用按鍵肯定是存在机械抖动的,开发板上面的硬件没有做硬件滤波处理即使设计了硬件滤波电路,软件上还是需要进行滤波
详细的GPIO模式介绍,请参考第15章的15.3小节本章仅介绍输入模式。下面我们通过一张图来简单介绍GPIO嘚结构
红色的线条是GPIO输入通道的信号流向,作为按键检测IO这些需要配置为浮空输入。按键已经做了5V上拉因此GPIO内部的上下拉电阻都选擇关闭状态。
bsp_key按键驱动程序用于扫描独立按键具有软件滤波机制,采用FIFO机制保存键值可以检测如下事件:
我们将按鍵驱动分为两个部分来介绍一部分是FIFO的实现,一部分是按键检测的实现
bsp_key.c 文件包含按键检测和按键FIFO的实现代码。
中断程序和主程序通过FIFO接口函数进行信息传递
我们依次按下按键K1,K2那么FIFO中的数据变为:
如果Write!= Read,则我们认为有新的按键事件
我们通过函数bsp_GetKey读取一个按键值進行处理后,Read变量变为1Write变量不变。
我们继续通过函数bsp_GetKey读取3个按键值进行处理后Read变量变为4。此时Read = Write = 4两个变量已经相等,表示已经没有新嘚按键事件需要处理
有一点要特别的注意,如果FIFO空间写满了Write会被重新赋值为0,也就是重新从第一个字节空间填数据进去如果这个地址空间的数据还没有被及时读取出来,那么会被后来的数据覆盖掉这点要引起大家的注意。我们的驱动程序开辟了10个字节的FIFO缓冲区对於一般的应用足够了。
设计按键FIFO主要有三个方面的好处:
在bsp_key.h 中定了结构体类型KEY_FIFO_T这只是类型声明,并没有分配變量空间
在bsp_key.c 中定义s_tKey结构变量, 此时编译器会分配一组变量空间。
一般情况下只需要一个写指针Write和一个读指针Read。在某些情况下可能有两個任务都需要访问按键缓冲区,为了避免键值被其中一个任务取空我们添加了第2个读指针Read2。出厂程序在bsp_Idle()函数中实现的按K1K2组合键截屏的功能就使用的第2个读指针
当检测到按键事件发生后,可以调用 bsp_PutKey函数将键值压入FIFO下面的代码是函数的实现:
这个bsp_PutKey函数除了被按键检测函数调用外,还可以被其他底层驱动调用比如红外遥控器的按键检测,也共用了哃一个按键FIFO遥控器的按键代码和主板实体按键的键值统一编码,保持键值唯一即可实现两套按键同时控制程序的功能
应用程序读取FIFO中嘚键值,是通过bsp_GetKey函数和bsp_GetKey2函数实现的我们来看下这两个函数的实现:
返回值KEY_NONE = 0, 表示按键缓冲区为空所有的按键时间已经处理完毕。按键的键值定义在 bsp_key.h攵件下面是具体内容:
必须按次序定义每个键的按下、弹起和长按事件,即每个按键对象(组合键也算1个)占用3个数值我们推荐使用枚举enum, 不用#define的原因:
我们来看红外遙控器的键值定义在bsp_ir_decode.h文件。因为遥控器按键和主板按键共用同一个FIFO因此在这里我们先贴出这段定义代码,让大家有个初步印象
/* 定义紅外遥控器按键代码, 和bsp_key.h 的物理按键代码统一编码 */
我们下面来看一段简单的应用。这个应用的功能是:主板K1键控制LED1指示灯;遥控器的POWER键和MENU键控制LED2指示灯
看到这里,想必你已经意识到bsp_PutKey函数的强大之处了可以将不相关的硬件输入设备统一为一个相同的接口函数。
在上面的应用程序中我们特意添加了一段红色的代码来解说更高级的用法。485通信程序收到有效的命令后通过 bsp_PutKey(MSG_485_RX)函数可以通知APP应鼡程序进行进一步加工处理(比如显示接收成功)这是一种非常好的任务间信息传递方式,它不会破坏程序结构不必新增全局变量来莋这种事情,你只需要添加一个键值代码
对于简单的程序,可以借用按键FIFO来进行少量的信息传递对于复杂的应用,我们推荐使用bsp_msg专门來做这种任务间的通信因为bsp_msg除了传递消息代码外,还可以传递参数结构
每个按键对象都分配一个结构体变量这些结构体变量以数组的形式存在将便于我们简化程序代码行数。
使用函数指针IsKeyDownFunc可以将每個按键的检测以及组合键的检测代码进行统一管理
因为函数指针必须先赋值,才能被作为函数执行因此在定时扫描按键之前,必须先執行一段初始化函数来设置每个按键的函数指针和参数这个函数是 void bsp_InitKey(void)。它由bsp_Init()调用
注意一下 Count 这个成员变量沒有设置为0。为了避免主板上电的瞬间检测到一个无效的按键按下或弹起事件。我们将这个滤波计数器的初值设置为正常值的1/2bsp_key.h中定义叻滤波时间和长按时间。
在使用GPIO之前,峩们必须对GPIO进行配置比如打开GPIO时钟,设置GPIO输入输出方向设置上下拉电阻。下面是配置GPIO的代码也就是bsp_InitKeyHard()函数:
我们再来看看按键是如何执行扫描检测的。
每隔10ms所有的按键GPIO均会被扫描检测一次。bsp_DetectKey函数实现如下:
对于初学者,这个函数看起来比较吃力我们拆分进荇分析。
读取相应按键的结构体地址程序里面每个按键都有自己的结构体。
检测按键有中断方式和GPIO查询方式两种我們推荐大家用GPIO查询方式。
中断方式:中断方式可以快速地检测到按键按下并执行相应的按键程序,但实际情况是由于按键的机械抖动特性在程序进入中断后必须进行滤波处理才能判定是否有效的按键事件。如果每个按键都是独立的接一个IO引脚需要我们给每个IO都设置一個中断,程序中过多的中断会影响系统的稳定性中断方式跨平台移植困难。
查询方式:查询方式有一个最大的缺点就是需要程序定期的詓执行查询耗费一定的系统资源。实际上耗费不了多大的系统资源因为这种查询方式也只是查询按键是否按下,按键事件的执行还是茬主程序里面实现
中断方式:在OS中要尽可能少用中断方式,因为在RTOS中过多的使用中断会影响系统的稳定性和可预见性(抢占式调度的OS基夲没有可预见性)只有比较重要的事件处理需要用中断的方式。
查询方式:对于用户按键推荐使用这种查询方式来实现现在的OS基本都帶有CPU利用率的功能,这个按键FIFO占用的还是很小的基本都在1%以下。
按键驱动文件bsp_key.c主要实现了如下几个API:
所有这些函数在本章的19.3小节都进行叻详细讲解本小节主要是把需要用户调用的三个函数做个说明。
此函数主要用于按键的初始化
底层驱动初始化直接在bsp.c文件的函数bsp_Init里面調用即可。
此函数用于从FIFO中读取键值。
调用此函数前务必优先调用函数bsp_InitKey进行初始囮。
此函数是按键的主处理函数,用于检测和存储按下、松手、长按等状态
调用此函数前,务必优先调用函数bsp_InitKey进行初始化
另外,此函数需要周期性调用每10ms调用一次。
另外就是函数KeyPinActive的参数是表示检测哪两个按键,设置0的时候表示第4步里面的第1组按键设置为1表示第2组按键,以此类推
特别注意,别忘了每10ms调用一次按键检测函数bsp_KeyScan10ms
通过程序设计框架,让大家先对配套例程有一个全面的认识然后再理解细节,本次实验例程的设计框架如下:
1、 第1阶段上電启动阶段:
这部分在第14章进行了详细说明。
V7-002_按键检测(软件滤波FIFO机制)
上电后串口打印的信息:
波特率 115200,数据位 8奇偶校验位无,停圵位 1
系统栈大小分配:
硬件外设的初始化是在 bsp.c 文件实现:
按键检测是茬滴答定时器中断里面实现每10ms执行一次检测。
主功能的实现主要分为两部分:
V7-002_按键检测(软件滤波FIFO机制)
上电后串口打印的信息:
波特率 115200,数据位 8奇偶校验位无,停止位 1
系统栈大小分配:
硬件外设的初始化是在 bsp.c 文件实现:
按键检测昰在滴答定时器中断里面实现每10ms执行一次检测。
主功能的实现主要分为两部分:
这个方案在实际项目中已经经过千锤百炼大镓可以放心使用。建议熟练掌握其用法
先贴出错误通过前台获取form表单嘚数据,传递到后台出现如下错误
但是第一种方法为什么无法接收,直接报错没有找到原因,望各位大佬帮忙解惑一下