Arduino Uno PWM - 奇怪的结果

时间:2014-01-27 01:37:56

标签: arduino synthesizer pwm

我一直在尝试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滴答。根据我的理解,在0到65535之间设置OCR1A将改变脉冲的占空比。因此,将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%占空比),我不能为我的生活找出原因。

我很感激您提供的任何见解。

2 个答案:

答案 0 :(得分:2)

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输出设置为PWM,这将导致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

答案 1 :(得分:1)

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

在第二个例子,为什么您使用我不能uderstand ICR1怎么可能你正在8V的输出作为Arduino的电压为5V运行...或者你fryng它还是你的阅读是不准确

请看一下这里,它将解释很多关于PWM,快速PWM等的事情。

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

编辑:我错过了您使用ICR1作为TOP而OCR1A作为计数器。然后问题是: 默认情况下,arduino库会将寄存器初始化为某个值。 TCCR1A = 1 - &gt; (WMG10 ON,这很糟糕) TCCR1B = 11 - &gt; (预分频器= 64)

你没有覆盖这个值,只是把它放到1位;这样,您不会使用WGM11WMG12WMG13激活,而是会启用位WMG10,从而导致模式15落入。

最终的预分频器仍然是16而不是8。

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