目录
一:定时器
1:简历
2:定时器/计数器0/1和相关寄存器
A:介绍
B: 相关寄存器
C:基本原理
3:定时器/计数器控制寄存器TCON
4:定时器/计数器工作模式寄存器TMOD
(1)门控制
(2):使用STC-iSP软件生成配置
(2):使用STC-iSP软件定时器
二:中断
1:简历
B:中断寄存器
2:中断流程
3:中断系统
4:中断函数
5:配置中断流程
6:中断源
三:配置定时器和中断
B:2,10,16进制转化
四:案例
1:定时器时钟
一:定时器
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; //设置定时初值
TH0 = 0xFC; //设置定时初值
//TCON配置
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
定时器配置大致完成
(2):使用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--;
}
}
二:中断
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:定时器时钟
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