3

我一直在 ATMega328P 上试验 PWM 波形生成模式。我得到了一些奇怪的结果,我不知道这是否与我编写固件或解释数据表的方式有关。

这是我为模拟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(比较值)设置为大约一半。我将通道 A 设置为非反相模式,但是(我认为)将其设置为反相模式不会有什么不同。当我把它刷到 Arduino 上时,我从引脚 9 获得了 5V(100% 占空比)的稳定电压平均值,但我终其一生都无法弄清楚原因。

我将不胜感激您能提供的任何见解。

4

2 回答 2

3

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 是一个 16 位定时器,所以它应该在 65536 滴答时溢出。据我了解,将 OCR1A 设置在 0 到 65535 之间会改变脉冲的占空比。那么,将 OCR1A 设置为 125,我不应该得到大约 0.01 V 而不是 2.5V 的输出吗?结果似乎暗示时钟在 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 写道:

而且,据我所知,在 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; 
}

JJ

于 2014-01-29T02:42:53.640 回答
1

OCR1A控制频率(溢出重启定时器),同时OCR1B控制占空比(溢出会改变输出引脚状态)

在第二个示例中,我无法理解您为什么使用ICR1以及当 arduino 以 5V 运行时,您如何获得 8V 作为输出……或者您正在使用它,或者您的读数不准确。

请看这里,它将解释很多关于 PWM、快速 PWM 等的内容。

http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM

编辑:我想念您ICR1用作 TOP 和OCR1A计数器。那么问题是:默认情况下,寄存器被arduino库初始化为某个值。 TCCR1A= 1 ->(WMG10打开,这很糟糕) TCCR1B= 11 ->(预分频器 = 64)

您没有覆盖此值,而只是将其设置为 1 ;这样,您将不会使用 activatid pin WGM11WMG12WMG13,而是会打开位WMG10,从而导致进入模式 15。

此外,最终的预分频器仍将是 16 而不是 8。

解决方案是将 |= 更改为 =,因此您将覆盖默认值。

于 2014-01-27T14:26:00.383 回答