You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ATMEGA328P中断切换switch case时多按钮误触发问题排查

问题分析与解决方案

核心问题拆解

  1. 引脚浮空引发误触发:PC1/PC2/PC3被设置为输入但未启用内部上拉电阻,引脚处于浮空状态,环境噪声会导致虚假电平变化,频繁触发中断。
  2. 无按钮消抖处理:按钮机械抖动会让单次操作多次触发中断,导致Bot变量持续累加,主循环出现误判。
  3. 中断逻辑存在缺陷:
    • 使用else if判断引脚状态,抖动时若多个引脚电平同时变化,会错误匹配分支;
    • 未区分按钮按下/释放的边沿,按下和松开动作都会触发中断,进一步加剧变量累加问题。
  4. 变量类型设计不合理:Bot1/Bot2/Bot3采用int类型累加,单次按钮操作可能让变量值大于1,主循环处理后仍有剩余值,引发多次模式切换。

硬件支持说明

ATMEGA328P的PCIE1中断(端口C引脚变化中断)支持同时监听PCINT9(PC1)、PCINT10(PC2)、PCINT11(PC3),硬件完全可以实现3路按钮的中断检测,问题出在软件与电路配置。

具体修复步骤

1. 启用内部上拉电阻

初始化时为PC1/PC2/PC3启用内部上拉,避免引脚浮空:

PORTC |= (1<<PC1)|(1<<PC2)|(1<<PC3); // 启用PC1-PC3内部上拉

(此时按钮需连接引脚与GND,按下时引脚为低电平,与代码中的!tst_bit判断逻辑匹配)

2. 修改中断服务程序(ISR)

添加消抖逻辑,仅响应按钮按下的下降沿,避免抖动与误判:

// 新增全局变量记录上一次引脚状态
volatile uint8_t last_pin_state = 0xFF;

ISR(PCINT1_vect)
{
    uint8_t current_state = PINC & ((1<<PC1)|(1<<PC2)|(1<<PC3));
    // 只处理下降沿(按钮按下:上拉状态下,引脚从高变低)
    if ((last_pin_state & (1<<PC1)) && !(current_state & (1<<PC1)))
    {
        Bot1 = 1; // 标记按钮按下,而非累加
    }
    if ((last_pin_state & (1<<PC2)) && !(current_state & (1<<PC2)))
    {
        Bot2 = 1;
    }
    if ((last_pin_state & (1<<PC3)) && !(current_state & (1<<PC3)))
    {
        Bot3 = 1;
    }
    last_pin_state = current_state;
    // 软件消抖(中断内短延时,避免过长影响其他功能)
    _delay_ms(10);
    // 再次读取状态确认
    current_state = PINC & ((1<<PC1)|(1<<PC2)|(1<<PC3));
    last_pin_state = current_state;
}

3. 修正变量定义

将Bot变量改为uint8_t类型,仅用0/1标记状态:

volatile uint8_t Bot1=0;
volatile uint8_t Bot2=0;
volatile uint8_t Bot3=0;

4. 主循环逻辑优化

确保每次按钮事件只处理一次,避免重复触发:

// 以case 1为例,其他case同理
case 1:
    // ... 原有LCD显示代码 ...
    if (Bot1==1)
    {
        Bot1=0;
        modo=0;
    }
    if (Bot2==1)
    {
        cpl_bit(PORTC,PC5);
        Bot2=0;
        modo=2;
    }
    break;

5. 修复定时器1的笔误

原代码中if(d==3)里的d==0是比较而非赋值,修正为:

if(d==3)
{
    d=0;
}

修改后完整代码示例

/*
 * Atividade Avaliativa 3.c
 *
 * Created: 30/11/2022 00:32:41
 * Author : Gustavo Feijó
 */

#include "def_principais.h"
#include "LCD.h"
#include <avr/interrupt.h>
#include <stdio.h>
#include <util/delay.h>

const unsigned char Segment[] PROGMEM = {0x30, 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39
                                        }; //Hexadecimal dos numeros para o display.
volatile uint8_t Bot1=0;
volatile uint8_t Bot2=0;
volatile uint8_t Bot3=0;
volatile uint8_t last_pin_state = 0xFF;
volatile int i=0;
volatile int j=0;
volatile int m=0;
volatile int k=0;
volatile int h=0;
volatile int d=0;
volatile int Ah=0;
volatile int Ad=0;
volatile int Am=0;

int main(void)
{
    int modo=1;
    DDRD = 0xFF;
    DDRC = 0x01; // PC0输出,PC1-PC3输入
    DDRB = 0x0F;

    // 启用PC1-PC3内部上拉电阻
    PORTC |= (1<<PC1)|(1<<PC2)|(1<<PC3);

    //TIMER 0 = 10ms
    TCCR0A |= 0x02;
    TCCR0B |= 0x05;
    OCR0A = 78;
    set_bit(TIMSK0, OCIE0A);


    //Timer 1 = 1s
    TCCR1A |= 0x00;
    TCCR1B |= ((1<<CS12)|(1<<CS10)|(1<<WGM12));
    OCR1A = 7812;
    set_bit(TIMSK1, OCIE1A);

    //Interrupções botões
    PCICR = 1<<PCIE1;
    PCMSK1 = (1<<PCINT11)|(1<<PCINT10)|(1<<PCINT9);

    sei();
    inic_LCD_4bits();

    while(1)
    {
        switch (modo)
        {
        case 0:
            cmd_LCD(0x01,0);
            if (Bot1==1)
            {
                Bot1=0;
                modo=1;
            }
            break;
        case 1:
            cmd_LCD(0x8B,0);
            cmd_LCD(pgm_read_byte(&Segment[i]),1);
            cmd_LCD(0x8A,0);
            cmd_LCD(pgm_read_byte(&Segment[j]),1);
            cmd_LCD(0x89,0);
            cmd_LCD(0x3A,1);
            cmd_LCD(0x88,0);
            cmd_LCD(pgm_read_byte(&Segment[m]),1);
            cmd_LCD(0x87,0);
            cmd_LCD(pgm_read_byte(&Segment[k]),1);
            cmd_LCD(0x86,0);
            cmd_LCD(0x3A,1);
            cmd_LCD(0x85,0);
            cmd_LCD(pgm_read_byte(&Segment[h]),1);
            cmd_LCD(0x84,0);
            cmd_LCD(pgm_read_byte(&Segment[d]),1);
            if (Bot1==1)
            {
                Bot1=0;
                modo=0;
            }
            if (Bot2==1)
            {
                cpl_bit(PORTC,PC5);
                Bot2=0;
                modo=2;
            }
            break;
        case 2:
            escreve_LCD("CU");
            if (Bot1==1)
            {
                Bot1=0;
                cmd_LCD(0x01,0);
                modo=1;
            }
            break;
        }
    }
}

ISR(TIMER0_COMPA_vect)
{
    cpl_bit(PORTB,PB2);
}
//interrupaçao de 1S
ISR(TIMER1_COMPA_vect)
{
    cpl_bit(PORTB,PB3);
    i++;
    if (i==10)
    {
        i=0;
        j++;
    }
    if (j==7)
    {
        j=0;
        m++;
    }
    if (m==10)
    {
        m=0;
        k++;
    }
    if (k==7)
    {
        k=0;
        h++;
    }
    if (h==5)
    {
        h=0;
        d++;
    }
    if(d==3)
    {
        d=0;
    }
}
ISR(PCINT1_vect)
{
    uint8_t current_state = PINC & ((1<<PC1)|(1<<PC2)|(1<<PC3));
    // 检测下降沿(按钮按下)
    if ((last_pin_state & (1<<PC1)) && !(current_state & (1<<PC1)))
    {
        Bot1 = 1;
    }
    if ((last_pin_state & (1<<PC2)) && !(current_state & (1<<PC2)))
    {
        Bot2 = 1;
    }
    if ((last_pin_state & (1<<PC3)) && !(current_state & (1<<PC3)))
    {
        Bot3 = 1;
    }
    last_pin_state = current_state;
    // 软件消抖
    _delay_ms(10);
    current_state = PINC & ((1<<PC1)|(1<<PC2)|(1<<PC3));
    last_pin_state = current_state;
}

内容的提问来源于stack exchange,提问作者Gustavo Feijo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 12:40:53