昨天写了一下STM8环境下GPIO的流水灯操作说明以及操作系统的OS_ThreadDelay延时函数,然而流水灯只是最基本的一个小实验,并没有太大的实用价值,所以今天的帖子是GPIO操作的升级版,也是操作系统环境下的"管理组件"的驱动开发示例帖,今天的帖子是示范一下系统环境下的矩阵键盘驱动编写以及在STM8环境下的实现。
在Myos操作系统环境下的按键驱动接口原型有三个,代码如下:- /*********************************************************************系统按键管理驱动函数*********************************************************************/
- /*
- *函数名:MCU_ButtonIOInit( void )
- *入参:无
- *返回值:无
- *作用域:本地
- *线程访问:回调
- *函数作用:初始化系统硬件成为可使用的按键输入
- */
- void MCU_ButtonIOInit( void )
- {
- }
- /*
- *函数名:MCU_GetButtonvallower( void )
- *入参:无
- *返回值:按下的按键状态
- *作用域:本地
- *线程访问:回调
- *函数作用:获取当前键盘中被按下的按键低32位
- */
- unsigned int32 MCU_GetButtonvallower( void )
- {
- return 0x00;
- }
- /*
- *函数名:MCU_GetButtonvalhigh( void )
- *入参:无
- *返回值:按下的按键状态
- *作用域:本地
- *线程访问:回调
- *函数作用:获取当前键盘中被按下的按键高32位
- */
- unsigned int32 MCU_GetButtonvalhigh( void )
- {
- return 0x00;
- }
复制代码 这三个驱动接口原型函数位于Myos_driver文件夹下的myosmanagedriver.c文件中,系统会在组件初始化阶段调用MCU_ButtonIOInit函数来执行按键的输入输出口属性初始化工作,而在需要进行按键检测的时候通过调用MCU_GetButtonvallower和MCU_GetButtonvalhigh函数来获取最大64个按键的状态,并在系统内部进行处理,对按键进行单击、长按检测,并提供一系列的接口来使能或禁能按键的连续输出,具体的功能会在后续的帖子中一一说明。
我所使用的开发板上有一个2x3的矩阵键盘,共计6个按键,一般情况下入门单片机的时候大家接触的按键一般都是独立按键,也就是每个IO接一个按键,通过IO的状态来判定按键的按下或抬起状态,而在实际的项目中大多使用的是矩阵键盘。
矩阵键盘的原理实际上是分而治之,举个例子,大家上小学的时候举手,一个教室有很多行有很多列的学生,有的学生举手而有的学生不一定举手,而老师看的时候也不是同时看的,一般都是先看第一行,然后再看第二行、第三行,以此类推。矩阵键盘也是一样的原理,一行按键一行按键的检测。
话不多说,根据我手里开发板的原理进行驱动的编写,上传的文件中所有的文件均为只读文件,请大家找到对应的文件路径然后解除只读属性后进行修改,编写后的代码如下:
- /*********************************************************************系统按键管理驱动函数*********************************************************************/
- /*
- *函数名:MCU_ButtonIOInit( void )
- *入参:无
- *返回值:无
- *作用域:本地
- *线程访问:回调
- *函数作用:初始化系统硬件成为可使用的按键输入
- */
- void MCU_ButtonIOInit( void )
- {
- GPIOD->DDR &= ~0x1C; //设置PD2-PD4为输入
- GPIOD->CR1 |= 0x1C; //设置PD2-PD4为上拉
- GPIOD->CR2 &= ~0x1C; //设置PD2-PD4不中断
- GPIOA->DDR |= 0x06; //设置PA1-PA2为输出
- GPIOA->CR1 &= ~0x06; //设置PA1-PA2为开漏
- GPIOA->CR2 |= 0x06; //设置PA1-PA2为10MHZ
- }
- /*
- *函数名:MCU_GetButtonvallower( void )
- *入参:无
- *返回值:按下的按键状态
- *作用域:本地
- *线程访问:回调
- *函数作用:获取当前键盘中被按下的按键低32位
- */
- unsigned int32 MCU_GetButtonvallower( void )
- {
- unsigned dshort16 regist; //寄存变量
- GPIOA->ODR |= 0x06; //设置PA1-PA2为高电平
- GPIOA->ODR &= ~0x04; //设置PA2位低电平,准备扫描第二行
- OS_Delay(0x02); //等待IO稳定
- regist = GPIOD->IDR; //获取第二行的状态
- GPIOA->ODR |= 0x06; //设置PA1-PA2为高电平
- GPIOA->ODR &= ~0x02; //设置PA1位低电平,准备扫描第一行
- regist >>= 0x02; //进行按键处理同时等待IO稳定
- regist <<= 0x03; //进行按键处理同时等待IO稳定
- regist |= 0xFFC0; //进行按键处理同时等待IO稳定
- regist |= ((GPIOD->IDR) >> 0x02); //获取第一行的状态
- regist = ~regist; //低电平有效转高位有效
- return regist; //返回按键状态
- }
- /*
- *函数名:MCU_GetButtonvalhigh( void )
- *入参:无
- *返回值:按下的按键状态
- *作用域:本地
- *线程访问:回调
- *函数作用:获取当前键盘中被按下的按键高32位
- */
- unsigned int32 MCU_GetButtonvalhigh( void )
- {
- return 0x00;
- }
复制代码 MCU_ButtonIOInit函数中对状态线PD2-PD4初始化为上拉输入,且不开启中断功能,那么这三个IO就相当于是一个高阻输入的IO再加一个上拉电阻,用来捕获按键的按下状态,而对扫描线PA1-PA2初始化为开漏输出,之所以设置为开漏输出是为了保证没有高电平输出,高电平由输入口的上拉电阻提供而不是由单片机的推挽输出提供,因为如果按键有时候会多个按键同时按下,一但PA1和PA2直接导通则会烧毁IO口,开漏输出由于没有高电平(扇出电流)输出所以不会导致IO的烧毁。另外我的板子上只有6个按键,所以没有必要使用高32位的驱动函数,只需要使用低32位的驱动函数来捕获按键状态既可。
MCU_GetButtonvallower函数里包含了矩阵按键的扫描方法,首先将置低PA2扫描第二行,因为我们采用的是左移的方式来处理,先扫描的按键值会更大,第一次扫描需要进行一些延时来等待IO状态稳定,OS_Delay这个函数也是一个系统函数,但是这个函数只是粗略延时,延时时长为:(参数*3)个时钟周期。而在第二次扫描的时候就可以进行regist变量的处理来模拟延时,同时也可以先进行一部分的处理工作,提高驱动的效率。当驱动写完后,则按键的键值编码如下:第一行(PA1)的第一个(PD2)为1、第二个(PD3)为2、第三个(PD3)为3;第二行第一个(PD2)为4、第二个(PD3)为5、第三个(PD3)为6。
当需要进行按键检测的时候调用函数OS_GetKey或OS_GetChar即可获取到当前的按键状态,该函数的函数原型为:
- /*
- *函数名:OS_GetKey( void )
- *入参:无
- *返回值:当前按下的键值
- *作用域:本地
- *线程访问:安全
- *函数作用:获取当前键盘的键值,该函数不会阻塞线程,如没有按键按下则返回0,如为长按则最高位为1
- */
- unsigned char8 OS_GetKey( void );
- /*
- *函数名:OS_GetChar( void )
- *入参:无
- *返回值:获取当前按下的按键
- *作用域:本地
- *线程访问:安全
- *函数作用:获取当前的按键情况,该函数会阻塞线程,如果是长按则返回值的最高位为1
- */
- unsigned char8 OS_GetChar( void );
复制代码 上述两个函数均可获取按键的按键值,不同之处在于OS_GetKey不会阻塞当前的线程,如果没有按键按下则返回0,而OS_GetChar会阻塞线程,如果没有按键按下则一直在本函数中等待,直到按键按下为止,而如果发生了长按则两个函数返回值的最高位(位7)均为1,低7位则是键值,键值对应关系为1~32为MCU_GetButtonvallower驱动函数返回值的0~31位,而33~64为MCU_GetButtonvalhigh驱动函数返回值的0~31位。
我在例程中使用的函数为OS_GetKey来获取按键值,并且根据按键的值改变流水灯的移动速度,代码如下:
- #include "myos.h"
- #include "osdemo.h"
- /*
- *函数名:OS_ApplicationMainThread(void *arg)
- *入参:
- arg:线程运行参数
- *返回值:无
- *作用域:本地
- *线程访问:安全
- *函数作用:应用程序主线程
- */
- void OS_ApplicationMainThread(void *arg)
- {
- unsigned char8 key = 0x00; //按键寄存
- unsigned char8 regist = 0x00; //寄存变量
- unsigned short16 delay = 1000; //延时寄存
- GPIOC->DDR = 0x1E; //设置PC1-PC4为输出模式
- GPIOC->CR1 = 0x1E; //设置PC1-PC4为推挽输出
- GPIOC->CR2 = 0x1E; //设置PC1-PC4位10MHZ输出
- while(true){
- regist &= 0x0F; //只取4位有效位
- regist = (regist == 0x00) ? 0x01 : (regist << 0x01); //流水灯移动及越界判定
- GPIOC->ODR = (~regist << 0x01); //LED灯点亮赋值
- key = OS_GetKey(); //获取键值
- if(key > 0x00){ //有按键按下
- delay = key & 0x7F; //去除长按标识
- delay *= 200; //计算延时时间
- }
- OS_ThreadDelay(delay); //系统提供的延时函数
- }
- OS_ThreadExit(); //线程结束退出
- }
复制代码 OK,今天的帖子到此为止,下一个帖子的内容是SPI(串行外设接口)的科普帖。
|