1.定时器介绍
51单片机的定时器属于单片机的内部资源,电路连接和运转均在单片机内部完成。
1.1定时器作用
(1)用于计时系统,可实现软件计时,是程序每隔一段时间完成一项操作。
(2)替代长时间的delay,提高cpu运行速度。
1.2定时器个数
定时器总共有三个(T0,T1,T2),T0和T1与传统的单片机兼容,T2是89C52增加的资源。T0和T1的操作方式是51单片机所共有的。
1.3定时器框图
时钟提供计数单位脉冲,每隔“1s"(自定义设定时间)计数单元的数值加1,当计数单元增加到设定的闹钟提醒时间时,计数单元就会像中断发出中断申请,使程序跳转到中断服务函数执行。
2.定时器工作模式
STC89C52的T0和T1有四种工作模式
模式0:13位定时器/计数器
模式1:16位定时器/计数器(常用)
模式2:8位自动重装模式
模式3:两个8位计数器
3.中断系统
3.1中断系统概念
CPU对外部紧急事件进行处理的能力。
当CPU处理某件事时,外界发生紧急事件请求,要求CPU停下当前工作,转而去处理紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。实现这种功能的部件称为中断系统。
3.2中断嵌套
4.STC89C52中断资源
中断源个数:8个(外部中断0、定时器0中断、外部中断1、定时器1中断、串口中断、定时器2中断、外部中断2、外部中断3)
中断优先级个数:4个(STC89C52有四个中断优先级)
中断优先级基本规则:(1)低优先级中断可被高优先级中断所中断,反之不能。
(2)任何一种中断(不管是高级还是低级),一旦得到相应,不会被同级中断所中断。
5.定时器和中断系统
5.定时器相关寄存器
5.1TCON定时器控制寄存器(可位寻址)
TCON为定时器/计数器T0,T1的控制寄存器同时也锁存T0,T1溢出中断源,外部请求中断源
TR0=1;代表定时器开始计数,这里只标注与定时器T0相关的参数TF0,TR0。
5.2TMOD定时器模式寄存器(不可位寻址)
定时和计数功能均由TMOD的控制位C/T位进行选择。两个定时/计数器有四种模式,均由TMOD的M0和M1进行操作。
C/T=0,用作定时器,单片机内部时钟
C/T=1,用作计数器,外部接入晶振
6.定时器控制(按键左右控制流水灯模式)
这里只写了main函数,key.c和key.h其他函数在定时器8均有展示
6.1main函数
#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include <INTRINS.H>
unsigned char KeyNum,LEDMode;//LEDMode即为LED模式
void main()
{
P2=0xFE;//P2LED灯初始状态1111 1110,最左边小灯亮
Timer0_Init();//定时器一定要初始化
while(1)
{
KeyNum=Key();
if(KeyNum)
{
if(KeyNum==1)//按键1控制流水灯左右移动
{
LEDMode++;
if(LEDMode>=2)LEDMode=0;
}
}
}
}
Timer0_Routine(void) interrupt 1//定时器0中断中显示了流水灯左右移动
{
static unsigned int T0count;//staitic循环一次后不清零,仍占据内存空间
//静态局部变量,只能由本函数引用
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0count++;//中断来一次就加一次;
if(T0count>=500) //1ms*500=500ms
{
T0count=0;
if(LEDMode==0)//LED模式为0
P2=_crol_ (P2,1);//LED灯向左移动
if(LEDMode==1)//模式2
P2=_cror_(P2,1);//LED灯向右移动
}
}
6.2key.c函数
#include <REGX52.H>
#include "Delay.h"
//获取独立按键键码
//无参数
//按下按键的键码范围1-4,无按键按下返回值为0
unsigned char Key()
{
unsigned char KeyNumber=0;
if(P3_1==0){Delay(2);while(P3_1==0);Delay(2);KeyNumber=1;}//按键1为P3_1端口
if(P3_0==0){Delay(2);while(P3_0==0);Delay(2);KeyNumber=2;}//按键2为P3_0端口
if(P3_2==0){Delay(2);while(P3_2==0);Delay(2);KeyNumber=3;}//按键3为P3_2端口
if(P3_3==0){Delay(2);while(P3_3==0);Delay(2);KeyNumber=4;}//按键4为P3_3端口
return KeyNumber;//返回按键值
}
7.3key.h函数
#ifndef __KEY_H__
#define __KEY_H__
unsigned char Key();
#endif
7.定时器时钟
定时器时钟需要的函数如下
7.1main函数
//main函数
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "Timer0.h"
unsigned char sec=55,min=59,hour=23;
void main()
{
LCD_Init();
Timer0_Init();
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);
}
}
//定时器中断模板 用的时候复制到main函数中
Timer0_Routine(void) interrupt 1//定时器0中断
{
static unsigned int T0count;//staitic循环一次后不清零,仍占据内存空间
//静态局部变量,只能由本函数引用
TH0=64535/256;
TL0=64535%256;
T0count++;//中断来一次就加一次;
if(T0count>=1000)// 1ms*1000=1s
{
T0count=0;
sec++;//每隔一秒sec加加
if(sec>=60)
{
sec=0;
min++;
if(min>=60)
{
min=0;
hour++;
if(hour>=24)
{
hour=0;
}
}//中断只能显示短的逻辑程序。
}
}
}
7.2Delay.c函数(延迟函数)
void Delay(unsigned char xms) //@12.000MHz
{
unsigned char i, j;
while(xms)
{
i = 195;
j = 138;
do
{
while (--j);
} while (--i);
xms--;
}
}
7.3Delay.h函数
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay(unsigned char xms);
#endif
7.4LCD1602.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');
}
}
7.5LCD1602.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
7.6Timer0.c函数(定时器函数)
TMOD=TMOD & 0xF0;//把TMOD的低四位清0,高四位保持不变 TMOD =TMOD & 0xF0;//TMOD原来的是与1111 0000进行“与”运算。
TMOD=TMOD | 0x01;//把TMOD的最低位置置1,高四位保持不变//TMOD原来的是与0000 0001进行“或”运算。 与置0,或置1
与置0,或置1.不影响高四位而对低四位置1.含义为定时器0寄存器且01代表是16位定时器
void Timer0Init(void)
{
//TMOD=0x01;//0000 0001 01代表16位定时器 不可位寻址需要整体赋值寻址
TMOD=TMOD & 0xF0;//把TMOD的低四位清0,高四位保持不变 TMOD =TMOD & 0xF0;//TMOD原来的是与1111 0000进行“与”运算。
TMOD=TMOD | 0x01;//把TMOD的最低位置置1,高四位保持不变//TMOD原来的是与0000 0001进行“或”运算。 与置0,或置1
// 与置0,或置1.不影响高四位而对低四位置1.含义为定时器0寄存器且01代表是16位定时器
TF0=0;//中断溢出标志位
TR0=1; //定时器开始 开始TCON可位寻址,可以对单独位进行赋值寻址
TH0=64535/256;//把高八位拿出开 定时器计数(2*16=65536,65535-64535=1000)TH0将1ms的高八位拿出
TL0=64535%256;//把低八位拿出来 定时器计数
ET0=1;//允许T0中断
EA=1;//总中断允许控制位
PT0=0;
//}
#include <REGX52.H>
//定时器0初始化1ms 12MHz
//无返回值
void Timer0_Init() //1毫秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;//允许T0中断
EA=1;//总中断允许控制位
PT0=0;//中断优先级
}
//定时器中断模板 用的时候复制到main函数中
//Timer0_Routine(void) interrupt 1//定时器0中断
//{
// static unsigned int T0count;//staitic循环一次后不清零,仍占据内存空间
//静态局部变量,只能由本函数引用
//TH0=64535/256;
//TL0=64535%256;
//T0count++;//中断来一次就加一次;
//if(T0count>=1000) 1ms*1000=1s
// {
// T0count=0;
// P2_0=~P2_0;
// }
//}
7.7Timer0.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
8.成果展示(定时器时钟)