【51单片机】配置定时器_51单片机定时器配置

为了方便观看把图放在这里

那么定时器的配置已经配置好了,接下来就是中断的配置了。

在这里我们只需要配置T0的那一条路线即可。这个配置就比上述容易许多了。

Ⅰ:ET0 = 1

Ⅱ:EA = 1

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

🍊void Timer0_Init(void)

那么上述的定时器初始化配置如下↓

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

那么以上void Timer0_Init() ⇥对定时器初始化的函数就已经好了。我们只需要在源文件.c的 main() 函数当中进行初始化,上电的时候它实际上已经是开始工作的了。哎,那现在有一个疑问:那么这样才能让它进行中断呢。

注:上述实际上是65536才到溢出,当然65535仅仅也知识多出了1微秒。

🍈STC89C52中断资源

interrupt0 表示的是外部中断。注:P3.2引脚可以使用。

interrupt1 表示(定时器中断0) 在这里我们使用的是定时器0的中断。

interrupt2 表示(外部中断1) 注:P3.3引脚可以使用。

interrupt3 表示(定时器中断1)

interrupt4 表示(串口中断)

我们此时还需要在main()函数后面再写一个子函数,这个子函数的作用就是代表"闹钟"响了之后跳转到这个子函数当中去执行最终的中断任务,⏰指的就是void Timer0_Init(void)

那么我们这个中断号的配置也就是上面用红色的框圈出来的那个了,注:interrupt是关键字。


#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)
	{
		
	}
}
void Timer0_Routine(void) interrupt 1
{
	
}

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

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

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

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

🍋流程图

主程序(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);
		} while (--i);
	}
}

Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif

MatrixKey.c

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

/**
  * @brief  矩阵键盘读取按键键码
  * @param  无
  * @retval KeyNumber 按下按键的键码值
			如果按键按下不放,程序会停留在此函数,松手的一瞬间,返回按键键码,没有按键按下时,返回0
  */
unsigned char MatrixKey()
{
	unsigned char KeyNumber=0;
	
	P1=0xFF;
	P1_3=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=1;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=5;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=9;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=13;}
	
	P1=0xFF;
	P1_2=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=2;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=6;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=10;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=14;}
	
	P1=0xFF;
	P1_1=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=3;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=7;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=11;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=15;}
	
	P1=0xFF;
	P1_0=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=4;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=8;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=12;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=16;}
	
	return KeyNumber;
}

MatrixKey.h

#ifndef __MATRIXKEY_H__
#define __MATRIXKEY_H__

unsigned char MatrixKey();
unsigned char e_KeyNum;
#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值