ATmega328PB中双AVR中断向量(PCINT0_vect与PCINT1_vect)仅能触发一个的问题咨询
嘿,我看了你的代码和问题描述,马上就发现了几个关键问题,咱们一步步来梳理:
核心问题:PCICR寄存器被覆盖导致仅一组中断启用
你在SonarSensor_init0()和SonarSensor_init1()里都是直接给PCICR赋值:
// 在init0里 PCICR = (1<<PCIE0); // 在init1里 PCICR = (1<<PCIE1);
这样的赋值方式会覆盖寄存器的原有值。比如先调用SonarSensor_init0(),此时PCICR的PCIE0位被置1,PCIE1位是0;接着调用SonarSensor_init1(),直接把PCICR设置成只有PCIE1位为1,之前的PCIE0位就被清零了!这就是为什么只有后调用的初始化函数对应的中断能工作。
正确的做法应该是用位或操作来保留原有位,只置位需要的位:
// 在init0里 PCICR |= (1<<PCIE0); // 在init1里 PCICR |= (1<<PCIE1);
这样两个中断组的使能位都会被置1,不会互相覆盖。
其他需要修正的问题
全局中断的错误操作:你的
main()循环里反复调用sei(),而且每个ISR最后都调用cli()关闭全局中断。一旦某个ISR执行完调用cli(),后续所有中断都无法触发,这也会导致另一个中断没法工作。你只需要在main()开头调用一次sei()就够了,ISR里不要随便调用cli(),除非你有明确的理由要临时关闭中断(显然这里不需要)。定时器预分频的注释错误:你在
SonarSensor_init1()里写的注释是“Timer without prescaler”,但实际设置的是TCCR1B |= (1<<CS12);——CS12置位、CS11和CS10为0的话,定时器1是256分频,不是无预分频。如果需要无预分频,应该设置TCCR1B |= (1<<CS10);(CS10置位,其他CS位清零)。这个错误会影响你的距离计算精度,得注意。定时器输入捕获的设置遗漏:在
SonarSensor_init1()里你设置了ICES1(上升沿捕获),但这个是定时器1的输入捕获功能,不过你现在用的是PCINT中断来检测电平变化,其实这个设置和当前的PCINT中断逻辑无关,要么你改成用输入捕获中断来做(可能更准确),要么就可以去掉这个设置,避免混淆。
修改后的关键代码示例
修正后的初始化函数片段
void SonarSensor_init1(void) { // ... 其他代码保持不变 // 改用位或操作启用PCIE1 PCICR |= (1<<PCIE1); // ... 其他代码保持不变 } void SonarSensor_init0(void) { // ... 其他代码保持不变 // 改用位或操作启用PCIE0 PCICR |= (1<<PCIE0); // ... 其他代码保持不变 }
修正后的main函数
int main(void) { SonarSensor_init0(); SonarSensor_init1(); sei(); // 只需要在这里启用一次全局中断 while(1) { PORTC |= (1 << PINC4); _delay_us(10); PORTC &= ~(1<<PINC4); PORTB |= (1 << PINB0); _delay_us(10); PORTB &= ~(1<<PINB0); // 可以在这里添加距离处理逻辑,比如打印distance1和distance2 } }
修正后的ISR函数(移除cli())
ISR(PCINT1_vect) { if ( (PINC & (1 << PINC5)) == (1 << PINC5)) { TCNT1 = 0; PORTB |= (1 << PINB5); } else { distance2 = TCNT1/3; PORTB &= ~(1 << PINB5); // 移除cli(); 不要关闭全局中断 } } ISR(PCINT0_vect) { if ( (PINB & (1 << PINB1)) == (1 << PINB1)) { TCNT0 = 0; PORTB |= (1 << PINB5); } else { distance1 = TCNT0; PORTB &= ~(1 << PINB5); // 移除cli(); 不要关闭全局中断 } }
按照这些修改,两个PCINT中断应该都能正常触发了,你可以试试看。
备注:内容来源于stack exchange,提问作者Tungstein

