设备有64个按键,至少占用单片机多少个GPIO口

设备有64个按键,至少占用单片机多少个GPIO口,第1张

7个,为什么?看下面文章就知道了。

本文以循序渐进的思路,引导大家思考如何用最少的IO驱动更多的按键,并依次给出5种方案原理图提供参考。在实际项目中我们经常会遇到有按键输入的需求,但有的时候为了节省资源成本,我们都会选择在不增加硬件的情况下使用最少的控制器IO驱动更多的按键,那么具体是怎么做的呢,下面我们就以用5个IO引脚为例,讲下怎么设计可以实现更多的按键?共有5种设计思路,下面依次介绍。

思路一

首先通常想到的可能是下面这样的设计:

上图形式的按键就是我们通常说的行列式按键,它的驱动思路是这样的:

1 对IO1、2、3配置为推挽输出,依次只让其中一个输出为0其他输出为1。

2 对IO4、5进行读 *** 作,根据读出的结果判断哪个按键按下

例如:配置IO1、2、3为011,读IO4、5,若IO4为0则SW14按下,若IO5为0则SW15按下;

依次的配置IO1、2、3为101,读IO4、5,若IO4为0则SW24按下,若IO5为0则SW25按下;

依次的配置IO1、2、3为110,读IO4、5,若IO4为0则SW34按下,若IO5为0则SW35按下;

思路二

但是我们在不知道行列式按键之前我们肯定是依次将IO口接一个按键到GND或者到VCC,然后去读IO口去判断哪个按键按下,这也是最简单的方法,但是很浪费IO口,下面这种就结合了这种简单方法和行列式的思路,实现了又多增加3个按键,如下图:

这里我们的思路是先依次读IO1、2、3的电平来识别S1、2、3,哪个按键按下,其后的流程和思路一是一样的,这样就可以识别11个按键了。

思路三

按照扫描的思想,某一时刻设置一个IO口为0,其他IO口读,如果有IO口读到0,则有对应按键按下。比如IO1为0,然后读到IO5也为0,那么K15就是按下的。对照这样的思路,我们可以有下面的设计:

这个电路按键识别思路是这样的:

1 只配置IO1为0,其他IO读,若IO5读到0,则K15按下,若IO4读到0,则K14按下,依次识别K13,K12;

2 只配置IO2为0,其他IO读,若IO5读到0,则K25按下,若IO4读到0,则K24按下,依次识别K23;

3 只配置IO3为0,其他IO读,若IO5读到0,则K35按下,若IO4读到0,则K34按下;

4 只配置IO4为0,其他IO读,若IO5读到0,则K45按下;

思路四

对于思路3我们发现,如果只配置IO5为0,其他IO读,若IO1读到0,则K15按下,若IO2读到0,则K25按下,依次可识别K35和K45。这样就存在重复,那么有么有好的方法,解决这样的重复呢?我们发现,若配置IO1为0,K15按下,电流流向IO1的,若配置IO5为0,同样K15按下,电流是流向IO5的。这样我们就可以通过区分电流的流向来避免重复。于是就有了下图的设计:

这样就可以避免重复,IO5为0时,按K15,IO1是读不到0的。那么怎样设计,IO5为0时对应一个按键按下IO1为0呢?如是就有人想到下面的设计:

这个电路按键识别思路是这样的:

1 只配置IO1为0,其他IO读,若IO5读到0,则K51按下,若IO4读到0,则K41按下,依次识别K31,K21;

2 只配置IO2为0,其他IO读,若IO5读到0,则K52按下,若IO4读到0,则K42按下,依次识别K32,K21';

3 只配置IO3为0,其他IO读,若IO5读到0,则K53按下,若IO4读到0,则K43按下,依次识别K32’,K31';

4 只配置IO4为0,其他IO读,若IO5读到0,则K54按下,若IO4读到0,则K43’按下,依次识别K42’,K41';

5 只配置IO5为0,其他IO读,若IO4读到0,则K54’按下,若IO3读到0,则K53’按下,依次识别K52’,K51'。

思路五

很多人可能认为思路四已经识别20个按键了,但是真的就没有其他方法了吗?不要忘了,我们还没有将思路二你介绍的那种最简单的方法结合进去,于是又可以多5个按键,如下图:

这样我们可以先识别K01、K02、K03、K04、K05,若没有按键按下然后再和思路四的设计一样去识别其他按键。但这样存在一个问题,如果IO1配置为0,IO5读到0,那么怎么知道是K51按下还是K05按下呢,这里只需要在程序里做下判断,先判断下是不是K05按下,若不是就是K51,因为按键K01、K02、K03、K04、K05在5个IO口都为读取的情况下,就可以识别,不需要扫描识别处理,相当于这5个按键优先级高与其他按键。

总结

综合上述,5个IO口最多可以识别25个按键,思路五程序上处理比较麻烦,若实际中只按思路四设计,也可识别20个按键,那么如果有N个IO口可识别多少按键呢?这里给出如下公式:

假设有N个IO口按照思路三可以识别N(N-1)/2个;

按照思路四可识别N(N-1)个;

按照思路5可以识别N(N-1)+N个。

最后再说下,如果实际设计时,还是按思路四设计好,软件也没那么麻烦。如果是你的话你会选择哪种方法呢?你还有没有其他的设计方法呢?

K60是一个机械键盘品牌,其设施下排按键的方法如下:

1 首先,将键盘翻转过来,把键帽从下方拆下来,揭开后将键帽和键轴分开,即可看到底部电路板上的按键。

2 通过调整按键的位置和方向,来实现下排按键的设施。按键的位置和方向需要与电路板的布局相对应,以确保按键能够正常工作。

3 拆下键帽后,可以在键轴上安装新的键帽,以实现更换按键的目的。键帽的形状和大小需要与键轴相匹配,以确保按键的灵敏度和舒适度。

需要注意的是,设施下排按键需要一定的技术和经验,如果您没有相关经验,建议寻求专业人士的帮助,以避免损坏键盘和电路板。同时,使用机械键盘时需要注意保养和清洁,避免灰尘和污垢影响键盘的使用寿命和性能。

实验内容: KEY0 控制 DS0,按一次亮,再按一次,就灭。KEY1 控制 DS1,效果同 KEY0。WK_UP 按键则同时控制 DS0 和 DS1,按一次,他们的状态就翻转一次。 KEY0连接在 PC5 上、KEY1 连接在 PA15 上、WK_UP 连接在 PA0 上。KEY0 和 KEY1 是低电平有效的,而 WK_UP 是高电平有效的,除了KEY1 有上拉电阻(与 JTDI 共用),其他两个都没有上下拉电阻,所以,需要在 STM32 内部设置上下拉。

输入模式配置:CNFMODE=1000(8)。因上下拉输入均为8,配置ODR,为1,上拉输入,为0,下拉输入。WK_UP高电平有效,设置为下拉(默认)。其余两个低电平有效,设置为上拉。PA15 占用了 JTAG 的一个 IO,所以要禁止 JTAG。

输入配置

输入控制灯的亮灭,需要读取输入的值(自带sys中有位 *** 作):

不用sys中的位 *** 作,读取寄存器中的输入:

定义KEY_Scan函数:

输入配置

读取输入的值

用STM32Cube编写程序。配置完成后,添加key_scan函数和主函数即可。

寄存器:RCC->APB2ENR|=1<<2配置时钟,然后配置CRL或CRH(先与清零再或)

库函数 (写输出还需配置speed)RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE)使能时钟;

取GPIO_InitTypeDef实例GPIO_InitStructure,配置引脚号和模式;

调用GPIO_Init()函数

HAL库函数

不用手动配置,自动生成。

寄存器:输入读取IDR相应端口的值。输出写ODR相应端口的值。

读输入:

写输出:

库函数

读输入:GPIO_ReadInputDataBit()

写输出:GPIO_SetBits(),GPIO_ResetBits ()

HAL库函数

读输入:HAL_GPIO_ReadPin()

写输出:

总得来说,HAL使用和库函数使用类似,而且不需要初始化。寄存器配置来说更简洁,但是向读写函数没有函数来的直接。

问的 好笼统呀

看来你是初学的

给点指导吧

单片机获取按键分为两种方式,分别是 扫描方式和中断方式

扫描很简单,就是把一个引脚p绑定到一个变量上面,比如a,a只有两个值1或者0,

在c代码里面,你可以对a进行赋值->就是输出了

也可以读取a的值就是->输入了

中断的话就更复杂一点了,

慢慢学吧,单片机很好玩的

两种方式 又分为 普通按键和矩阵键盘

更详细的385749807

给你一个矩阵键盘的参考程序

行列扫描:通过高四位全部输出低电平,低四位输出高电平。当接收到的数据,低四位不全为高电平时,说明有按键按下,然后通过接收的数据值,判断是哪一列有按键按下,然后再反过来,高四位输出高电平,低四位输出低电平,然后根据接收到的高四位的值判断是那一行有按键按下,这样就能够确定是哪一个按键按下了。

/

函数名

:KeyDown

函数功能

:

检测有按键按下并读取键值

输入

:

输出

:

/

voidKeyDown(void)

{

char

a=0;

GPIO_KEY=0x0f;

if(GPIO_KEY!=0x0f)//读取按键是否按下

{

Delay10ms();//延时10ms进行消抖

if(GPIO_KEY!=0x0f)//再次检测键盘是否按下

{

GPIO_KEY=0X0F;

//测试列

switch(GPIO_KEY)

{

case(0X07):

KeyValue=0;break;

case(0X0b):

KeyValue=1;break;

case(0X0d):

KeyValue=2;break;

case(0X0e):

KeyValue=3;break;

}

//测试行

GPIO_KEY=0XF0;

switch(GPIO_KEY)

{

case(0X70):

KeyValue=KeyValue;break;

case(0Xb0):

KeyValue=KeyValue+4;break;

case(0Xd0):

KeyValue=KeyValue+8;break;

case(0Xe0):

KeyValue=KeyValue+12;break;

}

while((a<50)&&(GPIO_KEY!=0xf0))//按键松手检测

{

Delay10ms();

a++;

}

}

}

}

day12:按键KEY1和KEY2控制LED灯的亮灭KEY1控制LED1,KEY2控制LED2

bsp_ledh:

/ 和LED功能模块相关的程序 /

#ifndef __BSP_LED_H__

#define __BSP_LED_H__

#include "stm32f10xh"

/宏定义/

#define GPIO_CLK_D4 RCC_APB2Periph_GPIOC // 时钟

#define GPIO_PORT_D4 GPIOC // C端⼝

#define GPIO_PIN_D4 GPIO_Pin_2 // PC2引脚

#define GPIO_CLK_D5 RCC_APB2Periph_GPIOC // 时钟

#define GPIO_PORT_D5 GPIOC // C端⼝

#define GPIO_PIN_D5 GPIO_Pin_3 // PC2引脚

/参数宏定义/

/

digitalTOGGLE(p,i)是参数宏定义,p表⽰LED的端⼝号,ODR是数据输出寄存器,

查stm32f10x的官⽅中⽂⼿册的第82章的ODR寄存器,要点亮LED,根据原理图,要输出低电平0,

C语⾔中,^表⽰异或,即a^b表⽰a和b不同时输出为1,相同时输出为0,⽐如0^1=1,1^1=0,0^0=0,

这⾥为什么 *** 作ODR,p是什么?查看stm32f10xh⽂件,搜索GPIO_TypeDef就会明⽩,

i是LED的引脚对应的位电平,经过digitalTOGGLE(p,i) {p->ODR ^= i;}之后,

第⼀次p为0,i⼀直为1,第⼀次异或结果输出1,第⼆次输出0,第三次输出1,这样间断输出010101,灯不断亮灭

/

#define digitalTOGGLE(p,i) {p->ODR ^= i;}

#define LED1_TOGGLE digitalTOGGLE(GPIO_PORT_D4,GPIO_PIN_D4)

#define LED2_TOGGLE digitalTOGGLE(GPIO_PORT_D5,GPIO_PIN_D5)

/配置GPIO/

void LED_GPIO_Config(void);

#endif /__BSP_LED_H__/

bsp_ledc:

/ 和LED功能模块相关的程序头⽂件 /

#include "/led/bsp_ledh" /绝对路径,也可在Options for target中设置头⽂件/

/GPIO初始化/

void LED_GPIO_Config(void)

{

/外设结构体/

GPIO_InitTypeDef GPIO_InitStruct_D4, GPIO_InitStruct_D5;

/第⼀步:打开外设的时钟,看stm32f10x_rccc这个⽂件的RCC_APB2PeriphClockCmd函数介绍/

RCC_APB2PeriphClockCmd(GPIO_CLK_D4, ENABLE);

/第⼆步:配置外设的初始化结构体/

GPIO_InitStruct_D4GPIO_Pin = GPIO_PIN_D4; // PC2的那盏LED灯(D4)的引脚

GPIO_InitStruct_D4GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出模式

GPIO_InitStruct_D4GPIO_Speed = GPIO_Speed_10MHz; // 引脚速率

GPIO_InitStruct_D5GPIO_Pin = GPIO_PIN_D5; // PC3的那盏LED灯(D5)的引脚

GPIO_InitStruct_D5GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出模式

GPIO_InitStruct_D5GPIO_Speed = GPIO_Speed_10MHz; // 引脚速率

/第三步:调⽤外设初始化函数,把配置好的结构体成员写到寄存器⾥⾯/

GPIO_Init(GPIO_PORT_D4, &GPIO_InitStruct_D4);

GPIO_Init(GPIO_PORT_D5, &GPIO_InitStruct_D5);

}

bsp_keyh:

#ifndef __BSP_KEY_H__

#define __BSP_KEY_H__

#include "stm32f10xh"

#define KEY_ON 1

#define KEY_OFF 0

// 按键相关的宏定义

#define GPIO_CLK_KEY1 RCC_APB2Periph_GPIOA // 端⼝A时钟

#define GPIO_PORT_KEY1 GPIOA // A端⼝

#define GPIO_PIN_KEY1 GPIO_Pin_0 // PA0引脚

#define GPIO_CLK_KEY2 RCC_APB2Periph_GPIOC // 端⼝C时钟

#define GPIO_PORT_KEY2 GPIOC // C端⼝

#define GPIO_PIN_KEY2 GPIO_Pin_13 // PC13引脚

// 配置GPIO

void KEY_GPIO_Config(void);

// 按键扫描,看按键是否被按下,如果按下返回KEY_ON,否则返回KEY_OFF(进⾏宏定义)

uint8_t KEY_SCAN(GPIO_TypeDef GPIOx, uint16_t GPIO_Pin);

#endif / __BSP_KEY_H__ /

bsp_keyc:

#include "/key/bsp_keyh"

/ 按键初始化 /

void KEY_GPIO_Config(void)

{

/外设结构体/

GPIO_InitTypeDef GPIO_InitStruct_KEY1, GPIO_InitStruct_KEY2;

/第⼀步:打开外设的时钟,看stm32f10x_rccc这个⽂件的RCC_APB2PeriphClockCmd函数介绍/

RCC_APB2PeriphClockCmd(GPIO_CLK_KEY1|GPIO_CLK_KEY2, ENABLE); // 按下KEY1或KEY2

/第⼆步:配置外设的初始化结构体/

GPIO_InitStruct_KEY1GPIO_Pin = GPIO_PIN_KEY1; // KEY1的引脚

GPIO_InitStruct_KEY1GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输出模式(引脚电平由外部决定) GPIO_InitStruct_KEY1GPIO_Speed = GPIO_Speed_10MHz; // 引脚速率

GPIO_InitStruct_KEY2GPIO_Pin = GPIO_PIN_KEY2; // KEY1的引脚

GPIO_InitStruct_KEY2GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输出模式(引脚电平由外部决定) GPIO_InitStruct_KEY2GPIO_Speed = GPIO_Speed_10MHz; // 引脚速率

/第三步:调⽤外设初始化函数,把配置好的结构体成员写到寄存器⾥⾯/

GPIO_Init(GPIO_PORT_KEY1, &GPIO_InitStruct_KEY1);

GPIO_Init(GPIO_PORT_KEY2, &GPIO_InitStruct_KEY2);

}

/ 按键扫描(检测按键是否被按下):GPIOx为端⼝,GPIO_Pin为引脚 /

uint8_t KEY_SCAN(GPIO_TypeDef GPIOx, uint16_t GPIO_Pin)

{

/查看stm32f10x_gpioh⽂件最后⾯的函数,这个函数表⽰读引脚的输⼊电平(按键触发后输⼊的)/

// 这个函数,如果按键按下,则输出18V⾼电平,置1,否则为0

if(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == KEY_ON)

{

// 如果⼀直按着就进⼊死循环

while(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == KEY_ON);

// 放开按键就置1

return KEY_ON;

}

else

{

// 否则置0

return KEY_OFF;

}

}

mainc:

#include "stm32f10xh"

#include "/led/bsp_ledh"

#include "/key/bsp_keyh"

// 延迟函数

void delay(unsigned int i)

{

for(; i!=0; i--);

}

int main(void)

{

/GPIO初始化,在程序来到main函数的时候,系统时钟已经配置成72MHz/

LED_GPIO_Config(); // LED初始化

KEY_GPIO_Config(); // KEY初始化

while(1)

{

// 如果按下KEY1,则D4亮灭,KEY1对应的是PA0,A端⼝的第1个引脚

if(KEY_SCAN(GPIOA, GPIO_PIN_KEY1) == KEY_ON)

{

LED1_TOGGLE;

}

// 如果按下KEY2,则D5亮灭,KEY2对应的是PC13,C端⼝的第14个引脚

if(KEY_SCAN(GPIOC, GPIO_PIN_KEY2) == KEY_ON)

{

LED2_TOGGLE;

}

}

}

实验现象:

程序烧录到板⼦中,⼀开始LED1和LED2都是亮的(应该都是灭的才对),按下KEY1控制LED1的亮和灭,按下KEY2控制LED2的亮和灭============================================

下⾯是默认情况下LED2和LED2都是熄灭的情况:

mainc

/

KEY控制LED亮灭实验,LED⼀开始默认都熄灭,等按下KEY1或KEY2后才能亮

/

#include "stm32f10xh"

#include "/led/bsp_ledh"

#include "/key/bsp_keyh"

// 延迟函数

void Delay(unsigned int time)

{

for(;time!=0;time--);

}

int main(void)

{

uint8_t count = 1;

KEY_GPIO_Config();

while(1)

{

// LED默认情况下是灭的,等按下KEY1或KEY2时,对应的LED才会亮

if(KEY_SCAN(GPIO_PORT_KEY1, GPIO_PIN_KEY1) == KEY_ON)

{

if(count == 1)

{

LED_GPIO_Config(); // 按下KEY1时两个LED都亮

LED2_TOGGLE; // 让LED2灭(其实是亮-->灭),时间很短,⼈眼分辨不出来

count++;

}

else

{

LED1_TOGGLE;

}

}

if(KEY_SCAN(GPIO_PORT_KEY2, GPIO_PIN_KEY2) == KEY_ON)

{

if(count == 1)

{

LED_GPIO_Config(); // 按下KEY2时两个LED都亮

LED1_TOGGLE; // 让LED1灭(其实是亮-->灭),时间很短,⼈眼分辨不出来

count++;

}

else

{

LED2_TOGGLE;

}

}

}

}

5

百度文库VIP限时优惠现在开通,立享6亿+VIP内容

立即获取

day12:按键KEY1和KEY2控制LED灯的亮灭

day12:按键KEY1和KEY2控制LED灯的亮灭KEY1控制LED1,KEY2控制LED2

bsp_ledh:

/ 和LED功能模块相关的程序 /

#ifndef __BSP_LED_H__

#define __BSP_LED_H__

#include "stm32f10xh"

/宏定义/

#define GPIO_CLK_D4 RCC_APB2Periph_GPIOC // 时钟

#define GPIO_PORT_D4 GPIOC // C端⼝

#define GPIO_PIN_D4 GPIO_Pin_2 // PC2引脚

我猜你是只在软件仿真,没有用JLINK或STLINK这些在线仿真的吧,软件没有收到你仿真的对应IO电平变化,所以你观察寄存器只是观察到GPIO的寄存器的值。

需要在pins对应的引脚处打上勾代替实际按键按下。

以上就是关于设备有64个按键,至少占用单片机多少个GPIO口全部的内容,包括:设备有64个按键,至少占用单片机多少个GPIO口、k60怎么设施下排按键、按键输入等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zz/10083901.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-05
下一篇 2023-05-05

发表评论

登录后才能评论

评论列表(0条)

保存