定时/计数器应用——从P1.0输出周期为1s的方波,让发光二极管以1Hz闪烁

本文是51单片机学习笔记,通过定时器T0和中断,实现1Hz频率让LED闪烁。详细注释解释了如何设置定时器工作模式、初值计算以及中断处理过程,帮助理解单片机定时中断原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

利用定时/计数器T0从P1.0输出周期为1s的方波,让发光二极管以1Hz闪烁,设晶振频率为12MHz。

2021-01-20,51单片机学习笔记

代码(精简注释):
/*********************************************************
利用定时/计数器T0从P1.0输出周期为1s的方波,
让发光二极管以1HZ闪烁,设晶振频率为12MHz
**********************************************************/
#include <reg52.h>
unsigned char num;
sbit LED1=P1^0;

void main()
{
	num=0;//初始化计数变量
	EA=1;//打开特殊功能寄存器IE内的总开关EA
	ET0=1;//打开特殊功能寄存器IE内的开关ET0
	TMOD=0X01;//0000 0001
	//低四位,GATE=0,C/T=0,M1M0=01;选用中断源T0,方式1,16位定时器
	TH0=(65536-50000)/256;//初始化,装填初值
	TL0=(65536-50000)%256;
	TR0=1;//启动定时器
	
	while(1)
	{
		if(num==20)
		{
			num=0;//中断函数每执行20次,初始化计数变量
			LED1=~LED1;//取反
		}
	}//中断函数每执行20次,led1取反一次,20*50ms=1s,即led1以1s的间隔交替点亮熄灭
}

//中断函数 中断源:"定时/计数器0(T0)",代号1
void time0() interrupt 1 
{
	TH0=(65536-50000)/256;//初始化,装填初值60(8进制)
	TL0=(65536-50000)%256;//初始化,装填初值176(8进制)
	num++;//计数变量自增
	
	/*方式1是对每一个机器周期进行计数,直到计满后,溢出,申请中断
	这里是从15536(=65536-50000)位置开始计数,逐次加1,直到65535位置即将溢出,再加1,回到0
	共50000次"加1",机器周期为1us时,每次加1耗时1us,全程耗时50ms*/
}
代码(详细注释):
/*********************************************************
利用定时/计数器T0从P1.0输出周期为1s的方波,
让发光二极管以1HZ闪烁,设晶振频率为12MHz
**********************************************************/

#include <reg52.h>
unsigned char num;
sbit LED1=P1^0;

void main()
{
	num=0;//初始化计数变量
	EA=1;//打开特殊功能寄存器IE内的总开关EA
	ET0=1;//打开特殊功能寄存器IE内的开关ET0
	TMOD=0X01;//0000 0001
	/*
	TMOD是定时/计数器的工作方式寄存器,
		确定工作方式和功能。低四位用于T/C0,高四位用于T/C1。
		自左向右:
		高四位:[GATE|C/T|M1|M0]低四位:[GATE|C/T|M1|M0]
	
	((这里选择T/C0,使用低四位,所以高四位设置0))‘0000 xxxx’
	
	
	GATE:门控位。
		GATE=0时,只要使TCON中的TR0或TR1为1,就可以启动定时/计数器(T/C)
		工作。(其中:TR0对应T/C0;TR1对应T/C1)
		GATA=1时,要满足两个条件,才能启动定时/计数器(T/C)工作:
		1.使TR0或TR1为1。
		2.同时外部中断引脚INT0/1也为高电平时。
		(INT0对应T/C0;INT1对应T/C1)
	
	((这里选择GATE=0,TR0=1))‘0000 0xxx’
	
	
	C/T:定时/计数模式选择位。
		C/T=0为定时模式;C/T=1为计数模式。
		
	((这里选择C/T=0,定时模式))‘0000 00xx’
	
	
	M1M0:工作方式设置位。
		定时/计数器有四种工作方式,由M1M0进行设置。
		M1M0=00,方式0,13位定时器/计数器
		M1M0=01,方式1,16位定时器/计数器
		M1M0=10,方式2,8位自动重装定时器/计数器
		M1M0=11,方式3,T0分成两个独立的8位定时器/计数器,T1此方式停止计数
	
	((这里选择M1M0=01,方式1))‘0000 0001’
	*/
	
	TH0=(65536-50000)/256;//初始化,装填初值,装填初值60(8进制)
	TL0=(65536-50000)%256;//初始化,装填初值,装填初值176(8进制)
	/*方式1(M1M0=01),定时器(C/T=0)模式
	方式1的计数位数是16位,由TL0作为低8位,TH0作为高8位,组成了16位加1计数器
	16位寄存器可以装填的范围是0~65535,共65536个数
		65535(10)=1111 1111 1111 1111(2)=FFFF(16)=177777(8)
	16位定时器0装初值的计算公式:
	假设要计的个数是n,
	TH0=(65536-n)/256;//高8位=差对256(2的8次方)求商
	TL0=(65536-n)%256;//低8位=差对256(2的8次方)求余
	*/
	TR0=1;//启动定时器
	/*TR0(TCON.4)T/C0运行控制位
	TR0置1时,T/C0开始工作;TR0置0时,T/C0停止工作.TR0由软件置1或清0.
	所以,用软件可控制定时/计数器的启动与停止.
	*/
	while(1)
	{
		if(num==20)
		{
			num=0;//中断函数每执行20次,初始化计数变量
			LED1=~LED1;//取反
		}
	}//中断函数每执行20次,led1取反一次,20*50ms=1s,即led1以1s的间隔交替点亮熄灭
}

//中断函数 中断源:"定时/计数器0(T0)",代号1
/*5个中断源的代号分别是:
外部中断0(INT0)-->0
定时/计数器0(T0)-->1
外部中断1(INT1)-->2
定时/计数器1(T1)-->3
串行口-->4
它们的优先级从上到下由高到低递减*/
void time0() interrupt 1 
{
	TH0=(65536-50000)/256;//初始化,装填初值60(8进制)
	TL0=(65536-50000)%256;//初始化,装填初值176(8进制)
	num++;//计数变量自增
	/*((这里要装填的个数为50000))
		((fosc=12MHz,震荡周期=1/12um,机器周期=1us))
		((50000*1us=50ms,单次执行延时50ms))
		振荡周期:为单片机提供振荡源的周期(是指振荡源的两个相邻同方向峰值之间的时间)
		机器周期:1个机器周期等于12个振荡周期
	
	方式1是对每一个机器周期进行计数,直到计满后,溢出,申请中断
	这里是从15536(=65536-50000)位置开始计数,逐次加1,直到65535位置即将溢出,再加1,回到0
	共50000次"加1",机器周期为1us时,每次加1耗时1us,全程耗时50ms
	*/
}

写在最后:
本文仅为个人学习笔记,诸多纰漏,欢迎斧正!

<think>嗯,用户的问题是关于C语言编程的两个嵌入式系统项目,涉及定时器和中断的使用。首先,我需要仔细分析每个要求,确保理解正确。 第一个问题分为两部分:第一部分是使用查询和中断方式控制LED频率,并通过开关调整。第二部分是用定时器测量脉冲宽度并用LED显示。 对于①,用户需要两种方法实现50Hz的LED循环,同时通过S1和S2调整频率。50Hz对应的周期是20ms,所以每个LED的点亮时间应该是20ms除以LED的数量。假设有4个LED,每个点亮5ms。但实际代码可能需要调整定时器的配置。查询方式是通过不断检查定时器标志位,而中断方式则是在中断服务程序中处理。需要配置定时器,处理按键去抖动,以及频率的增减逻辑。注意按键检测可能需要防抖处理,比如延时或状态机。 中断方式的优点是不占用CPU时间,效率高,但编写复杂,需要处理中断服务程序。查询方式简单,但会占用更多CPU资源,可能导致主循环延迟。 对于②,使用T1测量P33的正脉冲宽度。通常,定时器可以设置为门控模式,利用外部信号控制计数器的启停。当P33为高电平时,计数器开始计数,直到高电平结束,读取计数值换算成时间。然后通过LED显示周期,可能需要将数值转换为二进制,控制LED的亮灭。需要注意定时器的位数,是否会有溢出,以及如何计算实际时间。 接下来,我需要提供两种方式的示例代码。查询方式中使用轮询TF0标志,而中断方式使用中断服务函数。按键处理部分需要检测S1和S2的按下,并调整频率变量。频率变化后,重新计算定时器重装值。 在第二部分,配置T1为门控模式,测量高电平时间。读取TH1和TL1的值,组合成16位计数值,乘以机器周期时间得到脉宽。然后通过LED显示,可能需要将数值分解为二进制位,控制对应的LED。 最后,相关问题需要围绕涉及的技术点,如定时器模式、中断配置、门控测量、LED显示方法等。确保问题覆盖代码中的关键部分,帮助用户深入理解。</think><< 问题①解决方案: 查询方式代码: ```c #include <reg51.h> #define LED_PORT P2 sbit S1 = P3^0; sbit S2 = P3^1; unsigned int freq = 50; // 初始频率50Hz unsigned char led_index = 0; unsigned int timer_val; void timer_init() { TMOD = 0x01; // 定时0模式1 timer_val = 65536 - (12000000/(12*freq)); // 12MHz晶振计算 TH0 = timer_val >> 8; TL0 = timer_val & 0xFF; TR0 = 1; // 启动定时器 } void main() { LED_PORT = 0xFE; timer_init(); while(1) { // 查询定时器溢出标志 if(TF0 == 1) { TF0 = 0; TH0 = timer_val >> 8; TL0 = timer_val & 0xFF; // LED循环移位 LED_PORT = (LED_PORT << 1) | 0x01; if(LED_PORT == 0xFF) LED_PORT = 0xFE; } // 按键处理 if(S1 == 0) { // 频率增加 while(S1 == 0); // 等待释放 if(freq < 100) freq++; timer_val = 65536 - (12000000/(12*freq)); } if(S2 == 0) { // 频率减少 while(S2 == 0); if(freq > 1) freq--; timer_val = 65536 - (12000000/(12*freq)); } } } ``` 中断方式代码: ```c #include <reg51.h> #define LED_PORT P2 sbit S1 = P3^0; sbit S2 = P3^1; unsigned int freq = 50; unsigned char led_index = 0; unsigned int timer_val; void timer0_isr() interrupt 1 { TH0 = timer_val >> 8; TL0 = timer_val & 0xFF; LED_PORT = (LED_PORT << 1) | 0x01; if(LED_PORT == 0xFF) LED_PORT = 0xFE; } void main() { TMOD = 0x01; IE = 0x82; // 允许定时0中断 LED_PORT = 0xFE; timer_val = 65536 - (12000000/(12*freq)); TH0 = timer_val >> 8; TL0 = timer_val & 0xFF; TR0 = 1; while(1) { if(S1 == 0) { // 防抖延时 for(unsigned int i=0;i<500;i++); if(S1 == 0) { if(freq < 100) freq++; timer_val = 65536 - (12000000/(12*freq)); while(S1 == 0); } } if(S2 == 0) { for(unsigned int i=0;i<500;i++); if(S2 == 0) { if(freq > 1) freq--; timer_val = 65536 - (12000000/(12*freq)); while(S2 == 0); } } } } ``` 两种方式比较: 1. 查询方式: 优点:实现简单,不需要中断向量配置 缺点:CPU利用率低,需要持续轮询标志位 2. 中断方式: 优点:CPU利用率高,可执行其他任务 缺点:需要处理中断嵌套,编程复杂度较高 问题②解决方案: ```c #include <reg51.h> #define LED_PORT P1 void main() { TMOD = 0x90; // 定时1模式1,门控方式 TH1 = 0; TL1 = 0; TR1 = 1; // 启动定时器 while(1) { while(P3_3 == 0); // 等待高电平 TH1 = TL1 = 0; // 清零计数器 while(P3_3 == 1); // 等待低电平 TR1 = 0; // 停止计数 // 显示周期(取高8位) LED_PORT = TH1; // 重置定时器 TR1 = 1; } } ``` 原理说明: 1. 配置T1为门控模式,由P3.3引脚电平控制计数 2. 当检测到高电平时开始计数,低电平时停止 3. 将计数值的高8位通过LED显示 4. 实际周期计算:计数值 × 机器周期时间(12MHz晶振时为1μs)
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值