错误的脉冲串时序
Erroneous Pulsetrain Timing
我在使用 16MHz 时钟的 Arduino atmega328p-pu 上的定时器时遇到一些问题。
我有一个非常简单的程序,只有一个定时器、两个 ISR 和一个引脚。
该程序执行以下操作:
遍历 'sequence' 的位并分别将 pin4 设置为高电平或低电平。然而,它并没有在整个期间将位设置为高电平,只有 1/12。您在下面看到的是一个从 0 到 340 计数的单个计时器。在 28 处有 ISRB,然后 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 个。
我不明白为什么您应该期待位输出的精确计时。中断在请求延迟后开始,这将根据每条指令的指令执行时间而有所不同 运行 在任何被中断的情况下。我怀疑(没有在您的问题报告中看到证据)您看到的变化与指令执行时间变化相同。
如果您想要精确的硬件输出时序,您必须使用 never-interrupted 编程 I/O 或使用 uP 硬件外设集的各种 flip-bit-upon-timer-compare 功能。 ISR 可用于为下一次比较进行设置,但不能直接翻转输出位。
一旦您了解了如何设置硬件在比较器匹配时执行的操作,在单个 ISR 中完成所有操作会更简单。该服务例程可以安排条件位设置和随后的无条件位清除。您可能希望 ISR 在周期的较长部分 运行,以便 您的 [a] ISR 代码的实际 运行 中的延迟不会导致设置太晚了。
[一个。除了您的 ISR 代码之外,编程环境还导致一些上下文保存恢复以包装您编写的内容。这可能会增加预期之外的执行周期。 Auto-generated context save/restore 通常会过度隐藏状态,这样天真的程序员就不会被奇怪的 foreground-background 交互所迷惑。 ]
我在使用 16MHz 时钟的 Arduino atmega328p-pu 上的定时器时遇到一些问题。
我有一个非常简单的程序,只有一个定时器、两个 ISR 和一个引脚。
该程序执行以下操作: 遍历 'sequence' 的位并分别将 pin4 设置为高电平或低电平。然而,它并没有在整个期间将位设置为高电平,只有 1/12。您在下面看到的是一个从 0 到 340 计数的单个计时器。在 28 处有 ISRB,然后 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 个。
我不明白为什么您应该期待位输出的精确计时。中断在请求延迟后开始,这将根据每条指令的指令执行时间而有所不同 运行 在任何被中断的情况下。我怀疑(没有在您的问题报告中看到证据)您看到的变化与指令执行时间变化相同。
如果您想要精确的硬件输出时序,您必须使用 never-interrupted 编程 I/O 或使用 uP 硬件外设集的各种 flip-bit-upon-timer-compare 功能。 ISR 可用于为下一次比较进行设置,但不能直接翻转输出位。
一旦您了解了如何设置硬件在比较器匹配时执行的操作,在单个 ISR 中完成所有操作会更简单。该服务例程可以安排条件位设置和随后的无条件位清除。您可能希望 ISR 在周期的较长部分 运行,以便 您的 [a] ISR 代码的实际 运行 中的延迟不会导致设置太晚了。
[一个。除了您的 ISR 代码之外,编程环境还导致一些上下文保存恢复以包装您编写的内容。这可能会增加预期之外的执行周期。 Auto-generated context save/restore 通常会过度隐藏状态,这样天真的程序员就不会被奇怪的 foreground-background 交互所迷惑。 ]