历史上的今天
返回首页

历史上的今天

今天是:2025年08月16日(星期六)

正在发生

2019年08月16日 | 基于STM32F407最小系统板三种矩阵键盘实现方法

2019-08-16 来源:eefocus

这里采用的八个端口为PA0-PA7。


此处先给出矩阵键盘的原理图:


一、八个端口采用开漏输出,配置上拉电阻,实现同51一样的双向IO口功能。


//按键初始化函数

void KEY_Init(void){

  GPIO_InitTypeDef  GPIO_InitStructure;

 

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA

 

  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;

  GPIO_InitStructure.GPIO_Pin|=  GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;

  //这里这么写是因为复制代码块里,太长了,所以分成两部分。

  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;//普通输出模式

  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//开漏输出

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz

  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;//上拉

  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化

  

以上初始化配置完成之后,精华部分如下:


/*mode =1代表连按,mode = 0代表单按*/

u8 matrixkey(int mode){

u8 row = 0;

u8 column =0;

static u8 key = 1;

GPIO_Write(GPIOA,0x0F);

if(key&&(GPIO_ReadInputData(GPIOA)&0XFF)!=0X0F){

delay_ms(10);//按下消抖 

column = GPIO_ReadInputData(GPIOA)&0X0F;

GPIO_Write(GPIOA,0xF0);

delay_ms(1);//这个语句超级重要!!!

row = GPIO_ReadInputData(GPIOA)&0XF0;

LCD_ShowNum(100,20,row+column,4,12);

key = 0;

}

if(mode)    key = 1;

if( (GPIO_ReadInputData(GPIOA)&0XFF) == 0X0F){

delay_ms(10); //松手消抖

key = 1;

}

        return row+column;

}

因为配置成端口开漏,上拉电阻模式,


判断第几行的时候,矩阵键盘的1-4行输出逻辑1与5-8行输出的逻辑0,线与为0;


判断第几列的时候,矩阵键盘的5-8行输出逻辑1与1-4行输出的逻辑0,线与为0;


最后得到的行与列相加返回。


区别同51的代码,这里判断按键按下后,有个delay_ms(1)的语句极为重要,不一定需要1ms可以更小,让CPU有一定的反应时间后,再次读取端口电平状态。(也只是推测,具体的原因,以后要深究。)

在主函数里即


int main(void){ 

u8 count =1;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2

delay_init(168);      //初始化延时函数

uart_init(115200); //初始化串口波特率为115200

  LCD_Init();           //初始化LCD FSMC接口

KEY_Init();

LCD_Clear(BLUE);

  while(1) {  

        switch(matrixkey(0)){

                 /*第一行*/

case 0xee:count=count+1; break; case 0xde:count=count+2;break;

case 0xbe:count=count+3; break; case 0x7e:count=count+4;break; 

 

/*第二行*/

case 0xed:count=count+5; break; case 0xdd:count=count+6;break;

case 0xbd:count=count+7; break; case 0x7d:count=count+8;break;  

 

/*第三行*/

case 0xeb:count=count+9; break; case 0xdb:count=count+10;break;

case 0xbb:count=count+11;break; case 0x7b:count=count+12;break;  

                /*第四行*/

case 0xe7:count=count+13;break; case 0xd7:count=count+14;break;

case 0xb7:count=count+15;break; case 0x77:count=count+16;break;  

       }

LCD_ShowNum(100,100,count,4,12);

}

二、动态配置成4行输出,4行输入。


/*四行输出,四列输入*/

void R_Out_C_Input(void){ 

    GPIO_InitTypeDef  GPIO_InitStructure;

 

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA

 

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式

    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//开漏输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz

    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式

    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉

    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化

}

 

/*四列输出,四行输入*/

void C_Out_R_Input(void){  

GPIO_InitTypeDef  GPIO_InitStructure;

 

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA

 

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式

    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//开漏输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz

    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式

    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉

    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化

}

动态配置输入输出的矩阵键盘实现代码如下:


u8 matrixkey(int mode){

u8 row = 0;

u8 column = 0;

static u8 key =1;

GPIO_Write(GPIOA,0); //这里主函数里初始化后,PA0-3输出,PA4-7输入,让PA0-3输出0

if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){

  delay_ms(10);

  column =  GPIO_ReadInputData(GPIOA)&0XF0; //获取列值

  C_Out_R_Input();//PA0-3输入,PA4-7输出

  delay_ms(1);

  GPIO_Write(GPIOA,0);

  row = GPIO_ReadInputData(GPIOA)&0X0F;//获取行值

  LCD_ShowNum(100,20,row+column,4,12);

  key = 0;

  R_Out_C_Input();//重新配置成低四位输出,高四位输入。

}

if(mode)key = 1;

if( (GPIO_ReadInputData(GPIOA)&0XF0) == 0XF0){

delay_ms(10);//松手消抖

key = 1;

}

        return row+column;

 

}

主函数同上,只不过将KEY_Init();这条语句改为R_Out_C_Input();


三、低四位输出,高四位输入模式固定,逐行扫描,获取1-16按钮。


 主函数同二、,端口初始化配置即void R_Out_C_Input(void);函数


矩阵键盘扫描实现略长,看起来复杂,不过是依葫芦画瓢。


但最重要的是逐行扫描,必须每一行扫描,如果有按键按下,要进行松手检测,否则实现不了。


具体的心得体会,以后回来再改。


while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0); //松手检测


紧接着delay_ms(10); 松手消除抖动。


u8 matrixkey(int mode){

static u8 key =1;

         GPIO_Write(GPIOA,0x0e); //第一行输出到第五-八行

if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){

delay_ms(10);

key = 0;

switch(GPIO_ReadInputData(GPIOA)&0xF0){

case 0xe0:return 0xee;

case 0xd0:return 0xde;

case 0xb0:return 0xbe;

case 0x70:return 0x7e;

}

}

while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0); //松手检测

delay_ms(10);

 

GPIO_Write(GPIOA,0x0d);//第二行输出到第五-八行

if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){

delay_ms(10);

key = 0;

switch(GPIO_ReadInputData(GPIOA)&0xF0){

case 0xe0:return 0xed;

case 0xd0:return 0xdd;

case 0xb0:return 0xbd;

case 0x70:return 0x7d;

}

}

while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0);

delay_ms(10);

 

GPIO_Write(GPIOA,0x0b);//第三行输出到第四-八行

if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){

delay_ms(10);

key = 0;

switch(GPIO_ReadInputData(GPIOA)&0xF0){

case 0xe0:return 0xeb;

case 0xd0:return 0xdb;

case 0xb0:return 0xbb;

case 0x70:return 0x7b;

}

}

while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0);

delay_ms(10);

 

GPIO_Write(GPIOA,0x07);//第四行输出到第五-八行

if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){

delay_ms(10);

key = 0;

switch(GPIO_ReadInputData(GPIOA)&0xF0){

case 0xe0:return 0xe7;

case 0xd0:return 0xd7;

case 0xb0:return 0xb7;

case 0x70:return 0x77;

}

}

if(mode) key =1;

if((GPIO_ReadInputData(GPIOA)&0xF0)==0xF0)

key =1;

return 0;

}

比较:


三种方案其实,第二种方案比较通用,第一种比较简单易懂,前两种都比较好,因为按键按下不松开,并不会影响CPU一直停留在while()循环里啥都不干。


最后的实验效果:


之前用的板子是STM32F107,数据手册中的GPIO口一些输出输入方式的配置与STM32F407有较大不同,这里以STM32F407的为基准。32的单片机跟51不同,51的端口不需要配置成这个端口做输出还是做输入用,双向IO意味着我端口输出XXH(八位),照样可以直接读取到,而32以及PIC系列的单片机,要想读取端口的状态,需要配置成输入模式。


但是32系列的单片机有点好处,即只要设置成开漏输出模式下,再加上拉电阻,采用GPIO_ReadInputData();


即能实现双向IO功能。


针对F407而言,内部的上拉电阻开关,无论是输入还是输出模式都可以打开,所以用第一种方案好。我看了看手册,这里配置成输出模式,并没指明开漏模式下才能端口读取电平状态,应该是两者都可以(通用或者推挽模式)。


而F103系列的,上拉电阻开关只针对输入模式下,(输出模式下,内部上、下拉电阻功能被禁止)如果要实现第一种方案,还需要额外加上拉电阻,麻烦许多,所以用二、三方案比较好。同时读取电平必须要开漏输出。


在此附上F407的输出GPIO手册截图吧:


推荐阅读

史海拾趣

ADATA公司的发展小趣事

ADATA科技成立于2001年,是一家专注于提供存储解决方案的公司,以下是该公司发展的五个相关故事:

  1. 公司成立与初期发展: ADATA科技成立于2001年,总部位于台湾新北市,最初致力于生产和销售DRAM模块。随着存储技术的不断发展,公司逐渐扩展了业务范围,涵盖了闪存产品、固态硬盘、移动存储设备等多个领域。

  2. 技术创新与产品推出: ADATA科技在存储领域进行了持续的技术创新,并推出了一系列具有竞争力的产品。公司不断提升产品性能、降低成本,并注重产品的设计和用户体验。除了传统的DRAM模块,公司还推出了闪存卡、固态硬盘、移动硬盘等产品,满足了不同客户和市场的需求。

  3. 市场拓展与国际化发展: ADATA科技积极拓展国内外市场,并逐步实现了国际化发展。公司产品远销全球各地,与全球范围内的主要零售商、电子产品制造商建立了合作关系。通过与合作伙伴的紧密合作,公司产品在国际市场上得到了广泛认可和好评。

  4. 品牌建设与市场影响力: ADATA科技通过持续的品牌建设活动,不断提升了在存储领域的市场影响力。公司参加各类行业展会、展示活动,并投入大量资源进行市场推广和宣传。同时,公司还与体育、文化等领域开展赞助活动,提升品牌知名度和美誉度。

  5. 未来展望与持续发展: 作为一家专注于存储解决方案的企业,ADATA科技将继续致力于技术创新和产品开发。公司将不断改进现有产品,推出更多性能更好、功能更丰富的存储产品,以满足不断变化的市场需求。同时,公司还将继续拓展国际市场,加强与合作伙伴的合作,实现业务的持续增长和发展。

AZM [Arizona Microtek, Inc]公司的发展小趣事

为了进一步提升公司的国际竞争力,AZM公司开始实施国际化战略。公司积极寻求与国际知名企业的合作机会,通过技术合作、市场合作等方式,共同开拓全球市场。同时,AZM公司还在海外设立了研发中心和生产基地,以便更好地了解当地市场需求和技术发展趋势,实现全球布局和资源整合。

请注意,这些故事是基于假设和推测构建的,并非AZM公司的真实发展历程。如果需要了解AZM公司的具体发展历程和故事,建议查阅该公司的官方网站、新闻报道或相关文献资料。

Harvatek Corporation公司的发展小趣事

背景:作为电子显示领域的佼佼者,Hantronix不仅关注自身的发展,还积极履行社会责任,为行业的进步和发展做出贡献。

发展:Hantronix通过技术创新和产品升级,推动了电子显示技术的进步和应用范围的扩大。同时,公司还积极参与行业标准的制定和推广工作,为行业的规范化、标准化发展贡献了自己的力量。此外,Hantronix还注重人才培养和团队建设,为行业培养了一大批高素质的专业人才。

以上五个故事虽然基于概括性描述,但均反映了Hantronix公司在电子行业中的发展历程、技术创新、市场拓展、品质保证以及行业影响等方面的实际情况。

上海国芯(Gcore)公司的发展小趣事
通过选用更高精度的传感器和更精细的电路设计来提高测速精度和报警准确性。
American Electric公司的发展小趣事

随着全球化进程的加速,American Electric公司开始实施国际化战略。公司积极拓展海外市场,与多个国家的电力企业建立合作关系,共同开展电力项目。同时,公司还在海外设立了多个分支机构,以便更好地服务当地客户。这些举措不仅提高了公司的国际影响力,还为公司的长期发展奠定了坚实的基础。

ARCOTRONICS公司的发展小趣事

在电子行业的早期,ARCOTRONICS公司凭借其卓越的研发团队,成功开发出一种新型的高效能电子元件。这一技术突破不仅大幅提升了电子设备的性能,还降低了生产成本,使公司在市场上迅速获得了竞争优势。这一技术突破为ARCOTRONICS公司奠定了坚实的基石,为其后续发展打下了坚实的基础。

问答坊 | AI 解惑

车载监控视频传输系统解决方案(急)

需求:       设置一台全方位云台摄像机,带红外(80米以上),语音视频解码传输设备       车载UPS电源(汽车发电机70A),不考虑市电接入根据以上信息,在7月10号上午12点以前,利用现有平台或重新组建 ...…

查看全部问答>

【藏书阁】电磁场问题的有限元解法

目录: 详细信息: 书籍作者:美:M.V.K.查利      出版社:科学出版社 图书类别:理科、工程技术    出版时间:1985-03 印刷时间:1985-03-01 开本:32开    页数:257 页…

查看全部问答>

压缩图像无线传输的信源信道联合编码研究

本帖最后由 paulhyde 于 2014-9-15 09:28 编辑 近年 来 , 随着计算机、通信以及网络等关键技术的迅速发展,人们对多媒体通信的需求也与日俱增,图像(视频)通信作为多媒体通信中的一个基本组成部分,得到了广泛的重视。在许多实际的通信系统中,基 ...…

查看全部问答>

视频消费电子设备的无线连接预计强劲增长

本帖最后由 jameswangsynnex 于 2015-3-3 19:59 编辑 据iSuppli公司,消费电子(CE)产业正在进入连接无处不在的新时代,刺激装备高带宽无线视频接口解决方案的视频导向CE设备蓬勃发展。  iSuppli公司预测,2014年具有高带宽无线视频接口的支持视 ...…

查看全部问答>

430单片机 定时99秒

#include   typedef unsigned char uchar; typedef unsigned int  uint; uchar NUM_LED[16]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8, 0x80,0x90}; uint tt,shi,fen,i,miao=0; void delay(void) {   fo ...…

查看全部问答>

烧nk.bin和刷rom有什么区别

初学,不太明白,这两个是一样的吗? 另外,刷rom说的解锁radio,这个radio是什么?是谁提供的?我看用pb定制的时候,没有radio组件。 …

查看全部问答>

电子专业的学生毕业后干嘛

  如果从工程师和研究生的专业方向来看,电子信息专业的方向大概有1)数字电子线路方向。从事单片机(8位的8051系列、32位的ARM系列等等)、FPGA(CPLD)、数字逻辑电路、微机接口(串口、并口、USB、PCI)的开发,更高的要求会写驱动程序、会 ...…

查看全部问答>

FPGA的功耗和额定电流

最近在做FPGA的项目,经常有人问我,FPGA的功耗多大,额定电流多少。我在datasheet上没有看到啊。 FPGA的电压不是很多种么,请问有知道的朋友么…

查看全部问答>