正确实现Timer1以产生PWM [英] Proper implementation of Timer1 for PWM generation

查看:129
本文介绍了正确实现Timer1以产生PWM的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Atmel ATmega328P上((图片来自

解决方案

Timer1具有2个输出,分别是 OC1A OC1B .两者都使用相同的硬件计时器运行,因此是同步的.定时器能够以三种不同的模式运行:快速PWM模式,相位校正的PWM模式以及相位和频率校正的模式.您将需要为您选择正确的模式以及适合您的应用的正确定时器预分频器.下面是一个示例.

 //Timer1分辨率16位//Timer1 A输出为25%占空比,快速PWM模式//Timer1 B输出的占空比为75%,快速PWM模式#include< avr/io.h>int main(无效){//为OC1A和OC1B的timer1输出设置GPIODDRB | =(1<< DDB1)|(1<< DDB2);ICR1 = 0xFFFF;//25%的占空比OCR1A = 0x3FFF;//75%的占空比OCR1B = 0xBFFF;//设置同相模式TCCR1A | =((1<< COM1A1)|(1<< COM1B1)));//快速PWM模式TCCR1A | =(1<< WGM11);TCCR1B | =(1< WGM12)|(1< WGM13);//在没有预分频器的情况下启动计时器TCCR1B | =(1<< CS10);而(1);} 

On the Atmel ATmega328P (datasheet), there are three timers available for PWM generation (timer0, timer1, and timer2).

I already have what I need using the 8-bit timer2, I am just concerned with using different timer instad of timer2, because timer2 is used in various libraries, and I'd like to have more granularity. Thus I'd like to use the 16-bit timer1.

Here is what I am using to generate a 25 kHz, variable duty cycle using timer2. For this example, lets consider a 35% duty cycle:

void setup() 
{

    /*
    16*10^6/ [prescalar] / ([OCR2A]) / 2 = [desired frequency]
    16*10^6/ 8 / [OCR2A] / 2 = 25*10^3

    Prescalar table for Timer2 (from datasheet section 17-9):
    CS22    CS21    CS20
      0     0       0       = No clock source (Timer/couter stopped)
      0     0       1       = clkT2S/(No prescaling)
      0     1       0       = clkT2S/8 (From prescaler)
      0     1       1       = clkT2S/32 (From prescaler)
      1     0       0       = clkT2S/64 (From prescaler)
      1     0       1       = clkT2S/128 (From prescaler)
      1     1       0       = clkT2S/256 (From prescaler)
      1     1       1       = clkT2S/1024 (From prescaler)
    */

    pinMode(3, OUTPUT);
    TCCR2B = _BV(WGM22) | _BV(CS21);
    TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(COM2B0) | _BV(WGM20);
    OCR2A = 40; 
    OCR2B = 16; //40*0.35=16
}

void loop()
{ 
}

To get the same result using timer1 must be incredibly simple, however I'm not familiar with these registers. I have been looking around for explanations outside the datasheet. I have found this post: Secrets of Arduino PWM, however it only covers the use of timer2.

I have tried the following as per Stephan's suggestion, however it just leads to both outputs (D9 and D10) being held HIGH:

void setup() 
{

    pinMode(9, OUTPUT); //D9
    pinMode(10, OUTPUT); //D10

    // Set GPIO for timer1 output for OC1A and OC1B
    //DDRB |= (1 << DDB1) | (1 << DDB2);

    ICR1 = 0xFFFF;

    // 25% duty cycle
    OCR1A = 0x0009;

    // 75% duty cycle
    //OCR1B = 0xBFFF;

    //20.14.1, pg170
    // set none-inverting mode
    TCCR1A |= ((1 << COM1A1) | (1 << COM1B1));

    //Table 20-6, pg171
    // Fast PWM mode
    TCCR1A |= (1 << WGM11);
    TCCR1B |= (1 << WGM12) | (1 << WGM13);

    // START the timer with no prescaler
    TCCR1B |= (1 << CS10);

}

void loop()
{
}

I tried changing everything (ICR1, OCR1A, TCCR1A), but the only combination that did anything different was the following, which gives a 25kHz frequency on D10, and D9 held HIGH, but the HIGH duration is stuck at 4μs regardless of the registers. (I just guessed and checked with OCR1A to get 25kHz. I'm not sure why that works.)

void setup() 
{

    pinMode(9, OUTPUT);
    pinMode(10, OUTPUT);

    // Set GPIO for timer1 output for OC1A and OC1B
    //DDRB |= (1 << DDB1) | (1 << DDB2);

    ICR1 = 0xFFFF;

    // 25% duty cycle
    OCR1A = 0x0009;

    // 75% duty cycle 
    //This line causes both outputs to be held HIGH
    //OCR1B = 0xBFFF;

    //20.14.1, pg170
    // set none-inverting mode
    TCCR1A |= ((1 << COM1A1) | (1 << COM1B1));

    //Table 20-6, pg171
    // Fast PWM mode
    TCCR1A |= (1 << WGM11);
    TCCR1B |= (1 << WGM12) | (1 << WGM13);

    // START the timer with no prescaler
    TCCR1B |= (1 << CS10);

}

void loop()
{
}

I'm using an Arduino Nano breakout board for prototyping, with D9 and D10 being the timer1 output pins:

(Image from https://bigdanzblog.wordpress.com)

I've tried changing the board but I have the same result.

Here is relevant information from the datasheet:

解决方案

Timer1 has 2 outputs, OC1A and OC1B. Both run off the same hardware timer and are therefore synchronized. The timer is capable to running in three different modes: Fast PWM Mode, Phase Corrected PWM Mode and Phase and Frequency Corrected Mode. You will need to chose the correct mode for you as well as the correct timer prescaler that fits your application. Below is an example.

// Timer1 Resolution 16-bit
// Timer1 A output at 25% Duty Cycle, Fast PWM Mode
// Timer1 B output at 75% Duty Cycle, Fast PWM Mode 

#include <avr/io.h>

int main(void)
{
    // Set GPIO for timer1 output for OC1A and OC1B
    DDRB |= (1 << DDB1) | (1 << DDB2);

    ICR1 = 0xFFFF;

    // 25% duty cycle
    OCR1A = 0x3FFF;

    // 75% duty cycle
    OCR1B = 0xBFFF;

    // set none-inverting mode
    TCCR1A |= ((1 << COM1A1) | (1 << COM1B1));

    // Fast PWM mode
    TCCR1A |= (1 << WGM11);
    TCCR1B |= (1 << WGM12)|(1 << WGM13);

    // START the timer with no prescaler
    TCCR1B |= (1 << CS10);

    while (1);
}

这篇关于正确实现Timer1以产生PWM的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆