我一直在 ATMega328P 上试验 PWM 波形生成模式。我得到了一些奇怪的结果,我无法确定是我编写固件的方式有问题还是我解释 datasheet 的方式有问题。 .
这是我为模拟 analogWrite() 函数编写的第一段代码:
// Waveform Generation Mode 0
// Table 15-4 of the datasheet
void setup()
{
DDRB = (1<<PB1); // set pin 9 as output
TCCR1A |= (1<<COM1A1);
OCR1A = 125;
}
void loop()
{
}
上面的代码从引脚 9 产生了大约 2.5V(49% 占空比)的平均电压输出。奇怪的是(对我来说)根据数据表,TIMER1 是一个 16 位定时器,所以它应该溢出在 65536 个刻度。据我了解,将 OCR1A 设置在 0 到 65535 之间会改变脉冲的占空比。因此,将 OCR1A 设置为 125 后,我不应该得到大约 0.01 V 而不是 2.5V 的输出吗?结果似乎暗示时钟在 255 处溢出。
第二次涉足 PWM 领域时,我想尝试使用 ATMega 的快速 PWM 模式创建 2.5V 信号。这是我得到的:
// Waveform Generation Mode 14
// Table 15-4 of the datasheet
void setup()
{
DDRB = (1<<PB1);
TCCR1A |= (1<<COM1A1) | (1<<WGM11);
TCCR1B |= (1<<WGM13) | (1<<WGM12) | (1<<CS10);
ICR1 = 19999;
OCR1A = 10000;
}
void loop()
{
}
我将 ICR1(溢出值)任意设置为 20000 个滴答,然后将 OCR1A(比较值)设置为其一半左右。我将 channel A 设置为非反相模式,但(我认为)如果我将其设置为反相模式,它不会有什么不同。当我将它闪存到 Arduino 上时,我从引脚 9 获得了 5V 的稳定平均电压(100% 占空比),但我终究无法弄清楚原因。
如果您能提供任何见解,我将不胜感激。
最佳答案
joeymorin 在 AVRfreaks 上的回答:
请注意,在 Uno 上,在 setup() 之前运行的 Arduino 初始化代码配置了很多东西,包括 328P 上的所有三个计时器。
来自wiring.c:
sbi(TCCR0A, WGM01);
sbi(TCCR0A, WGM00);
sbi(TCCR0B, CS01);
sbi(TCCR0B, CS00);
sbi(TIMSK0, TOIE0);
sbi(TCCR1B, CS11);
sbi(TCCR1B, CS10);
sbi(TCCR1A, WGM10);
sbi(TCCR2B, CS22);
sbi(TCCR2A, WGM20);
这将以 64 的预分频器启动所有三个定时器。
TIMER0 被置于模式 3(快速 PWM)并启用溢出中断以支持计时功能(millis()、micros() 和 delay())。
TIMER1 置于模式 1(固定 8 位相位校正 PWM)。
TIMER2 置于模式 1(相位校正 PWM)。
void setup()
{
DDRB = (1<<PB1); // set pin 9 as output
TCCR1A |= (1<<COM1A1);
OCR1A = 125;
}
void loop()
{
}
由于 TCCR1A 中的 WGM10 已设置,设置 COM1A1 将在非反相模式下启用 PWM 输出,就像 analogWrite() 一样。
TIMER1 is a 16bit timer, so it should overflow at 65536 ticks. From what I understand setting OCR1A between 0 and 65535 will change the duty cycle of the pulse. So, having set the OCR1A at 125, shouldn't I be getting an output of around 0.01 V instead of 2.5V? The results seem to imply that the clock is overflowing at 255.
在模式 1 中,它的行为类似于 8 位定时器。
void setup()
{
DDRB = (1<<PB1);
TCCR1A |= (1<<COM1A1) | (1<<WGM11);
TCCR1B |= (1<<WGM13) | (1<<WGM12) | (1<<CS10);
ICR1 = 19999;
OCR1A = 10000;
}
void loop()
{
}
由于 TCCR1A 中的 WGM10 已设置,设置 WGM11、WGM13 和 WGM12 将选择模式 15,而不是模式 14。模式 15 是快速 PWM,TOP = OCR1A(不是 ICR1)。由于您还使用 COM1A1 为 PWM 设置 OC1A 输出,这将导致 OC1A 保持高电平。
如前所述,如果您想在 Arduino 环境中配置定时器,您应该从头开始使用 = 而不是 |=。
theusch 写道:
And, for all I know, more might be run after setup()
来自 Arduino 的 main.cpp: 代码:
#include <Arduino.h>
int main(void)
{
init();
#if defined(USBCON)
USB.attach();
#endif
setup();
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
return 0;
}
张杰
关于Arduino Uno PWM - 奇怪的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21371678/