Arduino定时器中断启用时出现意外窄脉冲的原因与解决
问题:Arduino Mega 2560中断激活后出现异常窄脉冲
我编写了一段代码,用于生成两路100Hz的同步脉冲:按下轻触开关后,第一路开始输出脉冲,第二路脉冲比第一路滞后25%的周期。
虽然输出频率符合预期,但用示波器观测时发现,中断激活后会立即出现一个窄脉冲,脉冲宽度1.8125us,电压4.75V。我担心这个脉冲会导致敏感计数设备误触发。
一开始以为是轻触开关的噪声,试过按下按钮后延迟1秒再激活中断,但问题依然存在。两台Arduino Mega 2560都出现同样现象,这是正常情况还是噪声?有没有办法消除?
初始代码(未加延迟)
int initialTime = 0; double frequency = 100; //设置目标频率 double period = 0, timerTicks = 0; double duty = 0.5; //占空比设为50% double pulseGap = 0.25; //第二路脉冲滞后25%周期 void setup() { pinMode(22, INPUT); //将数字引脚22设为输入(轻触开关) noInterrupts(); DDRE |= (1 << PE4); //将数字引脚2设为输出 DDRE |= (1 << PE3); //将数字引脚5设为输出 //启用CTC模式 TCCR1A = 0; TCCR3A = 0; TCCR1B = 0; TCCR3B = 0; TCCR1B |= (1 << WGM12); TCCR3B |= (1 << WGM32); TCCR1B |= (1 << CS12); //Timer1预分频器设为256(16MHz转为62.5KHz) TCCR1B &= ~(1 << CS11); TCCR1B &= ~(1 << CS10); TCCR3B |= (1 << CS32); //Timer3预分频器设为256 TCCR3B &= ~(1 << CS31); TCCR3B &= ~(1 << CS30); period = 1 / frequency; timerTicks = ((period * 16000000) / 256) - 1; OCR1A = timerTicks; //数字引脚2置高的时刻 OCR1B = timerTicks * duty; //数字引脚2置低的时刻 OCR1C = timerTicks * pulseGap; //数字引脚5置高的时刻 OCR3A = timerTicks; OCR3B = timerTicks * (duty + pulseGap); //数字引脚5置低的时刻 //将Timer1和Timer3同步置0 TCNT1 = initialTime; TCNT3 = TCNT1; interrupts(); } void loop() { //按下数字引脚22的轻触开关后启用中断 if(digitalRead(22) == HIGH){ TIMSK1 = (1 << OCIE1A) | (1 << OCIE1B) | (1 << OCIE1C); TIMSK3 = (1 << OCIE3B); } } ISR(TIMER1_COMPA_vect) { PORTE |= (1 << PE4); //数字引脚2置高 } ISR(TIMER1_COMPB_vect) { PORTE &= ~(1 << PE4); //数字引脚2置低 } ISR(TIMER1_COMPC_vect) { PORTE |= (1 << PE3); //数字引脚5置高 } ISR(TIMER3_COMPB_vect) { PORTE &= ~(1 << PE3); //数字引脚5置低 }
问题解决:重置中断标志位
参考建议后,我重构了代码,核心解决方法是重置TIFRn寄存器下的中断标志位,最终消除了异常脉冲。
更新后的代码
int initialTime = 0, once = 0; int frequency = 100; //设置目标频率 int duty = 2; //占空比设为50%(用除法替代浮点运算) int pulseGap = 4; //第二路脉冲滞后25%周期(用除法替代浮点运算) void setup() { pinMode(22, INPUT); //将数字引脚22设为输入(轻触开关) noInterrupts(); DDRE |= _BV(PE4); //将数字引脚2设为输出 DDRE |= _BV(PE3); //将数字引脚5设为输出 //启用CTC模式 TCCR1A = 0; TCCR3A = 0; TCCR1B = 0; TCCR3B = 0; TCCR1B = _BV(WGM12) | _BV(CS12); //设置为CTC模式,Timer1预分频器设为256 TCCR3B = _BV(WGM32) | _BV(CS32); //设置为CTC模式,Timer3预分频器设为256 OCR1A = (16000000 / (frequency * 256)) - 1; //数字引脚2置高的时刻 OCR1B = OCR1A / duty; //数字引脚2置低的时刻 OCR1C = OCR1A / pulseGap; //数字引脚5置高的时刻 OCR3A = OCR1A; OCR3B = (OCR1A / pulseGap) * 3; //数字引脚5置低的时刻 //将Timer1和Timer3同步置0 TCNT1 = initialTime; TCNT3 = TCNT1; interrupts(); } void loop() { //按下数字引脚22的轻触开关后启用中断(仅执行一次) if(digitalRead(22) == HIGH && once == 0){ noInterrupts(); TIFR1 = _BV(OCF1A) | _BV(OCF1B) | _BV(OCF1C); //重置Timer1的中断标志位 TIFR3 = _BV(OCF3A) | _BV(OCF3B); //重置Timer3的中断标志位 TIMSK1 = _BV(OCIE1A) | _BV(OCIE1B) | _BV(OCIE1C); TIMSK3 = _BV(OCIE3B); TCNT1 = initialTime; TCNT3 = TCNT1; interrupts(); once = 1; } } ISR(TIMER1_COMPA_vect) { PORTE |= (1 << PE4); //数字引脚2置高 } ISR(TIMER1_COMPB_vect) { PORTE &= ~(1 << PE4); //数字引脚2置低 } ISR(TIMER1_COMPC_vect) { PORTE |= (1 << PE3); //数字引脚5置高 } ISR(TIMER3_COMPB_vect) { PORTE &= ~(1 << PE3); //数字引脚5置低 }
内容的提问来源于stack exchange,提问作者Noobie
相关产品推荐
相关产品推荐

