找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1957|回复: 9
打印 上一主题 下一主题
收起左侧

最近在学习单片机用状态机来做按钮检测,遇到了一点问题,希望大佬看一下我是哪里出错?

[复制链接]
跳转到指定楼层
楼主
想尝试做一个简单带有闹钟功能的时钟,结果在按钮检测方面遇到了一些问题。在这里放上我写的按键代码:(我遇到的问题是:在判断不同按键按下,我使用了if...esle if...。结果只能执行else if后面的语句,不执行 if 语句。)


////////////////////////////////////////////////////////////////////////////////////////

单片机 avr 16

时钟频率 8M

///////////////////////////////////////////////////////////////////////////////////////



#include<iom16v.h>

#define uchar unsigned char

char time_10ms_ok;//10ms按键扫描标志

void main(void);
void io_init(void);
void time_init(void);
void cpu_init(void);
void time_10ms(void);
char key_scan(void);

void io_init(void)
{
         DDRB = 0X00;
        PORTB = 0XFF;
        DDRD = 0XFF;
        PORTD = 0XFF;
}

void time_init(void)
{
         TCCR1A = 0X00;
         TCCR1B = (1<<WGM12)|(3<<CS10);//CTC,64分频
        OCR1AH = 1250>>8;
        OCR1AL = 1250;
         TIMSK = 1<<OCIE1A;
         SREG = 1<<7;
}

void cpu_init(void)
{
         io_init();
        time_init();
}

void main(void)
{
         cpu_init();
        while(1)
        {
                 if(time_10ms_ok)
                {
                         time_10ms_ok = 0;
                        if(key_scan() == 2) PORTD = ~PORTD; //每按下按键2,3,
                        else if(key_scan() == 3) PORTD = ~PORTD; //PORTD取一次反。本是打算这样设计的。结果仿真的时候。只执行else if这一语句
                }
        }
}
#pragma interrupt_handler time_10ms:7
void time_10ms(void)
{
        time_10ms_ok = 1;
}

#define key_state0 0
#define key_state1 1
#define key_state2 2
谢谢。

char key_scan(void)
{
         static char key_state = 0;
        char key_press, key_return = 0;
        key_press = PINB;
        switch(key_state)
        {
                 case key_state0:
                {
                         if(~key_press)//若有按键按下,则进入确认状态
                        {
                                 key_state = key_state1;
                        }
                        break;
                }
                case key_state1:
                {
                         if(~key_press)//确认有按键按下,开始识别按键按下位置
                        {
                                 if(!(key_press & (1<<PB0))) key_return = 1;//按键1被按下
                                else if(!(key_press & (1<<PB1))) key_return = 2;//按键2被按下
                                else if(!(key_press & (1<<PB2))) key_return = 3;//按键3被按下
                                 key_state = key_state2;
                        }
                        else key_state = key_state0;//若按键抬起则回到初始状态
                        break;
                }
                case key_state2:
                {
                         if(key_press == 0XFF)//若按键抬起则回到初始状态
                        {
                                 key_state = key_state0;
                        }
                        break;
                }
        }
        return key_return;
}


状态机test.rar

13.42 KB, 下载次数: 1

仿真文件

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:962686 发表于 2021-8-31 21:26 | 只看该作者
找到解决方法,但还是不太清楚这个问题出在哪
回复

使用道具 举报

板凳
ID:390416 发表于 2021-9-1 08:18 | 只看该作者

回复

使用道具 举报

地板
ID:190577 发表于 2021-9-1 08:26 | 只看该作者
if(key_scan() == 2) PORTD = ~PORTD; //每按下按键2,3,
                        else if(key_scan() == 3) PORTD = ~PORTD; //PORTD取一次反。本是打算这样设计的。结果仿真的时候。只执行else if这一语句,你这个执行语句不对,都是一样的。
回复

使用道具 举报

5#
ID:190577 发表于 2021-9-1 08:27 | 只看该作者

有限状态机由有限的状态和相互之间的转移构成,在任何时候只
能处于给定数目的状态中的一个。当接收到一个输入事件时,状态机产生一个输出,同时也可能伴随着状态的转移。
#define key_input    PIND.7      // 按键输入口
#define key_state_0  0
#define key_state_1  1
#define key_state_2  2
char read_key(void)
{
static char key_state = 0;
char key_press, key_return = 0;
key_press = key_input;        // 读按键 I/O (状态机的输入)
switch (key_state)
{
case key_state_0:        // 按键初始态
        if (!key_press) key_state = key_state_1;  // 键被按下,状态转换到键确认态 , 确定下一次按键的状态值
break;
case key_state_1:        // 按键确认态
if (!key_press)
{
            key_return = 1;      // 按键仍按下,按键确认输出为“1”
            key_state = key_state_2;  // 状态转换到键释放态
}
else
            key_state = key_state_0;  // 按键已抬起,转换到 按键初始态
       break;
case key_state_2:
            if (key_press) key_state = key_state_0;  //按键已释放,转换到按键初始态
       break;
}  
      return key_return;
}
该简单按键接口函数 read_key()在整个系统程序中应每隔10ms
调用执行一次,每次执行时将先读取与按键连接的 I/O 的电平到变量 key_press 中,然后进入用 switch 结构构成的状态机。switch 结构中的 case 语句分别实现了 3 个不同状态的处理判别过程,在每个状态中将根据状态的不同,以及 key_press 的值(状态机的输入)确定输出值(key_return),和确定下一次按键的状态值(key_state) 。 函数 read_key()的返回参数提供上层程序使用。返回值为 0 时,表示按键无动作;而返回 1 表示有一次按键闭合动作,需要进入按键处理程序做相应的键处理。在函数 read_key()中定义了 3 个局部变量,其中 key_press和key_return为一般普通的局部变量,每次函数执行时,key_press 中保存着刚检测的按键值。key_return 为函数的返回值,总是先初始化为 0,只有在状态 1 中重新置 1,作为表示按键确认的标志返回。变量 key_state 非常重要,它保存着按键的状态值,该变量的值在函数调用结束后不能消失,
必须保留原值,因此在程序中定义为“局部静态变量” ,用static 声明。如果使用的语言环境不支持 static 类型的局部变量,应将 key_state 定义为全局变量(关于局部静态变量的特点请参考相关介绍 C 语言程序设计的书籍) 。
回复

使用道具 举报

6#
ID:584195 发表于 2021-9-1 09:23 | 只看该作者
楼主,建议你 1、在写程序时,一行只写一句。
2、if(key_scan() == 2) PORTD = ~PORTD; //每按下按键2,3,
                        else if(key_scan() == 3) PORTD = ~PORTD; //PORTD取一次反。本是打算这样设计的。结果仿真的时候。只执行else if这一语句
这两句执行结果都是一样,为什么还要写两句。
3、你这是想写状态机,感沉这状态设计得让人看不懂。
4、最好你要把安揵消抖的算法加进去。
回复

使用道具 举报

7#
ID:130230 发表于 2021-9-1 09:24 | 只看该作者
按键扫描里面判断对应按键被按下去应该是对0x01左移分别取出PB0 PB1 PB2。
回复

使用道具 举报

8#
ID:962686 发表于 2021-9-1 21:03 | 只看该作者
zyluglugl 发表于 2021-9-1 09:23
楼主,建议你 1、在写程序时,一行只写一句。
2、if(key_scan() == 2) PORTD = ~PORTD; //每按下按键2,3 ...

谢谢指点。其实我也是刚刚开始学习,有一些自己认为理所应当,到了实践的时候才发现这样不正确
回复

使用道具 举报

9#
ID:750420 发表于 2021-9-2 15:25 | 只看该作者
同意6楼观点同时希望楼主将按键检测的结果以变量的形式单独存储。你的程序相当于读取了两次按键值,这样写是有可能出现按键切换时键值被改变了的情况。
回复

使用道具 举报

10#
ID:962686 发表于 2021-9-2 19:03 | 只看该作者
wyqf 发表于 2021-9-2 15:25
同意6楼观点同时希望楼主将按键检测的结果以变量的形式单独存储。你的程序相当于读取了两次按键值,这样写 ...

确实,我已经发现这个问题,不对其进行存储,数据太容易丢失了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表