我在16MHz时钟的Arduino atmega328p-pu上使用计时器遇到麻烦。
我有一个非常简单的程序,它只有一个定时器,两个ISR和一个引脚。
该程序执行以下操作:
遍历“序列”的各个位并将pin4分别设置为高或低。但是,它不会在整个周期内都将其设置为高电平,只有其1/12。您在下面看到的是一个从0到340递增的计时器。ISRB在28,然后ISRA在340发生,然后循环(这是CTC模式的工作,在ISRA之后循环)。 ISRB始终关闭引脚,而ISRA负责处理引脚是否应为高电平。
现在是问题了。所有时序都对每个位有效,但由于某种原因,环回事件会使脉冲间隔缩短。是的,请缩短,而不要加宽(由于执行循环事件需要额外的时钟周期,所以这是我期望的)。
它使波形看起来像这样。
_|_|_|_|_ _ _ _ _|_|_|_||_|_|_|_ _ _ _
您可以看到问题出在两个数据包之间的交界处,但是其余时间很好。我似乎无法找到原因。
#include <stdint.h>
uint32_t sequence =0b111100001111; // example data sequence
uint8_t packetlength = 12;
uint8_t index = 0;
void setup(){
DDRD = 0xFF; // all port D as input
bitSet(DDRD, 4); // board pin 4 output
bitSet(PORTD, 4); // start high
// initialize timer1
TCCR1A = 0; // zeros timer control register
TCCR1B = 0;
TCNT1 = 0; // sets timer counter to 0
OCR1A = 340; // compare match register 340*62.5ns = 21.25us
OCR1B = 28; // 28*62.5ns = 1.75us
TIMSK1 = 0;
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= (1 << CS10); // CS10 no prescaler (use CS12 for 256 prescaler for testing)
TIMSK1 |= (1 << OCIE1A); // enable timer compare A interrupt
TIMSK1 |= (1 << OCIE1B); // enable timer compare B interrupt
}
ISR(TIMER1_COMPA_vect){ // controls bit repeat rate
if (bitRead(sequence,index) == 1){
bitSet(PORTD, 4); //set high
}
index ++;
if (index == packetlength){ //loop over when end reached.
index = 0;
}
}
ISR(TIMER1_COMPB_vect){ // controls duty cycle
bitClear(PORTD, 4); // set low
}
void loop(){
//nothing
}
编辑:4月5日。示波器照片显示了脉冲串间周期的缩短。
重要的测量值是BX-AX
正常。 340 + 6个计算时钟周期(示波器的最佳估计)
坏。在触发中断之前,计时器仅计数284个周期。
同样不好,但不是一个大问题。该脉冲的宽度很宽,无法通过将位设置为低所需的时钟周期进行合理解释。它似乎需要17,我希望3。
最佳答案
我不明白为什么您应该期望位输出具有精确的时序。中断是在请求延迟后开始的,延迟将随正在中断的任何内容中正在运行的每个指令的指令执行时间而变化。我怀疑(没有在问题报告中看到证据)您看到的变化与指令执行时间的变化相同。
如果要精确的硬件输出时序,则必须使用永不中断的编程I / O或使用uP硬件外设集的各种翻转位定时器比较功能。 ISR可用于为下一个比较设置条件,但不能直接翻转输出位。
一旦弄清楚了如何设置比较器匹配时硬件执行的操作,那么在单个ISR中完成所有操作就会变得更加简单。该服务例程可以安排条件位的设置和随后的无条件位的清除。您可能希望ISR在周期的较长部分中运行,以使[a] ISR代码实际运行中的延迟不会导致设置为时已晚。
[一种。除了您的ISR代码之外,编程环境还导致一些上下文保存还原来包装您编写的内容。这可能会增加执行周期,这可能是不希望的。自动生成的上下文保存/恢复通常过于浪费状态,以免天真的程序员不会被奇怪的前台与后台交互所困扰。 ]