当前位置: 首页 > article >正文

04:定时器

定时器

  • 1、定时器如何定时
  • 2、寄存器简介
  • 3、定时器定时
    • 实验①
    • 实验②
  • 4、定时器中断
    • 实验③
    • 实验④
  • 5、定时器计数功能
    • 实验⑤
    • 实验⑥

1、定时器如何定时

通过晶振(晶体振荡器)发出脉冲,记录分频后的脉冲的个数来进行定时。51单片机的定时器时钟源分频系数为6T和12T。在下载器的STC-ISP中选择。默认为12T(12分频)。

在这里插入图片描述

在这里插入图片描述
如图:51单片机的晶振为12MHz,经过12T后变为1MHz,则时钟周期为1S/(1M) = 1(us),即定时器的计时器每隔1(us)进行+1。当定时器的计数器使用16位时,最大计数为65536。所以定时器最长定时时间为65536 * 1(us) = 65536(us) = 65.536ms

2、寄存器简介

在这里插入图片描述在这里插入图片描述

综上:若12T下需要定时1ms(即定时器启动,1ms后定时器溢出标志位置位),那么定时器T0的计数器的初值应该设置为多少喃?

1ms = 1000us,即65536 - 1000 = 64536。定时计数器T0的初值应该设置为64536,即定时计时器从64536开始计数。
TH0 = 64536 >> 8;
TL0 = 64536 & 0x00FF
定时器溢出后,计数器从0开始向65535计数。

3、定时器定时

实验①

实验:使用T0,定时器定时10ms,轮询式让LED每隔1s闪烁

①Time.c文件的代码如下:

#include "Time.h"

/**
 * 定时器T0的初始化
 */
void Time0_Init(void)
{
	/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
	TMOD &= 0xF0;			//将低4位置0
	TMOD |= 0x01;			//最低位置1
	
	/* 设置定时计数器的初始值,定时10ms */
	TH0 = ((65536 - 10000) >> 8);
	TL0 = (65536 - 10000) & 0x00FF;
	
	/* 启动T0 */
	TF0 = 0;				//清除标志位
	TR0 = 1;				//启动定时器0
}

②Time.h文件的代码如下:

#ifndef __Time_H
#define __Time_H
#include <reg51.h>		//包含51头文件,里面全是寄存器地址>

void Time0_Init(void);

#endif

③main.c文件的代码如下:

#include "Time.h"
#include "LED.h"

void main(void)
{
	unsigned char Num = 0;
	Time0_Init();			//定时器10ms
	
	while(1)
	{
		if(TF0 == 1)		//10ms定时器到了
		{
			TF0 = 0;		//清除标志位
			
			/* 重新设置定时计数器的初始值,定时10ms */
			TH0 = ((65536 - 10000) >> 8);
			TL0 = (65536 - 10000) & 0x00FF;
			Num++;
			if(Num == 100)	//定时1s到了
			{
				Num = 0;
				LED_Turn(1);//翻转LED1
			}
		}
	}
}

实验②

实验:使用T0构造延时函数,让LED每隔1s闪烁
①Time.c文件的代码如下:

#include "Time.h"

/**
 * 使用T0构造延时函数
 */
void Delay_ms(unsigned short second)
{
	/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
	TMOD &= 0xF0;			//将低4位置0
	TMOD |= 0x01;			//最低位置1
	
	/* 设置定时计数器的初始值,定时1ms */
	TH0 = ((65536 - 1000) >> 8);
	TL0 = (65536 - 1000) & 0x00FF;
	
	/* 启动T0 */
	TF0 = 0;				//清除标志位
	TR0 = 1;
	
	/* 循环等待 */
	while(second)
	{
		if(TF0 == 1)
		{
			TF0 = 0;		//清除标志位
			--second;		//每隔1ms进行减1
			/* 设置定时计数器的初始值,定时1ms */
			TH0 = ((65536 - 1000) >> 8);
			TL0 = (65536 - 1000) & 0x00FF;
		}
	}
	TR0 = 0;				//关闭定时器
}

②Time.h文件的代码如下:

#ifndef __Time_H
#define __Time_H
#include <reg51.h>		//包含51头文件,里面全是寄存器地址>

void Delay_ms(unsigned short second);

#endif

③main.c文件的代码如下:

#include "Time.h"
#include "LED.h"

void main(void)
{
	while(1)
	{
		Delay_ms(1000);
		LED_Turn(1);
	}
}

4、定时器中断

实验③

实验:使用T0,定时器定时1s,轮询式让LED每隔1s闪烁
①Time.c文件的代码如下:

#include "Time.h"

/**
 * 定时器T0的初始化,且开启中断
 */
void Time0It_Init(void)
{
	/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
	TMOD &= 0xF0;			//将低4位置0
	TMOD |= 0x01;			//最低位置1
	
	/* 设置定时计数器的初始值,定时10ms */
	TH0 = ((65536 - 10000) >> 8);
	TL0 = (65536 - 10000) & 0x00FF;
	
	/* 启动T0 */
	TF0 = 0;				//清除标志位
	TR0 = 1;
	
	/* 开启中断 */
	ET0 = 1;				//使能定时器溢出中断
	EA = 1;					//开启总开关
}


/********中断服务函数*******/
/**
 * T0的中断服务函数(每隔10ms对Number加1,Number加到100,相当于隔了1s,在执行LED翻转)
 */
static unsigned char Number = 0;
void Time0_Rountine(void) interrupt 1
{
	//这里不用清除标志位,硬件会自动清除
	TH0 = ((65536 - 10000) >> 8);
	TL0 = (65536 - 10000) & 0x00FF;
	
	Number++;
	if(Number == 100)
	{
		Number = 0;
		LED_Turn(1);//翻转LED
	}
}

②Time.h文件的代码如下:

#ifndef __Time_H
#define __Time_H
#include <reg51.h>		//包含51头文件,里面全是寄存器地址>
#include "LED.h"

void Time0It_Init(void);

#endif

③main.c文件的代码如下:

#include "Time.h"
#include "LED.h"

void main(void)
{
	Time0It_Init();			//定时器10ms
	
	while(1)
	{
		
	}
}

在这里插入图片描述

实验④

实验:
①使用T0中断,定时器定时1s,让数码管从60每隔1s减1,减到0后重新变为60,并且每次数值变化,LED就翻转一次

②使用T0中断每隔1ms对数码管进行刷新显示
①Time.c文件的代码如下:

#include "Time.h"

/**
 * 定时器T0的初始化,且开启中断
 */
void Time0It_Init(void)
{
	/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
	TMOD &= 0xF0;			//将低4位置0
	TMOD |= 0x01;			//最低位置1
	
	/* 设置定时计数器的初始值,定时1ms */
	TH0 = ((65536 - 1000) >> 8);
	TL0 = (65536 - 1000) & 0x00FF;
	
	/* 启动T0 */
	TF0 = 0;				//清除标志位
	TR0 = 1;
	
	/* 开启中断 */
	ET0 = 1;				//使能定时器溢出中断
	EA = 1;					//开启总开关
}

/********中断服务函数*******/
/**
 * T0的中断服务函数(刷新数码管显示,执行每隔1s数值+1,且LED的翻转)
 */
static unsigned short Number = 0;
static unsigned char Counter1 = 0;
void Time0_Rountine(void) interrupt 1
{
	//这里不用清除标志位,硬件会自动清除
	TH0 = ((65536 - 1000) >> 8);
	TL0 = (65536 - 1000) & 0x00FF;
	
	/* 计数:累加时间,等隔1s后在执行 */
	Number++;				//每隔1ms加1
	if(Number == 1000)		//1s了
	{
		Number = 0;
		Counter1++;
		if(Counter1 > 60)
		{
			Counter1 = 0;
		}
		LED_Turn(1);		//LED翻转
	}
	/* 每隔1ms就刷新显示数码管 */
	Show_Num(Counter1);		//注意将此函数的延时函数取消
}

②Time.h文件的代码如下:

#ifndef __Time_H
#define __Time_H
#include <reg51.h>		//包含51头文件,里面全是寄存器地址>
#include "LED.h"
#include "User_Seg.h"

void Time0It_Init(void);

#endif

③main.c文件的代码如下:

#include "Time.h"

void main(void)
{
	Time0It_Init();	//启动定时器0
	while(1)
	{

	}
}

在这里插入图片描述

综上:使用上面的定时器中断精髓在于,使用Number++;//每隔1ms加1,if(Number == 1000)//1s了,再去执行if里面的函数,这样相当于无形的构造了一个1s的定时器中断函数

5、定时器计数功能

定时器的计数功能就是记录外部脉冲信号的个数,此时定时器不在使用内部晶振时钟源。则寄存器TMOD中的C/T位应该置1,如下图所示:
在这里插入图片描述

实验⑤

实验:
①使用定时器T1对外部脉冲信号进行计数,并通过数码管显示出来
②并测量出脉冲信号的频率,并通过数码管显示出来
③使用外部中断0,按键切换数码管的显示界面(显示测量的脉冲个数/脉冲频率)

在这里插入图片描述
在这里插入图片描述

①Time.c文件的代码如下:

#include "Time.h"

/**
 * 定时器T0的初始化,且开启中断
 */
void Time0It_Init(void)
{
	/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
	TMOD &= 0xF0;			//将低4位置0
	TMOD |= 0x01;			//最低位置1
	
	/* 设置定时计数器的初始值,定时1ms */
	TH0 = ((65536 - 1000) >> 8);
	TL0 = (65536 - 1000) & 0x00FF;
	
	/* 启动T0 */
	TF0 = 0;				//清除标志位
	TR0 = 1;
	
	/* 开启中断 */
	ET0 = 1;				//使能定时器溢出中断
	EA = 1;					//开启总开关
}

/**
 * 定时器T1的初始化,用作计数器测量外部脉冲个数
 */
void Time1_Init(void)
{
	/* 设置T1的计数器为16,且时钟来源为外部 TMOD = 0101 xxxx */
	TMOD &= 0x0F;			//将高4位置0
	TMOD |= 0x50;			//高4位为0101
	
	/* 设置定时计数器的初始值为0 */
	TH1 = 0;
	TL1 = 0;
	
	/* 启动T1*/
	TF1 = 0;				//清除标志位
	TR1 = 1;
}

/********中断服务函数*******/
/**
 * T0的中断服务函数(刷新显示数码管)
 */
static unsigned short Number = 0;
static unsigned short Freq = 0;
static unsigned int Data = 0;
extern unsigned char Flag;
void Time0_Rountine(void) interrupt 1
{
	//这里不用清除标志位,硬件会自动清除
	TH0 = ((65536 - 1000) >> 8);
	TL0 = (65536 - 1000) & 0x00FF;
	
	/* 计数:累加时间,等隔1s后在执行 */
	Number++;				//每隔1ms加1
	if(Number == 1000)		//1s了
	{
		/* 测频率法:得出脉冲频率 */
		Data = (TH1 << 8) + TL1;
		Freq = Data - Freq; 			//脉冲频率
	}
	/* 每隔1ms就刷新显示数码管 */
	switch(Flag % 2)					//外部中断改变Flag的状态值
	{
		case 0:
			LED_On(1);					//LED1点亮
			Show_Num((TH1 << 8) + TL1);	//显示脉冲信号个数
			break;
		case 1:
			LED_OFF(1);					//LED1熄灭
			Show_Num(Freq);				//显示脉冲信号频率
			break;
		default:break;
	}
}

②Time.h文件的代码如下:

#ifndef __Time_H
#define __Time_H
#include <reg51.h>		//包含51头文件,里面全是寄存器地址>
#include "User_Seg.h"
#include "LED.h"

void Time0It_Init(void);
void Time1_Init(void);

#endif

③EXTI.c文件的代码如下:

#include "EXTI.h"

/**
 * 外部中断INT0的初始化(下降沿触发)
 */
void INT0_Init(void)
{
	IT0 = 1; 	//下降沿触发标志位
	EX0 = 1;	//IN0中断使能
	EA = 1;		//中断总开关	
}

/**
 * 外部中断INT1的初始化(下降沿触发)
 */
void INT1_Init(void)
{
	IT1 = 1; 	//下降沿触发标志位
	EX1 = 1;	//IN1中断使能
	EA = 1;		//中断总开关
}

/*************中断服务函数******************/
/**
 * 外部中断0的服务函数
 */
unsigned char Flag = 0;
void Int0_Routine(void) interrupt 0
{
	Flag++;		//改变Flag的值,用于切换数码管的显示状态
	if(Flag == 2)
	{
		Flag = 0;//防止Flag数值溢出
	}
}

/**
 * 外部中断1的服务函数
 */
void Int1_Routine(void) interrupt 2
{
	LED_OFF(1);	//熄灭LED
}

④main.c文件的代码如下:

#include "Time.h"
#include "EXTI.h"

void main(void)
{
	Time1_Init();		//计数器1初始化
	Time0It_Init();		//定时器0初始化
	INT0_Init();		//外部中断0初始化
	
	while(1)
	{
		
	}
}

在这里插入图片描述
综上:代码代码得出频率是使用的测频法,即在1s内测量出脉冲的个数即为频率,此方法适用于高频信号。若信号频率较低,则使用测周法,即测量周期的时间,继而转换为频率。

实验⑥

使用测周法测量信号发生器产生的频率,并通过数码管显示出来

信号发生器连接到外部中断引脚,当信号由高电平变为低电平时,触发外部中断,在外部中断服务函数里面获取定时器T1的计数器的值,通过T1计数器的值得出周期。

①Time.c文件的代码如下:

#include "Time.h"

/**
 * 定时器T0的初始化,且开启中断
 */
void Time0It_Init(void)
{
	/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
	TMOD &= 0xF0;			//将低4位置0
	TMOD |= 0x01;			//最低位置1
	
	/* 设置定时计数器的初始值,定时1ms */
	TH0 = ((65536 - 1000) >> 8);
	TL0 = (65536 - 1000) & 0x00FF;
	
	/* 启动T0 */
	TF0 = 0;				//清除标志位
	TR0 = 1;
	
	/* 开启中断 */
	ET0 = 1;				//使能定时器溢出中断
	EA = 1;					//开启总开关
}

/**
 * 定时器T1的初始化,用作计数器测量外部脉冲个数
 */
void Time1_Init(void)
{
	/* 设置T1的计数器为16,且时钟来源为晶振 TMOD = 0001 xxxx */
	TMOD &= 0x0F;			//将高4位置0
	TMOD |= 0x10;			//高4位为0001
	
	/* 设置定时计数器的初始值为0 */
	TH1 = 0;
	TL1 = 0;
	
	/* 启动T1 */
	TF1 = 0;				//清除标志位
	TR1 = 1;
}

/********中断服务函数*******/
/**
 * T0的中断服务函数
 */
extern unsigned int Freq;	//频率
void Time0_Rountine(void) interrupt 1
{
	//这里不用清除标志位,硬件会自动清除
	TH0 = ((65536 - 1000) >> 8);
	TL0 = (65536 - 1000) & 0x00FF;
	
	/* 数码管显示频率值 */
	Show_Num(Freq);
}

②Time.h文件的代码如下:

#ifndef __Time_H
#define __Time_H
#include <reg51.h>		//包含51头文件,里面全是寄存器地址>
#include "User_Seg.h"

void Time0It_Init(void);
void Time1_Init(void);

#endif

③EXTI.c文件的代码如下:

#include "EXTI.h"

/**
 * 外部中断INT0的初始化(下降沿触发)
 */
void INT0_Init(void)
{
	IT0 = 1; 	//下降沿触发标志位
	EX0 = 1;	//IN0中断使能
	EA = 1;		//中断总开关	
}

/*************中断服务函数******************/
/**
 * 外部中断0的服务函数
 */
unsigned int Freq = 0;
extern unsigned int timeVlau;
void Int0_Routine(void) interrupt 0
{
	TR1 = 0;//关闭定时器T1
	
	/* 获取定时器的计数器的值 */
	Freq = 1000000 / (((TH1 << 8) + TL1) + timeVlau * 65535);

	/* 将定时器中的计数器清0 */
	TH1 = 0;
	TL1 = 0;
	timeVlau = 0;
	
	TR1 = 1;//开启定时器T1
}

④main.c文件的代码如下:

#include "Time.h"
#include "EXTI.h"

unsigned int timeVlau = 0;
void main(void)
{
	Time1_Init();	
	Time0It_Init();
	INT0_Init();
	while(1)
	{
		if(TF1 == 1)
		{
			TF1 = 0;
			timeVlau++;
		}
	}
}

在这里插入图片描述


http://www.kler.cn/a/540866.html

相关文章:

  • JavaScript:||=的写法作用
  • git如何把多个commit合成一个
  • 【文档智能多模态】英伟达ECLAIR-端到端的文档布局提取,并集成阅读顺序方法
  • DDR原理详解
  • 2.10日学习总结
  • [RabbitMQ] RabbitMQ常见面试题
  • ArcGIS中的空值问题
  • Java中的设计模式应用与最佳实践
  • 本地部署模型全攻略阶段二_3---Kiln AI
  • 51单片机独立按键的基本操作
  • 区块链可投会议CCF B--ICWS 2025 截止3.3 附2024录用率
  • win11+mac键盘+PowerToys 重映射热键
  • 随手记:小程序手机号一键登录
  • R语言的软件开发工具
  • 如何使用C++将处理后的信号保存为PNG和TIFF格式
  • 基于微信小程序的博物馆预约系统的设计与实现
  • Web UI自动化测试中的显示等待、隐式等待有什么区别?
  • 电子时钟html
  • 如何转型为量化分析师 - FinTech
  • FFmpeg 命令规则与参数详解:输入/输出 YUV 文件的要求与分析
  • 哨兵模式与 Redis Cluster:高可用 Redis 的深度剖析
  • 2024 Navicat Premium最新版简体中文版破解激活永久图文详细教程(亲测可用)
  • PHP盲盒商城系统源码 晒图+免签+短信验证+在线回收 thinkphp框架
  • 深入理解指针初阶:从概念到实践
  • centos 和 ubuntu 区别
  • 半边数据结构(Half-Edge Data Structures)详细介绍