ATMEGA328P中断切换switch case时多按钮误触发问题排查
问题分析与解决方案
核心问题拆解
- 引脚浮空引发误触发:PC1/PC2/PC3被设置为输入但未启用内部上拉电阻,引脚处于浮空状态,环境噪声会导致虚假电平变化,频繁触发中断。
- 无按钮消抖处理:按钮机械抖动会让单次操作多次触发中断,导致
Bot变量持续累加,主循环出现误判。 - 中断逻辑存在缺陷:
- 使用
else if判断引脚状态,抖动时若多个引脚电平同时变化,会错误匹配分支; - 未区分按钮按下/释放的边沿,按下和松开动作都会触发中断,进一步加剧变量累加问题。
- 使用
- 变量类型设计不合理:
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
相关产品推荐
相关产品推荐

