Arduino 定时器中断:为什么我在启用中断时收到一个小的意外脉冲?

问题描述 投票:0回答:1

我编写了一个简单的代码来产生 2 条具有相同频率(例如 100Hz)的输出线。

一旦我按下按钮(触觉开关),第一个输出线应该开始产生脉冲,而第二个输出线应该滞后第一个周期的 25%。

当我获得所需的频率时,在使用示波器观察输出时,我注意到中断激活后立即出现了一个小脉冲。

Image of continuous reading Image of the start of reading

这是噪音吗?还是只是正常行为?有什么办法可以摆脱这个吗?

我尝试缩放并测量它并得到 1.8125us 脉冲宽度@4.75V。 Zoomed image & measurement

我担心,如果我尝试将其用于敏感的脉冲计数设备,可能会导致计数不准确。

我认为这可能只是我使用的触觉开关引起的噪音。

作为一个快速调整,我尝试在按下按钮后中断激活之前设置 1 秒的延迟。但问题依然存在。

也许我在这里遗漏了一些基本的东西?

顺便说一句,我正在使用 Arduino Mega 2560。我有 2 个,并尝试了两者,但它们具有相同的行为。

这是我使用的代码(1秒延迟仍然没有应用):


int initialTime = 0;
double frequency = 100; //set my desired frequency
double period = 0, timerTicks = 0;
double duty = 0.5; //set duty cycle to 50%
double pulseGap = 0.25; //offset 2nd pulse by 25%

void setup() {
  pinMode(22, INPUT); //set digitalpin 22 as INPUT (tactile switch)

  noInterrupts();
  DDRE |= (1 << PE4); //set digitalpin 2 as OUTPUT
  DDRE |= (1 << PE3); //set digitalpin 5 as OUTPUT

  //enable CTC mode
  TCCR1A = 0;
  TCCR3A = 0;
  TCCR1B = 0;
  TCCR3B = 0;
  TCCR1B |= (1 << WGM12);
  TCCR3B |= (1 << WGM32);

  TCCR1B |= (1 << CS12); //timer1 initial prescaler = 256 (switch max Frequency from 16 MHz into 62.5 Khz)
  TCCR1B &= ~(1 << CS11);
  TCCR1B &= ~(1 << CS10);
  
  TCCR3B |= (1 << CS32); //timer3 prescaler = 256  (switch max Frequency from 16 MHz into 62.5 Khz)
  TCCR3B &= ~(1 << CS31);
  TCCR3B &= ~(1 << CS30);

  period = 1 / frequency;
  timerTicks = ((period * 16000000) / 256) - 1;

  OCR1A = timerTicks;               //the time where digitalPin2 will be HIGH
  OCR1B = timerTicks * duty;        //the time where digitalPin2 will be LOW
  OCR1C = timerTicks * pulseGap;    //the time where digitalPin5 will be HIGH

  OCR3A = timerTicks;
  OCR3B = timerTicks * (duty + pulseGap); //the time where digitalPin5 will be LOW

  //set & sync Timer1 & Timer3 to 0 
  TCNT1 = initialTime;
  TCNT3 = TCNT1;

  interrupts();
}

void loop() {
  //interrupts will be enabled once the tactile switch connected in @digitalPin22 is pressed
  if(digitalRead(22) == HIGH){
    TIMSK1 = (1 << OCIE1A) | (1 << OCIE1B) | (1 << OCIE1C);
    TIMSK3 = (1 << OCIE3B);
  }
}

ISR(TIMER1_COMPA_vect) {
  PORTE |= (1 << PE4); //toggle digitalPin2 = HIGH
}

ISR(TIMER1_COMPB_vect) {
  PORTE &= ~(1 << PE4); //toggle digitalPin2 = LOW
}

ISR(TIMER1_COMPC_vect) {
  PORTE |= (1 << PE3); //toggle digitalPin5 = HIGH
}

ISR(TIMER3_COMPB_vect) {
  PORTE &= ~(1 << PE3); //toggle digitalPin5 = LOW
}


c++ timer arduino interrupt pulse
1个回答
0
投票

您的代码中有几个问题。观察到的行为最可能的原因是在 TIFRn 寄存器中已设置比较匹配标志时取消屏蔽中断。即使相应的中断被屏蔽,这些标志也是由过去的比较匹配事件设置的。因此,当您取消屏蔽中断时,ISR 会立即被调用。

为了避免这种行为,您应该在计时器停止时重置中断标志(如果过去发生过比较匹配并且 ISR 被屏蔽),然后取消屏蔽中断。然后等待外部事件并启动计时器。您可以通过向 TIFRn 中相应的 OCFnX 位写入 1 来重置标志。

另一个潜在问题是设置 CSnn 位。在您当前的代码中,只有一个 CSnn 位需要设置,因此它应该可以正常工作。但如果预分频器需要设置多个 CSnn 位,则应一次设置所有这些位。 当这些位为 0 时,定时器停止。如果按顺序设置这些位,则在定时器已经运行时启动定时器并切换预调用器设置。

要原子设置位,您可以一次写入所有位,例如:

TCCR1B = _BV(WGM12) | _BV(CS12) | _BV(CS11);

或者,如果您需要保留其他位,请停止计时器并使用新的 CSnn 再次运行它:

TCCR1B &= ~(_BV(CS12) | _BV(CS11) | _BV(CS10);
...
TCCR1B |= _BV(CS12) | _BV(CS11);

当然,您可以通过显式读取-修改-写入来自动切换预分频器位,而无需停止计时器,但这几乎没有实际意义:

TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11) | _BV(CS10)) | _BV(CS12) | _BV(CS11);

另一个潜在问题是定时器共享的预分频器模块。请参阅数据表中的第 18 部分定时器/计数器 0、1、3、4 和 5 预分频器

© www.soinside.com 2019 - 2024. All rights reserved.