Atmega16-定时器2的使用 step by step。
编译环境:AVR Studio 4.19 +avr-toolchain-installer-3.4.1.1195-win32.win32.x86
芯片型号:ATmega16
芯片主频:8MHz
1、OC2引脚输出比较匹配的波形
2、PA0在TOV2中断时取反
3、PA1在OCF2中断时取反
-------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
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); }
而计算出的溢出周期为 T = ((1.0 / 8000000) * 1000000) * 1024* 256 = 32.768ms
考虑示波器的误差,可以认为这两个结果一致。
-------------------------------------------------------------------------------------------------------------------------------------
如下图是比较匹配时、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.125us、OC2引脚翻转一次,方波周期是(0.125 * 2) us
对应的频率为 F = 1 / (0.125 * 2) * 1000000 = 4000000 = 4MHz
示波器测试如下:
CH1为OC2引脚输出的波形、结果为4.07MHz,和计算结果基本一致。
CH2为PA0的输出,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的方式。
-------------------------------------------------------------------------------------------------------------------------------------
如果使用普通模式做定时功能,比如需要定时200个定时器周期,就需要设置TCNT2=55,然后每次TCNT2计数溢出后都要重新设置TCNT2=55 。
如果使用CTC模式,用OCF2中断定时,就可以只设置OCR2=200,每次比较匹配后,TCNT2自动清0,重新计数到OCR2,不需要手动任何设置。
-------------------------------------------------------------------------------------------------------------------------------------
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、CH1为OC2引脚输出,CH2为PA1引脚输出
2、OC2引脚一直为低电平,在每次比较匹配时都被清0拉低,而PA1引脚在每次比较匹配时都翻转一次
示波器测试如下:
在8MHz/1024预分频下,TCNT2=0,OCR2=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引脚才会有波形输出。
-------------------------------------------------------------------------------------------------------------------------------------
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。
示波器测试如下:
在Drv_Timer.c的比较匹配中断服务程序中,修改OCR2的值,就可以更改OC2引脚的方波的占空比。
使用变量来控制占空比,就可以得到了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); } }
1、占空比为64 / 256 = 25%。
示波器测试如下:
高电平宽度为8ms,脉冲周期为30.89ms,占空比为25.9%,和计算结果基本一致。
2、中断周期为32.768ms,每次进入中断都需要CPU来重新配置,如果中断频率很高,CPU的任务就会很繁重。
使用第三步和第四步的两种PWM模式,就可以让CPU避开这些工作。
而CTC模式一般就用来产生方波。
比如:8MHz、1预分频下,OCR2=10时,OC2的PWM波形和PA1引脚的输出都不正常。
在中断中不调用Drv_Timer2_init()来修改TCCR2,PA1得到的PWM波形就OK,只是CPU的任务会较重。
-------------------------------------------------------------------------------------------------------------------------------------
最后,普通模式下使用FOC2强制比较匹配有什么效果呢
要强制匹配有输出,就得设置OC2引脚为比较匹配模式中的一种,如比较匹配时取反。
但是只要设置了OC2引脚,比较匹配立即生效,好像和FOC2是否设置无关。
-------------------------------------------------------------------------------------------------------------------------------------
利用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波形的时序图如下:
COM[21:20]的配置中的这一项为保留项,但芯片手册P115最后一段(版本2466G-AVR-10/03)中又有这一项功能,所以这里测试下。
测试结果是OC2引脚没有任何输出,所以Atmega16没有这一功能。
-------------------------------------------------------------------------------------------------------------------------------------
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; }
示波器输出如下(CH1为OC2引脚输出的PWM波形,CH2为PA1引脚的输出):
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控制而言,并不要求精确的频率和占空比。
-------------------------------------------------------------------------------------------------------------------------------------
main.c中修改定时器的配置如下(其他都不变):
Drv_Timer2_init(T2_WGM_FAST_PWM, T2_COM_MODE_SET, T2_CLK_SOURCE_CLK_1024);
1、结果和上一步一致,只是OC2的波形都反向而已。
-------------------------------------------------------------------------------------------------------------------------------------
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);
示波器输出为:
1、此时的高电平宽度为 T = ((1.0 / 8000000) * 1000000) * 1024 * 1 = 128us,实际测试结果为126us,基本一致。
也就是说,要得到0%的占空比,只能通过将OC2引脚设置成普通输出IO,然后将其拉低来实现。
2、而最低的这个高电平对应的占空比是126us/30400us = 0.41%。
而占空比的分辨率,也就是最小的占空比为1/256 = 3.9%,二者基本一致。
3、CH2为PA0引脚的输出,即计数溢出时的输出,每次计数溢出,OCR2都被置1。
-------------------------------------------------------------------------------------------------------------------------------------
1、也就是说,可以得到100%的占空比,也就是计数溢出后设置的电平。
2、此时比较匹配仍然发生,PA1引脚仍然输出翻转的电平。
3、PA0引脚也在计数溢出时翻转电平,PA0和PA1的相位一致,同时翻转。
-------------------------------------------------------------------------------------------------------------------------------------
说明:
1、此时依然有PWM输出。
例如OCR2=300=0x012C,此时OC2在OCR2&0xFF=0x012C&0xFF=0x2C=44的时刻发生比较匹配。
-------------------------------------------------------------------------------------------------------------------------------------
1、比如设置COM[21:20] = 0b10,对应的配置是:在升序计数时发生比较匹配将清0 OC2、降序计数时发生比较匹配将置位OC2。
2、那么波形发生图如下:
3、这里演示了2路OCR2不同的波形,也就是占空比不一样,高电平在TCNT2计数溢出到0的点的两侧对称。
也就是说、每次修改OCR2之后,高电平的起点都会改变,不是每次都从0相位点开始高电平,而是从某一相位开始,保持在TCNT2=0的两端对称。
由于对电机控制没有研究,所以手册上说这种对称更适合电机控制,还需进一步了解。
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。
-------------------------------------------------------------------------------------------------------------------------------------
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);
示波器输出如下:
1、CH1是OC2引脚输出的PWM波形,CH2是PA0输出的波形、它在每次计数溢出后翻转。
所以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波形,CH2是PA1的波形,每次发生比较匹配时,PA1引脚就翻转一次。
1、同样、这个PWM的周期依然是离散的值,不能精确的得到38KHz的红外载波等等频率,只能得到近似的频率。
占空比=N/255,也得不到精确的值,我们需要10%的占空比时,就得不到精确的10%,只能得到接近10%,即25/255。
比如我们需要10%的占空比时,就得不到精确的10%,只能得到接近10%,即25/255。
有些IC可以得到精确的占空比和更为连续的频率,这些IC有2个比较寄存器,一个比较用来产生周期,另一个比较用来产生占空比。
2、但对于普通电机控制、LED控制而言,并不要求精确的频率和占空比。
-------------------------------------------------------------------------------------------------------------------------------------
1、这个和上面的情况一致,只是OC2引脚的输出反向而已。
-------------------------------------------------------------------------------------------------------------------------------------
示波器输出如下:
1、CH1是OC2引脚的PWM波形,CH2是PA0的波形、在TCNT2计数溢出后翻转电平。
OCR2引脚输出低电平,即0%占空比的PWM,不像快速PWM那样得不到0%的占空比。
2、PA0和PA1在同一时刻翻转电平,即在TCNT2=0的时刻就是比较匹配的时刻,和设置一致。
他们的周期都为64ms,说明PWM运行正常,只是占空比为0%而已。
3、最小的占空比是1/255 = 0.39%,也就是占空比的分辨率是 0.39%。
-------------------------------------------------------------------------------------------------------------------------------------
示波器输出如下:
0、CH1是PA1引脚波形,每次比较匹配时翻转电平。
CH2是PA0的波形、在TCNT2计数溢出后翻转电平。
1、示波器没有给出OC2引脚的PWM波形,OCR2引脚输出的是高电平,即100%占空比的PWM,这不是我们讨论的重点,所以没有出波形。
2、PA0和PA1的周期都为64ms,说明PWM运行正常,只是占空比为100%而已。
PA1发生电平翻转时,PA0的高电平或低电平刚好输出一半。
TCNT2一个周期的计数方式是:0-->255-->254-->1,下一个计数时钟里就计数到0溢出,PA0的电平翻转。
所以PA0的电平输出一半的时刻,正是TCNT2计数过程完成一半的时刻,即TCNT2由0计数到255(0-->255),此时发生比较匹配。
比较匹配之后PA1的电平发生翻转。
也就是比较匹配发生在TCNT2=255的时刻,和设置的一样。
-------------------------------------------------------------------------------------------------------------------------------------
1、定时器2的各项功能到此测试完毕,也验证了定时器设置函数和中断函数正确书写和用法。
使用32768Hz晶振作为时钟源,让定时器2作为RTC来允许的功能和普通模式下的定时功能没区别,这里就不测试了。
2、CTC模式用来输出占空比为50%的脉冲,频率最高是定时器时钟频率的一半。
比如定时器时钟=8MHz时钟时,最高可得到4MHz的脉冲。
3、PWM模式下,我一直使用的都是快速PWM。接触到Atmega16之后才了解到相位修正PWM,还不明白他的特殊性和必要性在哪里。
4、最后,快速PWM的占空比是N/256,相位修正PWM的占空比是N/255,都是离散的值,不一定能得到精确占空比和想要的频率。
比如我们需要10%的占空比时,就得不到精确的10%,只能得到接近10%,即25/255。
有些IC可以得到精确的占空比不同,这些IC有2个比较寄存器,一个比较用来产生周期,另一个比较用来产生占空比。