使用Timer1设置Arduino Uno(ATMEGA328P)PWM

问题描述 投票:0回答:2

我想用16MHz Arduino Uno(ATMEGA328P)设置自定义频率(12Hz)和占空比(20%)。

AVR计算器产量:

ICR1 = 20833
OCR1A = 4167

我已经阅读了大量的论坛和tuts,但由于某种原因,我无法让这个工作。

以下是我的代码:

void setup()
{
  // PB1 is now an output (Pin9 Arduino UNO)
  DDRB |= (1 << DDB1);
  // PB2 is now an output (Pin10 Arduino UNO)
  DDRB |= (1 << DDB2);

      // Set PWM frequency/top value
      ICR1 = 20833;

      // Set PWM duty cycle
      OCR1A = 4167;

      // Set inverting mode (start low, go high)
      TCCR1A |= (1 << COM1A1);
      TCCR1A |= (1 << COM1B1);
      TCCR1A |= (1 << COM1A0);
      TCCR1A |= (1 << COM1B0);

      // Set fast PWM Mode
      TCCR1A |= (1 << WGM11);
      TCCR1B |= (1 << WGM12);
      TCCR1B |= (1 << WGM13);

      // Set prescaler to 64 and starts PWM
      TCCR1B |= (1 << CS10);
      TCCR1B |= (1 << CS11);
    }

    void loop() {
      // Refresh PWM frequency
      OCR1A = 4167;
    }

如果有人能提供帮助,那就太好了!

谢谢,

迪伦

arduino-uno pwm
2个回答
1
投票

好的,所以我似乎找到了这个问题。我没有为模式14中的快速PWM正确设置寄存器(ATMEGA328P有15个timer1模式)。经过大量的实验和进一步的阅读,下面是可变频率和占空比的正确设置。 ICR1表示TOP值(控制频率),OCR1A表示开关值(占空比)。

// ADJUSTABLE VARIABLES
// Strobe frequency
uint16_t timer1Prescaler = 64;
uint8_t strobeFreq = 20,
        strobeDutyCycle = 20;

void setup
{
  // Set PB1 to be an output (Pin9 Arduino UNO)
  DDRB |= (1 << PB1);

  // Clear Timer/Counter Control Registers
  TCCR1A = 0;
  TCCR1B = 0;

  // Set non-inverting mode
  TCCR1A |= (1 << COM1A1);

  // Set fast PWM Mode 14
  TCCR1A |= (1 << WGM11);
  TCCR1B |= (1 << WGM12);
  TCCR1B |= (1 << WGM13);

  // Set prescaler to 64 and starts PWM
  TCCR1B |= (1 << CS10);
  TCCR1B |= (1 << CS11);

  // Set PWM frequency/top value
  ICR1 = (F_CPU / (timer1Prescaler*strobeFreq)) - 1;
  OCR1A = ICR1 / (100 / strobeDutyCycle);
}

void loop()
{
  // main loop code
}

注意:在使用Arduino IDE时清除T / C控制寄存器非常重要,因为它在执行setup()函数之前会在后台进行一些设置。


0
投票

变量“strobeDutyCycle”设置为任何大于50的数字后,PWM停止工作。这是因为变量“strobeDutyCycle”被声明为uint8_t,这意味着如果我们有例如51,那么100/51将等于1,因为unsigned int(“uint8_t”)会削减小数部分。因此,任何大于50的数字都会获得相同的数字,从而导致OC1A输出引脚上的输出为0。该问题的解决方案是将变量“strobeDutyCycle”声明为float,然后在除法完成后将其转换为uint16_t。

float strobeDutyCycle = 60;
uint16_t timer1Prescaler = 1024;
uint16_t strobeFreq= 2;
...
float pwmFrequency = (F_CPU / (timer1Prescaler*strobeFreq)) - 1;
float dutyCycleDivisor = 100 / strobeDutyCycle;
float pwmValueWithDutyCycle = pwmFrequency  / dutyCycleDivisor;

ICR1 = (uint16_t)pwmFrequency;
OCR1A = (uint16_t)pwmValueWithDutyCycle;
© www.soinside.com 2019 - 2024. All rights reserved.