B001-Atmega16-定时器2-(ques=4)

Atmega16-定时器2的使用 step by step。


编译环境:AVR Studio 4.19 +avr-toolchain-installer-3.4.1.1195-win32.win32.x86
芯片型号:ATmega16
芯片主频:8MHz


测试说明:

1、OC2引脚输出比较匹配的波形

2、PA0TOV2中断时取反

3、PA1OCF2中断时取反


-------------------------------------------------------------------------------------------------------------------------------------

第零步: 阅读手册


-------------------------------------------------------------------------------------------------------------------------------------

第一步: 普通模式

1、这一步使用普通模式输出脉冲,顺便测量一下自己设置的溢出时间是否OK。


测试代码:

Drv_Timer.h

// 定时器中断模式
typedef enum 
{
    INT_MODE_TOV = 0,
    INT_MODE_OCF = 1,
    INT_MODE_ICF = 2
} TIMER_INT_MODE;

// 定时器2
typedef enum 
{
    T2_WGM_NOMAL     = 0, // 普通模式
    T2_WGM_PHASE_PWM = 1,
    T2_WGM_CTC       = 2,
    T2_WGM_FAST_PWM  = 3,

    T2_COM_MODE_NONE       = 0,
    T2_COM_MODE_TOGGLE     = 1,
    T2_COM_MODE_CLEAR      = 2,
    T2_COM_MODE_SET        = 3,

    T2_CLK_SOURCE_NONE     = 0,
    T2_CLK_SOURCE_CLK_1    = 1,
    T2_CLK_SOURCE_CLK_8    = 2,
    T2_CLK_SOURCE_CLK_32   = 3,
    T2_CLK_SOURCE_CLK_64   = 4,
    T2_CLK_SOURCE_CLK_128  = 5,
    T2_CLK_SOURCE_CLK_256  = 6,
    T2_CLK_SOURCE_CLK_1024 = 7
} TIMER2_MODE;

Drv_Timer.c

// ==========================================================================================================
// 定时器0/1/2 驱动模块
// ==========================================================================================================
#include "Drv_Timer.h"
#include <avr/interrupt.h>

// ==========================================================================================================
// TIMER2 初始化
// 
// 参数:wave_mode       工作模式/波形产生模式选择
//       OC_mode         比较匹配/PWM输出模式选择
//       clk_source      时钟源和预分频选择
// 
// 写TCCR2时需要清除bit7=FOC2
// 
// 定时器溢出周期 T = ((1.0 / 8000000) * 1000000) * clk_source * 256 ( @ 8MHz )
// ==========================================================================================================
void Drv_Timer2_init(const uint8_t wave_mode, const uint8_t com_mode, const uint8_t clk_source)
{
    uint8_t wgm20,wgm21;

    wgm20 =  wave_mode & 0x01;
    wgm21 = (wave_mode & 0x02) >> 1;

    // 写TCCR2时需要将bit7=FOC2清0
    TCCR2 = (wgm20 << 6)|               // 工作模式/波形产生模式选择
            (wgm21 << 3)|
            ((com_mode & 0x03)   << 4)| // 比较匹配/PWM输出模式选择
            ((clk_source & 0x07) << 0); // 时钟源和预分频选择
}

// ==========================================================================================================
// TIMER2 中断使能
// 
// 参数:mode   = INT_MODE_TOV 或 INT_MODE_OCF 或 INT_MODE_ICF
//       enable = ENABLE 或 DISABLE
// 
// 可以单独使能/禁止一种模式的中断
// 
// ==========================================================================================================
void Drv_Timer2_INT_Enable(const uint8_t mode, const uint8_t enable)
{
    if(INT_MODE_TOV == mode)
    {
        if(DISABLE == enable)
        {
            TIMSK &= ~(1 << TOIE2);
        }
        else
        {
            TIMSK |=  (1 << TOIE2);
        }
        TIFR |= (1 << TOV2);
    }
    if(INT_MODE_OCF == mode)
    {
        if(DISABLE == enable)
        {
            TIMSK &= ~(1 << OCIE2);
        }
        else
        {
            TIMSK |=  (1 << OCIE2);
        }
        TIFR |= (1 << OCF2);
    }
}

// ==========================================================================================================
//  TIMER2 溢出中断服务程序
// ==========================================================================================================
ISR(TIMER2_OVF_vect)
{
    PORTA ^= (1 << PA0);
}
main.c
// ==========================================================================================================
// 主函数
// ==========================================================================================================
#include <avr/io.h>
#include <avr/interrupt.h>
#include "Drv_Timer.h"
#include "config.h"

// ==========================================================================================================
// main函数
// ==========================================================================================================
int main(void)
{
    // 关全局中断
    cli();

    // PA0初始化为:输出0
    DDRA  |=  (IO_OUTPUT << DDA0);
    PORTA &= ~(1 << PA0);

    // 定时器2初始化、工作在普通模式、OC2引脚不连接CTC、预分频=1024、使能溢出中断
    Drv_Timer2_init(T2_WGM_NOMAL, T2_COM_MODE_NONE, T2_CLK_SOURCE_CLK_1024);
    Drv_Timer2_INT_Enable(INT_MODE_TOV, ENABLE);

    // 开全局中断
    sei();

    while(1)
    {
    }
    return 0;
}

// ==========================================================================================================
//  伪中断BADISR_vect
// 
// ==========================================================================================================
ISR(BADISR_vect)
{
    DDRA  |= (IO_OUTPUT << DDA1);
    PORTA ^= (1 << PA1);
}



测试结果:

1、示波器查看 PA0引脚输出的脉冲的宽度为 32.2ms

      而计算出的溢出周期为 T = ((1.0 / 8000000) * 1000000) * 1024* 256 = 32.768ms

      考虑示波器的误差,可以认为这两个结果一致。






-------------------------------------------------------------------------------------------------------------------------------------

第二步: CTC模式

0、比较匹配如何产生:

而比较匹配的周期就是 OCR2的数值决定的。
也就是说,如果我们设置的 OCR2的值小于 255,那么 TCNT2就不能计数到最大值,也就不会发生计数溢出,没有 TOV2中断。
CTC模式下,当 TCNT2计数到 TCNT2= OCR2时,就发生比较匹配,此时 TCNT2会自动清0。

如下图是比较匹配时、OC2引脚取反的时序图:

B001-Atmega16-定时器2-(ques=4)_第1张图片

1、比较匹配发生时OC2取反,用于产生方波

测试代码:

Drv_Timer.c在第一步的基础上增加了2个函数(仍然使用PA0观察T2计数溢出的周期):

// ==========================================================================================================
//      设置TCNT2和OCR2的值
// 
// (1). 在比较匹配下、OCR2需要在TCNT2被设置之后设置
// ==========================================================================================================
void Drv_Timer2_set_TCNT2_OCR2(const uint8_t tcnt2, const uint8_t ocr2)
{
    TCNT2 = tcnt2;
    OCR2  = ocr2;
}

// ==========================================================================================================
//      强制触发一次比较匹配
// 
// (1). FOC2写1后、立即进行比较操作
// ==========================================================================================================
void Drv_Timer2_FOC2_enable(const uint8_t enable)
{
    if(DISABLE == enable)
    {
        TCCR2 |=  (1 << FOC2);
    }
    else
    {
        TCCR2 &= ~(1 << FOC2);
    }
}

main.c:

// ==========================================================================================================
// main函数
// ==========================================================================================================
int main(void)
{
    // ---------
    // 关全局中断
    cli();

    // PA0初始化为:输出0,置于T2溢出中断、用于测试T2的溢出时间
    DDRA  |=  (IO_OUTPUT << DDA0);
    PORTA &= ~(1 << PA0);

    // 定时器2初始化:TCNT2=0、OCR2=0、CTC模式、1分频(@ 8MHz)、使能溢出中断发生比较匹配时OC2取反
    Drv_Timer2_set_TCNT2_OCR2(0, 0);
    Drv_Timer2_init(T2_WGM_CTC, T2_COM_MODE_TOGGLE, T2_CLK_SOURCE_CLK_1);
    Drv_Timer2_INT_Enable(INT_MODE_TOV, ENABLE);

    //(OC2的比较状态的配置需要在设置数据方向寄存器之前完成)
    // PD7/OC2初始化为:输出0
    DDRD  |=  (IO_OUTPUT << DDD7);
    PORTD &= ~(1 << PD7);

    // 开全局中断
    sei();

    // ---------
    while(1)
    {
    }
    return 0;
}

测试结果:

1、这个代码产生了方波,同时TCNT2没有溢出,所以没有产生溢出中断,PA0引脚就没有方波输出。

2、在8MHz/1预分频下,TCNT2=OCR2=0,每个定时器时钟周期里都有一次比较匹配发生,此时的比较周期最小,将产生最大的频率。

      比较匹配周期 T = ((1.0 / 8000000) * 1000000) * 1* 1 = 0.125us,即每隔0.125usOC2引脚翻转一次,方波周期是(0.125 * 2) us

      对应的频率为 F = 1 / (0.125 * 2) * 1000000 = 4000000 = 4MHz

      示波器测试如下:


       CH1OC2引脚输出的波形、结果为4.07MHz,和计算结果基本一致。

       CH2PA0的输出,TCNT2只计数到0,TCNT2没有溢出,没有产生溢出中断,所以PA0引脚就没有方波输出

3、增大OCR2,方波频率降低。

      OCR2=255时对应最低频率为 F = 31.25KHz

      此时TCNT2可以计数到255并溢出,将产生溢出中断,所以PA0引脚有方波输出,频率也是 F = 31.25KHz

4、使用最大的1024预分频,方波频率最低。

      OCR2=255时对应最低频率为 F = 30.5Hz

      此时TCNT2可以计数到255并溢出,将产生溢出中断,所以PA0引脚有方波输出,频率也是 F = 30.5Hz


不能得到任意频率:

1、CTC模式产生的脉冲的频率 F = 1 / (2*((1.0 / clk_T2) * DIV* OCR2)),这个值是离散的值

      所以我们很可能不能输出某个特定的想要的频率,只能得到一个近似频率

      比如38KHz的红外载波,要用CTC来得到,

      最接近的就是 f= 1 / (2 * ( ((1.0 / 8000000) * 1) * 64 * 2 )) = 31250.0 = 31.25KHz,不能再精确了。

      要得到更精确的38KHz,只能使用外部晶振陶瓷振荡器,比如使用12MHz晶振时:f =  1 / (2 * ( ((1.0 / 12000000) * 1) * 1 * 158 )) = 37974Hz

      此时、DIV = 1,OCR2 = 158

2、有些IC使用周期寄存器来产生更多的更为连续的频率,定时器计数与这个寄存器比较,比较匹配后结束一个周期。

      而不是Atmega16的固定计数为256的方式。


-------------------------------------------------------------------------------------------------------------------------------------

最好使用CTC模式作为定时功能

如果使用普通模式做定时功能,比如需要定时200个定时器周期,就需要设置TCNT2=55,然后每次TCNT2计数溢出后都要重新设置TCNT2=55 。

如果使用CTC模式,用OCF2中断定时,就可以只设置OCR2=200,每次比较匹配后,TCNT2自动清0,重新计数到OCR2,不需要手动任何设置。


-------------------------------------------------------------------------------------------------------------------------------------

2、比较匹配时OC2清0

测试代码:

Drv_Timer.c中增加比较匹配中断,在其中对PA1引脚取反,其他部分不变:

// ==========================================================================================================
//  TIMER2 比较匹配中断服务程序
// ==========================================================================================================
ISR(TIMER2_COMP_vect)
{
    PORTA ^= (1 << PA1);
}
main.c中修改工作 CTC模式为:发生比较匹配时 OC2清0,并设置 PA1引脚初始化为:输出0,用于测试比较匹配周期:
// ==========================================================================================================
// main函数
// ==========================================================================================================
int main(void)
{
    // ---------
    // 关全局中断
    cli();

    // PA0初始化为:输出0,置于T2溢出中断函数里面、用于测试T2的溢出时间
    DDRA  |=  (IO_OUTPUT << DDA0);
    PORTA &= ~(1 << PA0);
    // PA1初始化为:输出0,置于T2比较匹配中断函数里面、用于测试T2的比较匹配周期
    DDRA  |=  (IO_OUTPUT << DDA1);
    PORTA &= ~(1 << PA1);

    // 定时器2初始化:TCNT2=0、OCR2=128、CTC模式、32分频(溢出时间=1ms @ 8MHz)、发生比较匹配时OC2清0
    Drv_Timer2_set_TCNT2_OCR2(0, 128);
    Drv_Timer2_init(T2_WGM_CTC, T2_COM_MODE_CLEAR, T2_CLK_SOURCE_CLK_1024);
    // 使能溢出中断,使能比较匹配中断
    Drv_Timer2_INT_Enable(INT_MODE_TOV, ENABLE);
    Drv_Timer2_INT_Enable(INT_MODE_OCF, ENABLE);

    //(OC2的比较状态的配置需要在设置数据方向寄存器之前完成)
    // PD7/OC2初始化为:输出0
    DDRD  |=  (IO_OUTPUT << DDD7);
    PORTD &= ~(1 << PD7);

    // 开全局中断
    sei();

    // ---------
    while(1)
    {
    }
    return 0;
}

测试结果:

1、CH1OC2引脚输出,CH2PA1引脚输出

2、OC2引脚一直为低电平,在每次比较匹配时都被清0拉低,而PA1引脚在每次比较匹配时都翻转一次

      示波器测试如下:


      在8MHz/1024预分频下,TCNT2=0OCR2=128的配置下、OCR比较匹配的周期为T = ((1.0 / 8000000) * 1000000) * 1024 * 128 = 16384 = 16.384ms

      PA1引脚在每次比较匹配时都翻转一次、输出的方波的周期是OCR比较匹配周期的2倍、即32.768ms,对应的频率为30.5Hz

     示波器测量结果为16.2ms、和30.7Hz,和计算结果基本一致。

     而TCNT2没有溢出,没有发生溢出中断,所以PA0引脚没有波形输出。

3、如果设置成比较匹配发生时OC2引脚置1,结果仅仅是OC2引脚一直拉高,PA1引脚的输出依然是30.5Hz的方波,PA0引脚无波形。

4、OCR2=255时,TCNT2才可以溢出,才会发生溢出中断,PA0引脚才会有波形输出。


-------------------------------------------------------------------------------------------------------------------------------------

3、在比较匹配时、可以让OC2引脚交替出现清0和置1,以输出方波和PWM波形

方波:

测试代码 (方波):

Drv_Timer.c中修改比较匹配中断、让OC2清0和置1两种配置交替出现,而main.c不变:

// ==========================================================================================================
//  TIMER2 比较匹配中断服务程序
// ==========================================================================================================
ISR(TIMER2_COMP_vect)
{
    volatile static uint8_t temp = 0;

    PORTA ^= (1 << PA1);

    if(0 == temp)
    {
        temp = 1;
        Drv_Timer2_init(T2_WGM_CTC, T2_COM_MODE_CLEAR, T2_CLK_SOURCE_CLK_1024);
    }
    else
    {
        temp = 0;
        Drv_Timer2_init(T2_WGM_CTC, T2_COM_MODE_SET, T2_CLK_SOURCE_CLK_1024);
    }
}

测试结果 (方波):

1、OC2引脚出现方波,周期和PA1引脚一致,都是比较匹配周期的2倍,即32.768ms

      示波器测试如下:

B001-Atmega16-定时器2-(ques=4)_第2张图片


PWM波形:

Drv_Timer.c的比较匹配中断服务程序中,修改OCR2的值,就可以更改OC2引脚的方波的占空比。

使用变量来控制占空比,就可以得到了PWM波形:

测试代码 (PWM波):

// ==========================================================================================================
//  TIMER2 比较匹配中断服务程序
// ==========================================================================================================
ISR(TIMER2_COMP_vect)
{
    volatile static uint8_t temp = 0;

    PORTA ^= (1 << PA1);

    if(0 == temp)
    {
        temp = 1;
        Drv_Timer2_set_TCNT2_OCR2(0, 64);
        Drv_Timer2_init(T2_WGM_CTC, T2_COM_MODE_CLEAR, T2_CLK_SOURCE_CLK_1024);
    }
    else
    {
        temp = 0;
        Drv_Timer2_set_TCNT2_OCR2(0, 255-64);
        Drv_Timer2_init(T2_WGM_CTC, T2_COM_MODE_SET, T2_CLK_SOURCE_CLK_1024);
    }
}

测试结果 (PWM波):

1、占空比为64 / 256 = 25%

      示波器测试如下:

B001-Atmega16-定时器2-(ques=4)_第3张图片

高电平宽度为8ms,脉冲周期为30.89ms,占空比为25.9%,和计算结果基本一致。


2、中断周期为32.768ms,每次进入中断都需要CPU来重新配置,如果中断频率很高,CPU的任务就会很繁重。

      使用第三步和第四步的两种PWM模式,就可以让CPU避开这些工作。

      而CTC模式一般就用来产生方波。


CTC模式不要用来做频率高的PWM

比如:8MHz、1预分频下,OCR2=10时,OC2PWM波形和PA1引脚的输出都不正常。

|<----待解释下面的输出波形-question-001
示波器输出如下:


在中断中不调用Drv_Timer2_init()来修改TCCR2PA1得到的PWM波形就OK,只是CPU的任务会较重。


-------------------------------------------------------------------------------------------------------------------------------------

强制比较匹配FOC2

最后,普通模式下使用FOC2强制比较匹配有什么效果呢

|<----待测试-question-002

要强制匹配有输出,就得设置OC2引脚为比较匹配模式中的一种,如比较匹配时取反。

但是只要设置了OC2引脚,比较匹配立即生效,好像和FOC2是否设置无关。



-------------------------------------------------------------------------------------------------------------------------------------

第三步: 快速PWM模式

0、PWM如何产生:

利用TCNT2计数的两个时刻:TCNT2=OCR2 TCNT2=MAX(=255)

TCNT2=MAX(=255)时、一个PWM周期结束,所以PWM周期只和分频系数有关:T = ((1.0 / 8000000) * 1) * DIV * 256,其中DIV为分频系数。

TCNT2=OCR2时、拉高或拉低OC2引脚,用来控制占空比TCNT2=MAX(=255)OC2引脚会翻转,结束这个周期的波形,占空比=OCR2/256

这个模式下,TCNT2会计数到MAX并溢出,所以有TOV2中断,同时在比较匹配时会有OCF2中断。

OC2引脚产生PWM波形的时序图如下:



1、比较匹配时OC2引脚取反

COM[21:20]的配置中的这一项为保留项,但芯片手册P115最后一段(版本2466G-AVR-10/03)中又有这一项功能,所以这里测试下。

测试结果是OC2引脚没有任何输出,所以Atmega16没有这一功能。


-------------------------------------------------------------------------------------------------------------------------------------

2、比较匹配时OC2清0,计数到TOP时OC2置1

测试代码:

Drv_Timer.c中定时器2的初始化函数和中断设置函数和上面的一致,修改了中断函数如下:

// ==========================================================================================================
// TIMER2 溢出中断服务程序
// 
// ==========================================================================================================
ISR(TIMER2_OVF_vect)
{
    PORTA ^= (1 << PA0);
}

// ==========================================================================================================
// TIMER2 比较匹配中断服务程序
// 
// ==========================================================================================================
ISR(TIMER2_COMP_vect)
{
    PORTA ^= (1 << PA1);
}

main.c

// ==========================================================================================================
// 主函数
// ==========================================================================================================
#include <avr/io.h>
#include <avr/interrupt.h>
#include "watch_dog.h"
#include "Drv_Timer.h"
#include "Drv_Sys.h"
#include "_noinit.h"
#include "system.h"
#include "config.h"

// ==========================================================================================================
//  伪中断BADISR_vect
// 
// ==========================================================================================================
ISR(BADISR_vect)
{
    
}

// ==========================================================================================================
// main函数
// ==========================================================================================================
int main(void)
{
    // ---------
    // 关全局中断
    cli();

    // PA0初始化为:输出0,置于T2溢出中断函数里面、用于测试T2的溢出时间
    DDRA  |=  (IO_OUTPUT << DDA0);
    PORTA &= ~(1 << PA0);
    // PA1初始化为:输出0,置于T2比较匹配中断函数里面、用于测试T2的比较匹配周期
    DDRA  |=  (IO_OUTPUT << DDA1);
    PORTA &= ~(1 << PA1);

    // 定时器2初始化:TCNT2=0、OCR2=128、快速PWM模式、1024分频、发生比较匹配时OC2清0
    Drv_Timer2_set_TCNT2_OCR2(0, 200);
    Drv_Timer2_init(T2_WGM_FAST_PWM, T2_COM_MODE_CLEAR, T2_CLK_SOURCE_CLK_1024);
    // 使能溢出中断,使能比较匹配中断
    Drv_Timer2_INT_Enable(INT_MODE_TOV, ENABLE);
    Drv_Timer2_INT_Enable(INT_MODE_OCF, ENABLE);

    //(OC2的比较状态的配置需要在设置数据方向寄存器之前完成)
    // PD7/OC2初始化为:输出0
    DDRD  |=  (IO_OUTPUT << DDD7);
    PORTD &= ~(1 << PD7);

    // 开全局中断
    sei();

    // ---------
    while(1)
    {
    }
    return 0;
}

测试结果:

示波器输出如下(CH1OC2引脚输出的PWM波形,CH2PA1引脚的输出):


1、PWM周期是30.4ms,理论计算是 T = ((1.0 / 8000000) * 1) * 1024 * 256 = 32.768ms,认为结果一致。

2、OC2引脚输出PWM波形,占空比在main.c中设置:duty = OCR2 / 256 = 200 / 256 = 0.78125 = 78%

      实际是25.4ms / 30.4ms = 0.786 = 78.6%,基本一致。

3、PA1在每次比较匹配时翻转,其翻转周期和PWM周期一致,而且每次翻转时、正值比较匹配发生、此时OC2引脚都被拉低。

4、PA0引脚在每次TOV2中断时翻转,也就是每次TCNT1计数溢出时,周期和PWM一致。


-------------------------------------------------------------------------------------------------------------------------------------

能得到任意的频率和占空比:

1、同样、这个PWM的周期依然是离散的值,不能精确的得到38KHz的红外载波等等频率,只能得到近似的频率。

     占空比=N/256,也得不到精确的值,比如我们需要10%的占空比时,就得不到精确的10%,只能得到接近10%,即25/256

     有些IC可以得到精确的占空比和更为连续的频率,这些IC有2个比较寄存器,一个比较用来产生周期,另一个比较用来产生占空比

2、但对于普通电机控制、LED控制而言,并不要求精确的频率和占空比。



-------------------------------------------------------------------------------------------------------------------------------------

3、比较匹配时OC2引脚置1,计数溢出时清0

测试代码:

main.c中修改定时器的配置如下(其他都不变):

Drv_Timer2_init(T2_WGM_FAST_PWM, T2_COM_MODE_SET, T2_CLK_SOURCE_CLK_1024);

测试结果:

1、结果和上一步一致,只是OC2的波形都反向而已。


-------------------------------------------------------------------------------------------------------------------------------------

4、极限值情况:

4-1、OCR2等于0时,得到的仍然是占空比不为0的PWM波形,只是占空比最低

测试代码:

main.c中修改定时器配置为(其他不变):

    // 定时器2初始化:TCNT2=0、OCR2=0、快速PWM模式、1024分频、发生比较匹配时OC2清0
    Drv_Timer2_set_TCNT2_OCR2(0, 0);
    Drv_Timer2_init(T2_WGM_FAST_PWM, T2_COM_MODE_CLEAR, T2_CLK_SOURCE_CLK_1024);

测试结果:

示波器输出为:

B001-Atmega16-定时器2-(ques=4)_第4张图片

1、此时的高电平宽度为 T =  ((1.0 / 8000000) * 1000000) * 1024 * 1 = 128us,实际测试结果为126us,基本一致。

      也就是说,要得到0%的占空比,只能通过将OC2引脚设置成普通输出IO,然后将其拉低来实现。

2、而最低的这个高电平对应的占空比是126us/30400us = 0.41%

      而占空比的分辨率,也就是最小的占空比为1/256 = 3.9%,二者基本一致。

3、CH2PA0引脚的输出,即计数溢出时的输出,每次计数溢出,OCR2都被置1。


-------------------------------------------------------------------------------------------------------------------------------------

4-2、OCR2等于255时,得到的是高电平,即100%的占空比

1、也就是说,可以得到100%的占空比,也就是计数溢出后设置的电平。

2、此时比较匹配仍然发生,PA1引脚仍然输出翻转的电平。

3、PA0引脚也在计数溢出时翻转电平,PA0PA1相位一致,同时翻转。


-------------------------------------------------------------------------------------------------------------------------------------

5、OCR2大于TOP=0xFF时的PWM

说明:

1、此时依然有PWM输出。

      例如OCR2=300=0x012C,此时OC2OCR2&0xFF=0x012C&0xFF=0x2C=44的时刻发生比较匹配。



-------------------------------------------------------------------------------------------------------------------------------------

第四步: 相位修正PWM模式

0、PWM如何产生:

1、比如设置COM[21:20] = 0b10,对应的配置是:在升序计数时发生比较匹配将清0 OC2、降序计数时发生比较匹配将置位OC2

2、那么波形发生图如下:

B001-Atmega16-定时器2-(ques=4)_第5张图片

3、这里演示了2路OCR2不同的波形,也就是占空比不一样,高电平在TCNT2计数溢出到0的点的两侧对称。

      也就是说、每次修改OCR2之后,高电平的起点都会改变,不是每次都从0相位点开始高电平,而是从某一相位开始,保持在TCNT2=0的两端对称。

      由于对电机控制没有研究,所以手册上说这种对称更适合电机控制,还需进一步了解。

|<----question-003

4、TCNT2一个周期的计数方式是:0-->255-->254-->1,下一个计数时钟里就计数到0溢出,所以计数次数=256+254=510

     波形的周期是TCNT2计数510次的时间,也就是、周期 T = ((1.0 / 8000000) * 1) * DIV * 510,其中DIV为分频系数。

     计数器在这510次计数中只溢出1次,产生1个TOV2中断。

5、占空比是OCR2 * 2 / 510 = OCR2 / 255



-------------------------------------------------------------------------------------------------------------------------------------

1、在升序计数时发生比较匹配将清0 OC2、降序计数时发生比较匹配将置位OC2

测试代码:

Drv_Timer.c不变,main.c修改定时器2的初始化如下:

    // 定时器2初始化:TCNT2=0、OCR2=200、相位修正PWM模式、1024分频、发生比较匹配时OC2清0
    Drv_Timer2_set_TCNT2_OCR2(0, 200);
    Drv_Timer2_init(T2_WGM_PHASE_PWM, T2_COM_MODE_CLEAR, T2_CLK_SOURCE_CLK_1024);

测试结果:

示波器输出如下:

B001-Atmega16-定时器2-(ques=4)_第6张图片

1、CH1OC2引脚输出的PWM波形,CH2PA0输出的波形、它在每次计数溢出后翻转。

      所以CH2翻转的时刻就是TCNT2=0的时刻,CH1的高电平在这个点的两端对称。

      周期 T = ((1.0 / 8000000) * 1000000) * 1024* 510 = 65280us = 65.3ms,示波器测得64ms,基本一致。

2、用示波器测量PWM波形的高电平宽度为50.4ms,周期为64ms,占空比为50.4/64=78.8%

      实际计算占空比为200 / 055 = 78.4%,和测量基本一致。

      只是实际测量得到的高电平宽度和周期都和计算的有出入。

3、PA1引脚在每次比较匹配时翻转一次电平,和OC2引脚的PWM波形一一对应,示波器输出如下。

      CH1通道是OC2引脚的PWM波形,CH2PA1的波形,每次发生比较匹配时,PA1引脚就翻转一次。

B001-Atmega16-定时器2-(ques=4)_第7张图片


不能得到任意的频率和占空比:

1、同样、这个PWM的周期依然是离散的值,不能精确的得到38KHz的红外载波等等频率,只能得到近似的频率。

     占空比=N/255,也得不到精确的值,我们需要10%的占空比时,就得不到精确的10%,只能得到接近10%,即25/255

     比如我们需要10%的占空比时,就得不到精确的10%,只能得到接近10%,即25/255

     有些IC可以得到精确的占空比和更为连续的频率,这些IC有2个比较寄存器,一个比较用来产生周期,另一个比较用来产生占空比

2、但对于普通电机控制、LED控制而言,并不要求精确的频率和占空比。


-------------------------------------------------------------------------------------------------------------------------------------

2、在升序计数时发生比较匹配将置位OC2、降序计数时发生比较匹配将清0 OC2

1、这个和上面的情况一致,只是OC2引脚的输出反向而已。


-------------------------------------------------------------------------------------------------------------------------------------

3、极限值的情况

1、OCR2 = 0时,占空比=0%

示波器输出如下:

B001-Atmega16-定时器2-(ques=4)_第8张图片

1、CH1OC2引脚的PWM波形,CH2PA0的波形、在TCNT2计数溢出后翻转电平。

      OCR2引脚输出低电平,即0%占空比的PWM,不像快速PWM那样得不到0%的占空比。

2、PA0PA1在同一时刻翻转电平,即在TCNT2=0的时刻就是比较匹配的时刻,和设置一致。

      他们的周期都为64ms,说明PWM运行正常,只是占空比为0%而已。

3、最小的占空比是1/255 = 0.39%,也就是占空比的分辨率是 0.39%


-------------------------------------------------------------------------------------------------------------------------------------

2、OCR2 = 255时,占空比=100%

示波器输出如下:

B001-Atmega16-定时器2-(ques=4)_第9张图片

0、CH1PA1引脚波形,每次比较匹配时翻转电平。

      CH2PA0的波形、在TCNT2计数溢出后翻转电平。

1、示波器没有给出OC2引脚的PWM波形,OCR2引脚输出的是高电平,即100%占空比的PWM,这不是我们讨论的重点,所以没有出波形。

2、PA0PA1的周期都为64ms,说明PWM运行正常,只是占空比为100%而已。

      PA1发生电平翻转时,PA0的高电平或低电平刚好输出一半。

     TCNT2一个周期的计数方式是:0-->255-->254-->1,下一个计数时钟里就计数到0溢出,PA0的电平翻转。

     所以PA0的电平输出一半的时刻,正是TCNT2计数过程完成一半的时刻,即TCNT20计数到255(0-->255),此时发生比较匹配。

     比较匹配之后PA1的电平发生翻转。

     也就是比较匹配发生在TCNT2=255的时刻,和设置的一样。


-------------------------------------------------------------------------------------------------------------------------------------

小结:

1、定时器2的各项功能到此测试完毕,也验证了定时器设置函数和中断函数正确书写和用法。

     使用32768Hz晶振作为时钟源,让定时器2作为RTC来允许的功能和普通模式下的定时功能没区别,这里就不测试了。

2、CTC模式用来输出占空比为50%的脉冲,频率最高是定时器时钟频率的一半。

      比如定时器时钟=8MHz时钟时,最高可得到4MHz的脉冲。

3、PWM模式下,我一直使用的都是快速PWM。接触到Atmega16之后才了解到相位修正PWM,还不明白他的特殊性和必要性在哪里。

|<-----question-004

4、最后,快速PWM的占空比是N/256相位修正PWM的占空比是N/255,都是离散的值,不一定能得到精确占空比和想要的频率。

     比如我们需要10%的占空比时,就得不到精确的10%,只能得到接近10%,即25/255

     有些IC可以得到精确的占空比不同,这些IC有2个比较寄存器,一个比较用来产生周期,另一个比较用来产生占空比

你可能感兴趣的:(Atmega16-定时器2)