国庆带女友回家了,第一次去家里,女友好像不是很开心。哎,好好学习,天天向上吧。
今天开始来看看开发板上的按键并学习相关的驱动程序。
实物图与原理图
先来看看实物图:
由下往上,由左往右,六个按键依次是 K1、K2、K4、K3、K5、K6
各按键定义的功能如下:
再来看看原理图:
可以看到,六个按键依次连接S3C2410的GPF口线的 2-7等6位(GPF[7:2])。均接上拉电阻,非按下时候各口线电平均为高电平,按下按键为低电平,机械键盘,程序中要注意延时防抖。
GPF
现在来看看GPIOF口操作的相关注意事项。
上S3C2410datasheet
简单说一下,GPF口共8根口线,用作普通输入输出,也可以专门配置为外部唤醒模式,此时相应IO口线要配置为中断模式。
总共3个相关寄存器,GPFCON 配置引脚功能、GPFDAT输入输出相关数据、GPFUP使能上拉电阻。并没有什么特别的。现在就来看相关驱动程序设计。
KEY驱动程序设计
为了不侵害原作者的版权,此处仅分析功能,不对部分代码细节作详细展示。
此处先定义了7个常量值,具体作用下面会具体分析。
先看看处理按键任务的主函数:
// 函数功能:按键任务主函数
// 函数名称:Key_Main
void Key_Main( void )
{
UINT32 dwValReg;
UINT8 byKey;
//KEY1--KEY6 配置GPF[7-2]为普通输入模式,GPFCON的[15-4]全部清零即可
SNGS3C_REG_READ( rGPFCON, dwValReg );//GPF2-7----[15:4]
dwValReg = dwValReg & 0x000F;
SNGS3C_REG_WRITE( rGPFCON, dwValReg );
while( 1 )
{
Key_Process( );
taskDelay( 1 ); // 任务函数中延时,主动让出CPU
}
}
再来看看具体的按键处理函数 Key_Process
// 函数功能:去抖处理;连续按键处理;
// 函数名称:Key_Process
void Key_Process( void )
{
BYTE byKeyTemp;
if( EN_KEY_FLAG_NEW ==g_tKey.byKeyValid ) return; // 按键已经处理
if( EN_KEY_FLAG_NULL==g_tKey.byKeyValid ) // 没有新按键
{
byKeyTemp = Key_Sample( ); // 继续新一轮采样
g_tKey.byKeyValid = EN_KEY_FLAG_SAM;
if( g_tKey.byKeyValue==byKeyTemp ) // 与上一轮采样值相同
{
g_tKey.bSameKey = TRUE;
}
else
{
g_tKey.bSameKey = FALSE;
g_tKey.byKeyValue = byKeyTemp;
}
}
else if( EN_KEY_FLAG_SAM==g_tKey.byKeyValid ) // 正在采样过程中...
{
byKeyTemp = Key_Sample( ); // 消抖采样
if( byKeyTemp !=g_tKey.byKeyValue ) // 采到不同值则无需消抖计时
{
g_tKey.bSameKey = FALSE;
g_tKey.byKeyValue = byKeyTemp;
g_tKey.byKeyTimes = 0;
printf("不同的按键g_tKey.byKeyValue=0x%x\n",byKeyTemp);
}
else if( byKeyTemp != CN_KEY_NONE )
{
g_tKey.byKeyTimes++; // 消抖按键计时(次数增加)
if( ((FALSE==g_tKey.bSameKey)
&&(g_tKey.byKeyTimes>=CN_KEY_TIMES_ONE)) // 80ms(时间计算不好)
|| (g_tKey.bSameKey
&&(g_tKey.byKeyTimes>=CN_KEY_TIMES_TWO)) ) // 240ms
{
g_tKey.byKeyTimes = 0;
g_tKey.byKeyValid = EN_KEY_FLAG_NEW;
}
printf("相同按键g_tKey.byKeyValue=0x%x\n",byKeyTemp);
} // end "if( byKeyTemp )... else "
}
return;
}
来简单学习一下这段代码,首先是一个全局结构体变量 g_tKey 用来存储和按键状态相关的信息,其定义如下:
// 按键控制结构
typedef struct
{
BYTE byKeyValid; // 有效标志,用以标识是否有新按键按下
BYTE byKeyValue; // 键值
BYTE byKeyTimes; // 去抖动的次数
BOOLEAN bSameKey; // 标志是否是连续按同一个键
}tagKeyCtrl;
tagKeyCtrl g_tKey;
Key_Process 分三种情况处理:
- 当按键值已经有效 EN_KEY_FLAG_NEW (处理过),则直接返回
- 若没有新按键 EN_KEY_FLAG_NULL,则:
- 先读取按键值 Key_Sample()
- 置按键采样保持状态 EN_KEY_FLAG_SAM
- 若新读取的键值与原值相同,则置连续同一按键标志;否则,存新值,复位同一按键标志
- 若正在采样中 EN_KEY_FLAG_SAM,则继续读取按键值 Key_Sample(),并判断是否是同一按键
- 若为不同按键,则置 g_tKey 相关位,并在串口输出相关信息和键值
- 若为相同按键,则 去抖次数自加 byKeyTimes,并判断完成延时次数后,置处理完成标志EN_KEY_FLAG_NEW
再来看看键值采样函数 Key_Sample()
// 函数名称:Key_Sample
// 函数功能:读取按键值
BYTE Key_Sample( void )
{
UINT32 iKey;
iKey = *(volatile UINT32 *)rGPFDAT;
iKey = (iKey & 0xFF)>>2; // GPF[7-2]最低两位不需要
return( iKey );
}
这个函数没什么好说的,直接读取端口电平值,唯一要注意的是硬件访问方式,注意加关键字 volatile,
要学会这种写法 * (volatile UINT32 *) rGPFDAT
同时也给其他需要读取按键值的任务提供一个读取按键值的接口函数 Key_Read():
// 函数名称:Key_Read
// 函数功能:读取当前按键值并清除新按键标志
// 返回值: 当前按键值
BYTE Key_Read( void )
{
BYTE byKeyTmp;
if( EN_KEY_FLAG_NEW ==g_tKey.byKeyValid ) // 新按键值已经处理完成状态
{
byKeyTmp = g_tKey.byKeyValue; // 直接读取键值
g_tKey.byKeyValid = EN_KEY_FLAG_NULL; // 复位无按键状态
}
else
{
byKeyTmp = CN_KEY_NONE; // 新按键值正在消抖处理过程中,直接返回无效值
}
return( byKeyTmp ); // 返回键值
}
小结
此处的Key_Process和Key_Sample都挺简单的,但思想很重要:
- 用一个全局结构体变量 tagKeyCtrl g_tKey 来存储有关按键的信息,系统内其他任务可以直接读取变量 g_tKey 的相关值; 一个任务专门负责写键盘值,其他需要按键值的任务直接读取 g_tKey 相关值,任务之间互不影响,实时性好,不会造成资源互斥。本质其实就是用一个全局变量进行内存共享。
- 提供读按键值接口函数 BYTE Key_Read(void) ,其他任务直接利用此函数即可获取按键值
此KEY驱动程序简单但完善,读键值-向其他任务提供键值接口。
(代码来自 Rock,若有侵权请联系我删除)