Author : Jim
Date : 2020.07.12
矩阵键盘硬件原理图
使用了三个GPIO做输出(COL),3个GPIO做输入(ROW),原理就是行列扫描,用了杰理的定时器,10毫秒扫描一次,这里还另外多出了3个按键,是没有列的,只用ROW1\2\3做GPIO检测。具体实现方式将在后面的程序中介绍。
矩阵键盘扫描原理
原理很简单,就是做一个定时器,在定时器中有一个for循环,三个COL GPIO轮流输出低电平(或高电平也可),并给当前输出的GPIO一个标号。若当ROW1\2\3接收到低电平时,根据前面记下的COL标号,就可以精确定位到行列坐标了,然后判断到底是哪个按键按下了。
程序代码
#define COL_PORTX1 JL_PORTA
#define COL_BIT1 BIT(6)
#define COL_PORTX2 JL_PORTA
#define COL_BIT2 BIT(7)
#define COL_PORTX3 JL_PORTA
#define COL_BIT3 BIT(8)
#define ROW_PORTX1 JL_PORTA
#define ROW_BIT1 BIT(3)
#define ROW_PORTX2 JL_PORTA
#define ROW_BIT2 BIT(4)
#define ROW_PORTX3 JL_PORTA
#define ROW_BIT3 BIT(5)
#define KEY_PAD_EN() do{COL_PORTX1->PU &= ~COL_BIT1;COL_PORTX1->PD &= ~COL_BIT1;COL_PORTX1->DIR &= ~COL_BIT1;\
COL_PORTX2->PU &= ~COL_BIT2;COL_PORTX2->PD &= ~COL_BIT2;COL_PORTX2->DIR &= ~COL_BIT2;\
COL_PORTX3->PU &= ~COL_BIT3;COL_PORTX3->PD &= ~COL_BIT3;COL_PORTX3->DIR &= ~COL_BIT3;\
ROW_PORTX1->PU |= ROW_BIT1;ROW_PORTX1->PD &= ~ROW_BIT1;ROW_PORTX1->DIR |= ROW_BIT1;\
ROW_PORTX2->PU |= ROW_BIT2;ROW_PORTX2->PD &= ~ROW_BIT2;ROW_PORTX2->DIR |= ROW_BIT2;\
ROW_PORTX3->PU |= ROW_BIT3;ROW_PORTX3->PD &= ~ROW_BIT3;ROW_PORTX3->DIR |= ROW_BIT3;\
}while(0)
#define COL1_PAD_OFF() do{COL_PORTX1->OUT |= COL_BIT1;}while(0)
#define COL1_PAD_ON() do{COL_PORTX1->OUT &= ~COL_BIT1;}while(0)
#define COL2_PAD_OFF() do{COL_PORTX2->OUT |= COL_BIT2;}while(0)
#define COL2_PAD_ON() do{COL_PORTX2->OUT &= ~COL_BIT2;}while(0)
#define COL3_PAD_OFF() do{COL_PORTX3->OUT |= COL_BIT3;}while(0)
#define COL3_PAD_ON() do{COL_PORTX3->OUT &= ~COL_BIT3;}while(0)
#define IS_ROW1_DOWN() (!(ROW_PORTX1->IN & ROW_BIT1))
#define IS_ROW2_DOWN() (!(ROW_PORTX2->IN & ROW_BIT2))
#define IS_ROW3_DOWN() (!(ROW_PORTX3->IN & ROW_BIT3))
void user_set_col_out(u8 status)
{
if(status==0)
{
COL1_PAD_OFF();
COL2_PAD_OFF();
COL3_PAD_OFF();
}
else if(status==1)
{
COL2_PAD_OFF();
COL3_PAD_OFF();
COL1_PAD_ON();
}
else if(status==2)
{
COL3_PAD_OFF();
COL1_PAD_OFF();
COL2_PAD_ON();
}
else if(status==3)
{
COL1_PAD_OFF();
COL2_PAD_OFF();
COL3_PAD_ON();
}
}
/*----------------------------------------------------------------------------*/
/**@brief ad按键初始化
@param void
@param void
@return void
@note void ad_key0_init(void)
*/
/*----------------------------------------------------------------------------*/
void ad_key_init(void)
{
KEY_PAD_EN() ;
user_set_col_out(0);
printf("col_raw_key_init");
}
/*----------------------------------------------------------------------------*/
/**@brief 获取ad按键值
@param void
@param void
@return key_number
@note tu8 get_adkey_value(void)
*/
/*----------------------------------------------------------------------------*/
u8 get_adkey_value(void)
{
u8 user_i=0;
u8 key_number=NO_KEY;
for(user_i=0;user_i<4;user_i++)
{
user_set_col_out(user_i);
//printf("i:%d ",user_i);
if(IS_ROW1_DOWN() )
{
switch(user_i)
{
case(0): printf("11"); key_number=11;user_i=4;break;
case(1): printf("0"); key_number=0;user_i=4;break;
case(2): printf("1"); key_number=1;user_i=4;break;
case(3): printf("2"); key_number=2;user_i=4;break;
}
}
else if(IS_ROW2_DOWN() )
{
switch(user_i)
{
case(0): printf("10"); key_number=10;user_i=4;break;
case(1): printf("3"); key_number=3;user_i=4;break;
case(2): printf("4"); key_number=4;user_i=4;break;
case(3): printf("5"); key_number=5;user_i=4;break;
}
}
else if(IS_ROW3_DOWN() )
{
switch(user_i)
{
case(0): printf("9"); key_number=9;user_i=4;break;
case(1): printf("6"); key_number=6;user_i=4;break;
case(2): printf("7"); key_number=7;user_i=4;break;
case(3): printf("8"); key_number=8;user_i=4;break;
}
}
else
{
// printf("#");
user_set_col_out(0);
key_number=NO_KEY;
}
}
return key_filter(key_number);
//printf("#");
}
在这里我偷了一下懒,直接把AD按键扫描改成矩阵按键扫描。
然后把AD按键的宏定义置1(#define KEY_AD_VDDIO_EN 1),并手动注销以下代码:
把代码烧录到开发板,就可以开始测试了。
需要注意的是这里有12个按键,而默认的AD按键只有10个,所以需要再增加2个。
#define KEY_REG_AD_MAX (12)
为了方便看到测试效果,我在蓝牙模式下添加测试按键:
然后把代码烧录到开发版中测试,通过log口输出可以看到测试效果:
按键值是11,短按是MSG_BT_PP, 长按是音量减,按住不松手是音量加,抬起是MSG_POWER_KEY_UP,可以看到log引脚打印出来的消息跟按键定义的消息是对应的。
矩阵按键结合杰理这套代码的移植的方式就大概是这样的,同样具有长按,短按,抬起等功能,也可以做双击三击。贴出来的代码有可以优化的地方,但是在不需要扣RAM空间的情况下,这样用也就可以了。