我编写了一个简单的代码来产生 2 条具有相同频率(例如 100Hz)的输出线。
一旦我按下按钮(触觉开关),第一个输出线应该开始产生脉冲,而第二个输出线应该滞后第一个周期的 25%。
当我获得所需的频率时,在使用示波器观察输出时,我注意到中断激活后立即出现了一个小脉冲。
这是噪音吗?还是只是正常行为?有什么办法可以摆脱这个吗?
我尝试缩放并测量它并得到 1.8125us 脉冲宽度@4.75V。
我担心,如果我尝试将其用于敏感的脉冲计数设备,可能会导致计数不准确。
我认为这可能只是我使用的触觉开关引起的噪音。
作为一个快速调整,我尝试在按下按钮后中断激活之前设置 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
}
您的代码中有几个问题。观察到的行为最可能的原因是在 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 预分频器