最全【51单片机】配置定时器_51单片机定时器配置(1),2024年最新2024年抓住金三银四涨薪好时机

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!



上述代码总的意思实际上就是→执行main()函数当中的定时器初始化Timer0\_Init(); 定时器初始化好之后。原本这个程序是在while(1);执行的时候就会跳转到 void Timer0\_Routine(void) interrupt 1 这个中断子函数当中去执行完这个子函数完的时候就会回到while(1);依次往复这个过程。


注⇢在中断任务当中需要重新赋值定时器初始值,如果你赋值它为2ms的话,那么也只是第一次为1ms,等到第二次的时候到中断任务就会编程2ms。所以我们通常会把中断任务的赋初始值和定时器任务赋初始值都是一样的。


但是如果我们在while(1);死循环+个让一脚点灯,它既可以执行while(1)循环里面的点灯的程序又可以执行中断任务,这个就是为什么使用定时器是可以在一定情况下同时完成**⒉**项任务


实验很简单我们可以点个灯试一下或者调试看下是不是如上述所说。


#### 🍋流程图


![](https://img-blog.csdnimg.cn/94f221fc39114e1a9b5cea24322aea05.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5rO95aWA,size_20,color_FFFFFF,t_70,g_se,x_16)​**主程序(main)函数 --> 其它子函数 --> 中断来了定时**


**器溢出,中断请求 --> 跳转到中断的一个函数里面执行中断里面的任务 --> 中断执行完之后再返回原来断点的地方就是原来从哪里停的现在从哪里回来 --> 然后再继续执行主函数。**


从上面的理解当中我们可以看出,这个程序当中在一定的程度上,可以同时完成两项任务。比如说:执行主程序其它子程序也在执行。就像是LED的流水灯上,既可以执行流水灯的扫描,其次按键也可以进行控制流水灯。好处是:大大的提高了运行效率,有兴趣可以了解一下中断的概念实际上是非常多的。


#### 🍋定时器点灯⇢间隔1s


程序代码如下所示↓




#include <REGX52.H>
void Timer0_Init(void)
{
TMOD = 0x01;//定时器工作模式
TF0 = 0; //允许计数
TR0 = 1; //开始计数
TH0 = 64536/256;//设置定时初值,高8位
TL0 = 64536%256;//设置定时初值,低8位
ET0=1;
EA=1;
PT0=0;
}
int main(void)
{
Timer0_Init();
while(1)
{

}
}
//全局变量默认初始化为0
unsigned int g_Count;
void Timer0_Rountine(void) interrupt 1
{
TL0 = 64536%256;
TH0 = 64536/256;//重新赋初值才能保证下一次还是1ms

  g_Count++;
  if(g_Count>=1000)
  {
  	g_Count = 0;
  	P2_0 = ~P2_0;//上电默认为高电平
  }

}



拓展:注如果g\_Count不是用全局变量的话,而是局部变量的话,那么那个局部变量需要被static 进行修饰,其作用:延长生命周期,出了局部变量原来修饰变量的值不会被销毁,再次回到函数当中被修饰的变量依旧会保持原来的值的大小。⇢ **非常关键!!!**


注:我们这里的 g\_Count++ 是每次+1ms。所以当它不满足if()语句的判断条件的时候是还需要返回到while(1);语句当中去的。再次进入到中断执行中断任务。


#### 🍋TMOD问题


当然上述代码当中还是存在着缺陷的,那就是TMOD,在上述当中我们TMOD是不可谓寻址就是不能单个赋值初始值必须⑧位才行。在这里我们使用一个定时器是当然没有任何问题的,因为我们的定时器1更本就没有开始工作不会出现问题。


出问题是在于:同时使用⒉个定时器的时候,定时器1和定时器0的话。**高位会被刷新!**


**当然,**实际上TOMD = 0x11;也是可以使用两个定时器的。但是它的操作上有很大的区别↓


TMOD &= 0x0F;            //设置定时器模式1


TMOD |= 0x10;            //设置定时器模式1


TMOD = TMOD&0xF0;  
 TMOD = TMOD|0x01;


这个👆是可以进行统一的处理,而也建议大家这样养成一个好的习惯。


TMOD = 0x11;


这种👆是单独进行处理。


所以,在使用TMOD的时候推荐用这种代码方式↓



TMOD = TMOD&0xF0;
TMOD = TMOD|0x01;
//同
TMOD&=0xF0;
TMOD|=0x01;

注:TMOD默认为低电平。

TMOD低四位清0,高四位保持不变。                    0000 0000 & 1111 0000 = 0000 0000

任何数1&1还是等于1,任何数&0还是等于0的。

TMOD最低为置1,高四位保持不变。                    0000 0000 | 0000 0001  = 0000 0001

任何数或上一个0,就是等于自身的那个数。

那么这个与或赋值法就可以操作其中的几个位而不会影响其它的位。

🍈STC-ISP-自动配置

在STC-ISP软件上可以直接配置定时器计算器的,如下图所示↓

AUTR:可以删除因为已经是12T模式了。当然这里还是少了中断的配置我们需要加上的。

Ⅰ:ET0 = 1

Ⅱ:EA = 1

Ⅲ:PT0 = 0 『默认:即为zero』

上述的TL1和TH1的配置都是可以的。

🍑作业⇢时钟

  • 题目内容→定时器时钟。
  • 功能如下↓

⒈显示时钟的时间,显示在LCD1602的屏幕上。

⒉分别定义全局变量 Hour小时、Min分钟、Sec秒的时间显示在LCD1602的屏幕上。

⒊分别用独立按键可以实现修改Hour小时Min分钟Sec秒,每按一次时钟或分钟或秒+1,也要实现-1的功能。还有按键可以提供定时器的计数功能的打开或者开启,也就是实现时钟开关的功能模式。

⒋当然也是可以在你的能力范围之内在增加一些你觉得有必要的功能哟(@^0^)

时钟作业示例代码如下↓

main.c

#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "Timer0.h"
#include "MatrixKey.h"
/**
  * @brief  独立按键控制数码管以及定时器的功能
  * @param  无
  * @retval 无
  */
unsigned char Sec=0,Min=0,Hour=12;
void Contorl()
{
	e_KeyNum = MatrixKey();
	if(e_KeyNum!=0)
	{
		switch(e_KeyNum)
		{
			case 1:Hour++;break;
			case 2:Hour--;break;//小时
			case 3:Min++; break;
			case 4:Min--; break;//分钟
			case 5:Sec++; break;
			case 6:Sec--; break;//秒
			//控制TR0的启动(7)暂停(8)
			case 7:Timer0_Init();Delay(1);break;
			case 8:TR0=0;Delay(1);break;
		}
			if(e_KeyNum>=1 && e_KeyNum<=6)
				LCD_ShowString(1,14,"Yes");
			else if(e_KeyNum==7 || e_KeyNum==8)
				LCD_ShowString(1,14,"Sus");
			else
				LCD_ShowString(1,14,"Not");
	}
}

int main(void)
{
	LCD_Init();
	LCD_ShowString(1,14,"xxx");
	LCD_ShowString(1,1,"Clock:");	//上电显示静态字符串
	LCD_ShowString(2,1,"  :  :");
	
	while(1)
	{
		Contorl();
		LCD_ShowNum(2,1,Hour,2);	//显示时分秒
		LCD_ShowNum(2,4,Min,2);
		LCD_ShowNum(2,7,Sec,2);
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;
	if(T0Count>=1000)	//定时器分频,1s
	{
		T0Count=0;
		Sec++;			//1秒到,Sec自增
		if(Sec>=60)
		{
			Sec=0;		//60秒到,Sec清0,Min自增
			Min++;
			if(Min>=60)
			{
				Min=0;	//60分钟到,Min清0,Hour自增
				Hour++;
				if(Hour>=24)
				{
					Hour=0;	//24小时到,Hour清0
				}
			}
		}
	}
}

Timer0.c

#include <REGX52.H>

/**
  * @brief  定时器0初始化,1毫秒@12.000MHz
  * @param  无
  * @retval 无
  */
void Timer0_Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0=1;
	EA=1;
	PT0=0;
}

Timer0.h

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0_Init(void);

#endif

LCD1602.c

#include <REGX52.H>

//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0

//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
	unsigned char i, j;

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

/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}

/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line,Column);
	if(Number>=0)
	{
		LCD_WriteData('+');
		Number1=Number;
	}
	else
	{
		LCD_WriteData('-');
		Number1=-Number;
	}
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
	}
}

LCD1602.h

#ifndef __LCD1602_H__
#define __LCD1602_H__

//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);

#endif

Delay.c

void Delay(unsigned int xms)
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

wBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);

#endif



**Delay.c**




void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms–)
{
i = 2;
j = 239;
do
{
while (–j);

[外链图片转存中…(img-pmCpmUUE-1715820253802)]
[外链图片转存中…(img-hNvdxYQr-1715820253802)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值