STC89C52---定时器,中断

目录

一:定时器

1:简历

2:定时器/计数器0/1和相关寄存器

A:介绍

B: 相关寄存器

C:基本原理

3:定时器/计数器控制寄存器TCON

4:定时器/计数器工作模式寄存器TMOD

 (1)门控制

(2):使用STC-iSP软件生成配置

(3):使用STC-iSP软件定时器

(4)时间的计算

二:中断

1:简历

B:中断寄存器

2:中断流程

 3:中断系统

4:中断函数

5:配置中断流程 

6:中断源

 三:配置定时器和中断

B:2,10,16进制转化

四:案例

1:定时器点亮LED灯

2:定时器时钟


一:定时器

1:简历

51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成 定时器

作用: (1)用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作

            (2)替代长时间的Delay,提高CPU的运行效率和处理速度 

                (…)

定时器个数:3个(T0、T1、T2),T0和T1与传统的51单片机兼容,T2是此型号单片机增加的资源 注意:定时器的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式,但一般来说,T0和T1的操作方式是所有51单片机所共有的

2:定时器/计数器0/1和相关寄存器

A:介绍

STC89C52系列单片机的定时器0和定时器1,与传统8051的定时器完全兼容,当在定时器1做波特率发生器时,定时器0可以当两个8位定时器用。


STC89C52系列单片机内部设置的两个16位定时器/计数器TO和T1都具有计数方式和定时方式两种工作方式。对每个定时器/计数器(TO和T1),在特殊功能寄存器TMOD中都有一控制位-C/T来选择TO或T1为定时器还是计数器。定时器/计数器的核心部件是一个加法(也有减法)的计数器,其本质是对脉冲进行计数。只是计数脉冲来源不同: 如果计数脉冲来自系统时钟,则为定时方式,此时定时器/计数器每12个时钟或者每6个时钟得到一个计数脉冲,计数值加1:如果计数脉冲来自单片机外部引脚(TO为P3.4,T1为P3.5),则为计数方式,每来一个脉冲加1.

当定时器/计数器工作在定时模式时,可在烧录用户程序时在STC-ISP编程器中设置(如下图所示)是系统时钟一12还是系统时钟一6后让TO和T1进行计数。当定时器/计数器工作在计数模式时,对外部脉冲计数不分频。

定时器/计数器:0有4种工作模式: 模式0(13位定时器/计数器),模式1(16位定时器/计数器模式这个模式是最常使用的模式), 模式2(8位自动重装模式),模式3(两个8位定时器/计数器)。定时器/计数器1除模式3外,其他工作模式与定时器/计数器0相同,T1在模式3时无效,停止计数。

B: 相关寄存器

C:基本原理

3:定时器/计数器控制寄存器TCON

TCON: 定时器/计数器中断控制寄存器(可位寻址)  可位寻址-----可以单独给每一位赋值

TCON为8位寄存器,控制中断源的开启,可以位寻址。高4位控制定时器0/1,低4位控制外部中断。

①TR0:定时器0控制位。     TR1:定时器1控制位。

②TF0:定时器0溢出位,置1时申请中断    TF1:定时器1溢出位,置1时申请中断

 TCON一般赋值以下的这两个位
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //启动定时器0

4:定时器/计数器工作模式寄存器TMOD


   不可位寻址----只能整体给8位赋值

  • GATE:门控制;GATE=0,仅有运行控制位TRx来控制定时/计数器的开启;GATE=1,由TRx和外部中断脉冲计数。(用于计算外部中断负跳变的次数)
  • C/T:计数器模式和定时器模式选择;C/T=0,选择定时器模式;C/T=1,选择计数器模式

 (1)门控制

A:GATE=0仅有运行控制位TRx来控制定时/计数器的开启;      当启动控制为1时关闭(通路),   0打开(断路)

B:GATE=1    置1时受外部中断引脚和TCON控制器控制

 配置TMOD打开定时器0   (下面这中写法只在开启一个定时器时有用, 开启动多个会覆盖)

//TMOD打开定时器0
TMOD=0x01    //0000 0001    GATE=0  C/T=0,选择定时器模式  01->16位定时器

开多个定时器下面这个写法

 //TMOD是不可位寻址,必须8位同时赋值  TMOD=0000 0001  

	TMOD &= 0xF0;		//设置定时器模式   TMOD=TMOD & 1111 0000   TMOD的低四位清零,高四位保持不变;
	TMOD |= 0x01;		//设置定时器模式   TMOD=TMOD | 0000 0001    TMOD的最低位置1,高四位保持不变;
    

/*
    TMOD的低四位清零,高四位保持不变;
    TMOD=1010 0011
    1010 0011 & 1111 0000(0xF0)= 1010 0000
    */

/*
    TMOD的最低位置1,高四位保持不变;
    TMOD=1010 0000
    1010 0000 | 0000 0001(0x01)= 1010 0001
    */

(2):使用STC-iSP软件生成配置

void Timer0Init(void)		//1毫秒@12.000MHz
{    
    //TMOD配置
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
    //设置定时器(计算)初值(TH0,TL0)因为打开的是0计时器
	TL0 = 0x18;		//设置定时初值    定时器0的高低位
	TH0 = 0xFC;		//设置定时初值  定时器0的高8位
    //TCON配置
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
}

定时器配置大致完成

(3):使用STC-iSP软件定时器

//改造下给传传递延长时间
#include <REGX52.H>
void Delay(unsigned int xms)   //
{
	unsigned char i, j;
	while(xms)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
}

(4)时间的计算

我们使用的是16位定时器, 并且没有自动装载, 所以在写中断函数的时需要手动写装载

 

        在16位全部变为1111 1111 1111 1111  (16进制)也就是65536每隔1us加1 (10进制)启动中断

        如果我们不设置初值它会从0开始加到了65536(10进制)每隔1us加1;   也就是65.536ms启动中断

        一般情况的使用我们会设置初值(TH0,TL0)它从初值开始加,加到65536启动中断(每隔1us加1),我们使用的是16位,并且没有自动装载的,所以需要手动在中断函数把TH0,TL0在写一遍

TH0:定时器0的高8位           TH1:定时器1的高8位

TL0:定时器0的低8位            TL01:定时器1的低8位

我们想每隔1ms让程序中断,进入中断函数,只需要将初值设置为64536既可以

//16进制
TH0=(65536-1000)/256    //定时器0的高8位    
TLO=(65536-1000)%256    //定时器0的低8位

解释

//10进制
125/100=1     
125%10=25

具体的应用在下面的实例代码的1:定时器点亮LED灯

二:中断

1:简历

中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的

当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。     实现这种功能的部件称为中断系统,请示CPU中断的请求源称为中断源。微型机的中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断,要求为它服务的时候,这就存在CPU优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU总是先响应优先级别最高的中断请求。


当CPU正在处理一个中断源请求的时候(执行相应的中断服务程序) ,发生了另外一个优先级比它还高的中断源请求。如果CPU能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服务程序,这样的过程称为中断嵌套。这样的中断系统称为多级中断系统,没有中断嵌套功能的中断系统称为单级中断系统。

B:中断寄存器

2:中断流程

 3:中断系统

 A:打开中断时必须打开全局中断 (EA=1)

B:打开对应的中断允许位

C:中断开的少,可以不用管中断优先级这个

4:中断函数

中断源个数:8个(外部中断0、定时器0中断、外部中断1、定时器1中断、串口中断、定时器2中断、外部中断2、外部中断3) 中断优先级个数:4个

中断号:                    中断函数名可以改但是不建议

注意:中断的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的中断资源,例如中断源个数不同、中断优先级个数不同等等

5:配置中断流程 

大置流程TMOD-->TCON-->计数器初值-->中断配置

6:中断源

 三:配置定时器和中断

void Timer0Init(void)		//1毫秒@12.000MHz
{    
    //定时器配置    (可以使用STC生成)
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
    //中断配置
    ET=1;   //打开中断允许位
	EA=1;  //使能总开关

}

B:2,10,16进制转化

四:案例

1:定时器点亮LED灯

mian.c

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

void main(){
     Init_time();  
    while (1){

    }
}

time.c

#include <REGX52.H>

//一般来说&是清零;|是置1
unsigned int count=0;

sbit LED_D1= P2^0;
void Init_time()		//1微秒@12.000MHz
{   
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
    //中断配置
    ET0=1;   //打开中断允许位
	EA=1;  //使能总开关
    LED_D1=1;
}

void Timer0_Routine() interrupt 1
{   
  
    //每隔1微秒启动中断函数
    count++;
    /*因为它是16位,没有自动装载\
    每次进去中断函数都会从0开始计数.计数到65536在中断
    所以加上 TL0 = 0x18;TH0 = 0xFC;让它从64536计数
    这个就是手动装载
    */
    TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
   
    if (count==1000)
        {   
             count=0;
             LED_D1=~ LED_D1;
        
        }


}
//每隔1微秒启动中断,然后自动打开中断函数

time.h

#ifndef __TIME_H__
#define __TIME_H__
void Init_time();
void Timer0_Routine();
#endif

2:定时器时钟

main.c

#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
#include "Timer.h"
unsigned char Sec,Min,Hour;

void main()
{   
    //初始化lcd和定时器
    LCD_Init();
    Timer0Init();
    LCD_ShowString(1,1,"Clock:");	//上电显示静态字符串
	LCD_ShowString(2,1,"  :  :");
    
    
	while(1)
	{
		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
				}
			}
		}
	}
}

Timer.c

#include <REGX52.H>

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

/*定时器中断函数模板
void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		
	}
}
*/

Timer.h

#idndef __TIMER_H__
#define __TIMER_H__
void Timer0Init(void);
#endif

Delay.c

void Delay(unsigned int xms)		
{
	unsigned char i, j, k;

	while (xms--)
    {
	i = 6;
	j = 157;
	k = 59;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
}

Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__
void Delay(unsigned int xms);

#ednif

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

猜你喜欢

转载自blog.csdn.net/m0_74739916/article/details/131406405