B001-Atmega16-定时器1-(ques=零)
Atmega16-定时器1的使用 step by step。
之前完成了定时器2的各项功能的测试后,这里就很容易做了。
编译环境:AVR Studio 4.19 +avr-toolchain-installer-3.4.1.1195-win32.win32.x86
芯片型号:ATmega16
芯片主频:8MHz
测试说明:
1、OC1A和OC1B引脚输出比较匹配的波形2、PA2在TOV1中断时取反
3、PA3在OCF1A中断时取反
4、PA5在OCF1B中断时取反
5、PA6在ICF1中断时取反
-------------------------------------------------------------------------------------------------------------------------------------
第一步: 普通模式
使用定时器1的作为定时器使用,每次溢出后PA2的电平取反,由此可得知溢出时间。测试代码:
Drv_Timer.h
typedef enum { INT_MODE_TOV = 0, INT_MODE_OCF = 1, INT_MODE_ICF = 2, INT_MODE_OCF1A = 3, INT_MODE_OCF1B = 4 } TIMER_INT_MODE; typedef enum { T1_COM_MODE_NONE = 0, T1_COM_MODE_TOGGLE = 1, T1_COM_MODE_CLEAR = 2, T1_COM_MODE_SET = 3, T1_WGM_NOMAL = 0, T1_WGM_8_PHASE_PWM = 1, T1_WGM_9_PHASE_PWM = 2, T1_WGM_10_PHASE_PWM = 3, T1_WGM_CTC = 4, T1_WGM_8_FAST_PWM = 5, T1_WGM_9_FAST_PWM = 6, T1_WGM_10_FAST_PWM = 7, T1_WGM_PHASE_FRQ_PWM_ICR = 8, T1_WGM_PHASE_FRQ_PWM_OCR = 9, T1_WGM_PHASE_PWM_ICR = 10, T1_WGM_PHASE_PWM_OCR = 11, T1_WGM_CTC_ICR1 = 12, T1_WGM_SERVED = 13, T1_WGM_FAST_PWM_ICR1 = 14, T1_WGM_FAST_PWM_OCR1A = 15, T1_CLK_SOURCE_NONE = 0, T1_CLK_SOURCE_CLK_1 = 1, T1_CLK_SOURCE_CLK_8 = 2, T1_CLK_SOURCE_CLK_64 = 3, T1_CLK_SOURCE_CLK_256 = 4, T1_CLK_SOURCE_CLK_1024 = 5, T1_CLK_SOURCE_T1_RAISE = 6, T1_CLK_SOURCE_T1_FALL = 7 } TIMER1_MODE; typedef enum { T1_ICP_FALL_EDGE = 0, T1_ICP_RAISE_EDGE = 1 } TIMER1_ICP;Drv_Timer.c
// ========================================================================================================== // 定时器1初始化 // // 参数:OCM1A_mode 通道A比较匹配/PWM输出模式选择 // OCM1B_mode 通道B比较匹配/PWM输出模式选择 // com_mode 工作模式/波形产生模式选择 // clk_source 时钟源和预分频选择 // // PWM模式下、写TCCR1A时需要清除FOC1A/B // 写TCCR1B时需要清除bit5 // // ========================================================================================================== void Drv_Timer1_init(const uint8_t com_mode, const uint8_t OCM1A_mode, const uint8_t OCM1B_mode, const uint8_t clk_source) { TCCR1A = ((OCM1A_mode & 0x03) << 6) | // 通道A比较匹配/PWM输出模式选择 ((OCM1B_mode & 0x03) << 4) | // 通道B比较匹配/PWM输出模式选择 ((com_mode & 0x03) << 0); // 工作模式/波形产生模式选择(WGM[11:10]) TCCR1B = (((com_mode & 0x0C) >> 2) << 3) | // 工作模式/波形产生模式选择(WGM[13:12]) ( (clk_source & 0x07) << 0); // 时钟源和预分频选择 } // ========================================================================================================== // TIMER1 中断使能 // // 参数:mode = INT_MODE_TOV 或 INT_MODE_ICF 或 INT_MODE_OCF1A 或 INT_MODE_OCF1B // enable = ENABLE 或 DISABLE // // 可以单独使能/禁止一种模式的中断 // // ========================================================================================================== void Drv_Timer1_INT_Enable(const uint8_t mode, const uint8_t enable) { if(INT_MODE_TOV == mode) { if(DISABLE == enable) { TIMSK &= ~(1 << TOIE1); } else { TIMSK |= (1 << TOIE1); } TIFR |= (1 << TOV1); return ; } if(INT_MODE_OCF1A == mode) { if(DISABLE == enable) { TIMSK &= ~(1 << OCIE1A); } else { TIMSK |= (1 << OCIE1A); } TIFR |= (1 << OCF1A); return ; } if(INT_MODE_OCF1B == mode) { if(DISABLE == enable) { TIMSK &= ~(1 << OCIE1B); } else { TIMSK |= (1 << OCIE1B); } TIFR |= (1 << OCF1B); return ; } if(INT_MODE_ICF == mode) { if(DISABLE == enable) { TIMSK &= ~(1 << TICIE1); } else { TIMSK |= (1 << TICIE1); } TIFR |= (1 << ICF1); } } // ========================================================================================================== // TIMER1 溢出中断服务程序 // // ========================================================================================================== ISR(TIMER1_OVF_vect) { PORTA ^= (1 << PA2); }main.c
// ========================================================================================================== // 主函数 // ========================================================================================================== #include <avr/io.h> #include <avr/interrupt.h> #include "watch_dog.h" #include "Drv_Timer.h" #include "_noinit.h" #include "system.h" #include "sys_timer.h" #include "config.h" // ========================================================================================================== // 伪中断BADISR_vect // // ========================================================================================================== ISR(BADISR_vect) { } // ========================================================================================================== // main函数 // ========================================================================================================== int main(void) { // ------------------------------------------------------------------------------------------------------ // 关全局中断 cli(); // 系统初始化 sys_init(); // PA[5:2]初始化为输出0 DDRA = (1 << DDA2) | (1 << DDA3) | (1 << DDA5) | (1 << DDA6); PORTA &= ~((1 << PA2 ) | (1 << PA3 ) | (1 << PA5 ) | (1 << PA6 )); // 定时器1 初始化:普通模式、COM1A不启用、COM1B不启用、8预分频 Drv_Timer1_init(T1_WGM_NOMAL, T1_COM_MODE_NONE, T1_COM_MODE_NONE, T1_CLK_SOURCE_CLK_8); // 使能TOV1中断 Drv_Timer1_INT_Enable(INT_MODE_TOV, ENABLE); // 开全局中断 sei(); // ------------------------------------------------------------------------------------------------------ while(1) { } return 0; }
测试结果:
1、PA2引脚电平每个66ms翻转一次。定时器1溢出周期为 T = ((1.0/8000000)*1000000) * 8 * 65536 / 1000 = 65.536 ms。
两者基本一致。
-------------------------------------------------------------------------------------------------------------------------------------
第二步: CTC模式
1、使用OCR1A作为TOP值:WGM[13:10] = 0100
TCNT1加计数到TCNT1 = OCR1A时,比较匹配A发生、OC1A引脚电平翻转,同时TCNT1会被清0。
TCNT1加计数到TCNT1 = OCR1B时,比较匹配B发生、 OC1B引脚电平翻转。
但是,如果OCR1A < OCR1B,那么因为在TCNT1 = OCR1A时、TCNT1会被清0,不会继续增加到更大的OCR1B,
所以OCF1B永远不会发生、OC1B引脚将永远不会翻转。
测试代码:
Drv_Timer.c中增加1个初值设置函数和2个中断服务函数:
// ========================================================================================================== // 设置TCNT1、OCR1A、OCR1B的值 // // (1). 在比较匹配下、OCR1A、OCR1B需要在TCNT1被设置之后设置 // 相应的,ICP1也需要在TCNT1被设置之后设置 // // ========================================================================================================== void Drv_Timer1_set_TCNT1_OCR1A_OCR1B_ICR1(const uint16_t tcnt1, const uint16_t ocr1a, const uint16_t ocr1b, const uint16_t icr1) { TCNT1 = tcnt1; OCR1A = ocr1a; OCR1B = ocr1b; ICR1 = icr1; } // ========================================================================================================== // TIMER1 比较匹配A 中断服务程序 // // ========================================================================================================== ISR(TIMER1_COMPA_vect) { PORTA ^= (1 << PA3); } // ========================================================================================================== // TIMER1 比较匹配B 中断服务程序 // // ========================================================================================================== ISR(TIMER1_COMPB_vect) { PORTA ^= (1 << PA5); }
main.c如下:
// ========================================================================================================== // 主函数 // ========================================================================================================== #include <avr/io.h> #include <avr/interrupt.h> #include "Drv_Timer.h" #include "system.h" #include "config.h" // ========================================================================================================== // 伪中断BADISR_vect // // ========================================================================================================== ISR(BADISR_vect) { } // ========================================================================================================== // main函数 // ========================================================================================================== int main(void) { // ------------------------------------------------------------------------------------------------------ // 关全局中断 cli(); // 系统初始化 sys_init(); // PA[5:2]初始化为输出0 DDRA = (1 << DDA2) | (1 << DDA3) | (1 << DDA5) | (1 << DDA6); PORTA &= ~((1 << PA2 ) | (1 << PA3 ) | (1 << PA5 ) | (1 << PA6 )); // 定时器1 初始化:CTC模式、COM1A启用(电平翻转)、COM1B启用(电平翻转)、8预分频 Drv_Timer1_init(T1_WGM_CTC, T1_COM_MODE_TOGGLE, T1_COM_MODE_TOGGLE, T1_CLK_SOURCE_CLK_8); // 设置TCTN1=0、OCR1A=200、OCR1B=150、ICR1=0 Drv_Timer1_set_TCNT1_OCR1A_OCR1B_ICR1(0, 200, 150, 0); // 使能TOV1中断 Drv_Timer1_INT_Enable(INT_MODE_TOV, ENABLE); // PD[5:4](OC1B和OC1A)初始化为输出0 DDRD = (1 << DDD4) | (1 << DDD5); PORTD &= ~((1 << PD2 ) | (1 << PD5 )); // 开全局中断 sei(); // ------------------------------------------------------------------------------------------------------ while(1) { } return 0; }
测试结果:
示波器输出:
1、CH1通道是OC1A引脚的输出,CH2通道是 OC1B引脚的输出。
OC1A引脚和OC1B引脚都输出脉冲,电平翻转的周期是202us ,示波器测得的频率是2.46668Khz。
理论计算得到比较匹配的周期、即电平翻转周期 T = ((1.0/8000000)*1000000)*8*(200+1) = 201 us。
对应的脉冲周期是402us,频率是2487hz,两者基本一致。
2、另外、OC1A和OC1B的相位不同,原因是OCR1A=200,而OCR1B=150。
所以OC1B在TCNT1=150时先发生比较匹配,OC1B引脚的电平翻转。
等到TCNT1=200时OC1A才发生比较匹配,OC1A引脚的才翻转电平。
上图中、左边光标CurA处于0.00us的位置,正好是OC1A发生比较匹配电平翻转的时刻,此时TCNT1被清0。
而定时器1的一个时钟周期是 t = ((1.0/8000000)*1000000)*8 = 1 us 。
TCNT1从0计数到150,将到达右边光标CurB=150us的位置,此时OC1B发生比较匹配。
在等50us,TCNT1=200,OC1A才发生比较匹配。
所以、OC1A滞后OC1B50个计数周期(50us)才发生比较匹配。
也就是、OC1B的相位有OCR1B决定。
3、TCNT1没有计数溢出,所以TOV1没有发生,PA2引脚就没有电平变化。
4、使能OCF1A和OCF1B后,PA3和PA5才会有波形输出。
而且PA3在OC1A发生比较匹配时翻转引脚电平,PA5在OC1B发生比较匹配时翻转引脚电平。
示波器输出如下:
CH1是PA3的波形,CH2是PA5的波形。
PA3比PA5滞后50us发生引脚电平翻转,和OC1A滞后OC1B的情形一致。
-------------------------------------------------------------------------------------------------------------------------------------
2、使用ICR1作为TOP值(WGM[13:10] = 1100)
1、此时OCR1A和OCR1B都用来控制OC1A和OC1B的相位,而波形的周期有ICR1决定。
OCR1A和OCR1B都不要大于ICP1,否则就没有波形输出。
测试代码:
修改main.c如下:
// 定时器1 初始化:CTC_ICR1模式、COM1A启用(电平翻转)、COM1B启用(电平翻转)、8预分频 Drv_Timer1_init(T1_WGM_CTC_ICR1, T1_COM_MODE_TOGGLE, T1_COM_MODE_TOGGLE, T1_CLK_SOURCE_CLK_8); // 设置TCTN1=0、OCR1A=20000、OCR1B=30000、ICR1=65535 Drv_Timer1_set_TCNT1_OCR1A_OCR1B_ICR1(0, 20000, 30000, 65535); // 使能TOV1中断、OCF1A中断,OCF1B中断 Drv_Timer1_INT_Enable(INT_MODE_TOV, ENABLE); Drv_Timer1_INT_Enable(INT_MODE_OCF1A, ENABLE); Drv_Timer1_INT_Enable(INT_MODE_OCF1B, ENABLE);
测试结果:
0、预测一下输出波形:
OCR1A=20000、OCR1B=30000、ICR1=65535。
每次TCNT1=20000时、OC1A发生比较匹配并翻转电平,每次TCNT1=30000时、OC1B发生比较匹配并翻转电平。
每次TCNT1=65535时清0,又重头计数,同时TOV1溢出、PA2引脚翻转电平(每次TCNT1溢出时翻转电平)。
OC1A和OC1B之间的相位相差10ms,OC1B和PA2之间的相位相差35.535ms。
OC1A、OC1B和PA2的波形产生如下:
示波器输出如下:
1、CH1是OC1A的输出,CH2是OC1B的输出,测试得他们之间相隔10ms。
2、CH1是OC1B的输出,CH2是PA2的输出,测试得他们之间相隔35.6ms。
3、他们的周期都是66ms,和理论计算基本一致。
他们互相之间的相位差也和上面预测的一致。
-------------------------------------------------------------------------------------------------------------------------------------
3、其他测试:
1、CTC输出的脉冲频率最大可达4MHz。
即系统时钟clk_IO的一半。
此时OCR1A=0。
2、比较匹配时OC1A和OC1B引脚清0/置1的情况也没什么需要特别测试的。
3、强制比较匹配FOC1A和FOC1B的用途有待测试
|<----待测试-question-001
-------------------------------------------------------------------------------------------------------------------------------------
第三步: 快速PWM模式
0、PWM如何产生
1、TCNT1=OCR1A/OCR1B时,OC1A/OC1B引脚电平翻转一次。
接着、继续计数到TCNT1=TOP时、OC1A/OC1B引脚电平再翻转一次,完成一个周期。
也就是说、TOP值决定了周期 T = ((1.0/clk_T1)*1000000)*DIV*(TOP+1),其中DIV为分频系数。
2、TOV1在TCNT1=TOP时触发,而不是计数到0xFFFF才触发,与普通计数不同。
比如TOP=0x01FF时,TCNT1计数到0x01FF就被清0,完成一个周期,
而TOV1会被触发,虽然TCNT1没有计数到0xFFFF并溢出。
-------------------------------------------------------------------------------------------------------------------------------------
1、8位快速PWM( TOP = 0xFF )
1-1、正常输出
测试代码:
// ========================================================================================================== // 主函数 // ========================================================================================================== #include <avr/io.h> #include <avr/interrupt.h> #include "Drv_Timer.h" #include "system.h" #include "config.h" // ========================================================================================================== // 伪中断BADISR_vect // // ========================================================================================================== ISR(BADISR_vect) { } // ========================================================================================================== // main函数 // ========================================================================================================== int main(void) { // ------------------------------------------------------------------------------------------------------ // 关全局中断 cli(); // 系统初始化 sys_init(); // PA[5:2]初始化为输出0 DDRA = (1 << DDA2) | (1 << DDA3) | (1 << DDA5) | (1 << DDA6); PORTA &= ~((1 << PA2 ) | (1 << PA3 ) | (1 << PA5 ) | (1 << PA6 )); // 定时器1 初始化:快速PWM-OCR1A模式、COM1A启用(取反)、COM1B启用(清0)、8预分频 Drv_Timer1_init(T1_WGM_8_FAST_PWM, T1_COM_MODE_CLEAR, T1_COM_MODE_CLEAR, T1_CLK_SOURCE_CLK_8); // 设置TCTN1=0、OCR1A=100、OCR1B=200、ICR1=200 Drv_Timer1_set_TCNT1_OCR1A_OCR1B_ICR1(0, 100, 200, 200); // 使能TOV1中断、OCF1A中断,OCF1B中断 Drv_Timer1_INT_Enable(INT_MODE_TOV, ENABLE); Drv_Timer1_INT_Enable(INT_MODE_OCF1A, ENABLE); Drv_Timer1_INT_Enable(INT_MODE_OCF1B, ENABLE); // PD[5:4](OC1B和OC1A)初始化为输出0 DDRD = (1 << DDD4) | (1 << DDD5); PORTD &= ~((1 << PD4 ) | (1 << PD5 )); // 开全局中断 sei(); // ------------------------------------------------------------------------------------------------------ while(1) { } return 0; }
测试结果:
示波器输出如下:
1、CH1为OC1A的输出,CH2为OC1B的输出。
OC1A的高电平宽度为100us,之后OC1A被拉低,也就是TCNT1=OCR1A=100时,OC1A被拉低。
OC1B的高电平宽度为200us,波形产生的情形和OC1A一致。
波形的周期是3.87KHz,对应的周期为258us,和计算基本一致。
2、也就是说OCR1A和OCR1B定义了高电平的宽度,TOP定义了波形的周期,高电平的占空比=OCR1A/TOP和OCR1B/TOP。
3、TOV1在TCNT1=TOP值时被触发,所以PA2引脚每隔256us翻转一次。
PA3在OC1A被拉低时(发生比较匹配) 翻转电平,PA5则在OC1B被拉低时翻转电平。
-------------------------------------------------------------------------------------------------------------------------------------
1-2、OCR1A和OCR1B > TOP,依然有PWM输出
1、TOP=0xFF,那么比较逻辑就只和OCR1A/OCR1B的低8位比较,超过TOP的部分都被屏蔽为0。
即、在TCNT1=(OCR1A或OCR1B)&0xFF的时刻发生比较匹配。
例如OCR1A=20000=0x4E20,OCR1B=30000=0x7530。
那么、OC1A在TCNT1=(OCR1A)&0xFF=0x4E20&0xFF=0x20=32的时刻发生比较匹配。
OC1B在TCNT1=(OCR1B)&0xFF=0x7530&0xFF=0x30=48的时刻发生比较匹配。
测试代码:
Drv_Timer.c不变,main.c修改定时器1的配置如下:// 设置TCTN1=0、OCR1A=20000、OCR1B=30000、ICR1=65535 Drv_Timer1_set_TCNT1_OCR1A_OCR1B_ICR1(0, 20000, 30000, 65535);
测试结果:
示波器输出如下:
1、CH1为OC1A的输出,CH2为OC1B的输出。
OC1A的高电平宽度为32us,即在TCNT1=32的时刻发生比较匹配、将OC1A引脚拉低。
OC1B的高电平宽度为48us,即在TCNT1=48的时刻发生比较匹配、将OC1B引脚拉低。
随后,TCNT1=TOP=255时,一个周期结束,OC1A和OC1B的引脚再次翻转,结束这个周期。
波形频率为3.87KHz,对应的周期为258us,和计算基本一致。
-------------------------------------------------------------------------------------------------------------------------------------
1-3、极限值情形( OCR1A = 0,OCR1B = 255 )
1、这里在OC1A和OC1B这两个通道上分别测试两种极限值情形
测试代码:
// 设置TCTN1=0、OCR1A=0、OCR1B=255、ICR1=200 Drv_Timer1_set_TCNT1_OCR1A_OCR1B_ICR1(0, 0, 255, 200);
测试结果:
示波器输出如下:
1、CH1为OC1A的输出,CH2为OC1B的输出。
OC1A的高电平宽度为1us,OC1B为100%占空比的高电平。
这说明不能得到0%的占空比,只能通过将OC1A/OCR1B引进设置为普通IO,再将其拉低来实现0%的占空比。
但可以得到100%的占空比。
-------------------------------------------------------------------------------------------------------------------------------------
2、9位快速PWM 和 10位快速PWM ( TOP = 0x01FF 和 0x03FF )
2-1、OCR1A 和 OCR1B > TOP的PWM
1、OCR1A和OCR1B超过TOP值,则实际的比较匹配时刻是TCNT1=(OCR1A或OCR1B)&TOP。
比如、10位快速PWM模式下,设置OCR1A=1500,OCR1B=2000。
此时实际的比较匹配时刻是TCNT1=(OCR1A或OCR1B)&TOP=(1500或2000)&0x3FF=476us或976us。
测试代码:
main.c中修改定时器1的配置如下:
// 定时器1 初始化:快速PWM模式、COM1A启用(清0)、COM1B启用(清0)、8预分频 Drv_Timer1_init(T1_WGM_10_FAST_PWM, T1_COM_MODE_CLEAR, T1_COM_MODE_CLEAR, T1_CLK_SOURCE_CLK_8); // 设置TCTN1=0、OCR1A=1500、OCR1B=2000、ICR1=200 Drv_Timer1_set_TCNT1_OCR1A_OCR1B_ICR1(0, 1500, 2000, 200);
测试结果:
示波器输出如下:
1、CH1为OC1A的输出,CH2为OC1B的输出。
OC1A的高电平宽度为480us,OC1B的高电平宽度为984us,波形周期是1030us,和预测的基本一致。
-------------------------------------------------------------------------------------------------------------------------------------
3、TOP = ICR1的快速PWM
1、这个和8位快速PWM一样,只是分辨率由ICR1决定,TOP=ICR1,ICR1这个寄存器决定了波形周期。
由于是使用ICR1寄存器确定周期,所以可以得到更多的频率,而不像定时器2那样只能是固定的分辨率。
另外、使用OCR1A/OCR1B寄存器确定占空比,所以可以得到相当精确的占空比,比如OCR1A=20,ICR1=200时、得到精确的10%的占空比。
而要得到38KHz(周期为26us)的红外载波,就可以设置ICR1=25,得到的周期为T= ((1.0/8000000)*1000000)*8*(25+1) = 26us,没有任何误差。
2、当然,正常输出时也不要让OCR1A和OCR1B超过ICR1值,超出了ICR1值、就没有波形输出,和上面的3种固定分辨率的TOP值不一样。
因为这里的TOP值时可变的,超过TOP的部分不会被屏蔽为0。
结果是TCNT1在TCNT1=ICR1时被清0,不会再继续加1、达到比ICR1更大的值。
结果就是、OCR1A和OCR1B永远也不会和TCNT1发生比较匹配。
3、上面讨论的极限值的情况适用于这里。
3-1、产生精确的占空比
测试代码:
main.c修改定时器1的配置如下:
// 定时器1 初始化:快速PWM-ICR1模式、COM1A启用(清0)、COM1B启用(清0)、8预分频 Drv_Timer1_init(T1_WGM_FAST_PWM_ICR1, T1_COM_MODE_CLEAR, T1_COM_MODE_CLEAR, T1_CLK_SOURCE_CLK_8); // 设置TCTN1=0、OCR1A=400、OCR1B=1600、ICR1=40000 Drv_Timer1_set_TCNT1_OCR1A_OCR1B_ICR1(0, 400, 1600, 4000);
测试结果:
示波器输出如下:
1、CH1为OC1A的输出,CH2为OC1B的输出,波形周期是4002us。
OC1A的高电平宽度为400us,占空比为精确的10%。
OC1B的高电平宽度为1600us,占空比为精确的40%。
2、其他的20%、80%等占空比都能精确的得到。
-------------------------------------------------------------------------------------------------------------------------------------
3-2、产生38KHz,30%占空比的红外载波
0、ICR1设置为25,得到26us的周期,对应的频率为38KHz。
26.0/3=8.6,所以设置OCR1A=7、得到8us的高电平,对应的占空比是30.7%。
设置OCR1B=8、得到9us的高电平,对应的占空比是34.6%。
测试代码:
main.c修改定时器1的配置如下:
// 定时器1 初始化:快速PWM-ICR1模式、COM1A启用(清0)、COM1B启用(清0)、8预分频 Drv_Timer1_init(T1_WGM_FAST_PWM_ICR1, T1_COM_MODE_CLEAR, T1_COM_MODE_CLEAR, T1_CLK_SOURCE_CLK_8); // 设置TCTN1=0、OCR1A=7、OCR1B=8、ICR1=25 Drv_Timer1_set_TCNT1_OCR1A_OCR1B_ICR1(0, 7, 8, 25);
测试结果:
示波器输出如下:
1、CH1为OC1A的输出,CH2为OC1B的输出,波形周期26.2us,对应的频率是38.1KHz。
OC1A的高电平宽度为8us,占空比为的30.5%。
OC1B的高电平宽度为9us,占空比为精确的34.4%。
-------------------------------------------------------------------------------------------------------------------------------------
4、TOP = OCR1A的快速PWM
1、这个模式用来在OCR1A引脚产生50%占空比的方波,和CTC模式类似,波形周期由OCR1A决定。
而OC1B引脚的COM1A[1:0]=1配置不是PWM引脚,而是普通IO。
但OC1B的COM1A[1:0]=2或COM1A[1:0]=3是可以输出PWM波形的,波形周期和OCR1A引脚的方波一致。
2、OCR1B超过TOP=OCR1A时超过TOP的部分不会被屏蔽为0,结果是OC1B引脚就没有脉冲输出。
因为TCNT1的计数值不会超过TOP=OCR1A。
3、OCR1A是双缓冲寄存器,所以更新OCR1A时,当前的OCR1A值还可以和TCNT1进行比较,不会丢失可能的一次比较匹配。
在下一个时钟周期里,才从缓冲寄存器里面更新OCR1A。
这比较适合用来产生频率变化的波形,而不会产生毛刺。
4-1、OCR1A产生方波、OCR1B产生PWM
1、COM1A[1:0]=1的配置下、OC1A产生方波。
测试代码:
main.c修改定时器1的配置如下:
// 定时器1 初始化:快速PWM-OCR1A模式、COM1A启用(取反)、COM1B启用(清0)、8预分频 Drv_Timer1_init(T1_WGM_FAST_PWM_OCR1A, T1_COM_MODE_TOGGLE, T1_COM_MODE_CLEAR, T1_CLK_SOURCE_CLK_8); // 设置TCTN1=0、OCR1A=400、OCR1B=200、ICR1=400 Drv_Timer1_set_TCNT1_OCR1A_OCR1B_ICR1(0, 400, 100, 400);
测试结果:
示波器输出如下:
1、CH1为OC1A的输出,CH2为OC1B的输出,
OC1A引脚输出周期为2*400us的方波、占空比为50%。
OC1B 引脚输出周期为400us的PWM波形,占空比为100/400=25%。
2、OC1A的频率可变,但用于是方波。
-------------------------------------------------------------------------------------------------------------------------------------
4-2、OC1A引脚的其他配置
1、在配置为COM1A[1:0]=2或COM1A[1:0]=3上、OC1A引脚都没有脉冲输出。
因为OCR1A用来控制周期,那么就没有寄存器来控制OC1A引脚的占空比了。
-------------------------------------------------------------------------------------------------------------------------------------
第四步: 相位修正PWM
-------------------------------------------------------------------------------------------------------------------------------------