【51单片机】计时器/计数器中断

51单片机中有2个 定时器/计数器 中断,之所以叫定时器/计数器,是因为它既可以当定时器,也可以当计数器,其实本质上都是计数器,如果给一个稳定的时钟源,那么它就是一个定时器的。(下面我说的计数器或定时器都是一回事,不要在意为什么有的地方是定时器,有的地方是计数器)

首先,定时器的计数是加1的,而不是减1的,当加到最大值时,再次加1就会变成0,此时就会触发计数器中断。51中常用的是16位计数器,最大值是65535,如果要计数1000次,那么计数器赋值应该为65535-1000+1(达到最大值后要再加一次才行。)计数器的初值是放在两个8位寄存器中,分别是TH0, TL0(计数器0的高8位和低8位),计数器1使用的是TH1,TL1,原理相同。
65535-1000+1=64536=0xFC18
所以
TH0=0xFC;
TL0=0x18;

上面我们让定时器计数1000次,但是我们要的是固定时长,而不是固定次数,一千次是多长时间呢? 我们需要计算计数器加1需要的时间。

计数器加1需要一个机器周期,而一个机器周期等于6个状态周期,12个震荡周期。如果51芯片的频率是12MHz,那么震荡周期就是1/12M秒,而机器周期就是12×1/12M秒,就是1/1M秒,即1/1000000秒,也就是1微秒。

计数1000次就是1毫秒了;

然后就是如何使用计数器中断了
计数器中断定义和外部中断定义相同格式如下:

void timer0() interrupt 1

定义好了就是如何让他正常工作了

	TMOD=0x11; //这种计数器0和1的工作模式,高4位设置计数器1,低4位设置计数器0,我们都设置在工作模式1上。
	EA=1;  //中断的全局是能开关,使用任何中断都需要打开
	ET0=1; //计数器0中断允许控制位
	ET1=1; //计数器1中断允许控制位
	TR0=1; //计数器工作开关,1-开始工作; 0-停止工作
	TR1=1; //同上

在这里插入图片描述
TMOD各个字段的含义:
GATE: 为1时,与INT0/1引脚共同控制计数器0/1。我们默认使用0,不受INT0/1控制;
C/T: C为Couter,计数器模式,T为Timer,定时器模式;T上面有横杠,说明是0有效;Timer模式的时候,使用系统内部时钟输入;Couter模式的时候,使用T0/1引脚输入;
M1M0两位组成了4种工作模式:

  • 00 :13位定时器,TL0/1只用低5位;TH0/1全部使用;
  • 01 : 16位定时器, TH0/1,TL0/1都使用
  • 10 : 8位自动重装定时器,当溢出时,将TH0/1存放的值自动装入TL0/1
  • 11 : 计时器1停止计数;计时器0作为两个8位寄存器使用;TL0由计数器0控制位控制,TH0由计数器1控制位控制

下面是完整代码实现,实现每秒种LED状态翻转:

#include<reg52.h>
typedef unsigned char u8;
typedef unsigned int u16;

sbit LED1=P2^0;
sbit LED2=P2^1;

void timerInit()
{
	TMOD=0x11;
	EA=1;  
	ET0=1; 
	ET1=1;
	TR0=1;
	TR1=1;
	TH0=0xFC;
	TL0=0x6A;
	TH1=0xFC;
	TL1=0x6A;
}
void timer0() interrupt 1
{
	static u16 counter=0;
	if(counter==1000)
	{
		LED1=~LED1;
		counter=0;
	}
	counter++;
	TH0=0xFC;
	TL0=0x6A;
}
void timer1() interrupt 3
{
	static u16 counter=0;

	if(counter==1000)
	{
		LED2=~LED2;
		counter=0;
	}
	counter++;
	TH1=0xFC;
	TL1=0x6A;
}

void main()
{
	timerInit();
	while(1);
}

代码中我们的计数值不是0xFC18,而是0xFC6A,因为我使用的单片机频率不是12MHz,而是11.018398MHz.

  • 9
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是一个基于51单片机的程序,使用定时器/计数器中断实现秒闪烁和分闪烁的效果,LED灯分别连接在P1口和P2口上: ```C #include <reg51.h> #define FREQ 11059200L // 定义晶振频率 #define TIMER_MS 10 // 定义定时器中断时间间隔(毫秒) #define LED_SEC P1 // 定义秒LED所在的端口 #define LED_MIN P2 // 定义分LED所在的端口 unsigned int timer_count = 0; // 定义计数器变量,用于记录定时器中断次数 // 定时器中断服务函数 void Timer0_ISR() interrupt 1 { TH0 = (65536 - (FREQ / 12 / 1000)); // 重新设置计时器初值 TL0 = (65536 - (FREQ / 12 / 1000)) % 256; timer_count++; // 计数器加1 } // 延时函数,用于控制LED闪烁的时间间隔 void Delay(unsigned int time) { unsigned int i, j; for (i = time; i > 0; i--) { for (j = 0; j < 120; j++); } } void main() { unsigned int sec_flag = 0; // 秒闪烁标志位 unsigned int min_flag = 0; // 分闪烁标志位 TMOD = 0x01; // 设置定时器0为模式1(16位定时器) TH0 = (65536 - (FREQ / 12 / 1000)); // 设置计时器初值 TL0 = (65536 - (FREQ / 12 / 1000)) % 256; EA = 1; // 开启总中断 ET0 = 1; // 开启定时器0中断 TR0 = 1; // 开启定时器0 while (1) { if (timer_count * TIMER_MS >= 1000) { // 判断是否达到1秒 timer_count = 0; // 计数器清零 sec_flag = !sec_flag; // 秒闪烁标志位反转 if (sec_flag) { LED_SEC |= 0x01; // 点亮秒LED } else { LED_SEC &= 0xFE; // 熄灭秒LED } } if (timer_count * TIMER_MS >= 60000) { // 判断是否达到1分钟 min_flag = !min_flag; // 分闪烁标志位反转 timer_count = 0; // 计数器清零 if (min_flag) { LED_MIN |= 0x01; // 点亮分LED } else { LED_MIN &= 0xFE; // 熄灭分LED } } Delay(10); // 延时10毫秒 } } ``` 这份代码使用了定时器/计数器中断来实现LED的闪烁效果,当中断次数达到一定值时,就判断是否达到需要闪烁的时间间隔,来控制LED的状态变化。需要注意的是,定时器频率的设置需要根据实际的晶振频率进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值