在EC的各项功能中,按键的作用非常重要。机器上的PowerSwitch按键,亮度加减按键,声音加减按键,WIFI&Bluetooth开关等,都有可能用到按键功能。
按键的做法有两种:一是使用中断的方式;二是使用轮循GPIO的电平状态来做按键功能。这篇文章的重点在于介绍轮循的方式,因为第一种方式让我吃了不少苦头,现在我都一般不会采用它。
1 中断方式
这是做按键功能的正统方式,做的时候一板一眼,上学读书的时候也是这么做的。分为以下几个步骤:第一步,想使用哪个GPIO做按键,就使能相应GPIO的中断,并设置相应的中断方式,即电平或边沿触发;第二步,在中断函数中,关中断清中断标志并延时去抖,然后判断电平状态,如果无误,则调用该按键的功能函数;第三步,开中断,迎接下一步的中断。
2 轮循GPIO电平状态
这是我大比较喜欢的方式,当我一接触到这种方式时,便将以前的代码全改成这种方式。甚至连其它的8051单片机程序,我都这样做。
2.1 优势
这种方法相对第1种方式,占据比较大的优势。首先,它不会用程序延时,所以不会拖慢其他模块的功能;其次,编程相对简单,只要逻辑层做起来,留出一个数据数组给OEM层来使用,填一个数组总比写程序失误率要小得多;再者,有很多按键功能需要repeat,用中断的方式没法准确控制repeat的时间,而且延时得太多,用轮循的方式可以轻而易举完美地做到这个功能(我自己加的)。
2.2 具体实现方式
我们来看一下OEM接口:
typedefvolatile struct _EVENT_DESCRIPTOR
{
BYTE PortPin; //使用的GPI
BYTE Bounce; // 去抖延时时间
}EVENT_DESCRIPTOR;
static constEVENT_DESCRIPTOR Events_Table[] =
{
{PWRBTN_IN, 10}, //PowerSwitch
{VOLUME_UP, 10}, //声音加
{VOLUME_DOWN, 10}, //声音减
};
签了NDA,就要为别人保密。扫描按键的代码就不贴了,画张流程图吧。
流程图:
2.3 加repeat功能
Vendor毕竟不是OEM,并不知道OEM厂商的具体需求,所以没有加入repeat功能,但基于vendor的codebase,加repeat功能并不困难。
首先改变结构体如下:
typedefvolatile struct _EVENT_DESCRIPTOR
{
BYTE PortPin; //使用的GPI
BYTE Bounce; // 去抖延时时间
BYTE DelayToRepeat; //开始Repeat的时间
BYTE PeroidRepeat; //Repeat的间隔时间
}EVENT_DESCRIPTOR;
static constEVENT_DESCRIPTOR Events_Table[] =
{
{PWRBTN_IN, 10,0,0}, //PowerSwitch
{VOLUME_UP, 10,50,10}, //声音加
{VOLUME_DOWN, 10,50,10}, //声音减
};
然后再改变一下扫描按键的代码,需要加三部分:一,在按键电平状态不一致时,除了保存上一电平外,还要清除一下保存DelayToRepeat和PeroidRepeat值的计数数组;二,在去拌时间结束后,要判断当前状态是否为ACTIVE和是否需要Repeat功能,如果这两条件满足,则将DelayToRepeat赋值给计数数组;三,计数数组计数结束后,执行按键功能函数,并将PeroidRepeat值重新赋给计数数组。