上图为单片机内部定时系统,51单片机的定时器属于内部的资源,电路连接与运转都在单片机内部完成;单片机内部定时器与计数器实际工作原理相同,通过配置相应寄存器来确定是计数还是定时,他们的存储范围为0~65535大小,超过后被称为溢出,配合中断操作,让主程序停下来去执行中断(中断就类似于你在写作业,突然接到了电话让你拿外卖,这时候你停下来去拿外卖,拿完外卖接着写作业,拿外卖的这个过程就是中断,写作业就是主程序)
由上图,左上角为时钟部分,中间为定时器/计数器,最右边为中断。单片机的时钟系统,是经过12或者6分频,产生一个时钟周期,每一个时钟周期,定时器加1,一直加到65535,超出后就会向系统单元发出中断请求,使程序跳至中断函数执行。
1. 定时器工作模式:模式0——13位定时器/计数器
模式1——16位定时器计数器
模式2——8位自动重装模式
模式3——两个8位计数器
2.中断流程:
以12分频为例,一个机器周期=12/主振频率,假设主振频率为12MHZ,一个机器周期=12/12x10^6 us=1us,也就是每隔1us定时器/计数器就加1,因为定时器可以装下65535,假设我们需要定时10us,那就用65535减去10us,得到的这个数赋值给定时器作为初值,定时器从这个数开始运行时,再过10us是不是就溢出产生中断了,就达到了我们定时10us的效果了。
1ms=1000次机器周期
振荡周期: 为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期)
状态周期:2个振荡周期为1个状态周期,用S表示。振荡周期又称S周期或时钟周期。
机器周期:1个机器周期含6个状态周期,12个振荡周期。
指令周期:完成1条指令所占用的全部时间,它以机器周期为单位。
例如:外接晶振为12MHz时,51单片机相关周期的具体值为:
振荡周期=1/12us;
状态周期=1/6us;
机器周期=1us;
指令周期=1~4us;
软件配置步骤:
配置工作方式寄存器——TMOD
配置控制寄存器——TCON
给定时器赋初值——THX\TLX
配置中断——开中断
#include
void Timer0_Init() //定时器初始化
{
TMOD=(TMOD&0XF0)|0X01; //配置TMOD寄存器,TMOD赋值为0000 0001(16位定时器模式)
TF0=0; //配置TCON寄存器,中断标志位置0
TR0=1; //配置TCON寄存器,定时器开始计数
TH0=64535/256; //给定时器赋初值,存16进制高位
TL0=64535%256; //存16进制低位
EA=1; //中断配置,打开中断总开关
ET0=1; //中断配置,打开T0的中断
PT0=0; //中断优先级设置
}
/*用STC软件也可直接生成如下定时器初始化函数
void Timer0Init(void) //1毫秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
**********************************************/
void main()
{
Timer0_Init(); //定时器初始化函数
while(1)
{
}
}
unsigned int T0Cout; //T0Cout用于传参,记录中断次数
void Timer0_Routine() interrupt 1 //中断函数,interrupt 1为中断先后次序标志号
{
TH0=64535/256; //重新给定时器赋初值,因为范围满了,需要重新刷新下
TL0=64535%256;
T0Cout++; //每产生一个机器周期,计数器加1
if(T0Cout>=1000) //定时1000us
{
T0Cout=0; //T0Cout重新归0
P2_0=~P2_0; //为观察现象,P2.0口的LED灯状态取反,每隔1000us状态反转一次
}
}
/************main.c主函数************/
#include //包含52单片机相关寄存器的头文件
#include //定时器函数的头文件
#include //按键函数的头文件
#include //移位操作头文件,左边的高位移出后,右边的补位,(_crol_ 左移;_cror_ 右移)
unsigned char KeyNum,LEDMode; //定义了按键值,LED模式
void main()
{
P2=0XFE; //给P2口赋值,让一个LED亮,因为后面要用循环移位
Timer0_Init(); //定时器初始化
while(1)
{
KeyNum=Key(); //键值赋给KeyNum
if(KeyNum)
{
if(KeyNum==1) //判断按键是否按下
{
LEDMode++; //按键每按下一次,LED模式加一
if(LEDMode>=2)LEDMode=0; //让LEDMode随着按键按下,进行0 1 循环
}
}
}
}
void Timer0_Routine() interrupt 1 //中断函数
{
static unsigned int T0count; //定义计数器,用于判断中断次数;static用于局部静态变量,程序运行结束,不销毁
TH0=64535/256; //再一次给定时器赋初值,因为定时器已经溢出产生了中断
TL0=64535%256;
T0count++; //让计数器加1
if(T0count>=1000) //每隔1000次,执行一次条件
{
T0count=0; //先让T0Count归0
if(LEDMode==0) //对LEDMode状态判断,为0时,P2当前状态左移一次
{
P2=_crol_(P2,1);
}
if(LEDMode==1) //对LEDMode状态判断,为1时,P2当前状态右移一次
{
P2=_cror_(P2,1);
}
}
}
/*****************Timer0.c************************/
#include
void Timer0_Init()
{
//TMOD=0X01; //0000 0001
TMOD=TMOD&0xF0; //把TMOD的低四位清零,高四位保持不变
TMOD=TMOD|0X01; //把TMOD的最低位置1,高四位保持不变
TF0=0;
TR0=1;
TH0=64535/256;
TL0=64535%256;
ET0=1;
EA=1;
PT0=0;
}
/*****************key.c***********************/
#include
#include "Delay.h"
unsigned char key()
{
unsigned char KeyNumber=0;
if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;} //剪纸判定后,给KEYNUMBER赋值
if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}
if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
return KeyNumber;
}
/****************Delay.c*******************/
void Delay(unsigned int xms)
{
while(xms)
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
xms--;
}
}
使用时需要把.c和.h文件放进去,这里将代码模块化了,不会的可以看之前的教程
#include
#include
#include
#include
unsigned char Sec,Min,Hour; //定义秒,分钟,小时
void main()
{
LCD_Init(); //LCD初始化
Timer0_Init(); //定时器初始化
LCD_ShowString(1,1,"clock:"); //LCD的1行1列显示clock
LCD_ShowString(2,3,":"); //LCD的2行3列显示冒号
LCD_ShowString(2,6,":"); //LCD的2行6列显示冒号
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) //定时了1000毫秒,即1秒
{
T0Count=0;
Sec++; //与定时器协同,每秒累加
if(Sec>=60) //秒计满了60,秒清0,向分钟进1
{
Sec=0;
Min++;
if(Min>=60) //分钟满60,分钟清0,向小时进1
{
Min=0;
Hour++;
if(Hour>=24) //小时满24,小时清0
{
Hour=0;
}
}
}
}
}
/**************************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;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
Timer0和Delay的.c和.h直接复制上面就行。。。。定时器和中断要多看看STC官方手册的描述领悟下,才容易理解