列的按钮(,列的按钮(

4×4矩阵键盘实拍照如下图。其重组是4行(L1:4)x
4列(R1:4)共16个按键,当第n行、第m列的按钮(n, m)按下时,引脚
Ln 与 Rm 导通:

4×4矩阵键盘实拍照如下图。其构成是4行(L1:4)x
4列(R1:4)共16个按键,当第n行、第m列的按钮(n, m)按下时,引脚
Ln 与 Rm 导通:

1.按键的利用特点

图片 1

图片 2

  按键的运用关键是在按键闭合时改变电路的电平,但是一般情况下按键的开关都是机械弹性触点开关,即接纳触点的触发和分手来落实电路的通断,所以在按键按下和刑释解教时一再会发生振动困扰。

 

 

清除抖动烦扰的三种情势:

 

 

  (1)硬件设计:硬件消抖要在硬件设计上扩大消抖电路,如用R-S触发器等,这样就会大增系统成本。

一篇著作,对矩阵键盘的接口讲解得很详细。概括起来说,按键检测分为3个级次。第一个级次,扫描行。行I/O口设为input模式,使用上拉电阻。列I/O口设为output形式,输出0。逐行扫描,某一行若没有按键按下,则在上拉电阻的功用下pin值读取为1;若该行任一按键按下,则被按键短路到列I/O口,因而pin值读为0。检测到有按键被按下后,进入第二等级,列扫描,以确定被按下的按键的列。列扫描阶段,行/列的I/O格局互换,即:行I/O口设置为output形式,输出0;列I/O口设为input格局,使用上拉电阻。类似于行扫描,逐列举办围观,当读取到pin值为0则讲明被按下的按键属于该列。通过第一、二等级,就能确定被按下的按键。第三品级,监听被按下的按键的列I/O口,直到pin值为1,即阐明按键被松手。

一篇小说,对矩阵键盘的接口讲解得很详细。概括起来说,按键检测分为3个阶段。第一个等级,扫描行。行I/O口设为input形式,使用上拉电阻。列I/O口设为output模式,输出0。逐行扫描,某一行若没有按键按下,则在上拉电阻的机能下pin值读取为1;若该行任一按键按下,则被按键短路到列I/O口,因而pin值读为0。检测到有按键被按下后,进入第二阶段,列扫描,以确定被按下的按键的列。列扫描阶段,行/列的I/O形式互换,即:行I/O口设置为output情势,输出0;列I/O口设为input格局,使用上拉电阻。类似于行扫描,逐列举行围观,当读取到pin值为0则声明被按下的按键属于该列。通过第一、二阶段,就能确定被按下的按键。第三等级,监听被按下的按键的列I/O口,直到pin值为1,即表明按键被松手。

  (2)软件设计:在软件中对按键举行二次测试确认,即当第一次检测到按键被按下后,间隔10
纳秒左右双重检测该按键是否被按下,只有两回都册到按键按下时才认同该按键被按下了,从而裁撤抖动困扰。

关于上拉/下拉电阻,此处有一篇介绍作品。上拉电阻的效益在于,在常态下,按钮开放,IO口被“往上拉”到VDD,读数为1;当按钮闭合,I/O口通过按钮短路到VSS,读数为0;而VDD通过上拉电阻和按钮与VSS连通。若没有上拉电阻的存在,则VDD与VSS短路,会招致灾难性的结果,这显然是必须避免的。使用上拉电阻时,按钮开放时,pin值为1;当按钮闭合时,pin值为0。即,pin值与按钮闭合状态相反,这叫做“负逻辑”。

关于上拉/下拉电阻,此地有一篇介绍著作。上拉电阻的意义在于,在常态下,按钮开放,IO口被“往上拉”到VDD,读数为1;当按钮闭合,I/O口通过按钮短路到VSS,读数为0;而VDD通过上拉电阻和按钮与VSS连通。若没有上拉电阻的存在,则VDD与VSS短路,会促成灾难性的结局,这明明是必须避免的。使用上拉电阻时,按钮开放时,pin值为1;当按钮闭合时,pin值为0。即,pin值与按钮闭合状态相反,这称之为“负逻辑”。

2.单键盘扫描应用

在前述矩阵键盘
的接口算法中,五个级次都采用了上拉电阻。其检测逻辑为负逻辑。

在详谈矩阵键盘
的接口算法中,七个阶段都利用了上拉电阻。其检测逻辑为负逻辑。

用PB口接一个LED数码管,来显示按下按键的次数;

STM32的I/O口内部电路中蕴藏有上拉电阻和下拉电阻,可以通过程序启用或剥夺。

STM32的I/O口内部电路中涵盖有上拉电阻和下拉电阻,可以通过程序启用或剥夺。

用PC0端口接一个按键电路;

流水灯试验的硬件基础上,增添矩阵键盘接口。4×4矩阵键盘共有16个按键,4个LED刚好可以来得16个二进制值(0-0x0F)。

流水灯实验的硬件基础上,扩张矩阵键盘接口。4×4矩阵键盘共有16个按键,4个LED刚好可以显示16个二进制值(0-0x0F)。

图片 3

矩阵键盘的按键检测是分等级举行的,因而,程序的关键性结构特别吻合采用“状态机”设计格局。下列代码中,4个行I/O口的Label依次为R1:4,列I/O口为C1:4。首先定义状态结构体及3个实例:

矩阵键盘的按键检测是分等级展开的,因而,程序的本位结构特别符合利用“状态机”设计模式。下列代码中,4个行I/O口的Label依次为R1:4,列I/O口为C1:4。首先定义状态结构体及3个实例:

  实现的效能是每三次按键,LED数码管显示的数据加1,到9回0。

typedef struct {
    void (*enter)();
    uint8_t (*loop)();
} App_ScanningState;

#define App_STAY 0
#define App_LEAVE 1

void rowScanningEnter();
uint8_t rowScanningLoop();
void colScanningEnter();
uint8_t colScanningLoop();
void colScanningPressedEnter();
uint8_t colScanningPressedLoop();

App_ScanningState rowScanning = { rowScanningEnter, rowScanningLoop };
App_ScanningState colScanning = { colScanningEnter, colScanningLoop };
App_ScanningState colScanningPressed = { colScanningPressedEnter, colScanningPressedLoop };

App_ScanningState *currState = &rowScanning;
typedef struct {
    void (*enter)();
    uint8_t (*loop)();
} App_ScanningState;

#define App_STAY 0
#define App_LEAVE 1

void rowScanningEnter();
uint8_t rowScanningLoop();
void colScanningEnter();
uint8_t colScanningLoop();
void colScanningPressedEnter();
uint8_t colScanningPressedLoop();

App_ScanningState rowScanning = { rowScanningEnter, rowScanningLoop };
App_ScanningState colScanning = { colScanningEnter, colScanningLoop };
App_ScanningState colScanningPressed = { colScanningPressedEnter, colScanningPressedLoop };

App_ScanningState *currState = &rowScanning;

图片 4图片 5

 

 

 1 #include <iom8v.h>
 2 #include "Delay.h"
 3 /**
 4   *PB口:连接一个LED数码管
 5   *PC0:连接一个按键电路,按下呈低电平
 6   *
 7   */
 8 unsigned char CountNum; //全局变量,用来计数
 9 
10 //按键扫描函数
11 void ScanKey(void)
12 {
13     unsigned char key;
14     
15     key = PINC; //检测按键状态
16     if(0x01 == key) //未按下,退出
17         return;
18     delay_ms(10);
19     
20     key = PINC;  //再次检测,防抖动
21     if(0x01 == key) //未按下,退出
22         return;
23     CountNum++;
24     while(0 == key&0x01) //等到按键释放
25         key = PINC;
26 }
27 
28 //主函数,扫描按键显示数据
29 void main()
30 {
31     unsigned char num[10] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
32     //初始化端口
33     DDRB = 0xFF; //设置B口为输出模式
34     PORTB = 0xFF; //置高电平
35     DDRC = 0x00; //设置C口为输入模式
36     
37     CountNum = 0; //初始化全局变量
38     while(1)
39     {
40         ScanKey(); //扫描按键
41         if(CountNum >= 10)
42             CountNum -= 10;
43         PORTB = num[CountNum];
44     }
45 }
46 
47 //按键长按的情况
48 void ScanKey_Long(void)
49 {
50     unsigned char key;
51     
52     key = PINC; //检测按键状态
53     if(0x01 == key) //未按下,退出
54         return;
55     delay_ms(10);
56     
57     key = PINC;  //再次检测,防抖动
58     if(0x01 == key) //未按下,退出
59         return;
60         
61     while(0x00 == key) //按键长按,不断加加
62     {
63         CountNum++;
64         key = PINC;
65     }
66 }

结构体 App_ScanningState
代表1个处境,当进入该情况时,调用其 (函数指针)成员enter()。在先后主循环中,则调用其 loop() 成员。loop() 函数再次回到值为 App_STAY 或
App_LEAVE,若重回前者,则注明应该停留在该情状,下次主循环将另行调用此意况的
loop() 函数;反之,若重回后者,则注明应该切换来下一个情形。

结构体 App_ScanningState
代表1个情况,当进入该意况时,调用其 (函数指针)成员enter()。在先后主循环中,则调用其 loop() 成员。loop() 函数再次回到值为 App_STAY 或
App_LEAVE,若再次回到前者,则注明应该停留在该意况,下次主循环将再度调用此情状的
loop() 函数;反之,若再次回到后者,则表明应该切换来下一个状态。

按键一回LED加一

rowScanning,
colScanning, colScanningPressed
3个App_ScanningState实例,分别为行扫描阶段、列扫描阶段及第三等级(检测按键甩手)。程序起头时为行扫描状态,例如,使用CubeMX自动生成的起先化代码。程序主循环内的代码为:

rowScanning,
colScanning, colScanningPressed
3个App_ScanningState实例,分别为行扫描阶段、列扫描阶段及第三等级(检测按键放手)。程序起先时为行扫描状态,例如,使用CubeMX自动生成的开始化代码。程序主循环内的代码为:

 问:下边的次序没有设想长按的状态,倘若向我们应用的键盘一样,长日子按下一个按键,在屏幕上就不绝于耳的打印该字符,在那一个例子里什么贯彻:若长日子按下按键,CountNum就时时刻刻加加?

    if (App_LEAVE != currState->loop()) {
        return;
    }

    // Button released
    if (currState == &colScanningPressed) {
        lightLedsUp(key);
    }

    // Next state
    currState = currState == &rowScanning ? &colScanning //
            :
                currState == &colScanning ? &colScanningPressed //
                        : &rowScanning;
    currState->enter();
    if (App_LEAVE != currState->loop()) {
        return;
    }

    // Button released
    if (currState == &colScanningPressed) {
        lightLedsUp(key);
    }

    // Next state
    currState = currState == &rowScanning ? &colScanning //
            :
                currState == &colScanning ? &colScanningPressed //
                        : &rowScanning;
    currState->enter();

答:只需要改变ScanKey函数,更改结果如下

  

  

图片 6图片 7

先是,调用当前情景的
loop()函数,其再次回到值注脚是否应该切换来下一个气象。如果切换来下一个气象,则调用其
enter()函数。淌尽管偏离第三阶段,则已检测到两次按键事件(按下并松手),遵照按键键值(0-15)点亮LED。点亮LED的函数定义如下,其无外乎按位依次点亮或消灭每一个LED:

第一,调用当前气象的
loop()函数,其再次回到值注明是否相应切换来下一个情状。假使切换到下一个意况,则调用其
enter()函数。假如是离开第三阶段,则已检测到五回按键事件(按下并甩手),按照按键键值(0-15)点亮LED。点亮LED的函数定义如下,其无外乎按位依次点亮或消失每一个LED:

 1 //按键长按的情况
 2 void ScanKey_Long(void)
 3 {
 4     unsigned char key;
 5     
 6     key = PINC; //检测按键状态
 7     if(0x01 == key) //未按下,退出
 8         return;
 9     delay_ms(10);
10     
11     key = PINC;  //再次检测,防抖动
12     if(0x01 == key) //未按下,退出
13         return;
14         
15     while(0x00 == key) //按键长按,不断加加
16     {
17         CountNum++;
18         key = PINC;
19     }
20 }
#define BIT_TO_PIN_VALUE(key, bit) ( (1 & (key >> bit)) ? GPIO_PIN_SET : GPIO_PIN_RESET )

void lightLedsUp(uint8_t key) {
    HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, BIT_TO_PIN_VALUE(key, 3));
    HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, BIT_TO_PIN_VALUE(key, 2));
    HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, BIT_TO_PIN_VALUE(key, 1));
    HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, BIT_TO_PIN_VALUE(key, 0));
}
#define BIT_TO_PIN_VALUE(key, bit) ( (1 & (key >> bit)) ? GPIO_PIN_SET : GPIO_PIN_RESET )

void lightLedsUp(uint8_t key) {
    HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, BIT_TO_PIN_VALUE(key, 3));
    HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, BIT_TO_PIN_VALUE(key, 2));
    HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, BIT_TO_PIN_VALUE(key, 1));
    HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, BIT_TO_PIN_VALUE(key, 0));
}

长按按键不断加加

 

 

 

对此行扫描状态,进入本场合时,应该对行、列的I/O口举办安装。也即,在其enter()实现中装置行I/O口为input格局,并启用其内部上拉电阻;列I/O为output格局,并输出0。其
loop()实现则相继检测行I/O口是否读数为0,若读数为0,则讲明该行有按键按下,记下行号,并离开本状态:

对于行扫描状态,进入这一场所时,应该对行、列的I/O口举行设置。也即,在其enter()实现中安装行I/O口为input模式,并启用其里面上拉电阻;列I/O为output格局,并输出0。其
loop()实现则相继检测行I/O口是否读数为0,若读数为0,则注脚该行有按键按下,记下行号,并离开本状态:

3.矩阵按键(键盘)扫描的采纳

#define configInputPullUp(port, pin, GPIO_InitStruct) { \
/*        HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET); */ \
        (GPIO_InitStruct)->Pin = pin ; \
        (GPIO_InitStruct)->Mode = GPIO_MODE_INPUT ; \
        (GPIO_InitStruct)->Pull = GPIO_PULLUP ; \
        (GPIO_InitStruct)->Speed = GPIO_SPEED_FREQ_LOW; \
        HAL_GPIO_Init(port, GPIO_InitStruct) ; \
}

#define configOutputLow(port, pin, GPIO_InitStruct) { \
        (GPIO_InitStruct)->Pin = pin ; \
        (GPIO_InitStruct)->Mode = GPIO_MODE_OUTPUT_PP ; \
        (GPIO_InitStruct)->Pull = GPIO_NOPULL ; \
        (GPIO_InitStruct)->Speed = GPIO_SPEED_FREQ_LOW; \
        HAL_GPIO_Init(port, GPIO_InitStruct) ; \
        HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET);  \
}

#define DEBOUNCE_DELAY 5

void rowScanningEnter() {

    GPIO_InitTypeDef GPIO_InitStruct;

    // Row pins: input, pull-up enabled
    configInputPullUp(R1_GPIO_Port, R1_Pin, &GPIO_InitStruct);
    configInputPullUp(R2_GPIO_Port, R2_Pin, &GPIO_InitStruct);
    configInputPullUp(R3_GPIO_Port, R3_Pin, &GPIO_InitStruct);
    configInputPullUp(R4_GPIO_Port, R4_Pin, &GPIO_InitStruct);

    // Col pins: output 0
    configOutputLow(C1_GPIO_Port, C1_Pin, &GPIO_InitStruct);
    configOutputLow(C2_GPIO_Port, C2_Pin, &GPIO_InitStruct);
    configOutputLow(C3_GPIO_Port, C3_Pin, &GPIO_InitStruct);
    configOutputLow(C4_GPIO_Port, C4_Pin, &GPIO_InitStruct);

}

GPIO_PinState checkPressedLow(GPIO_TypeDef *port, uint16_t pin) {
    if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(port, pin)) {
        // Delay & read again
        HAL_Delay(DEBOUNCE_DELAY);
        return HAL_GPIO_ReadPin(port, pin);
    }

    return GPIO_PIN_SET;
}

uint8_t rowScanningLoop() {

    if (GPIO_PIN_RESET == checkPressedLow(R1_GPIO_Port, R1_Pin)) {
        key = 0;
        return App_LEAVE;
    }
    if (GPIO_PIN_RESET == checkPressedLow(R2_GPIO_Port, R2_Pin)) {
        key = 1 << 2;
        return App_LEAVE;
    }
    if (GPIO_PIN_RESET == checkPressedLow(R3_GPIO_Port, R3_Pin)) {
        key = 2 << 2;key
        return App_LEAVE;
    }
    if (GPIO_PIN_RESET == checkPressedLow(R4_GPIO_Port, R4_Pin)) {
        key = 3 << 2;
        return App_LEAVE;
    }

    return App_STAY;
}
#define configInputPullUp(port, pin, GPIO_InitStruct) { \
/*        HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET); */ \
        (GPIO_InitStruct)->Pin = pin ; \
        (GPIO_InitStruct)->Mode = GPIO_MODE_INPUT ; \
        (GPIO_InitStruct)->Pull = GPIO_PULLUP ; \
        (GPIO_InitStruct)->Speed = GPIO_SPEED_FREQ_LOW; \
        HAL_GPIO_Init(port, GPIO_InitStruct) ; \
}

#define configOutputLow(port, pin, GPIO_InitStruct) { \
        (GPIO_InitStruct)->Pin = pin ; \
        (GPIO_InitStruct)->Mode = GPIO_MODE_OUTPUT_PP ; \
        (GPIO_InitStruct)->Pull = GPIO_NOPULL ; \
        (GPIO_InitStruct)->Speed = GPIO_SPEED_FREQ_LOW; \
        HAL_GPIO_Init(port, GPIO_InitStruct) ; \
        HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET);  \
}

#define DEBOUNCE_DELAY 5

void rowScanningEnter() {

    GPIO_InitTypeDef GPIO_InitStruct;

    // Row pins: input, pull-up enabled
    configInputPullUp(R1_GPIO_Port, R1_Pin, &GPIO_InitStruct);
    configInputPullUp(R2_GPIO_Port, R2_Pin, &GPIO_InitStruct);
    configInputPullUp(R3_GPIO_Port, R3_Pin, &GPIO_InitStruct);
    configInputPullUp(R4_GPIO_Port, R4_Pin, &GPIO_InitStruct);

    // Col pins: output 0
    configOutputLow(C1_GPIO_Port, C1_Pin, &GPIO_InitStruct);
    configOutputLow(C2_GPIO_Port, C2_Pin, &GPIO_InitStruct);
    configOutputLow(C3_GPIO_Port, C3_Pin, &GPIO_InitStruct);
    configOutputLow(C4_GPIO_Port, C4_Pin, &GPIO_InitStruct);

}

GPIO_PinState checkPressedLow(GPIO_TypeDef *port, uint16_t pin) {
    if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(port, pin)) {
        // Delay & read again
        HAL_Delay(DEBOUNCE_DELAY);
        return HAL_GPIO_ReadPin(port, pin);
    }

    return GPIO_PIN_SET;
}

uint8_t rowScanningLoop() {

    if (GPIO_PIN_RESET == checkPressedLow(R1_GPIO_Port, R1_Pin)) {
        key = 0;
        return App_LEAVE;
    }
    if (GPIO_PIN_RESET == checkPressedLow(R2_GPIO_Port, R2_Pin)) {
        key = 1 << 2;
        return App_LEAVE;
    }
    if (GPIO_PIN_RESET == checkPressedLow(R3_GPIO_Port, R3_Pin)) {
        key = 2 << 2;key
        return App_LEAVE;
    }
    if (GPIO_PIN_RESET == checkPressedLow(R4_GPIO_Port, R4_Pin)) {
        key = 3 << 2;
        return App_LEAVE;
    }

    return App_STAY;
}

  按键太多的情形下,为了节约I/O资源,平时采纳矩阵式的接口。矩阵键盘由行和列组成,每个键都有它的行值和列值,行值和列值的结合就是识别每个键盘的编码。

 

 

规定是哪些按键的流程:(???)

留意,在读取pin值时,为了de-bouncing,扩张了一个5ms的延时复读。一般,de-bouncing延时取5-10ms。

留神,在读取pin值时,为了de-bouncing,扩展了一个5ms的延时复读。一般,de-bouncing延时取5-10ms。

(1)在行和列的一个口中输出高电平,在另一个队列口读取一个扫描码;

列扫描状态的兑现与行扫描相接近,这里便不再给出代码了。需要验证的是,程序中采取了一个字节型全局变量
key
用来保存键值,其第2-3位为行号(0-3),第0-1位为列号(0-3),因而,key
的值为0-0x0F,依次对应16个按键。

列扫描状态的贯彻与行扫描相仿佛,这里便不再给出代码了。需要申明的是,程序中使用了一个字节型全局变量
key
用来保存键值,其第2-3位为行号(0-3),第0-1位为列号(0-3),由此,key
的值为0-0x0F,依次对应16个按键。

(2)在后一个行列口中输出高电平,在前一行列口读取第二个扫描码;

而第三品级无需变更I/O口设置,只需检测被按下按键所在的列是否读取pin值为1。读取pin值为1标志按键被卸掉,应该离开此意况,切换回行扫描状态:

而第三品级无需变更I/O口设置,只需检测被按下按键所在的列是否读取pin值为1。读取pin值为1标志按键被卸掉,应该离开此意况,切换回行扫描状态:

(3)查表确定哪些按键被按下。

uint8_t colScanningPressedLoop() {

    int col = 3 & key;

    if (0 == col) {
        if (GPIO_PIN_SET == HAL_GPIO_ReadPin(C1_GPIO_Port, C1_Pin)) {
            return App_LEAVE;
        }
    } else if (1 == col) {
        if (GPIO_PIN_SET == HAL_GPIO_ReadPin(C2_GPIO_Port, C2_Pin)) {
            return App_LEAVE;
        }
    } else if (2 == col) {
        if (GPIO_PIN_SET == HAL_GPIO_ReadPin(C3_GPIO_Port, C3_Pin)) {
            return App_LEAVE;
        }
    } else { // 3== col
        if (GPIO_PIN_SET == HAL_GPIO_ReadPin(C4_GPIO_Port, C4_Pin)) {
            return App_LEAVE;
        }
    }

    return App_STAY;
}
uint8_t colScanningPressedLoop() {

    int col = 3 & key;

    if (0 == col) {
        if (GPIO_PIN_SET == HAL_GPIO_ReadPin(C1_GPIO_Port, C1_Pin)) {
            return App_LEAVE;
        }
    } else if (1 == col) {
        if (GPIO_PIN_SET == HAL_GPIO_ReadPin(C2_GPIO_Port, C2_Pin)) {
            return App_LEAVE;
        }
    } else if (2 == col) {
        if (GPIO_PIN_SET == HAL_GPIO_ReadPin(C3_GPIO_Port, C3_Pin)) {
            return App_LEAVE;
        }
    } else { // 3== col
        if (GPIO_PIN_SET == HAL_GPIO_ReadPin(C4_GPIO_Port, C4_Pin)) {
            return App_LEAVE;
        }
    }

    return App_STAY;
}

电路图如下:

 

 

程序实现步骤:

(1)确定有无按键按下;

(2)确定是哪些按键;

(3)再次回到该按键值或拍卖相应的天职;

(4)再添加,考虑抖动消除,等待按键的断开。

要实现每按下一个按键,就在LED数码管展现出该按键对应的值,按键断开后或默认突显“-”:

图片 8图片 9

  1 #include <iom8v.h>
  2 #include "Delay.h"
  3 /**
  4   *PB口:连接一个LED数码管
  5   *PC0-PC5:连接9个按键电路,按下呈低电平
  6   *        PC0-PC2:按键的行
  7   *        PC3-PC5:按键的列
  8   *
  9   *1:确定有无按键按下,main函数两次判断实现;
 10   *2:确定是哪个按键,ScanKey函数实现;
 11   *3:返回该键值或处理对应的任务,main函数中处理。
 12   */
 13   
 14 
 15 //按键扫描函数,确定是哪个按键,返回按键的值
 16 unsigned char ScanKey(void)
 17 {
 18     unsigned char tempH,tempL,key;
 19     
 20     tempH = PINC & 0x07; //取PC0-PC2用于判断行
 21     switch (tempH)
 22     {
 23         case 0x06:
 24             DDRC = 0x07; PORTC = 0x38;//将PC3-PC5(列)置为输入模式
 25             delay_us(1);
 26             tempL = PINC & 0x38; //取PC3-PC5用于判断列
 27             switch (tempL)
 28             {
 29                 case 0x30: key = 0x01; //得到键值
 30                     break;
 31                 case 0x28: key = 0x02;
 32                     break;
 33                 case 0x18: key = 0x03;
 34                     break;
 35                 default:   key = 0;
 36                     break;
 37             }
 38             DDRC = 0x38; PORTC = 0x07;//输入完毕恢复默认输出模式
 39             break;
 40             
 41         case 0x05:
 42             DDRC = 0x07; PORTC = 0x38;//将PC3-PC5(列)置为输入模式
 43             delay_us(1);
 44             tempL = PINC & 0x38; //取PC3-PC5用于判断列
 45             switch (tempL)
 46             {
 47                 case 0x30: key = 0x04; //得到键值
 48                     break;
 49                 case 0x28: key = 0x05;
 50                     break;
 51                 case 0x18: key = 0x06;
 52                     break;
 53                 default:   key = 0;
 54                     break;
 55             }
 56             DDRC = 0x38; PORTC = 0x07;//输入完毕恢复默认输出模式
 57             break;
 58             
 59         case 0x03:
 60             DDRC = 0x07; PORTC = 0x38;//将PC3-PC5(列)置为输入模式
 61             delay_us(1);
 62             tempL = PINC & 0x38; //取PC3-PC5用于判断列
 63             switch (tempL)
 64             {
 65                 case 0x30: key = 0x07; //得到键值
 66                     break;
 67                 case 0x28: key = 0x08;
 68                     break;
 69                 case 0x18: key = 0x09;
 70                     break;
 71                 default:   key = 0;
 72                     break;
 73             }
 74             DDRC = 0x38; PORTC = 0x07;//输入完毕恢复默认输出模式
 75             break;
 76             
 77         default:
 78             key = 0;
 79             break;
 80     }
 81     return (key);
 82     
 83 }
 84 
 85 //主函数,扫描按键显示数据
 86 void main()
 87 {
 88     unsigned char temp,keynum;
 89     unsigned char num[10] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
 90     //初始化端口
 91     DDRB = 0xFF; //设置B口为输出模式
 92     PORTB = 0xFF; //置高电平
 93         //将PC0-PC2(行)置为输入模式
 94     DDRC = 0x38;
 95     PORTC = 0x07;
 96     
 97     while(1) //两次检测行中有没有按下
 98     {
 99         PORTC = 0x40; //没有按键时,LED默认显示-
100         //第一次检验
101         temp = PINC & 0x07;
102         if(0x07 == temp)
103             continue;
104         delay_ms(10);
105         //第二次检验
106         temp = PINC & 0x07;
107         if(0x07 == temp)
108             continue;
109         
110         //有按下,LED显示键值
111         keynum = ScanKey();
112         PORTB = num[keynum];
113         //等到按键被释放
114         while(0x07 != temp)
115             temp = PINC & 0x07;
116     }
117 }

矩阵键盘,LED显示其值

代码总括:

  主函数:判断是否有按键按下,并清除抖动搅扰,若有则将取得的键值呈现在LED数码管中;

  ScanKey函数:得到扫描码确定是哪个按键,等待按键释放,重临该按键的值。

格局扩充:

(1)除了像下面的对按键的接口不停的围观,

(2)还足以使用定时扫描,例如用一个定时器,每隔10MS
对按键接口举行扫描,看是否有按键按下;

(3)也足以运用中断的主意去扫描,当按键按下时由硬件电路暴发一个戛不过止,MCU
响应该中断,确定哪些按键被按下,处理相应函数。

相关文章