yzhkpli 发表于 2025-4-23 21:42:56

看b站lkl0305的状态机处理按钮视频有点小问题

采用状态机思路,利用定时器中断方式实现按键计数检测,
每按下按键K1 一次,计数值加 1,加到 23 后再按下,
计数值回到 0,并用动态显示计数值。
电路原理图如图所示

状态机转移示意图


以下为老师提供的代码。他使用了·10ms这个按键抖动时间作为检查状态的间隔时间。
/*
P0.0~P0.8输出到两位一体的共阳数码管7SEG-MPX2-CA的段码部分
P2.4和P2.5经过74HC04的反相分别接到7SEG-MPX2-CA的位选部分
P2.0通过一个按钮接地
要求按下按钮,数码管显示数字加1
本程序使用状态机框架
相关视频:https://www.bilibili.com/video/BV11a4y1v7k9
*/
#include "reg52.h"
#include "INTRINS.H"

#define LEDPORT P0
sbit BUTTON=P2^0;
sbit gewei=P2^4;
sbit shiwei=P2^5;

unsigned char code CA_numbers[]=
{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0x8c,0xc1,0x91,0x7c,0x00,0xff};

signed char count=0;

void display(void);

void delaynms(unsigned int n);
void Delay1ms(void);

void Timer0_Init(void);
void timer0_isr(void);

void Action();

//void Sys_Init(){
//      state=0;
//}

void main(void){
      EA=1;
      Timer0_Init();
      //Sys_Init();
      while(1){
                display();
      }
}

void Timer0_Init(void)                //10毫秒@11.0592MHz
{
      //AUXR &= 0x7F;                        //定时器时钟12T模式
      TMOD &= 0xF0;                        //设置定时器模式
      TMOD |= 0x01;                        //设置定时器模式
      TL0=(65536-10000)%256;                              //设置定时初始值
      TH0=(65536-10000)/256;                              //设置定时初始值
      TF0 = 0;                              //清除TF0标志
      PT0=1;
      ET0 = 1;                              //使能定时器0中断
      TR0 = 1;                              //定时器0开始计时
}
void timer0_isr(void) interrupt 1
{
      static signed char state=0;
      TF0=0;
      TH0=(65536-10000)/256;
      TL0=(65536-10000)%256;

      switch(state){
                case 0:
                {
                        if(0==BUTTON){
                              state=1;
                        }
                        break;
                }
                case 1:
                {
                        if(0==BUTTON){
                              state=2;
                              Action();
                        }else{
                              state=0;
                        }
                        break;
                }
                case 2:
                {
                        if(1==BUTTON){
                              state=3;
                        }
                        break;
                }
                case 3:
                {
                        if(1==BUTTON){
                              state=0;
                        }else{
                              state=2;
                        }
                        break;
                }
                default:{
                        ;//留空
                }
      }
}
void Action(){
      count++;
      if(count>=23){
                count=0;
      }
}

void display(void){
      //先显示个位
      gewei=1;
      shiwei=0;
      LEDPORT=CA_numbers;
      gewei=0;
      delaynms(1);
      //再显示十位
      gewei=0;
      shiwei=1;
      LEDPORT=CA_numbers;
      shiwei=0;
      delaynms(1);
}

void Delaynms(unsigned int n){
      while(n--){
                Delay1ms();
      }
}
void Delay1ms(void)      //@11.0592MHz
{
      unsigned char data i, j;

      _nop_();
      _nop_();
      _nop_();
      i = 11;
      j = 190;
      do
      {
                while (--j);
      } while (--i);
}



国芯人工智能 发表于 2025-4-23 21:58:00

在分析您提供的状态机处理按钮的代码和电路原理图后,我发现了一些潜在的问题和改进建议。以下是我对代码和实现思路的详细分析:

1. 状态机设计分析
状态机是一种用于处理复杂逻辑的有效方法,尤其是在处理按键输入时,可以有效避免抖动和误触发。您提供的状态机转移示意图和代码框架基本符合状态机的设计原则,但存在一些细节问题。

1.1 状态机状态定义
在状态机设计中,通常需要明确定义各个状态及其转移条件。您的代码中使用了定时器中断来检测按键状态,但状态机的状态定义不够清晰。建议明确以下几个状态:
IDLE:按键未按下,等待按键按下。
DEBOUNCE:按键按下,进入消抖状态。
PRESSED:按键确认按下,执行计数操作。
RELEASE:按键释放,等待下一次按下。

1.2 状态转移条件
状态转移条件需要根据按键的实际状态进行判断。您的代码中使用了10ms的定时器中断来检测按键状态,但未明确状态转移的条件。建议在定时器中断中根据按键的当前状态和上一次状态来判断是否进行状态转移。

2. 代码实现问题
2.1 按键消抖处理
按键消抖是按键处理中的关键步骤。您的代码中使用了10ms的定时器中断来检测按键状态,但未进行消抖处理。建议在DEBOUNCE状态中,连续多次检测按键状态,确保按键确实按下后再进入PRESSED状态。

2.2 计数逻辑
您的代码中实现了按键计数功能,但未对计数值进行范围检查。建议在计数逻辑中加入范围检查,确保计数值在0到23之间循环。

2.3 数码管显示
数码管显示部分代码未完整提供,但根据电路原理图,您使用了两位一体的共阳数码管。建议在显示函数中分别处理个位和十位的显示,并确保显示更新频率足够高,避免闪烁。

3. 改进建议
3.1 状态机实现
以下是改进后的状态机实现代码框架:

c
typedef enum {
    STATEIDLE,
    STATEDEBOUNCE,
    STATEPRESSED,
    STATERELEASE
} ButtonState;

ButtonState buttonState = STATEIDLE;
unsigned char debounceCount = 0;

void Timer0ISR(void) interrupt 1 {
    static unsigned char lastButtonState = 1;

    unsigned char currentButtonState = BUTTON;

    switch (buttonState) {
      case STATEIDLE:
            if (currentButtonState == 0 && lastButtonState == 1) {
                buttonState = STATEDEBOUNCE;
                debounceCount = 0;
            }
            break;

      case STATEDEBOUNCE:
            if (currentButtonState == 0) {
                debounceCount++;
                if (debounceCount >= 5) { // 50ms消抖
                  buttonState = STATEPRESSED;
                }
            } else {
                buttonState = STATEIDLE;
            }
            break;

      case STATEPRESSED:
            count = (count + 1) % 24; // 计数循环
            display();
            buttonState = STATERELEASE;
            break;

      case STATERELEASE:
            if (currentButtonState == 1) {
                buttonState = STATEIDLE;
            }
            break;
    }

    lastButtonState = currentButtonState;
}

3.2 数码管显示
以下是改进后的数码管显示函数:

c
void display(void) {
    unsigned char shi = count / 10;
    unsigned char ge = count % 10;

    LEDPORT = CAnumbers;
    shiwei = 0;
    delaynms(5);
    shiwei = 1;

    LEDPORT = CAnumbers;
    gewei = 0;
    delaynms(5);
    gewei = 1;
}

4. 总结
通过以上改进,您的状态机处理按钮的代码将更加健壮和可靠。明确的状态定义和转移条件可以有效避免按键抖动和误触发,而改进后的数码管显示函数可以确保显示效果更加稳定。希望这些建议对您有所帮助。

[本文内容由国芯人工智能辅助生成,仅供参考]

yzhkpli 发表于 2025-4-23 22:56:14

国芯人工智能 发表于 2025-4-23 21:58
在分析您提供的状态机处理按钮的代码和电路原理图后,我发现了一些潜在的问题和改进建议。以下是我对代码和 ...
您们这是搞了一个内部的qwen3还是deepseek 70b?

我的count设置了范围
void Action(){
      count++;
      if(count>=23){
                count=0;
      }
}

仔细看了一下状态机有点意思,就是没有考虑按下抬起阶段的抖动吧。
另外居然是在中断函数中去执行的显示数码管,这样做,会不会太暗了?我哪天得用普中或者别的开发板跑跑这个状态机。proteus貌似没法模拟数码管的亮度。{:weiqu:}

再补充一下protues文件。制作于proteus8.16

yzhkpli 发表于 2025-4-23 23:19:56

国芯人工智能 发表于 2025-4-23 21:58
在分析您提供的状态机处理按钮的代码和电路原理图后,我发现了一些潜在的问题和改进建议。以下是我对代码和 ...

我按照您给出的display函数做了修改,更新后的main.c见附件。但是这样搞得话,十位和个位还是反了。{:yiwen:}

yzhkpli 发表于 2025-4-23 23:36:10

干脆做了一个测试项目
真是活见鬼了。明明这个二位一体的共阳数码管(左面可以清晰的看到是CA!)1号是低电平表示未选中,2号是高电平表示选中。


又仔细bing上搜了一下
51单片机教程:数码管动态显示(0~99999999)74hc138驱动_c51单片机数码管动态倒数-CSDN博客
发现1号位是最左边的那个!
在本例中,shi应该是P2.4接那个。。。。。




#include "reg52.h"
#include "INTRINS.H"
#define LEDPORT P0
sbit BUTTON=P2^0;
sbit gewei=P2^4;
sbit shiwei=P2^5;
unsigned char code CA_numbers[]=
{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0x8c,0xc1,0x91,0x7c,0x00,0xff};

void Delay1ms(void)      //@11.0592MHz
{
      unsigned char data i, j;

      _nop_();
      _nop_();
      _nop_();
      i = 11;
      j = 190;
      do
      {
                while (--j);
      } while (--i);
}
void Delaynms(unsigned int n){
      while(n--){
                Delay1ms();
      }
}
void main(){
        while(1){
                shiwei = 0;gewei=1;
                LEDPORT = CA_numbers;
                Delaynms(5);
                shiwei = 1;
        }
}


页: [1]
查看完整版本: 看b站lkl0305的状态机处理按钮视频有点小问题