师从江科大
51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成的。
1、计时功能,用于计时系统,可以实现软件计时,或者使程序每隔一固定时间完成一项操作
2、延时功能,代替长时间的Delay,提高CPU的运行效率和处理速度
3、中断功能,当定时器计数值达到预设值时,会产生硬件中断请求。CPU响应中断后将执行相应的中断服务程序,这样可以在固定的时间间隔内自动切换执行不同任务,提高了CPU的工作效率,实现了多任务的协同处理。
4、频率测量与计数脉冲,定时器也可以用作计数器,对外部输入的脉冲信号进行计数,从而可以测量外部事件发生的频率或次数。
STC89C52单片机中,定时器/计数器T0和T1都有4种不同的工作模式。以下是这四种工作模式的简要描述:
方式0(Mode 0):13位计数器
方式1(Mode 1):16位计数器(常用)
方式2(Mode 2):8位自动重载模式
方式3(Mode 3):两个8位计数器
非门(NOT Gate): 非门用于实现逻辑反相操作,它有一个输入和一个输出。当输入为高电平(1)时,输出为低电平(0);(给1变0,给0变1)
与门(AND Gate): 与门有至少两个输入和一个输出,只有当所有输入均为高电平时,输出才为高电平。(有0即0)
或门(OR Gate): 或门同样具有至少两个输入和一个输出,只要有任何一个输入为高电平,输出就为高电平。(有1即1)
注:图中左下角从左到右第一个位为非门,第二个为
注:左上是定时系统,左下是计数系统,右边是中断系统
SYSclk:系统时钟,即晶振周期,本开发板上的晶振为12MHZ
1、中断源个数:8个(外部中断0、定时器0中断、外部中断1、定时器1中断、串口中断、外部中断2、外部中断3)
2、中断优先级个数:4个
3、中断号:
注:寄存器是连接软硬件的媒介
可位寻址: 可位寻址是指可以单独对某个寄存器或存储器中的某一位进行读取、设置或清除操作。这意味着CPU可以直接指定并操作内存或寄存器中的任何一个独立的位(bit)。例如,在一些单片机架构中,有一些特殊功能寄存器(SFRs)和内部RAM区域能够以位为单位进行操作,这些位置有专门的位地址。这对于实现诸如标志位检查、中断控制等逻辑控制非常有用。
不可位寻址: 不可位寻址则意味着只能以字节(8位)或者其他固定大小的数据单元(如半字、全字等)作为最小访问单位来操作寄存器或存储器。对于不可位寻址的寄存器,如果要改变其中的某个位状态,必须先读取整个寄存器的内容,修改该位后再将整个数据写回寄存器中。例如,某些定时器模式寄存器可能就是不可位寻址的,需要通过编程一次性更改整个寄存器的值。
main.c
#include
#include "Timer0.h"
#include "Key.h"
#include
unsigned char KeyNum,LEDMode;
void main()
{
P2=0xFE;//点亮最低位LED
Timer0_Init();//定时器工作
while(1)
{
KeyNum=Key();
if(KeyNum)
{
if(KeyNum==1)
{
LEDMode++;
if(LEDMode>=2)LEDMode=0;
}
}
}
}
//中断处理程序
void Timer0_Rountine() interrupt 1 //1是中断序号
{
static unsigned int TOCount;
TH0 = 0xFC; //高位
TL0 = 0x18; //低位 重新赋初值保证下一次也是1ms
TOCount++;
if(TOCount>=500)
{
TOCount=0;
if(LEDMode==0)
P2=_crol_(P2,1);
if(LEDMode==1)
P2=_cror_(P2,1);
}
}
Timer0.c
#include
/**
* @brief 定时器初始化//1毫秒@12.000MHz
* @param 无
* @retval 无
*/
void Timer0_Init()
{
//TMOD=0x01; //0000 0001
TMOD &= 0xF0; //设置定时器模式 把TMOD的低四位清零,高四位保持不变
TMOD |= 0x01; //设置定时器模式 把TMOD的把最低位清零,高四位保持不变
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1; //将中断部分打通
EA=1;//继续打通中断部分
PT0=0;//定时器0中断为低优先级 PT0=1;意思是将定时器中断为高优先级
}
/*
//定时器中断模版
void Timer0_Rountine() interrupt 1 //1是中断序号
{
static unsigned int TOCount;
TH0 = 0xFC; //高位
TL0 = 0x18; //低位 重新赋初值保证下一次也是1ms
TOCount++;
if(TOCount>=1000)
{
TOCount=0;
P2_0=~P2_0;
}
}
*/
Timer0.h
#ifndef __TIMER0_H__
#define __TIMER0_H__
void Timer0_Init();
#endif
Key.c
#include
#include "Delay.h"
/**
* @brief 获取独立按键建码
* @param 无
* @retval 按下按键的建码, 范围:0~4,无按键按下时返回值为0
*/
unsigned char Key()
{
unsigned char KeyNumber=0;
if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}
if(P3_0==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=2;}
if(P3_2==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=3;}
if(P3_3==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=4;}
return KeyNumber;
}
Key.h
#ifndef __KEY_H__
#define __KEY_H__
unsigned char Key();
#endif
main.c
#include
#include "Delay.h"
#include "Timer0.h"
#include "LCD1602.h"
unsigned char sec=0,min=19,hour=10;
void main()
{
LCD_Init(); //LCD初始化
LCD_ShowString(1,1,"COLCK:");
Timer0Init(); //定时器0初始化
while(1)
{
LCD_ShowNum(2,1,hour,2);
LCD_ShowString(2,3,":");
LCD_ShowNum(2,4,min,2);
LCD_ShowString(2,6,":");
LCD_ShowNum(2,7,sec,2);
}
}
void TimerRoutine() interrupt 1
{
static unsigned int T0Count;
//当触发中断后,每次中断结束后,初始值还是为64535 即1ms
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=1000) //一次是1ms,*1000就是一秒
{
T0Count=0;
sec++;
if(sec>=60)
{
sec=0;
min++;
if(min>=60)
{
min=0;
hour++;
if(hour>=24)
{
hour=0;
sec=0;
min=0;
}
}
}
}
}
Timer0.c
#include
/**
* @brief 定时器0初始化
* @param
* @retval
*/
void Timer0Init()
{
TMOD&=0xF0; //高四位不变
TMOD|=0x01; //设置定时器模式1 以及设置为定时方式 0
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0=0; //定时器0溢出标志位
TR0=1; //定时器0运行控制位
TF0=1; //设置外部中断
ET0=1;
EA=1;
PT0=0;
}
/*
//定时器中断模版
void Timer0_Rountine() interrupt 1 //1是中断序号
{
static unsigned int TOCount;
TH0 = 0xFC; //高位
TL0 = 0x18; //低位 重新赋初值保证下一次也是1ms
TOCount++;
if(TOCount>=1000)
{
TOCount=0;
P2_0=~P2_0;
}
}
*/
Timer0,h
#ifndef __TIMER0_H__
#define __TIMER0_H__
void Timer0_Init();
#endif
LCD1602.c
#include
//引脚配置:
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;i0;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
若有侵权,请联系作者