您的位置:首页 > 产品设计 > UI/UE

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

2016-06-02 10:22 399 查看
Atmega16-定时器2的使用 step by step。

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

待解决问题数量 = 2

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

第零步: 阅读手册

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

第一步: 普通模式

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

测试代码:

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初始化、使能溢出中断
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
// .用于捕获未定义中断函数的中断
//  没有包含interrupt.h的文件中的ISR()都被视为未定义的ISR(),这种ISR()可以被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模式

比较匹配如何产生:

而比较匹配的周期就是OCR2的数值决定的。

也就是说,如果我们设置的OCR2的值小于255,那么TCNT2就不能计数到最大值,也就不会发生计数溢出,没有TOV2中断。

CTC模式下,当TCNT2计数到TCNT2= OCR2时,就发生比较匹配,此时TCNT2会自动清0。



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

测试代码:

Drv_Timer2.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[b]/1预分频[/b]下,TCNT2=OCR2=0,每个定时器时钟周期里都有一次比较匹配发生,此时的比较周期最小,将产生最大的频率。

比较匹配周期 T = ((1.0 / 8000000) * 1000000) * 1* 1 = 0.125us,即每隔0.125us、OC2引脚翻转一次,方波周期是(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

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

2、比较匹配时OC2清0

测试代码:

Drv_Timer2.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_Timer2.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

示波器测试如下:



PWM波形:

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

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

测试代码([b]PWM波):[/b]

// ==========================================================================================================
//  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);
}
}


测试结果([b]PWM波):[/b]
1、占空比为64 / 256 = 25%

示波器测试如下:



高电平宽度为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模式

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

第四步: 相位修正PWM模式

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: