第8章 8位定时计数器结构与应用
PORTB=0x01;
// T/C0 初始化 TCCR0=0x07; TCNT0=0x83; OCR0=0x00;
TIMSK=0x01;
#asm(\
while (1) }
{
// Place your code here };
// 开放全局中断
// 允许T0溢出中断
// T/C0工作于普通模式,T0上升沿触发
// 设置PB0(T0)为输入方式
DDRB=0x00;
上面的程序,就是先利用CVAVR的程序生成向导功能进行配置,然后在它生成的程序框架基础上完成的。
在程序中,主程序对T/C0进行初始化后进入一个无限的循环。在T/CO的中断服务程序中,重新设置TCNT0的初值,并将PA0取反输出。这时PA0上可以获得1Hz的方波输出。 3) 思考与实践
? 为什么在中断服务程序中要重新设置TCNT0的初值? ? 如何计算TCNTO的初值,使得PA0输出0.5Hz的方波。 例8.2 2N分频系统设计二 1)硬件电路
同2N分频系统设计一。 2)软件设计
2N分频系统设计一中使用了T/C0的普通模式,因此在中断服务程序中必须重新对TCNT0进行初始化。其实,更方便的方法是使用T/CO的CTC模式,利用T/CO的自动重装特性。当T/C0工作在CTC模式时,计数器TCNT0的值与OCR0的值比较,一旦相等,在下一次计数脉冲到来时,清零TCNT0,并产生T/C0的比较匹配中断(参考图8-13)。此时在比较匹配中断服务程序中改变PA0的输出即可。
/********************************************* File name
: demo_8_2.c
Chip type : ATmega16 Program type : Application Clock frequency : 4.000000 MHz Memory model : Small External SRAM size : 0 Data Stack size : 256
*********************************************/
,基于AVR的单片嵌入式系统原理与实践应用
#include
// Timer 0 比较匹配中断服务
interrupt [TIM0_COMP] void timer0_comp_isr(void) { }
void main(void) {
PORTA=0x01; DDRA=0x01;
PORTB=0x01; DDRB=0x00;
// T/C0 初始化 TCCR0=0x0F; TCNT0=0x00; OCR0=0x7C;
TIMSK=0x02; #asm(\
while (1) { }; }
// Place your code here
// 允许T/C0的比较匹配中断 // 开放全局中断
// 设置OCR0的比较值为124(0x7C)
// T/C0工作于CTC模式,T0上升沿触发
PORTA.0=~PORTA.0;
// PA0取反输出
3) 思考与实践
? 比较demo_8_1.c和demo_8_2.c中T/C0两种方式的特点。 ? 如何利用T/C0实现N分频? 例8.3 N分频系统设计 1)硬件电路
同2N分频系统设计,但在PA0上得到N分频的输出。 2)软件设计
实际上,利用T/C0的比较匹配的特点,可以实现N分频的系统。
/********************************************* File name Chip type Program type Memory model
: demo_8_3.c : ATmega16 : Application : 4.000000 MHz : Small
Clock frequency
External SRAM size : 0
华东师范大学 电子系 马 潮 8-17
第8章 8位定时计数器结构与应用
Data Stack size
#include
// Timer 0 溢出中断服务
interrupt [TIM0_OVF] void timer0_ovf_isr(void) {
TCNT0=0xFB;
}
// Timer 0 比较匹配中断服务
interrupt [TIM0_COMP] void timer0_comp_isr(void) { }
void main(void) {
PORTA=0x00; DDRA=0x01;
PORTB=0x01; DDRB=0x00;
// T/C0 初始化 TCCR0=0x07; TCNT0=0xFB; OCR0=0xFD;
TIMSK=0x03; #asm(\
while (1)
{ }; }
// Place your code here
// 允许T/C0的溢出和比较匹配中断 // 开放全局中断
// 设置OCR0的比较值,>TCNT0的初始值,<0xFF
// T/C0工作于普通模式,T0上升沿触发
PORTA = ~PORTA;
// PA0取反输出
PORTA = ~PORTA;
// 重新设置TCNT0的初值 // PA0取反输出
: 256
*********************************************/
程序demo_8_3.c设置T/CO工作在普通模式,并结合比较匹配的特性,在比较匹配中断和溢出中断中都改变PA0的输出,在PA0上获得5分频的脉冲信号。 3) 思考与实践
? 请读者自己分析程序实现N分频的原理。
? 如果要实现11分频的输出,TCNT0的初值应该如何计算,为什么同例8.1 不同?
,基于AVR的单片嵌入式系统原理与实践应用
? 在N分频的系统中,OCR0的值应该如何设置?
? 在PA0上输出的方波序列的占空比同例8.1和例8.2有何不同? ? 利用这个方法,能否在PA0上获得占空比可调的PWM波?
8.2.2 定时器应用设计
实际上不管定时计数器是作为计数器使用还是作为定时器使用,其根本的工作原理并没有改变,都是对一个脉冲系列信号进行计数。通常所谓的定时器,更多的情况是指其计数脉冲信号来自芯片本身的内部。由于内部的计数脉冲信号的频率(周期)是已知的或固定的,因此用户可以根据需要来设定计数器脉冲计数的个数,以获得一个等间隔的定时中断。利用定时中断,可以方便的实现系统定时访问外设或处理事物,以及获得更加准确的延时等等。
同其他一些单片机类似,AVR的定时计数器的计数脉冲可以来自外部的引脚,也可以从内部系统时钟获得,但AVR的定时计数器在内部系统时钟和计数单元之间增加了一个可设置的预分频器,利用这个预分频器,定时计数器可以从内部系统中获得不同频率的计数脉冲信号。表8.6给出了系统时钟为4MHz时,ATmega16芯片本身能够提供给T/C0的计数脉冲信号的最高计时精度和时宽范围。
表8.1 T/C0计时精度和时宽(系统时钟4MHz)
分频系数 1 8 64 256 1024 计时频率 4MHz 500KHz 62.5KHz 15.625KHz 3906.25Hz 最高计时精度 (TCNT0=255) 0.25us 2us 16us 64us 256us 最宽时宽 (TCNT0=0) 64us 512us 4.096ms 16.384ms 65.536ms 从表中看出,在系统时钟为4Mh条件下,8位的T/C0最高计时精度为0.25us,而最长的时宽可达到65.536ms。而如果使用16位的定时计数器T/C1时,不需要使用辅助软件计数器,就可以非常方便的设计一个时间长达16.777216秒(精度为256us)的定时器,这是其它的8位单片机所做不到的。
AVR单片机的每一个定时计数器都配备独立的、多达10位的预分频器,由软件设定分频系数,与8/16位定时计数器配合,可以提供多种档次的定时时间。使用时可选取最接近的定时档次,即选8/16位定时计数器与分频系数的最优组合,减少了定时误差。所以,AVR定时计数器的显著特点之一是:高精度和宽时范围,使得用户应用起来更加灵活和方便。 例8.4 采用T/C0硬件定时器的时钟系统 1) 硬件电路
在第六章中的例6.5,“六位LED数码管动态扫描控制显示设计(一)”中,使用调用CVAVR中软件延时函数的方法给出了一个使用6个数码管组成时钟系统。采用软件延时,时钟是不准确的,因为一旦系统中使用了中断,就可能打断延时程序的执行,使延时时间发生变化。另外使用软件延时的方法,也降低了MCU的效率。
而在第七章中的例7.2,“采用外部中断方式,用外部振荡源为基准的时钟系统”中,系统时钟的基准信号来自外部的标准方波信号源,这样尽管定时时间比采用软件延时方式要准确的多,但由于采用外部标准方波信号源而增加了系统的成本。
实际上更加方便和简单的方式是采用系统本身的时钟信号,配合T/C0产生时钟系统的定时信号。下面给出采用T/C0硬件定时器实现的时钟系统设计。时钟系统的硬件电路仍旧
华东师范大学 电子系 马 潮 8-19
第8章 8位定时计数器结构与应用
与图6-15相同。 2) 软件设计
下面是一个采用C编写的系统源程序。
/********************************************* File name
: demo_8_4.c
Chip type : ATmega16 Program type : Application Clock frequency : 4.000000 MHz Memory model : Small External SRAM size : 0 Data Stack size : 256
*********************************************/
#include
flash char led_7[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; flash char position[6]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
char time[3]; int bit
void display(void) {
PORTC = 0xff;
}
// Timer 0 比较匹配中断服务
interrupt [TIM0_COMP] void timer0_comp_isr(void) { }
void time_to_disbuffer(void)
// 时钟时间送显示缓冲区函数
display(); { }
time_counter = 0; time_1s_ok = 1;
// 调用LED扫描显示
if (++time_counter>=500)
PORTA = led_7[dis_buff[posit]];
if (point_on && (posit==2||posit==4)) PORTA |= 0x80; PORTC = position[posit]; if (++posit >=6 ) posit = 0;
// 6位LED数码管动态扫描函数
// 时、分、秒计数单元
// 显示缓冲区,存放要显示的6个字符的段码值 // 中断次数计数单元
char dis_buff[6];
time_counter;
char posit;
point_on, time_1s_ok;