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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 00:17:04