STC89C52单片机学习——第17节: [7-1]定时器

发布于:2025-03-16 ⋅ 阅读:(18) ⋅ 点赞:(0)

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做!

本文写于:2025.03.14

前言

   本次笔记是用来记录我的学习过程,同时把我需要的困难和思考记下来,有助于我的学习,同时也作为一种习惯,可以督促我学习,是一个激励自己的过程,让我们开始51单片机的学习之路。
   欢迎大家给我提意见,能给我的嵌入式之旅提供方向和路线,现在作为小白,我就先学习51单片机了,就跟着B站上的江协科技开始学习了.
   在这里会记录下江协科技51单片机开发板的配套视频教程所作的实验和学习笔记内容,因为我之前有一个开发板,我大概率会用我的板子模仿着来做.让我们一起加油!
   另外为了增强我的学习效果:每次笔记把我不知道或者问题在后面提出来,再下一篇开头作为解答!

开发板说明

   本人采用的是慧净的开发板,因为这个板子是我N年前就买的板子,索性就拿来用了。不再另外购买视频中的普中开发板了。
   原理图如下
在这里插入图片描述
视频中的都用这个开发板来实现,如果有资源就利用起来。
仔细看了看:开发板的晶振为:11.0592Mhz;12Mhz晶振是用来给CH340G芯片外置晶振;

下图是实物图
在这里插入图片描述

引用

51单片机入门教程-2020版 程序全程纯手打 从零开始入门
还参考了下图中的书籍:
手把手教你学51单片机(C语言版)
在这里插入图片描述

解答和科普

1、逻辑电路和逻辑运算
&&逻辑与;||逻辑或;!逻辑非
&按位与,|按位或,~按位取反,^按位异或
在这里插入图片描述

一、定时器的简介

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
定时器/计数器0有4种工作模式:模式0(13位定时器/计数器) ,模式1(16位定时器/计数器模式),模式2(8位自动重装模式),模式3(两个8位定时器/计数器)。定时器/计数器1除模式3外,其他工作模式与定时器/计数器0相同,T1在模式3时无效,停止计数。
在这里插入图片描述
①定时器/计数器控制寄存器TCON
TCON为定时器/计数器T0、T1的控制寄存器,同时也锁存T0、T1溢出中断源和外部请求中断源。
在这里插入图片描述
TF1:定时器/计数器溢出标志,T1被允许计数以后,从初值开始加1计数。当最高位产生溢出时,由硬件置"1"TF1,向CPU请求中断,一直保持到CPU响应中断时,才由硬件清零。(TF1也可由程序查询清0)。
==TR1:定时器T1的运行控制位。该位由软件置位和清零。==当GATE(TMOD.7)=0,TR1=1时就允许T1开始计数,TR1=0时禁止T1计数。当GATE(TMOD.7)=1,TR1=1,且INT1输入高电平时,才允许T1计数。
IE1:外部中断1请求源(INT1/P3.3)标志IE1=1,外部中断向CPU请求中断,当CPU响应该中断时,由硬件清0.

IT1:外部中断触发方式控制位。 IT1=0时,外部中断1为低电平触发方式,当(-INT1/P3.3)输入低电平时,置为IE1,采用低电平触发方式,必须保持低电平有效,直到该中断被CPU响应,同时在该中断服务程序执行完之前,外部中断源必须被清除(P3.3要变高),否则将产生另一次中断。IT1=1时,则外部中断1为下降沿触发。
T0也是同理。
②定时器/计时器工作模式寄存器TMOD
定时和计数功能由特殊功能寄存器TMOD的控制位C/-T进行选择,2个定时/计时器有4种操作模式,通过TMOD的M1和M0选择。2个定时/计数器的模式0、1和2都相同,模式3不同。
在这里插入图片描述
在这里插入图片描述
下面接着介绍工作模式:
在这里插入图片描述
在这里插入图片描述
时钟周期: 时钟周期T 是时序中最小的时间单位,具体计算的方法就是 1/ 时钟源频率,单片机开发板上用的晶振是 11.0592M ,那么对于我们这个单片机系统来说,时钟周期 =1/11059200 秒。
机器周期: 我们的单片机完成一个操作的最短时间。机器周期主要针对汇编语言而言,单片机系列,在其标准架构下一个机器周期是 12 个时钟周期,也就是 12/11 059200 秒。

SYSclk表示时钟频率,因为 1 个机器周期等于 12 个时钟周期,所以那个 d 就等于 12 。或者1个周期等于6个时钟周期,d就等于6,这个可以在烧录的时候选择。

==要想让定时器工作,就是自动加 1 ,从图 上看有两种方式,第一种方式是那个开关打就是 C/T = 0 的时候,一个机器周期 TL 就会加 1 一次,此时为定时器。==当开关打到下边的箭头,即 C/T =1 的时候, T0 引脚即 P3.4 引脚来一个脉冲, TL 就加 1 一次,这也就是计数器。

下边 GATE 右边的那个门是一个非门电路,再右侧是一个或门,再往右是一个与门电路,图上可以看出来,下边部分电路是控制了上边部分,那我们先来看下边是如何控制的,
我们以定时器 0 为例。
1、 TR0 和下边或门电路的结果要进行与运算, TR0 如果是 0 的话, 与运算完了肯定是 0所以如果要让定时器工作,那么 TR0 就必须置 1 。
2、这里的与门结果要想得到 1 ,那么前面的或门出来的结果必须也得是 1 才行。在 GATE位为 1 的情况下,经过一个非门变成 0 ,或门电路结果要想是 1 的话,那 INT0 即 P3.2 引脚必须是 1 的情况下,这个时候定时器才会工作, 而 INT0 引脚是 0 的情况下,定时器不工作,这就是 GATE 位的作用。
当 GATE 位为 0 的时候,经过一个非门会变成 1 ,那么不管 INT0 引脚是什么电平,经过或门电路后都肯定是 1 ,定时器就会工作。
在这里插入图片描述

在这里插入图片描述
模式1 ,是 THn 和 TLn 组成了一个 16 位的定时器,计数范围是 0 65535 ,溢出后,只要不对 THn 和 TLn 重新赋值,则从 0 开始计数。
模式 2 ,是 8 位自动重装载模式,只有 TLn做加 1 计数,计数范围 0 255 THn 的值并不发生变化,而是保持原值, TLn 溢出后, TFn就直接置 1 了,并且 THn 原先的值直接赋给 TLn ,然后 TLn 从新赋值的这个数字开始计数。这个功能可以用来产生串口的通信波特率,我们讲串口的时候要用到。
在这里插入图片描述
写程序之前,我们要先来学会计算如何用定时器定时时间。我们的晶振是11.0592M ,时钟周期就是 1/11059200 ,机器周期是 12/11059200 ,假如要定时 20ms ,就是 0.02 秒,要经过x 个机器周期得到 0.02 秒,我们来算一下 x*12/11059200=0.02 ,得到 x= 18432 。 16 位定时器的溢出值是 65536 (因 65535 再加 1 才是溢出),于是我们就可以这样操作,先给 TH0 和 T L0一个初始值,让它们经过18432 个机器周期后刚好达到 65536 ,也就是溢出,溢出后可以通过检测 TF0 的值得知,就刚好是 0.02 秒。那么初值 y = 65536 18432 = 47104 ,转成 16 进制就是 0xB800 ,也就是 TH0 = 0xB8 TL0 = 0x00。
这样
0.02 秒的定时我们就做出来了,细心的同学会发现,如果初值直接给一个 0x0000一直到 65536 溢出,定时器定时值最大也就是 71ms 左右,那么我们想定时更长时间怎么办呢?用你小学学过的逻辑,倍数关系就可以解决此问题。
在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述
单片机通过配置寄存器来控制内部线路的连接,主要是配置寄存器,来完成整个定时器的过程。在这里插入图片描述

实验现象:

二、用数码管显示15秒

1.代码
1.1main.c

#include <REGX52.H>
#include <INTRINS.H>


sbit  	wei= P2^1;
sbit	duan=P2^0;
sbit	dianzheng=P2^2;

unsigned char NixieDuanTable[]= {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00};
unsigned char NixieWeiTable[]={0xFF,0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F};

void Timer0_Init()
{
	TMOD &= 0xF0; //0000 0001 T0设置为模式1
	TL0 = 0x00;		//设置定时初值
	TH0 = 0xB8;		//设置定时初值
	
	TF0=0;
	TR0=1;
	
	
}

 void Nixie(unsigned char WeiNum, DuanNum)
{

	wei=1;
	P0=NixieWeiTable[WeiNum];
	wei=0;

	duan=1;
	P0=NixieDuanTable[DuanNum];
	duan=0;

}


void Delayxms(unsigned int xms)		//@11.0592MHz
{
	
	while(xms--)
	{
	unsigned char i, j;

	_nop_();
	i = 2;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
	}
}
	

void main()
{
	unsigned char count=0;
	unsigned char sec=0;
	Timer0_Init();
	Nixie(1,sec);

	while(1)
	{
		if(TF0==1)
		{
			TF0=0;
			TL0 = 0x00;		//设置定时初值
			TH0 = 0xB8;		//设置定时初值
			count++;
		}
		if(count>=50)
		{
			count=0;
			Nixie(1,sec);
			sec++;
		}
		if(sec>=16)
		{
		sec=0;
		}
	}

}




实验现象:

数码管1s定时器完成

2.代码

#include <REGX52.H>
#include <INTRINS.H>


sbit  	wei= P2^1;
sbit	duan=P2^0;
sbit	dianzheng=P2^2;

unsigned char NixieDuanTable[]= {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00};
unsigned char NixieWeiTable[]={0xFF,0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F};

void Timer0_Init()
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	
}

 void Nixie(unsigned char WeiNum, DuanNum)
{

	wei=1;
	P0=NixieWeiTable[WeiNum];
	wei=0;

	duan=1;
	P0=NixieDuanTable[DuanNum];
	duan=0;

}


void Delayxms(unsigned int xms)		//@11.0592MHz
{
	
	while(xms--)
	{
	unsigned char i, j;

	_nop_();
	i = 2;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
	}
}
	

void main()
{
	unsigned char count=0;
	unsigned char sec=0;
	Timer0_Init();
	Nixie(1,sec);

	while(1)
	{
	Nixie(1,sec);
		if(TF0==1)
		{
			TF0=0;
			TL0 = 0x00;		//设置定时初值
			TH0 = 0xB8;		//设置定时初值
			count++;
		}
		if(count>=50)
		{
			count=0;
			
			sec++;
		}
		if(sec>=16)
		{
		sec=0;
			Nixie(1,sec);
		}
	}

}

实验现象:

数码管定时1s加一正确

二、用数码管显示60秒(当然可以60秒倒计时)

#include <REGX52.H>
#include "Delay.h"
#include "Nixie.h"
#include "Init.h"


void Timer0_Init()
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	
}





void main()
{
	unsigned char count=0;
	unsigned char sec=0,SWEI=0;
	 DianZhengGuan();
	
	Timer0_Init();
	Nixie(1,sec);

	while(1)
	{ 
		Nixie(1,SWEI);
		Nixie(2,sec);
		
		if(TF0==1)
		{
			TF0=0;
			TL0 = 0x00;		//设置定时初值
			TH0 = 0xB8;		//设置定时初值
			count++;
		}
		if(count>=50)
		{
			count=0;
			
//			Nixie(2,SWEI);
//			Nixie(1,sec);
			sec++;
		}
		if(sec>=10)
		{
		sec=0;
			SWEI++;
//			Nixie(1,sec);
//			Nixie(2,SWEI);
	
		}
		
		if(SWEI>=6)
		{
		SWEI=0;
		sec=0;
			
//			Nixie(1,sec);
//			Nixie(2,SWEI);
			

		}
	}
		
		
		
	

}


我在写代码是犯了一个很大的错误,那就是可以看到我想让他们在这里直接显示,好像和LCD不一样的地方,也就是为什么说数码管是需要不断的扫描,只能放在while下面(应该吧),反正就是得不断的扫描,我刚开始设置的是只有一个数码管亮,我把数码管放在while里,就完成了动态扫描。
实验现象:

数码管显示60s

问题

注意是不断的扫描呀!;
1、不知道为什么我的时间好像不是1s很奇怪呀。
在这里插入图片描述
有关系,我改成TMOD=0x01,就是不行的。

2、我的数码管貌似没有做到真真的消影呀,我看着还是有其他数码管显示(虽然不是很亮)。

总结

本节课主要学了了定时器的有关知识,主要是如何配置定时器寄存器,还浅显的学习了中断系统。还有可寻址的寄存器和不可寻址的寄存器。code(Flash)。