AVR 328pb定时器0基本介绍和使用
- 📌参考ATmega328PB文档.
- 📍结合参考同架构
lgt8f328p
中文文档:http://www.prodesign.com.cn/wp-content/uploads/2023/03/LGT8FX8P_databook_v1.0.4.pdf
📗定时器0基本功能描述
- 两个独立的输出比较单元
- 双缓冲输出比较寄存器
- 比较匹配发生时自动清零计数器并自动加载
- 无干扰脉冲的相位修正的PWM输出
- 三个独立的中断源:溢出和比较匹配中断 (TOV0、OCF0A和OCF0B)
📑TC0寄存器列表
- TC0控制寄存器A- TCCR0A
- TC0控制寄存器B- TCCR0B
- 波形生成模式位(
WGM0[1:0]
)描述:
📘定时器0工作模式
定时计数器0有四种不同的工作模式,包括普通模式(Normal),比较匹配时清零(CTC)模式,快速脉冲宽度调制(FPWM)模式和相位修正脉冲宽度调制(PCPWM)模式,由波形产生模式控制位WGM0[2:0]来选择。
- 🌿328PB定时器0架构
🌿相位修正PWM模式
- 时序图:
当设置WGM0[2:0]=1 或 5 时,定时计数器 0 进入相位修正PWM模式,计数的最大值TOP 分别为MAX(0xFF)或OCR0A。计数器采用双向操作,由BOTTOM递增到TOP,然后又递减到BOTTOM,再重复此操作。计数到达TOP和BOTTOM时均改变计数方向,计数值在TOP或BOTTOM 上均只停留一个计数时钟。在递增或递减过程中,计数值TCNT0与OCR0x匹配时,输出比较信号OC0x将会被清零或置位,取决于比较输出模式COM0x的设置。与单向操作相比,双向操作可获得的最大频率要小,但其极好的对称性更适合于电机控制。
- 相位修正PWM模式下,当计数到达BOTTOM时置位TOV0标志,当计数到达TOP时把比较缓冲器的值更新到比较值。如果中断使能,在中断服务程序中可以更新比较缓冲器OCR0x寄存器。
设置OC0x 引脚的数据方向寄存器为输出时才能得到输出比较信号OC0x的波形。波形的频率可用下面的公式来计算:
FOCnxPCPWM = Fclk /N*510 (N由CS0[2:0] 决定)
其中,N表示的是预分频因子(1,8,64,256或者1024)。
在递增计数过程中,当TCNT0与OCR0x匹配时,波形产生器就清零(置位)OC0x信号。在
递减计数过程中,当TCNT0与OCR0x匹配时,波形产生器就置位(清零)OC0x信号。由此OCR0x 的极值会产生特殊的PWM波。当OCR0x设置为最大值或最小值时,OC0x信号输出会一直保持低电平或高电平。
为了保证输出PWM波在最小值两侧的对称性,在没有发生比较匹配时,有两种情况下也会翻转OC0x信号。第一种情况是,当OCR0x的值由最大值0xFF改变为其他数据时。当OCR0x为最大值,计数值达到最大时,OC0x 的输出与前面降序计数时比较匹配的结果相同,即保持OC0x不变。此时会更新比较值为新的OCR0x的值(非0xFF), OC0x的值会一直保持,直
到升序计数时发生比较匹配而翻转。此时OC0x信号并不以最小值为中心对称,因此需要在TCNT0 到达最大值时翻转 OC0x 信号,此即没有发生比较匹配时翻转 OC0x 信号的第一种情况。第二种情况是,当TCNT0从比OCR0x 高的值开始计数时,因而会丢失一次比较匹配,从而引起不对称情形的产生。同样需要翻转OC0x信号去实现最小值两侧的对称性。
- 📝相位修正PWM模式测试例程:
/*
* Timer0_ OC0A_PWM_Pulse.c
*
* Created: 2024/2/1 10:45:11
* Author : Administrator
*/
#include <avr/io.h>
void Sys_Clock_Init(void)
{
// Crystal Oscillator division factor: 1 PSC 16MHz HSE
CLKPR=(1<<CLKPCE);
CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (0<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0);
}
void Gpio_Init(void)
{
// Port D initialization PD6 PD5 OutPut
// Function: Bit7=In Bit6=Out Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
DDRD=(0<<DDD7) | (1<<DDD6) | (1<<DDD5) | (0<<DDD4) | (0<<DDD3) | (0<<DDD2) | (0<<DDD1) | (0<<DDD0);
// State: Bit7=T Bit6=0 Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);
}
void Timer0_PWM_Init(void)
{
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 250.000 kHz、
// Mode: Phase correct PWM top=0xFF
// OC0A output: Non-Inverted PWM
// OC0B output: Disconnected
// Timer Period: 2.04 ms
// Output Pulse(s):
//Phase Correct PWM Mode PWM频率
// OC0A Period: 2.04 ms Width: 1.024 ms
//N represents the prescale divider (1, 8, 64, 256, or 1024)
//1 PSC->62.5khZ
//FOCnxPWM = Fclk /N*510 (N由CS0[2:0] 决定)
/*
TCCR0A=(1<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (1<<WGM00);//250KHz
TCCR0B=(0<<WGM02) | (0<<CS02) | (1<<CS01) | (1<<CS00);//64PSC :PWM频率= 1600 000/64*510
*/
TCCR0A=(1<<COM0A1) | (0<<COM0A0) | (1<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (1<<WGM00);
TCCR0B=(0<<WGM02) | (0<<CS02) | (1<<CS01) | (0<<CS00);//CS0-CS2:0-7 prescale divider (1, 8, 64, 256, or 1024) 8PSC :PWM频率= 1600 000/8*510=3921Hz
TCNT0=0x06;
OCR0A=0x40;//使能比较器A输出值:0 - 255占空比
OCR0B=0x40;//使能比较器B输出值:0 - 255占空比
// Ensure that the Timer/Counter 0 is enabled
PRR0&= ~(1<<PRTIM0);
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=(0<<OCIE0B) | (0<<OCIE0A) | (0<<TOIE0);
}
int main(void)
{
/* Replace with your application code */
Sys_Clock_Init();
Gpio_Init();
Timer0_PWM_Init();
while (1)
{
}
}
-
🔖
TCCR0B=(0<<WGM02) | (0<<CS02) | (1<<CS01) | (0<<CS00);
:8分频,频率计算:
-
📏PD5和PD6输出波形图:
🌿CTC 模式
设置WGM0[2:0]=2 时,定时计数器0进入CTC模式,计数的最大值TOP为OCR0A。在这个模式下,计数方式为每一个计数时钟加一递增,当计数器的数值TCNT0等于TOP时计数器清零。OCR0A定义了计数的最大值,亦即计数器的分辨率。这个模式使得用户可以很容易的控制比较匹配输出的频率,也简化了外部事件计数的操作。
当计数器到达计数的最大值时,输出比较匹配标志OCF0被置位,相应的中断使能置位时将会产生中断。在中断服务程序里可以更新 OCR0A 寄存器即计数的最大值。在这个模式下OCR0A 没有使用双缓冲,在计数器以无预分频器或很低的预分频器工作下将最大值更新为接近最小值的时候要小心。如果写入OCR0A的数值小于当时的TCNT0值时,计数器将丢失一次比较匹配。在下一次比较匹配发生之前,计数器不得不先计数到TOP,然后再从BOTTOM开始计数到OCR0A值。和普通模式一样,计数值回到BOTTOM的计数时钟里置位TOV0标志。
设置OC0x引脚的数据方向寄存器为输出时才能得到输出比较信号OC0x的波形。当COM0x=1时,发生比较匹配时会翻转 OC0x 信号,这种情况下波形的频率可以用下面的公式来计算:
foc0xctc = fsys/(2N(1+OCR0x))
其中,N表示的是预分频因子(1,8,64,256或者1024)。
从公式可以看出,当设置OCR0A为0x0且无预分频器时,可以获得最大频率为fsys/2的输出波形。
-🔧 使用CodeVisionAVR软件配置CTC模式参数:
- 📝定时器0 CTC模式下,使用示例:
/*
* Timer0_1ms.c
*
* Created: 2024/1/31 20:40:22
* Author : Administrator
*/
#include <avr/io.h>
#include "avr//interrupt.h"
ISR(TIMER0_OVF_vect)
{
static volatile int iTimes = 0;
// Reinitialize Timer 0 value
TCNT0=0x06;
// Place your code here
if (++iTimes == 1000)
{
PORTB ^= (1 << PINB5);//1秒钟闪烁一次
iTimes = 0;
}
}
void Timer0_Init()
{
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 250.000 kHz
// Mode: Normal top=0xFF
// OC0A output: Disconnected
// OC0B output: Disconnected
// Timer Period: 1 ms
TCCR0A=(0<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (0<<WGM00);
TCCR0B=(0<<WGM02) | (0<<CS02) | (1<<CS01) | (1<<CS00);
TCNT0=0x06;
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=(0<<OCIE0B) | (0<<OCIE0A) | (1<<TOIE0);//溢出中断启用当TOIE0位写入1,并设置状态寄存器中的I位时,启用定时器/计数器0溢出中断。
// Ensure that the Timer/Counter 0 is enabled
PRR0&= ~(1<<PRTIM0);
}
int main(void)
{
/* Replace with your application code */
// Crystal Oscillator division factor: 1
CLKPR =(1<<CLKPCE);
CLKPR =(0<<CLKPCE) | (0<<CLKPS3) | (0<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0);
cli();//关中断
Timer0_Init();//定时器0初始化,配置1ms中断
DDRB = PINB5;//配置PB5为输出模式
// Globally enable interrupts
sei();//开中断
while (1)
{
}
}
🌿快速PWM模式
设置WGM0[2:0]=3 或 7 时,定时计数器 0 进入快速PWM模式,可以用来产生高频的PWM波形,计数最大值TOP分别为MAX(0xFF)或OCR0x。快速PWM模式和其他PWM模式不同在于它是单向操作。计数器从最小值0x00累加到TOP后又回到BOTTOM重新计数。
当计数值TCNT0到达OCR0x或BOTTOM时,输出比较信号OC0x会被置位或清零,取决于比较输出模式COM0x的设置,详情见寄存器描述。由于采用单向操作,快速PWM模式的操作
频率是采用双向操作的相位修正PWM模式的两倍。高频特性使得快速PWM模式适用于功率调节,整流以及DAC应用。高频信号可以减小外部元器件(电感电容等)的尺寸,从而降低系统成本。
当计数值到达最大值时,定时计数器溢出标志TOV0将会被置位,并把比较缓冲器的值更新
-
Fast PWM mode频率计算公式:
-
📑快速PWM例程,使用
CodeVisionAVR
配置参数:
/*
* Timer0_ OC0A、B_PWM_Pulse.c
*
* Created: 2024/2/1 10:45:11
* Author : Administrator
*/
#include <avr/io.h>
void Sys_Clock_Init(void)
{
// Crystal Oscillator division factor: 1 PSC 16MHz HSE
CLKPR=(1<<CLKPCE);
CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (0<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0);
}
void Gpio_Init(void)
{
// Port D initialization PD6 PD5 OutPut
// Function: Bit7=In Bit6=Out Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
DDRD=(0<<DDD7) | (1<<DDD6) | (1<<DDD5) | (0<<DDD4) | (0<<DDD3) | (0<<DDD2) | (0<<DDD1) | (0<<DDD0);
// State: Bit7=T Bit6=0 Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);
}
void Timer0_PWM_Init(void)
{
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 250.000 kHz
// Mode: Fast PWM top=0xFF
// OC0A output: Non-Inverted PWM
// OC0B output: Non-Inverted PWM
// Timer Period: 1.024 ms
// Output Pulse(s):
// OC0A Period: 1.024 ms Width: 0.257 ms
// OC0B Period: 1.024 ms Width: 0.6666 ms
TCCR0A=(1<<COM0A1) | (0<<COM0A0) | (1<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (1<<WGM00);
TCCR0B=(0<<WGM02) | (0<<CS02) | (1<<CS01) | (1<<CS00);
TCNT0=0x06;
OCR0A=0x40;
OCR0B=0xA6;
// Ensure that the Timer/Counter 0 is enabled
PRR0&= ~(1<<PRTIM0);
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=(0<<OCIE0B) | (0<<OCIE0A) | (0<<TOIE0);
}
int main(void)
{
/* Replace with your application code */
Sys_Clock_Init();
Gpio_Init();
Timer0_PWM_Init();
while (1)
{
}
}
- 🎞定时器0快速PWM模式下,比较器A:PD5和比较器B:PD6输出波形:
- 🪓快速模式下Fast PWM top=OCR0A
/*Fast PWM top=OCR0A
* Timer0_ OC0A_PWM_Pulse.c
*
* Created: 2024/2/1 10:45:11
* Author : Administrator
*/
#include <avr/io.h>
void Sys_Clock_Init(void)
{
// Crystal Oscillator division factor: 1 PSC 16MHz HSE
CLKPR=(1<<CLKPCE);
CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (0<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0);
}
void Gpio_Init(void)
{
// Port D initialization PD6 PD5 OutPut
// Function: Bit7=In Bit6=Out Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
DDRD=(0<<DDD7) | (1<<DDD6) | (1<<DDD5) | (0<<DDD4) | (0<<DDD3) | (0<<DDD2) | (0<<DDD1) | (0<<DDD0);
// State: Bit7=T Bit6=0 Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);
}
void Timer0_PWM_Init(void)
{
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 250.000 kHz
// Mode: Fast PWM top=OCR0A
// OC0A output: Disconnected
// OC0B output: Non-Inverted PWM
// Timer Period: 1 ms
// Output Pulse(s):
// OC0B Period: 1 ms Width: 0.7992 ms
TCCR0A=(0<<COM0A1) | (0<<COM0A0) | (1<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (1<<WGM00);
TCCR0B=(1<<WGM02) | (0<<CS02) | (1<<CS01) | (1<<CS00);
TCNT0=0x06;
OCR0A=0xF9;
OCR0B=0xC7;
// Ensure that the Timer/Counter 0 is enabled
PRR0&= ~(1<<PRTIM0);
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=(0<<OCIE0B) | (0<<OCIE0A) | (0<<TOIE0);
}
int main(void)
{
/* Replace with your application code */
Sys_Clock_Init();
Gpio_Init();
Timer0_PWM_Init();
while (1)
{
}
}
- 🔖Fast PWM top=OCR0A模式下,由于占用了OCR0A所以只有输出比较器B才有输出。PD6输出: