用C51语言实现单片机高精度定时的新算法
C51单片机定时计数器应用编程归纳总结
C51 T and C● 80C51单片机内部有两个定时/计数器T0和T1,其核心是计数器,基本功能是加1。
● 对外部事件脉冲(下降沿)计数,是计数器;对片内机周脉冲计数,是定时器。
● 计数器由二个8位计数器组成。
● 定时时间和计数值可以编程设定,其方法是在计数器内设置一个初值,然后加1计满后溢出。
调整计数器初值,可调整从初值到计满溢出的数值,即调整了定时时间和计数值。
● 定时/计数器作为计数器时,外部事件脉冲必须从规定的引脚Tx(P3.4、P3.5)输入。
且外部脉冲的最高频率不能超过时钟频率的1/24一、定时/计数器的结构定时/计数器的实质是加1计数器(16位),由高8位和低8位两个寄存器组成。
TMOD 是定时/计数器的工作方式寄存器,确定工作方式和功能;TCON 是控制寄存器,控制T0、T1的启动和停止及设置溢出标志。
二、定时/计数器的工作原理加1计数器输入的计数脉冲有两个来源,一个是由系统的时钟振荡器输出脉冲经12分频后送来;一个是T0或T1引脚输入的外部脉冲源。
每来一个脉冲计数器加1,当加到计数器为全1时,再输入一个脉冲就使计数器回零,且计数器的溢出使TCON 中TF0或TF1置1,向CPU 发出中断请求(定时/计数器中断允许时)。
如果定时/计数器工作于定时模式,则表示定时时间已到;如果工作于计数模式,则表示计数值已满。
可见,由溢出时计数器的值减去计数初值才是加1计数器的计数值。
设置为定时器模式时,加1计数器是对内部机器周期计数(1个机器周期等于12个振荡周期,即计数频率为晶振频率的1/12)。
计数值N 乘以机器周期Tcy 就是定时时间t 。
设置为计数器模式时,外部事件计数脉冲由T0或T1引脚输入到计数器。
在每个机器周期的S5P2期间采样T0、T1引脚电平。
当某周期采样到一高电平输入,而下一周期又采样到一低电平时,则计数器加1,更新的计数值在下一个机器周期的S3P1期间装入计数器。
由于检测一个从1到0的下降沿需要2个机器周期,因此要求被采样的电平至少要维持一个机器周期。
51单片机的几种精确延时
在很多情况下,定时器/计数器经常被用作其他用途,这时候就只能用软件方法延时。下面介绍几种软件延时的方法。
2.1短暂延时
可以在C文件中通过使用带_NOP_( )语句的函数实现,定义一系列不同的延时函数,如Delay10us( )、Delay25us( )、Delay40us( )等存放在一个自定义的C文件中,需要时在主程序中直接调用。如延时10μs的延时函数可编写如下:
同样对do…while,while循环语句中,也是如此
例:
unsigned char n;
n=255;
do{n--}
while(n);
或
n=255;
while(n)
{n--};
这两个循环语句经过C51编译之后,形成DJNZ来完成的方法,
故其精确时间的计算也很方便。
其三:对于要求精确延时时间更长,这时就要采用循环嵌套
i=255;
do{j=255;
do{j--}
while(j);
i--;
}
while(i);
或
unsigned char i,j
i=255;
while(i)
{j=255;
while(j)
{j--};
i--;
}
这三种方法都是用DJNZ指令嵌套实现循环的,由C51编
译器用下面的指令组合来完成的
MOVR7,#0FFH
当采用while (DlyT--)循环体时,DlyT的值存放在R7中。相对应的汇编代码如下:
C:0x000FAE07MOVR6, R7//1T
C:0x00111F DECR7//1T
C:0x0012EE MOVA,R6//1T
C:0x001370FAJNZC:000F//2T
Keil C51精确延时程序(C语言)
Keil C51精确延时程序程序说明如下:振荡频率:12MHz机器周期=12/振荡频率=12/12000000=1us#include <reg52.h>void delay1(unsigned char i){ while(--i);}说明:delay1程序为:延时时间=(2*i+2)*机器周期。
i=1~255。
void delay2(unsigned char i){ while(i--);}说明:delay2程序为:延时时间=(6*i+2)*机器周期。
i=1~255。
void main (void){unsigned char m;delay1(10); //赋值并调延时程序delay1说明:本句为赋值并调用Delayus1:延时时间=(1+2)*机器周期。
全部延时时间为:延时时间=(1+2+2*i+2)*机器周期。
i=1~255。
本例:延时时间=(1+2+2*10+2)*1us=25usdelay2(10); //赋值并调延时程序delay2说明:本句为赋值并调用Delayus2:延时时间=(1+2)*机器周期。
全部延时时间为:延时时间=(1+2+6*i+2)*机器周期。
i=1~255。
本例:延时时间=(1+2+6*10+2)*1us=65usm=10; //赋值,m=1~255while(--m) ; //计算,延时时间=2*m*机器周期说明:本两句为赋值并计算。
全部延时时间为:延时时间=(1+2*m)*机器周期。
m=1~255。
本例:延时时间=(1+2*10)*1us=25uswhile(1);}。
C51中精确的延时与计算的实现
C51中精确的延时与计算的实现C51由于其可读性和可移植性很强,在单片机中得到广泛的应用,但在某些时候由于C51编写的程序对在有精确时间要求下,可能就得要用汇编语言来编写,但在C51是否也能实现时间的精确控制呢?答案是肯定的。
在C51中要实现对时间的精确延时有以下几种方法其一:对于延时很短的,要求在us级的,采用“_nop_”函数,这个函数相当汇编NOP指令,延时几微秒,就插入几个这样的函数。
其二:对于延时比较长的,要求在大于10us,采用C51中的循环语句来实现。
在选择C51中循环语句时,要注意以下几个问题第一、定义的C51中循环变量,尽量采用无符号字符型变量。
第二、在FOR循环语句中,尽量采用变量减减来做循环。
第三、在do…while,while语句中,循环体内变量也采用减减方法。
这因为在C51编译器中,对不同的循环方法,采用不同的指令来完成的。
下面举例说明:unsigned char I;for(i=0;i255;i++);unsigned char I;for(i=255;i0;i--);其中,第二个循环语句C51编译后,就用DJNZ指令来完成,相当于如下指令:MOV 09H,#0FFHLOOP: DJNZ 09H,LOOP指令相当简洁,也很好计算精确的延时时间。
同样对do…while,while循环语句中,也是如此例:unsigned char n;n=255;do{n--}while(n);或n=255;while(n){n--};这两个循环语句经过C51编译之后,形成DJNZ来完成的方法,故其精确时间的计算也很方便。
其三:对于要求精确延时时间更长,这时就要采用循环嵌套的方法来实现,因此,循环嵌套的方法常用于达到ms级的延时。
对于循环语句同样可以采用for,do…while,while结构来完成,每个循环体内的变量仍然采用无符号字符变量。
unsigned char i,jfor(i=255;i0;i--)for(j=255;j0;j--);或unsigned char i,ji=255;do{j=255;do{j--}while(j);i--;}while(i);或unsigned char i,ji=255;while(i){j=255;while(j){j--};i--;}这三种方法都是用DJNZ指令嵌套实现循环的,由C51编译器用下面的指令组合来完成的MOV R7,#0FFHLOOP2: MOV R6,#0FFHLOOP1: DJNZ R6,LOOP1DJNZ R7,LOOP2这些指令的组合在汇编语言中采用DJNZ指令来做延时用,因此它的时间精确计算也是很简单,假上面变量i的初值为m,变量j的初值为n,则总延时时间为:m×(n×T+T),其中T为DJNZ指令执行时间。
第5章定时器计数器的C51编程
5.1.1
结构
• 计数功能: • 是指对外部事件进行计数:计数信号来 自T0(P3.4)、T1(P3.5)引脚。 • 定时功能: • 也是通过计数器的计数功能来完成的, 不过此时的计数脉冲来自单片机内部: 机器周期。
5.1.2
控制寄存器
• 与定时器/计数器应用有关的控制寄存 器有2个,分别为TCON、TMOD、TH、TL。
3.模式2:
计数与定时范围:
• • • • • • • • • • 计数器的计数值范围是:1~256(28) 当为计数器工作方式时: 计数器的初值范围为:0~28-1; 当为定时工作方式时: 定时时间=(28-计数初值)×定时周期 若晶振频率为12MHz,其定时周期1μs: 则最短定时时间为: Tmin=[28-(28-1)] ×1μs =1(μs) 最长定时时间为: Tmax=(28-0) ×1μs =256(μs)
模式2:
• • • • • •
TMOD=0x06; TH0=-100; TL0=-100; EA=1; ET0=1; TR0=1;
【例5-2】定时器工作方式初始化示例:
• 外接晶振频率fosc=12MHz,T1工作于定 时方式,且允许中断,定时时间为20ms, 令其工作在模式1。
• • • • • • TMOD=0x01; //设置定时器工作方式 TH0=(65536-20000)/256; //高8位TH0赋初值 TL0=(65536-20000)%256; //低8位TL0赋初值 ET0=1; //开计数器中断 EA=1; TR0=1; //启动计数器
【例5-1】计数器工作方式初始化示例:
• 定时器/计数器0工作于计数方式,且允 许中断,计数值n=100,分别令其工作 在模式1和模式2,初始化编程。 • 模式1:
用C51语言实现单片机高精度定时的新算法
用 C51 语言实现单片机
高精度定时的新算法
刘 帆 ’ 林育兹 ’ 戴玉珍 ”
( 1.厦门大学机电工程系,厦门 36 005 2 上海电子信息职业技术学院,上海 20 4 1 1 ; . 1 1 )
摘要 为提高805 单片 1 机定时精度, 扩展 805 系列单片 1 机的 用途, 本文分析了80 1 系列单 5 片机定时器溢出中断与CPU 响应中断的时间误差. 在此基础上, 提出了 应用 Cs 高级语言对多个 l 定时器进行精确定时的误差补偿方法,并且使用 Ke l Uv s onZ 仿真调试软件搭建一段数字显示式 i i i 倒计时的实 例程序, 通过分析和调试误差补偿算法, 80 1 单片 使 5 机在多个定时 器同时使用的 情况 下,定时误差最终小于3 个机器周期,是目 805 单片机高精度定时的一种新算法。 前 1 关键词: 单片机; 定时 器; 中断误差; Cs l
( 1)
镇止中断 停止定时肠
程序的执行时间; 几为中断返回指令的执行周期;
兀为中 返回断 后 断 点 执行下一 指 周期; .九 条 令的 鱿
为系统基准频率 ( MHz ; 1 是 80 1 系列单片机的 ) 2 5 固定分频倍数。 . 2 3 定时误差的非固定性特点
谈取THx. TL X
当前值
写入补偿后的 TH 狡 T以初1遥
2 定时误差产生的原因产生Biblioteka 时误差的主要原因有: ①定时器产生溢
出中断信号时,CPU 正在执行某指令; ②定时器溢 出中断信号时,CPU 正在执行某中断服务程序。 . 2 1 定时器工作方式的分析 805 单片机的2 个定时/计数器, 1 有方式 0~ 方 式 3 的 4 种工作方式。本文以最常用的定时方式 1 作讨论,其工作原理如图 1 所示。 由图 1 可知,单片机工作在方式 1 时, 若定时/ 计数值计满溢出后,则定时/计数器的初值将被置 零,并继续从 刀众 oxO 二 O,刀 =0x00 开始计数。由 玩 于定时初值不会被重新装入,所以需要利用程序重 新对 刀众、几沈 赋值。但赋值操作需要占用一定时 间, 如果不补偿, 会造成下一次定时 /计数溢出中断 信号的时间与理想值不符 ( 即误差) 。
51单片机 定时器 c语言
51单片机定时器 c语言51单片机是目前较为流行的一种单片机芯片,定时器是其重要的功能之一,可以用于实现各种定时任务,而c语言则是51单片机常用的编程语言之一。
下面将结合实例,阐述51单片机定时器在c语言中的使用方法。
一、引入头文件及定义定时器首先需要引入头文件“reg51.h”,然后需要定义一个定时器变量和一个计数变量。
在本文中,我们将使用定时器0,所以定义如下:```c#include<reg52.h>sbit led = P2^0; //定义led信号端口P2.0unsigned char count = 0; //计数变量unsigned char timerVal = 56; //定时器初值```需要注意的是,定时器初值的计算方法如下:$$定时器初值 = 256 - \frac{所需延时时间× 晶振频率}{12}$$在本例中,晶振频率为11.0592MHz,所需延时时间为0.001秒,则计算得到定时器初值为56。
二、设置定时器参数设置定时器参数前,需要先关闭定时器0。
设置完成后,再通过TR0位将定时器0启动。
```cvoid initTimer(){TMOD &= 0xF0; //定时器0, 方式1TMOD |= 0x01;TH0 = timerVal; //定时器初值高位TL0 = timerVal; //定时器初值低位ET0 = 1; //打开定时器0中断EA = 1; //打开总中断}void main(){initTimer(); //初始化定时器0while(1){if(count >= 100){led = !led; //LED翻转count = 0; //计数器清零}}}void timerHandler() interrupt 1{TH0 = timerVal;TL0 = timerVal;count++; //计数器+1}```在上述代码中,通过设置TMOD寄存器,将定时器工作在方式1。
Keil C51程序设计中几种精确延时方法
器 延 时 从 程 序 的 执 行 效 率 和 稳 定 性 两 方 面 考 虑 都 是 最 佳
的 方 案 。但 应该 注 意 , 1编 写 的 中断 服 务 程 序 编 译 后 会 C5
自动 加 上 PUS AC P H S 、 OP P W 和 PO H C、 US P W P S P
R
JJ J c…
…
维普资讯
。
珏 譬
I
z
一
N U -( ); J
◆ #p a maam、 rg n a m 和 a m 只 能在 函 rg s #p a mae d s s 数 内使 用 。
一
NOP () 一 ;
N0 P () ;
AC C语 句 , 行 时 占用 了 4个 机 器 周 期 ; 程 序 中还 有 计 执 如 数 值加 1语 句 , 又 会 占用 1个机 器 周 期 。这 些 语 句 所 消 则 耗 的 时 间在 计 算 定 时初 值 时 要 考 虑 进 去 , 初值 中减 去 以 从
达 到最 小 误 差 的 目的 。
一
一
将 汇 编 语 言 与 C 1结 合 起 来 , 分 发 挥 各 自的 优 势 , 5 充
无 疑 是 单 片 机 开 发 人 员 的 最佳 选 择 。
}
Dea l u () ly 0 s 函数 中共 用 了 6个一 NOP () 句 , 个 一 语 每
语 句 执 行 时 间 为 1U 。主 函 数 调 用 Dea l u () , 执 S ly 0 s 时 先 行一 个 L CALL指 令 ( s , 后 执 行 6个一 2 )然 NOP () 句 一 语
维普资讯
K iC 1 序设 计中几 种精确 延 时方法 * el 5 程
利用c51实现t0精确定时
Keil C51实现T0精确定时(通过对定时器的误差分析和校正)利用89C51设计一个简易日历时钟系统,时钟系统硬件主要由单片机控制的计时电路、复位等辅助电路、按键电路、数码管显示电路、电源系统等组成。
日历时钟可以显示年、月、时、分、秒;可以设置年、月、时、分其中计时控制电路由AT89C51单片机控制;按键电路包含时间设置;时间显示屏电路由7个数码管组成;电源系统由小功率整流滤波稳压电路组成,输出直流电压5 V,向主电路及显示电路供电。
系统框图如图1所示。
图1 日历时钟系统框图在计时过程中,系统利用89C51自身的计时器T0作为时钟基准,计时器中断的准确度直接关系到整个系统的精度,因此获取精确的定时时钟信号成为该系统的关键。
MCS-51单片机内有2个可编程的16位定时器/计数器,在本系统设计中采用AT89C51的定时器T0,并工作在方式1下,晶振频率为12 MHz。
1 T0定时中断定时器/计数器T0工作方式1的电路逻辑结构如图2所示。
T0定时特性功能寄存器由TL0(低8位)和TH0(高8位)构成。
特殊功能寄存器TMOD控制定时寄存器的工作方式;TCON则用于控制定时器T0和T1的启动和停止计数,同时管理定时器T0和T1的溢出标志等。
程序开始时需对TL0和TH0进行初始化编程,以定义它们的工作方式,并控制T0和T1的计数。
在系统的设计中,计时单位以s为基准,并要求日误差≤10 s,如果用循环去做,无法满足精度要求。
选用12 MHz的晶体可得到1 s的精度,经分析确定使用定时器0的方式1。
这个方式下,定时器0是16位定时器,也就是最大定时值为FFFFH,12 MHz晶体的每个定时周期为1 s,最多可以定时FFFFH×1 s=65635 us,即使使用最大值也无法一次定时1 s,设计中使用1次定时20 ms,50次定时中断得到1 s。
20 ms定时中断的定时值为:FFFFH-20 ms/1 s= B1DFH。
51单片机的定时器_计数器的C51编程
51单片机的定时器_计数器的C51编程相关知识点:1、单片机的定时器/计数器,实质是按一定时间间隔、自动在系统后台进行计数的。
2、当被设定工作在定时器方式时,自动计数的间隔是机器周期(12个晶振振荡周期),即计数频率是晶振振荡频率的1/12;3、当定时器被启动时,系统自动在后台,从初始值开始进行计数,计数到某个终点值时(方式1时是65535),产生溢出中断,自动去运行定时中断服务程序;注意,整个计数、溢出后去执行中断服务程序,都是单片机系统在后台自动完成的,不需要人工干预!4、定时器的定时时间,应该是(终点值-初始值)x机器周期。
对于工作在方式1和12MHz时钟的单片机,最大的计时时间是(65535-0)x1uS=65.535ms。
这个时间也是一般的51单片机定时器能够定时的最大定时时间,如果需要更长的定时时间,则一般可累加多定时几次得到,比如需要1秒的定时时间,则可让系统定时50ms,循环20次定时就可以得到1s的定时时间。
5、定时器定时得到的时间,由于是系统后台自动进行计数得到的,不受主程序中运行其他程序的影响,所以相当精确;6、使用定时器,必须先用TMOD寄存器设定T0/T1的工作方式,一般设定在方式1的情况比较多,所以可以这样设定:TMOD=0x01(仅设T0为方式1,即16位)、TMOD=0x10(仅设T1为方式1,即16位)、TMOD=0x11(设T0和T1为方式1,即都为16位)。
7、使用定时器,必须根据需要的定时时间,装载相应的初始值,而且在中断服务程序中,很多情况下得重新装载初始值,否则系统会从零开始计数而引起定时失败;8、要使用定时器前,还必须打开总中断和相应的定时中断,并启动之:EA=1(开总中断)、ET0=1(开定时器0中断)、TR0=1(启动定时器0)、ET1=1(开定时器1中断)、TR1=1(启动定时器1);9、注意中断服务程序尽可能短小精干,不要让它完成太多任务,尤其尽量避免出现长延时,以提高系统对其他事件的响应灵敏度.//定时器基本例程-1(未使用定时器,一个灯每隔500ms亮灭一次)//这是个特意安排的例程,以便与下面的例程2进行对比#include <reg52.h>sbit led=P2^7;void delay_ms(unsigned int xms); //ms级延时子程序//=================================================void main(){led=1; //上电初始化,led灯不亮while(1){led=!led;delay_ms(500);}}//=================================================void delay_ms(unsigned int xms) //ms级延时子程序{ unsigned int x,y;for(x=xms;x>0;x--)for(y=130;y>0;y--);}//-------------------------------------------------//定时器基本例程-2(使用定时器,一个灯每隔500ms亮灭一次)#include <reg52.h>sbit led=P2^7;unsigned char num;void delay_ms(unsigned int xms); //ms级延时子程序//=================================================void main(){led=1; //上电初始化,led灯不亮TMOD=0x01; //设定定时器0为工作方式1TH0=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL0=(65536-50000)%256; //EA=1; //开总中断ET0=1; //开定时器0中断TR0=1; //启动定时器0while(1){delay_ms(8000);}}//=================================================void delay_ms(unsigned int xms) //ms级延时子程序{ unsigned int x,y;for(x=xms;x>0;x--)for(y=130;y>0;y--);}//-------------------------------------------------void led_flash() interrupt 1 //使用了定时中断0的led闪烁子函数{ TH0=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000 TL0=(65536-50000)%256; //num++;if(num==10){num=0;led=!led;}}////定时器基本例程-3//(使用定时器T1,单片机整个口接的8个灯每隔500ms亮灭一次)#include <reg52.h>#define led_port P0 //宏定义,具体的端口尽量不要出现在主函数和主函数中unsigned char num;void delay_ms(unsigned int xms); //ms级延时子程序//=================================================void main(){led_port=0xff; //上电初始化,所有led灯不亮TMOD=0x10; //设定定时器1为工作方式1(16位方式)TH1=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL1=(65536-50000)%256; //EA=1; //开总中断ET1=1; //开定时器1中断TR1=1; //启动定时器1while(1){delay_ms(8000); //这句表明定时中断的运行是在系统后台自动运行的,不需要主函数“操心”}}//=================================================void delay_ms(unsigned int xms) //ms级延时子程序{ unsigned int x,y;for(x=xms;x>0;x--)for(y=130;y>0;y--);}//-------------------------------------------------void led_flash() interrupt 3 //使用了定时中断1的8灯闪烁子函数{ TH1=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL1=(65536-50000)%256; //num++; //计数if(num==10) //计够10次,时间就是10x50ms=500ms{num=0; //清零,以便进行下一次500ms的10次计数led_port=~led_port; //整个口接的led灯亮灭状态翻转}}//-------------------------------------------------//定时器基本例程-4//(同时使用定时器T0和定时器T1,单片机某个口的灯和某个口接的8个灯每隔500ms亮灭一次)#include <reg52.h>sbit led=P2^7;#define led_port P0 //宏定义,具体的端口尽量不要出现在主函数和主函数中unsigned char num_0,num_1;void delay_ms(unsigned int xms); //ms级延时子程序//=================================================void main(){led=1; //上电初始化,led灯不亮led_port=0xff; //上电初始化,该口所有led灯不亮TMOD=0x11; //设定定时器0和定时器1都为工作方式1(16位方式)TH0=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL0=(65536-50000)%256; //TH1=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL1=(65536-50000)%256; //EA=1; //开总中断ET0=1; //开定时器0中断TR0=1; //启动定时器0ET1=1; //开定时器1中断TR1=1; //启动定时器1while(1){delay_ms(8000); //这句表明定时中断的运行是在系统后台自动运行的,不需要主函数“操心”}}//=================================================void delay_ms(unsigned int xms) //ms级延时子程序{ unsigned int x,y;for(x=xms;x>0;x--)for(y=130;y>0;y--);}//-------------------------------------------------void led_flash() interrupt 1 //使用了定时中断0的led闪烁子函数{ TH0=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL0=(65536-50000)%256; //num_0++; //计数if(num_0==10) //计够10次,时间就是10x50ms=500ms{num_0=0; //清零,以便进行下一次500ms的10次计数led=!led; //led灯亮灭状态翻转}}//-------------------------------------------------void led_all_flash() interrupt 3 //使用了定时中断1的8灯闪烁子函数{ TH1=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL1=(65536-50000)%256; //num_1++; //计数if(num_1==10) //计够10次,时间就是10x50ms=500ms{num_1=0; //清零,以便进行下一次500ms的10次计数led_port=~led_port; //整个口接的led灯亮灭状态翻转}}//-------------------------------------------------//定时器基本例程-5//设定定时器T0工作在方式1的计数应用状态,//单片机T0口(P3.4)接一个按键充当外部脉冲源,//系统对进来的脉冲(每按一次键得一脉冲)进行计数,//计数的结果用接在单片机P0口的8个LED灯表示出来//(大家也可以改成用1602LCD来显示,这样更直观)//广西民大物电学院李映超2010年4月14日#include <reg52.h>#define led_port P0 //宏定义,具体的端口尽量不要出现在主函数和主函数中//=================================================void main(){TMOD=0x05; //设定定时器0为工作方式1、计数器TH0=0; //清零TL0=250; //TR0=1; //启动定时器0进行计数while(1){led_port=TL0; //将计数结果送去显示(用8个LED灯显示),//这里仅显示16位计数器的低8位}}定时器0仍旧工作在计数器状态,增加定时器1工作在定时状态,得到1s的定时时间,定时时间到后,将定时器0计数得到的脉冲数去显示,则这个脉冲数就是所输入的外部信号的频率,从而构成一个简单而准确的频率计!!不过,这个简单的“频率计”能够计量的信号频率(脉冲数),受单片机中断响应速度的影响,一般只能达到单片机系统时钟晶振的1/24,所以要能够测量更高的频率,必须使用前置分频器,对更高频率的待测输入信号进行预分频!。
零基础学单片机C语言程序设计 第9章 C51定时器
9.4 工作模式2的C51程序设计
51系列单片机的定时器/计数器工作模式2是8位自动重新装 入的计数器,16位计数器分成独立的2个部分,其中TH0 (或TH1)作为计数初值寄存器,用于存放和保持初值,初 值在程序中设置。
9.4.1 定时器/计数器工作模式2
在工作模式2中,定时器/计数器T1和T0的组成结构与功能 完全相同。当置寄存器TMOD的M1=1、M0=0时,T0和T1就工 作在模式2状态。这里以T0为例进行介绍,T1的使用类似。
第9章 C51定时器两个通用定时器/计数器T0和T1。T0和T1 都具有定时和计数两种功能,可以通过特殊功能寄存器来 选择。
计数。计数功能就是对计数脉冲进行计数。其中,计数脉 冲来自相应的外部输入引脚P3.4(T0)或P3.5(T1)。当 该引脚的输入信号发生由高电平至低电平的负跳变时,计 数器(TH0、TL0或TH1、TL0)的值增加1。
M1
M0
T1
T0
9.1.3 中断控制寄存器TCON
寄存器TCON的功能是在定时器溢出时设定标志位,并控制 定时器的运行、停止和中断请求。寄存器TCON的单元地址 为88H。中断控制寄存器TCON的组成,如图所示。其包含3 个部分,TF1和TR1位用于控制T1,TF0和TR0位用于控制T0, 其它的为中断控制。
9.4.2 模式2的程序设计
C51精确延时程序
看到了个好帖,我在此在它得基础上再抛抛砖!有个好帖,从精度考虑,它得研究结果是:void delay2(unsigned char i){while(--i);}为最佳方法。
分析:假设外挂12M(之后都是在这基础上讨论)我编译了下,传了些参数,并看了汇编代码,观察记录了下面的数据:delay2(0):延时518us 518-2*256=6delay2(1):延时7us(原帖写“5us”是错的,^_^)delay2(10):延时25us 25-20=5delay2(20):延时45us 45-40=5delay2(100):延时205us 205-200=5delay2(200):延时405us 405-400=5见上可得可调度为2us,而最大误差为6us。
精度是很高了!但这个程序的最大延时是为518us 显然不能满足实际需要,因为很多时候需要延迟比较长的时间。
那么,接下来讨论将t分配为两个字节,即uint型的时候,会出现什么情况。
void delay8(uint t){while(--t);}我编译了下,传了些参数,并看了汇编代码,观察记录了下面的数据:delay8(0):延时524551us 524551-8*65536=263delay8(1):延时15usdelay8(10):延时85us 85-80=5delay8(100):延时806us 806-800=6delay8(1000):延时8009us 8009-8000=9delay8(10000):延时80045us 80045-8000=45delay8(65535):延时524542us 524542-524280=262如果把这个程序的可调度看为8us,那么最大误差为263us,但这个延时程序还是不能满足要求的,因为延时最大为524.551ms。
那么用ulong t呢?一定很恐怖,不用看编译后的汇编代码了。
那么如何得到比较小的可调度,可调范围大,并占用比较少得RAM呢?请看下面的程序:/*--------------------------------------------------------------------程序名称:50us 延时注意事项:基于1MIPS,A T89系列对应12M晶振,W77、W78系列对应3M晶振例子提示:调用delay_50us(20),得到1ms延时全局变量:无返回:无--------------------------------------------------------------------*/void delay_50us(uint t){uchar j;for(;t>0;t--)for(j=19;j>0;j--);}我编译了下,传了些参数,并看了汇编代码,观察记录了下面的数据:delay_50us(1):延时63us 63-50=13delay_50us(10):延时513us 503-500=13delay_50us(100):延时5013us 5013-5000=13delay_50us(1000):延时50022us 50022-50000=22赫赫,延时50ms,误差仅仅22us,作为C语言已经是可以接受了。
51单片机的定时器_计数器的C51编程
51单片机的定时器_计数器的C51编程相关知识点:1、单片机的定时器/计数器,实质是按一定时间间隔、自动在系统后台进行计数的。
2、当被设定工作在定时器方式时,自动计数的间隔是机器周期(12个晶振振荡周期),即计数频率是晶振振荡频率的1/12;3、当定时器被启动时,系统自动在后台,从初始值开始进行计数,计数到某个终点值时(方式1时是65535),产生溢出中断,自动去运行定时中断服务程序;注意,整个计数、溢出后去执行中断服务程序,都是单片机系统在后台自动完成的,不需要人工干预!4、定时器的定时时间,应该是(终点值-初始值)x机器周期。
对于工作在方式1和12MHz时钟的单片机,最大的计时时间是(65535-0)x1uS=65.535ms。
这个时间也是一般的51单片机定时器能够定时的最大定时时间,如果需要更长的定时时间,则一般可累加多定时几次得到,比如需要1秒的定时时间,则可让系统定时50ms,循环20次定时就可以得到1s的定时时间。
5、定时器定时得到的时间,由于是系统后台自动进行计数得到的,不受主程序中运行其他程序的影响,所以相当精确;6、使用定时器,必须先用TMOD寄存器设定T0/T1的工作方式,一般设定在方式1的情况比较多,所以可以这样设定:TMOD=0x01(仅设T0为方式1,即16位)、TMOD=0x10(仅设T1为方式1,即16位)、TMOD=0x11(设T0和T1为方式1,即都为16位)。
7、使用定时器,必须根据需要的定时时间,装载相应的初始值,而且在中断服务程序中,很多情况下得重新装载初始值,否则系统会从零开始计数而引起定时失败;8、要使用定时器前,还必须打开总中断和相应的定时中断,并启动之:EA=1(开总中断)、ET0=1(开定时器0中断)、TR0=1(启动定时器0)、ET1=1(开定时器1中断)、TR1=1(启动定时器1);9、注意中断服务程序尽可能短小精干,不要让它完成太多任务,尤其尽量避免出现长延时,以提高系统对其他事件的响应灵敏度.//定时器基本例程-1(未使用定时器,一个灯每隔500ms亮灭一次)//这是个特意安排的例程,以便与下面的例程2进行对比#include <reg52.h>sbit led=P2^7;void delay_ms(unsigned int xms); //ms级延时子程序//=================================================void main(){led=1; //上电初始化,led灯不亮while(1){led=!led;delay_ms(500);}}//=================================================void delay_ms(unsigned int xms) //ms级延时子程序{ unsigned int x,y;for(x=xms;x>0;x--)for(y=130;y>0;y--);}//-------------------------------------------------//定时器基本例程-2(使用定时器,一个灯每隔500ms亮灭一次)#include <reg52.h>sbit led=P2^7;unsigned char num;void delay_ms(unsigned int xms); //ms级延时子程序//=================================================void main(){led=1; //上电初始化,led灯不亮TMOD=0x01; //设定定时器0为工作方式1TH0=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL0=(65536-50000)%256; //EA=1; //开总中断ET0=1; //开定时器0中断TR0=1; //启动定时器0while(1){delay_ms(8000);}}//=================================================void delay_ms(unsigned int xms) //ms级延时子程序{ unsigned int x,y;for(x=xms;x>0;x--)for(y=130;y>0;y--);}//-------------------------------------------------void led_flash() interrupt 1 //使用了定时中断0的led闪烁子函数{ TH0=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000 TL0=(65536-50000)%256; //num++;if(num==10){num=0;led=!led;}}////定时器基本例程-3//(使用定时器T1,单片机整个口接的8个灯每隔500ms亮灭一次)#include <reg52.h>#define led_port P0 //宏定义,具体的端口尽量不要出现在主函数和主函数中unsigned char num;void delay_ms(unsigned int xms); //ms级延时子程序//=================================================void main(){led_port=0xff; //上电初始化,所有led灯不亮TMOD=0x10; //设定定时器1为工作方式1(16位方式)TH1=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL1=(65536-50000)%256; //EA=1; //开总中断ET1=1; //开定时器1中断TR1=1; //启动定时器1while(1){delay_ms(8000); //这句表明定时中断的运行是在系统后台自动运行的,不需要主函数“操心”}}//=================================================void delay_ms(unsigned int xms) //ms级延时子程序{ unsigned int x,y;for(x=xms;x>0;x--)for(y=130;y>0;y--);}//-------------------------------------------------void led_flash() interrupt 3 //使用了定时中断1的8灯闪烁子函数{ TH1=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL1=(65536-50000)%256; //num++; //计数if(num==10) //计够10次,时间就是10x50ms=500ms{num=0; //清零,以便进行下一次500ms的10次计数led_port=~led_port; //整个口接的led灯亮灭状态翻转}}//-------------------------------------------------//定时器基本例程-4//(同时使用定时器T0和定时器T1,单片机某个口的灯和某个口接的8个灯每隔500ms亮灭一次)#include <reg52.h>sbit led=P2^7;#define led_port P0 //宏定义,具体的端口尽量不要出现在主函数和主函数中unsigned char num_0,num_1;void delay_ms(unsigned int xms); //ms级延时子程序//=================================================void main(){led=1; //上电初始化,led灯不亮led_port=0xff; //上电初始化,该口所有led灯不亮TMOD=0x11; //设定定时器0和定时器1都为工作方式1(16位方式)TH0=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL0=(65536-50000)%256; //TH1=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL1=(65536-50000)%256; //EA=1; //开总中断ET0=1; //开定时器0中断TR0=1; //启动定时器0ET1=1; //开定时器1中断TR1=1; //启动定时器1while(1){delay_ms(8000); //这句表明定时中断的运行是在系统后台自动运行的,不需要主函数“操心”}}//=================================================void delay_ms(unsigned int xms) //ms级延时子程序{ unsigned int x,y;for(x=xms;x>0;x--)for(y=130;y>0;y--);}//-------------------------------------------------void led_flash() interrupt 1 //使用了定时中断0的led闪烁子函数{ TH0=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL0=(65536-50000)%256; //num_0++; //计数if(num_0==10) //计够10次,时间就是10x50ms=500ms{num_0=0; //清零,以便进行下一次500ms的10次计数led=!led; //led灯亮灭状态翻转}}//-------------------------------------------------void led_all_flash() interrupt 3 //使用了定时中断1的8灯闪烁子函数{ TH1=(65536-50000)/256; //装载初始值,12MHZ晶振50ms数为50000TL1=(65536-50000)%256; //num_1++; //计数if(num_1==10) //计够10次,时间就是10x50ms=500ms{num_1=0; //清零,以便进行下一次500ms的10次计数led_port=~led_port; //整个口接的led灯亮灭状态翻转}}//-------------------------------------------------//定时器基本例程-5//设定定时器T0工作在方式1的计数应用状态,//单片机T0口(P3.4)接一个按键充当外部脉冲源,//系统对进来的脉冲(每按一次键得一脉冲)进行计数,//计数的结果用接在单片机P0口的8个LED灯表示出来//(大家也可以改成用1602LCD来显示,这样更直观)//广西民大物电学院李映超2010年4月14日#include <reg52.h>#define led_port P0 //宏定义,具体的端口尽量不要出现在主函数和主函数中//=================================================void main(){TMOD=0x05; //设定定时器0为工作方式1、计数器TH0=0; //清零TL0=250; //TR0=1; //启动定时器0进行计数while(1){led_port=TL0; //将计数结果送去显示(用8个LED灯显示),//这里仅显示16位计数器的低8位}}定时器0仍旧工作在计数器状态,增加定时器1工作在定时状态,得到1s的定时时间,定时时间到后,将定时器0计数得到的脉冲数去显示,则这个脉冲数就是所输入的外部信号的频率,从而构成一个简单而准确的频率计!!不过,这个简单的“频率计”能够计量的信号频率(脉冲数),受单片机中断响应速度的影响,一般只能达到单片机系统时钟晶振的1/24,所以要能够测量更高的频率,必须使用前置分频器,对更高频率的待测输入信号进行预分频!。
基于51的定时控制程序(c语言)
#include <reg51.h>#include <intrins.h>unsigned char data dis_digit;unsigned char key_s, key_v,key_n,key_m,a,b,dhour,dmin;unsigned char key_1,i,e,f, key_2,key_3,key_4,c,d,dhour1,dmin1;/***************************1302:引角配置**********************/ sbit RST_1302=P2^6;sbit IO_1302=P2^5;sbit CLK_1302=P2^4;sbit K1 = P1^0;sbit K2 = P1^1;sbit K3 = P1^2;sbit K4 = P1^3;sbit K5 = P1^4;sbit K6 = P1^5;sbit K7 = P1^6;sbit K8 = P1^7;sbit q=P2^0;unsigned char clk_time[3]={0x10,0x21,0x18}; //秒,分,时寄存器初始值unsigned char temp=0x80; // 地址80:秒写入寄存器sbit ACC0=ACC^0;sbit ACC7=ACC^7;unsigned char code dis_code[11]={0xc0,0xf9,0xa4,0xb0, // 0, 1, 2, 3 0x99,0x92,0x82,0xf8,0x80,0x90, 0xff};// 4, 5, 6, 7, 8, 9, offunsigned char data dis_buf[8];unsigned char data dis_index;bit scan_key();void display();void proc_key();void delayms(unsigned char ms);void write_1302(unsigned char addr,unsigned char dat);unsigned char read_dat_1302(void);void write_dat_1302(unsigned char dat);unsigned char read_1302(unsigned char addr);void main(void){P0 = 0xff;P3 = 0xff;TMOD = 0x11; // 定时器0, 1工作模式1, 16位定时方式TH1 = 0xdc;TL1 = 0;TH0 = 0xFC;TL0 = 0x17;write_1302(0x8e,0x00); //WP=0 写操作for(i=0;i<3;i++){write_1302(temp,clk_time[i]);temp+=2;}write_1302(0x90,0xa5);write_1302(0x8e,0x80); //WP=1 写保护dis_digit = 0xfe;dis_index = 0;TCON = 0x01;IE = 0x8a; // 使能timer0,1 中断TR0 = 1;TR1 = 1;key_v=0x03;key_m=0x03;key_2=0x03;key_4=0x03;while(1){temp=0x81; // 地址81:秒写入寄存器for(i=0;i<3;i++) //更新时间{display();temp+=2;}P3 = 0xff; // 先关闭所有数码管P0 = dis_buf[dis_index]; // 显示代码传送到P0口P3 = dis_digit; //dis_digit = _crol_(dis_digit,1); // 位选通值左移, 下次中断时选通下一位数码管dis_index++; //dis_index &= 0x07; // 8个数码管全部扫描完一遍之后,再回到第一个开始下一次扫描if((clk_time[2]==dhour)&&(clk_time[1]==dmin))q=0;else if((clk_time[2]==dhour1)&&(clk_time[1]==dmin1))q=1;if(scan_key()){delayms(10);if(scan_key()){a=key_s;b=key_n;c=key_1;d=key_3;key_v=key_s;key_m=key_n;key_2=key_1;key_4=key_3;proc_key();}}}}void timer0() interrupt 1// 定时器0中断服务程序, 用于数码管的动态扫描// dis_index --- 显示索引, 用于标识当前显示的数码管和缓冲区的偏移量// dis_digit --- 位选通值, 传送到P2口用于选通当前数码管的数值, 如等于0xfe时,// 选通P2.0口数码管// dis_buf --- 显于缓冲区基地址{TH0 = 0xFC;TL0 = 0x17;temp=0x81; // 地址81:秒写入寄存器for(i=0;i<3;i++) //更新时间{display();temp+=2;}P3 = 0xff; // 先关闭所有数码管P0 = dis_buf[dis_index]; // 显示代码传送到P0口P3 = dis_digit; //dis_digit = _crol_(dis_digit,1); // 位选通值左移, 下次中断时选通下一位数码管dis_index++; //dis_index &= 0x07; // 8个数码管全部扫描完一遍之后,再回到第一个开始下一次扫描}/*********************************************************//* DS1302 *//* *//********************************************************/void write_dat_1302(unsigned char dat) //DS1302:写入操作{unsigned char i;ACC=dat;for(i=8;i>0;i--){IO_1302=ACC0;CLK_1302=0;CLK_1302=1;ACC=ACC>>1;}}unsigned char read_dat_1302(void) //DS1302:读取操作{unsigned char i;for(i=0;i<8;i++){ACC=ACC>>1;CLK_1302 = 1;CLK_1302 = 0;ACC7 = IO_1302;}return(ACC);}void write_1302(unsigned char addr,unsigned char dat) //DS1302:写入数据(先送地址,再写数据?{RST_1302=0; //停止工作CLK_1302=0;RST_1302=1; //重新工作write_dat_1302(addr); //写入地址write_dat_1302(dat);RST_1302=0;CLK_1302=1;}unsigned char read_1302(unsigned char addr) //DS1302:读取数据(先送地址,再读数据){unsigned char temp;RST_1302=0; //停止工作CLK_1302=0;RST_1302=1; //重新工作write_dat_1302(addr); //写入地址temp=read_dat_1302();RST_1302=0;CLK_1302=1; //停止工作return(temp);}bit scan_key() //K2按下:K1=1,K2=0,return2;key_s=1.K1按下:K1=0,K2=1,return1;key_s=2. K1=K2=1,rturn3.{key_s = 0x00; //K3按下:K3=0,key_v=2.K4按下:K4=0,key_v=1.key_s |=K2;key_s <<= 1;key_s |= K1;key_n=0x00;key_n |= K4;key_n <<= 1;key_n |= K3;key_1 = 0x00;key_1 |=K6;key_1 <<= 1;key_1 |= K5;key_3=0x00;key_3 |= K8;key_3 <<= 1;key_3 |= K7;return((key_s ^ key_v)^(key_n^key_m)^(key_1 ^ key_2)^(key_3 ^ key_4));}void proc_key(){//EA = 0;if((a & 0x01) == 0) // K1按下{e=clk_time[2];e=e++;clk_time[2]=e;write_1302(0x8e,0x00); //WP=0 写操作write_1302(0x84,clk_time[2]);write_1302(0x8e,0x80); //WP=1 写保护display();}else if((a & 0x02) == 0) // K2按下{f=clk_time[1];f=f++;clk_time[1]=f;write_1302(0x8e,0x00); //WP=0 写操作write_1302(0x82,clk_time[1]);write_1302(0x8e,0x80); //WP=1 写保护display();}else if((b & 0x01) == 0) // K3按下(定时小时){dhour++;if(dhour > 23){dhour = 0;}if(dhour > 9)dis_buf[0] = dis_code[dhour / 10]; // 时十位elsedis_buf[0] = 0xff; // 当小时的十位为0时不显示dis_buf[1] = dis_code[dhour % 10]; // 时个位}else if((b & 0x02) == 0) // K4按下(定时分钟){dmin++;if(dmin > 59){dmin = 0;}dis_buf[3] = dis_code[dmin / 10]; // 分十位dis_buf[4] = dis_code[dmin % 10]; // 分个位}else if((c & 0x01) == 0) // K5按下(定时小时){dhour1++;if(dhour1 > 23){dhour1 = 0;}if(dhour1 > 9)dis_buf[0] = dis_code[dhour1 / 10]; // 时十位elsedis_buf[0] = 0xff; // 当小时的十位为0时不显示dis_buf[1] = dis_code[dhour1 % 10]; // 时个位}else if((c & 0x02) == 0) // K6按下(定时分钟){dmin1++;if(dmin1 > 59){dmin1 = 0;}dis_buf[3] = dis_code[dmin1 / 10]; // 分十位dis_buf[4] = dis_code[dmin1 % 10]; // 分个位}if((d & 0x01) == 0) // K7按下{display();}else if((d & 0x02) == 0) // K8按下{display();}// EA = 1;}void display(){clk_time[i]=(read_1302(temp)/16*10+read_1302(temp)%16);dis_buf[0] = dis_code[clk_time[2] / 10]; // 时十位dis_buf[1] = dis_code[clk_time[2] % 10]; // 时个位dis_buf[3] = dis_code[clk_time[1] / 10]; // 分十位dis_buf[4] = dis_code[clk_time[1] % 10]; // 分个位dis_buf[6] = dis_code[clk_time[0] / 10]; // 秒十位dis_buf[7] = dis_code[clk_time[0] % 10]; // 秒个位dis_buf[2] = 0xbf; // 显示"-"dis_buf[5] = 0xbf; // 显示"-" }void delayms(unsigned char ms)// 延时子程序{unsigned char j;while(ms--){for(j = 0; j < 120; j++);}}。
用KeilC51开发定时器计数器
用KeilC51开发定时器计数器用Keil C51开发定时器/计数器原文:基本的51单片机内部有两个16位可编程的定时器/计数器T0和T1。
它们各自具有4种工作状态,其控制字和状态均在相应的特殊功能寄存器中,可以通过软件对控制寄存器编程设置,使其工作在不同的定时状态或计数状态。
现在,许多厂家生产的8051兼容单片机上,还加入了定时器/计数器2,使单片机的应用更为灵活,适应性更强。
很多8051单片机的书籍都对定时器/计数器有详细的介绍,我们在此不再详细地讨论。
但因为编写或或阅读程序时经常要查阅定时器/计数器的设置情况,因此我们仅对一些编程时经常要用到的较重要的寄存器和设置方式进行简要简介。
1 定时器/计数器简介8051单片机的定时器/计数器基本结构如图1-1所示,定时器T0由两个8位计数器TH0和TL0构成,定时器T1也由两个8位计数器TH1和TL1构成,TMOD寄存器控制定时器的工作方式,TCON寄存器控制定时器的启动和停止以及定时器的状态。
图1-1 定时器/计数器结构在作定时器使用时,输入的时钟脉冲是由晶体振荡器的输出经12分频后得到的。
实际上,定时器就是单片机机器周期的计数器。
因为每个机器周期包含晶体振荡器的12个振荡周期,而每一个机器周期定时器加1,故其频率为晶振频率的1/12。
如果晶振频率为12MHz,则定时器每接收一个输入脉冲的时间为1?s。
选择计数器工作方式时,计数脉冲来自相应的外部输入引脚T0(P3.4)或T1(P3.5)。
在这种情况下,当检测到输入引脚上的电平由高跳变到低时,计数器就加1。
2 控制和状态寄存器(1)定时器控制寄存器(TCON)TCON为定时器/计数器的控制寄存器,同时也锁存外部中断请求标志,各位定义如下。
TF1:定时器/计数器1中断请求标志位。
当定时器计数满溢出回零时,由硬件置位,并可申请中断。
当CPU响应中断并进入中断服务程序后,TF1自动清零。
TR1:定时器/计数器1运行控制位,靠软件置位或清除。
c51高精准时钟c语言实用程序
//年误差2分钟#include<reg52.h>#include <string.h>#include <intrins.h>#define uchar unsigned char#define uint unsigned intsbit D0=P2^0; //** P0口sbit D1=P2^1; //** P0口sbit D2=P2^2; //** P0口sbit D3=P3^2;sbit D4=P3^4;sbit d0=P2^4;sbit d1=P2^5;sbit d2=P2^6;sbit d3=P2^7;sbit key0=P3^5;sbit key1=P3^6;sbit key2=P3^7;sbit key3=P1^3; //启动备用电源sbit clk1_164 = P3^2;sbit data_164 = P3^1; //数据线sbit clk_164 = P3^0; //时钟线sbit baoshi=P3^0;sbit led=P2^3;sbit led1=P0^3;sbit DQ = P3^3;//ds18B20温度传感器定义#define jump_ROM 0xCC //跳过ROM命令#define start 0x44#define read_EEROM 0xBE //读存储器命令uchar TMPH,TMPL,shi,shi1,shi2,fen,fen1,fen2,miao,miao1,miao2,ymonth,yday,m0,m1,m2,m3, sweek,ylmonth,ylmonth1,ylmonth2,ylday,ylday1,ylday2,num,week,temp,tt,tt1,b;uint yyear,ttm,mn,n;uchar code table[]={0x28,0xf9,0x4c,0x58,0x99,0x1a,0x0a,0xf8,0x08,0x18};uchar code tableweek[]={0,0,3,3,6,1,4,6,2,5,0,3,5};uchar code led_char[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};uchar code led_chardain1[]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};uchar nbai,nshi,nge,m,mbai,mshi,mge,a,b;uchar code table1[]={0x28,0xf9,0x4c,0x58,0x99,0x1a,0x0a,0xf8,0x08,0x18};uchar code table2[]={0x20,0xf1,0x44,0x50,0x91,0x12,0x02,0xf0,0x00,0x10};//点小数点uchar code temp_table1[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf};//数据最高位为1时数据表void delay3(uint s){uchar k;while(s--){for(k=0; k<125; k++);}}void init(){TMOD=0x11;TH0=(65536-50000)/256;TL0=(65536-50000)%256;EA=1;ET0=1;TR0=1;TH1=(65536-51182)/256;TL1=(65536-51182)%256;ET1=1;TR1=1;yyear=11;ymonth=12;yday=13;shi=18;fen=20;miao=0;P0 = 0xff;P1 = 0xff;clk_164 =0;// delay3(400);mn=50000;}void delay(uint z){uint x,y,n;if(shi<21){if(shi>5)n=20;}elsen=1;for(x=n;x>0;x--)for(y=z;y>0;y--);}void Conversion(bit c,uchar year,uchar month,uchar day);uchar code table_monthdate[]="0.31,59,90,120,151,181,212,243,273,304,334";//当前年每月前的总天数void delay1(uchar z){uchar x,y;for(x=80;x>0;x--)for(y=z;y>0;y--);}//21, 4, 19, 6, 21, 5, 21, 6,22, 6,22, 8, 23, 8, 24, 8, 24, 8, 24, 8,23, 8, 22///9, 6, 11,4, 9, 6, 10,6, 9,7, 9,7, 7, 8, 7, 9, 7, 9, 7, 9, 7,8, 7, 15///上面第一行数据为每月节气对应日期,15减去每月第一个节气,每月第二个节气减去15得第二行///这样每月两个节气对应数据都小于16,每月用一个字节存放,高位存放第一个节气数位存放///第二个节气的数据,可得下表uchar code table_jq[]={0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///1901 00x96, 0xA4, 0x96, 0x96, 0x97, 0x87, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, ///1902 120x96, 0xA5, 0x87, 0x96, 0x87, 0x87, 0x79, 0x69, 0x69, 0x69, 0x78, 0x78, ///1903 240x86, 0xA5, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x78, 0x87, ///1904 360x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///1905 480x96, 0xA4, 0x96, 0x96, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, ///19060x96, 0xA5, 0x87, 0x96, 0x87, 0x87, 0x79, 0x69, 0x69, 0x69, 0x78, 0x78, ///19070x86, 0xA5, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///19080x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///19090x96, 0xA4, 0x96, 0x96, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, ///19100x96, 0xA5, 0x87, 0x96, 0x87, 0x87, 0x79, 0x69, 0x69, 0x69, 0x78, 0x78, ///19110x86, 0xA5, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///19120x95, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///19130x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, ///19140x96, 0xA5, 0x97, 0x96, 0x97, 0x87, 0x79, 0x79, 0x69, 0x69, 0x78, 0x78, ///19150x96, 0xA5, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, ///19160x95, 0xB4, 0x96, 0xA6, 0x96, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x87, ///19170x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x77, ///19180x96, 0xA5, 0x97, 0x96, 0x97, 0x87, 0x79, 0x79, 0x69, 0x69, 0x78, 0x78, ///19190x96, 0xA5, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, ///19200x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x77, ///1922 0x96, 0xA4, 0x96, 0x96, 0x97, 0x87, 0x79, 0x79, 0x69, 0x69, 0x78, 0x78, ///1923 0x96, 0xA5, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, ///1924 0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x87, ///1925 0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///1926 0x96, 0xA4, 0x96, 0x96, 0x97, 0x87, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, ///1927 0x96, 0xA5, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///1928 0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, ///1929 0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///1930 0x96, 0xA4, 0x96, 0x96, 0x97, 0x87, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, ///1931 0x96, 0xA5, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///1932 0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///1933 0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///1934 0x96, 0xA4, 0x96, 0x96, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, ///1935 0x96, 0xA5, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///1936 0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///1937 0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///1938 0x96, 0xA4, 0x96, 0x96, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, ///1939 0x96, 0xA5, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///1940 0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///1941 0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///1942 0x96, 0xA4, 0x96, 0x96, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, ///1943 0x96, 0xA5, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///1944 0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, ///1945 0x95, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x77, ///1946 0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, ///1947 0x96, 0xA5, 0xA6, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, ///1948 0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x79, 0x78, 0x79, 0x77, 0x87, ///1949 0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x77, ///1950 0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, ///1951 0x96, 0xA5, 0xA6, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, ///1952 0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, ///1953 0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x78, 0x79, 0x78, 0x68, 0x78, 0x87, ///1954 0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///1955 0x96, 0xA5, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, ///1956 0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, ///1957 0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///1958 0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///1959 0x96, 0xA4, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///1960 0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///1961 0x96, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///1962 0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///1963 0x96, 0xA4, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///19640x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///19660x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///19670x96, 0xA4, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///19680xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///19690x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///19700x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, ///19710x96, 0xA4, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///19720xA5, 0xB5, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///19730x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///19740x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x77, ///19750x96, 0xA4, 0xA5, 0xB5, 0xA6, 0xA6, 0x88, 0x89, 0x88, 0x78, 0x87, 0x87, ///19760xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, ///19770x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x78, 0x87, ///19780x96, 0xB4, 0x96, 0xA6, 0x96, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x77, ///19790x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///19800xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x77, 0x87, ///19810xA5, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, ///1982 ********* 0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x77, ///1983 ********* 0x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x87, ///19840xA5, 0xB4, 0xA6, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, ///19850xA5, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, ///19860x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x79, 0x78, 0x69, 0x78, 0x87, ///19870x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, ///19880xA5, 0xB4, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///19890xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, ///19900x95, 0xB4, 0x96, 0xA5, 0x86, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///19910x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, ///19920xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///19930xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///19940x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x76, 0x78, 0x69, 0x78, 0x87, ///19950x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, ///19960xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///19970xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///19980x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///19990x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, ///20000xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///20010xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///20020x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///20030x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, ///20040xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///20050xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, ///20060x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, ///20070x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x87, 0x78, 0x87, 0x86, ///20080xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, ///20100x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x78, 0x87, ///20110x96, 0xB4, 0xA5, 0xB5, 0xA5, 0xA6, 0x87, 0x88, 0x87, 0x78, 0x87, 0x86, ///2012 ******* 0xA5, 0xB3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x87, ///2013*******0xA5, 0xB4, 0xA6, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, ///2014 ******** 0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, ///2015 ******** 0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x87, 0x88, 0x87, 0x78, 0x87, 0x96, ///2016 ******* 0xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x86, ///2017*******0xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, ///2018 ******* 0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, ///2019 ******* 0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x87, 0x87, 0x87, 0x78, 0x87, 0x96, ///2020 ******* 0xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, ///2021*******0xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, ///2022 ******0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///2023 ******* 0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x97, 0x88, 0x87, 0x78, 0x87, 0x96, ///2024 ******** 0xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, ///2025********0xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///2026 ******* 0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///2027 ******0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x78, 0x87, 0x96, ///2028 ******0xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, ///2029******0xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///2030 ****** 0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, ///2031 *****0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x78, 0x87, 0x96, ///2032 *****0xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, ///2033******0xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x78, 0x88, 0x78, 0x87, 0x87, ///20340xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, ///20350x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x78, 0x87, 0x96, ///20360xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, ///20370xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///20380xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, ///20390x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x78, 0x87, 0x96, ///20400xA5, 0xC3, 0xA5, 0xB5, 0xA5, 0xA6, 0x87, 0x88, 0x87, 0x78, 0x87, 0x86, ///20410xA5, 0xB3, 0xA5, 0xB5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, ///20420xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, ///20430x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x88, 0x87, 0x96, ///20440xA5, 0xC3, 0xA5, 0xB4, 0xA5, 0xA6, 0x87, 0x88, 0x87, 0x78, 0x87, 0x86, ///20450xA5, 0xB3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x87, ///20460x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA5, 0x97, 0x87, 0x87, 0x88, 0x86, 0x96, ///2048 0xA4, 0xC3, 0xA5, 0xA5, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x78, 0x87, 0x86, ///2049 0xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x78, 0x78, 0x87, 0x87 ///2050};code uchar year_code[597]={0x04,0xAe,0x53, //1901 00x0A,0x57,0x48, //1902 30x55,0x26,0xBd, //1903 60x0d,0x26,0x50, //1904 90x0d,0x95,0x44, //1905 120x46,0xAA,0xB9, //1906 150x05,0x6A,0x4d, //1907 180x09,0xAd,0x42, //1908 210x24,0xAe,0xB6, //19090x04,0xAe,0x4A, //19100x6A,0x4d,0xBe, //19110x0A,0x4d,0x52, //19120x0d,0x25,0x46, //19130x5d,0x52,0xBA, //19140x0B,0x54,0x4e, //19150x0d,0x6A,0x43, //19160x29,0x6d,0x37, //19170x09,0x5B,0x4B, //19180x74,0x9B,0xC1, //19190x04,0x97,0x54, //19200x0A,0x4B,0x48, //19210x5B,0x25,0xBC, //19220x06,0xA5,0x50, //19230x06,0xd4,0x45, //19240x4A,0xdA,0xB8, //19250x02,0xB6,0x4d, //19260x09,0x57,0x42, //19270x24,0x97,0xB7, //19280x04,0x97,0x4A, //19290x66,0x4B,0x3e, //19300x0d,0x4A,0x51, //19310x0e,0xA5,0x46, //19320x56,0xd4,0xBA, //19330x05,0xAd,0x4e, //19340x02,0xB6,0x44, //19350x39,0x37,0x38, //19360x09,0x2e,0x4B, //19370x0C,0x95,0x53, //1939 0x0d,0x4A,0x48, //1940 0x6d,0xA5,0x3B, //1941 0x0B,0x55,0x4f, //1942 0x05,0x6A,0x45, //1943 0x4A,0xAd,0xB9, //1944 0x02,0x5d,0x4d, //1945 0x09,0x2d,0x42, //1946 0x2C,0x95,0xB6, //1947 0x0A,0x95,0x4A, //1948 0x7B,0x4A,0xBd, //1949 0x06,0xCA,0x51, //1950 0x0B,0x55,0x46, //1951 0x55,0x5A,0xBB, //1952 0x04,0xdA,0x4e, //1953 0x0A,0x5B,0x43, //1954 0x35,0x2B,0xB8, //1955 0x05,0x2B,0x4C, //1956 0x8A,0x95,0x3f, //1957 0x0e,0x95,0x52, //1958 0x06,0xAA,0x48, //1959 0x7A,0xd5,0x3C, //1960 0x0A,0xB5,0x4f, //1961 0x04,0xB6,0x45, //1962 0x4A,0x57,0x39, //1963 0x0A,0x57,0x4d, //1964 0x05,0x26,0x42, //1965 0x3e,0x93,0x35, //1966 0x0d,0x95,0x49, //1967 0x75,0xAA,0xBe, //1968 0x05,0x6A,0x51, //1969 0x09,0x6d,0x46, //1970 0x54,0xAe,0xBB, //1971 0x04,0xAd,0x4f, //1972 0x0A,0x4d,0x43, //1973 0x4d,0x26,0xB7, //1974 0x0d,0x25,0x4B, //1975 0x8d,0x52,0xBf, //1976 0x0B,0x54,0x52, //1977 0x0B,0x6A,0x47, //1978 0x69,0x6d,0x3C, //1979 0x09,0x5B,0x50, //1980 0x04,0x9B,0x45, //19810x0A,0x4B,0x4d, //19830xAB,0x25,0xC2, //19840x06,0xA5,0x54, //19850x06,0xd4,0x49, //19860x6A,0xdA,0x3d, //19870x0A,0xB6,0x51, //19880x09,0x37,0x46, //19890x54,0x97,0xBB, //19900x04,0x97,0x4f, //19910x06,0x4B,0x44, //19920x36,0xA5,0x37, //19930x0e,0xA5,0x4A, //19940x86,0xB2,0xBf, //19950x05,0xAC,0x53, //19960x0A,0xB6,0x47, //19970x59,0x36,0xBC, //19980x09,0x2e,0x50, //1999 294 0x0C,0x96,0x45, //2000 297 0x4d,0x4A,0xB8, //20010x0d,0x4A,0x4C, //20020x0d,0xA5,0x41, //20030x25,0xAA,0xB6, //20040x05,0x6A,0x49, //20050x7A,0xAd,0xBd, //20060x02,0x5d,0x52, //20070x09,0x2d,0x47, //20080x5C,0x95,0xBA, //20090x0A,0x95,0x4e, //20100x0B,0x4A,0x43, //20110x4B,0x55,0x37, //20120x0A,0xd5,0x4A, //20130x95,0x5A,0xBf, //20140x04,0xBA,0x53, //20150x0A,0x5B,0x48, //20160x65,0x2B,0xBC, //20170x05,0x2B,0x50, //20180x0A,0x93,0x45, //20190x47,0x4A,0xB9, //20200x06,0xAA,0x4C, //20210x0A,0xd5,0x41, //20220x24,0xdA,0xB6, //20230x04,0xB6,0x4A, //20240x69,0x57,0x3d, //20250x0d,0x26,0x46, //2027 0x5e,0x93,0x3A, //2028 0x0d,0x53,0x4d, //2029 0x05,0xAA,0x43, //2030 0x36,0xB5,0x37, //2031 0x09,0x6d,0x4B, //2032 0xB4,0xAe,0xBf, //2033 0x04,0xAd,0x53, //2034 0x0A,0x4d,0x48, //2035 0x6d,0x25,0xBC, //2036 0x0d,0x25,0x4f, //2037 0x0d,0x52,0x44, //2038 0x5d,0xAA,0x38, //2039 0x0B,0x5A,0x4C, //2040 0x05,0x6d,0x41, //2041 0x24,0xAd,0xB6, //2042 0x04,0x9B,0x4A, //2043 0x7A,0x4B,0xBe, //2044 0x0A,0x4B,0x51, //2045 0x0A,0xA5,0x46, //2046 0x5B,0x52,0xBA, //2047 0x06,0xd2,0x4e, //2048 0x0A,0xdA,0x42, //2049 0x35,0x5B,0x37, //2050 0x09,0x37,0x4B, //2051 0x84,0x97,0xC1, //2052 0x04,0x97,0x53, //2053 0x06,0x4B,0x48, //2054 0x66,0xA5,0x3C, //2055 0x0e,0xA5,0x4f, //2056 0x06,0xB2,0x44, //2057 0x4A,0xB6,0x38, //2058 0x0A,0xAe,0x4C, //2059 0x09,0x2e,0x42, //2060 0x3C,0x97,0x35, //2061 0x0C,0x96,0x49, //2062 0x7d,0x4A,0xBd, //2063 0x0d,0x4A,0x51, //2064 0x0d,0xA5,0x45, //2065 0x55,0xAA,0xBA, //2066 0x05,0x6A,0x4e, //2067 0x0A,0x6d,0x43, //2068 0x45,0x2e,0xB7, //20690x05,0x2d,0x4B, //20700x8A,0x95,0xBf, //20710x0A,0x95,0x53, //20720x0B,0x4A,0x47, //20730x6B,0x55,0x3B, //20740x0A,0xd5,0x4f, //20750x05,0x5A,0x45, //20760x4A,0x5d,0x38, //20770x0A,0x5B,0x4C, //20780x05,0x2B,0x42, //20790x3A,0x93,0xB6, //20800x06,0x93,0x49, //20810x77,0x29,0xBd, //20820x06,0xAA,0x51, //20830x0A,0xd5,0x46, //20840x54,0xdA,0xBA, //20850x04,0xB6,0x4e, //20860x0A,0x57,0x43, //20870x45,0x27,0x38, //20880x0d,0x26,0x4A, //20890x8e,0x93,0x3e, //20900x0d,0x52,0x52, //20910x0d,0xAA,0x47, //20920x66,0xB5,0x3B, //20930x05,0x6d,0x4f, //20940x04,0xAe,0x45, //20950x4A,0x4e,0xB9, //20960x0A,0x4d,0x4C, //20970x0d,0x15,0x41, //20980x2d,0x92,0xB5, //2099};code uchar day_code1[9]={0x0,0x1f,0x3b,0x5a,0x78,0x97,0xb5,0xd4,0xf3};code uint day_code2[3]={0x111,0x130,0x14e};bit c_moon;data uchar year_moon,month_moon,day_moon;bit get_moon_day(uchar month_p,uint table_addr)//函数功能:输入BCD阳历数据,输出BCD阴历数据(只允许1901-2099年){/*子函数,用于读取数据表中农历月的大月或小月,如果该月为大返回1,为小返回0*/uchar temp;switch (month_p){case 1:{temp=year_code[table_addr]&0x08;if (temp==0)return(0);else return(1);}case 2:{temp=year_code[table_addr]&0x04;if (temp==0)return(0);else return(1);}case 3:{temp=year_code[table_addr]&0x02;if (temp==0)return(0);else return(1);}case 4:{temp=year_code[table_addr]&0x01;if (temp==0)return(0);else return(1);}case 5:{temp=year_code[table_addr+1]&0x80;if (temp==0) return(0);else return(1);}case 6:{temp=year_code[table_addr+1]&0x40;if (temp==0)return(0);else return(1);}case 7:{temp=year_code[table_addr+1]&0x20;if (temp==0)return(0);else return(1);}case 8:{temp=year_code[table_addr+1]&0x10;if (temp==0)return(0);else return(1);}case 9:{temp=year_code[table_addr+1]&0x08;if (temp==0)return(0);else return(1);}case 10:{temp=year_code[table_addr+1]&0x04;if (temp==0)return(0);else return(1);}case 11:{temp=year_code[table_addr+1]&0x02;if (temp==0)return(0);else return(1);}case 12:{temp=year_code[table_addr+1]&0x01;if (temp==0)return(0);else return(1);}case 13:{temp=year_code[table_addr+2]&0x80;if (temp==0)return(0);else return(1);}}}void Conversion(bit c,uchar year,uchar month,uchar day){ //c=0 为21世纪,c=1 为19世纪输入输出数据均为BCD数据uchar temp1,temp2,temp3,month_p;uint temp4,table_addr;bit flag2,flag_y;/* temp1=year/16; //BCD->hex 先把数据转换为十六进制temp2=year%16;year=temp1*10+temp2;temp1=month/16;temp2=month%16;month=temp1*10+temp2;temp1=day/16;temp2=day%16;day=temp1*10+temp2; *///定位数据表地址if(c==0){table_addr=(year+0x64-1)*0x3;}else {table_addr=(year-1)*0x3;}//定位数据表地址完成//取当年春节所在的公历月份temp1=year_code[table_addr+2]&0x60;temp1=_cror_(temp1,5);//取当年春节所在的公历月份完成//取当年春节所在的公历日temp2=year_code[table_addr+2]&0x1f;//取当年春节所在的公历日完成// 计算当年春年离当年元旦的天数,春节只会在公历1月或2月if(temp1==0x1){temp3=temp2-1;}else{temp3=temp2+0x1f-1;}// 计算当年春年离当年元旦的天数完成//计算公历日离当年元旦的天数,为了减少运算,用了两个表//day_code1[9],day_code2[3]//如果公历月在九月或前,天数会少于0xff,用表day_code1[9],//在九月后,天数大于0xff,用表day_code2[3]//如输入公历日为8月10日,则公历日离元旦天数为day_code1[8-1]+10-1//如输入公历日为11月10日,则公历日离元旦天数为day_code2[11-10]+10-1if (month<10){temp4=day_code1[month-1]+day-1;}else{temp4=day_code2[month-10]+day-1;}if ((month>0x2)&&(year%0x4==0)){ //如果公历月大于2月并且该年的2月为闰月,天数加1temp4+=1;}//计算公历日离当年元旦的天数完成//判断公历日在春节前还是春节后if (temp4>=temp3){ //公历日在春节后或就是春节当日使用下面代码进行运算temp4-=temp3;month=0x1;month_p=0x1; //month_p为月份指向,公历日在春节前或就是春节当日month_p指向首月flag2=get_moon_day(month_p,table_addr); //检查该农历月为大小还是小月,大月返回1,小月返回0flag_y=0;if(flag2==0)temp1=0x1d; //小月29天else temp1=0x1e; //大月30天temp2=year_code[table_addr]&0xf0;temp2=_cror_(temp2,4); //从数据表中取该年的闰月月份,如为0则该年无闰月while(temp4>=temp1){temp4-=temp1;month_p+=1;if(month==temp2){flag_y=~flag_y;if(flag_y==0)month+=1;}else month+=1;flag2=get_moon_day(month_p,table_addr);if(flag2==0)temp1=0x1d;else temp1=0x1e;}day=temp4+1;}else{ //公历日在春节前使用下面代码进行运算temp3-=temp4;if (year==0x0){year=0x63;c=1;}else year-=1;table_addr-=0x3;month=0xc;temp2=year_code[table_addr]&0xf0;temp2=_cror_(temp2,4);if (temp2==0)month_p=0xc;else month_p=0xd; ///*month_p为月份指向,如果当年有闰月,一年有十三个月,月指向13,无闰月指向12*/flag_y=0;flag2=get_moon_day(month_p,table_addr);if(flag2==0)temp1=0x1d;else temp1=0x1e;while(temp3>temp1){temp3-=temp1;month_p-=1;if(flag_y==0)month-=1;if(month==temp2)flag_y=~flag_y;flag2=get_moon_day(month_p,table_addr);if(flag2==0)temp1=0x1d;else temp1=0x1e;}day=temp1-temp3+1;}c_moon=c; //HEX->BCD ,运算结束后,把数据转换为BCD数据temp1=year/10;temp1=_crol_(temp1,4);temp2=year%10;year_moon=temp1|temp2;temp1=month/10;temp1=_crol_(temp1,4);temp2=month%10;month_moon=temp1|temp2;temp1=day/10;temp1=_crol_(temp1,4);temp2=day%10;day_moon=temp1|temp2;year_moon=year;month_moon=month;day_moon=day;}void delay2(uint N){int i;for(i=0;i<N;i++);}uchar Reset(void){uchar deceive_ready;delay2(10);DQ=0; //拉低DQ线delay2(29); //延时至少480us~960usDQ=1; //将DQ线设置位逻辑高delay2(3); //延时等待deceive_ready响应deceive_ready=DQ; //采样deceive_ready信号delay2(25); //等待时序结束return(deceive_ready); //有deceive_ready信号时返回0,否则返回1}uchar read_bit(void){uchar i;DQ=0; //拉低DQ线开始时序DQ=1; //升高DQ线for(i=0;i<3;i++); //延时至时序开始15usreturn(DQ); //返回DQ值}void write_bit(uchar bitval){DQ=0; //拉低DQ线开始时序if(bitval==1)DQ=1; //如果写逻辑为高delay2(5); //延时DQ=1; //升高DQ线}/****************************************************************************** /void write_byte(uchar val){uchar i,temp;for(i=0;i<8;i++){temp=val>>i; //将val位右移i位赋值给比temptemp=temp&0x01; //取temp最低位write_bit(temp);delay2(5); //延时至时序结束}}/****************************************************************************** /uchar read_byte(void){uchar i,m,receive_data;m=1;receive_data=0; //初始化for(i=0;i<8;i++){if(read_bit()){receive_data=receive_data+(m<<i);} //每读一位数据据,左移一位delay2(6); //延时至时序结束}return(receive_data); //返回value}void wr_byte(data_temp,n){ uchar i,m;m=temp_table1[n];for(i=0;i<8;i++){if((m&0x80)==0x80)data_164 =1; //164数据线elsedata_164 =0;m<<=1;clk_164 =0; //164时钟线clk_164 =1; //164时钟线}for(i=0;i<8;i++){if(data_temp&0x80)data_164 =1; //164数据线elsedata_164 =0;data_temp<<=1; //clk_164 =0; //164时钟线clk_164 =1; //164时钟线}clk1_164=0;// delay(3);clk1_164=1;}void Get_temp(void){Reset();write_byte(jump_ROM); //发跳过ROM命令write_byte(start); //发启动转换命令Reset();write_byte(jump_ROM); //发跳过ROM命令write_byte(read_EEROM); //发跳过ROM命令TMPL=read_byte(); //读低8位温度值TMPH=read_byte(); //读高8位温度值}void keyskan(){if(m0>0){if(key0==1){temp++;if(temp>8)temp=0;m0=0;tt1=0;}}if(temp==1){if(m1>0){if(key1==1){miao++;if(miao==60)miao=0;m1=0;tt1=0;}}if(m2>0){if(key2==1){miao--;if(miao==(-1))miao=60;m2=0;tt1=0;}}}if(temp==2){if(m1>0){if(key1==1){fen++;if(fen==60)fen=0;m1=0;tt1=0;}}if(m2>0){if(key2==1){fen--;if(fen==(-1))fen=60;m2=0;tt1=0;}}}if(temp==3){if(m1>0){if(key1==1){shi++;if(shi==24)shi=0;m1=0;tt1=0;}}if(m2>0){if(key2==1){shi--;if(shi==(-1))shi=23;m2=0;tt1=0;}}}if(temp==4){if(m1>0){if(key1==1){yyear++;if(yyear>99)yyear=0;m1=0;tt1=0;}}if(m2>0){if(key2==1){yyear--;if(yyear==(-1))yyear=99;m2=0;tt1=0;}}}if(temp==5){if(m1>0){if(key1==1){ymonth++;if(ymonth>12)ymonth=1;m1=0;tt1=0;}}if(m2>0){if(key2==1){ymonth--;if(ymonth<1)ymonth=12;m2=0;tt1=0;}}}if(temp==6){if(m1>0){if(key1==1){yday++;if(ymonth==1){if(yday>31)yday=1;}if(ymonth==2){if(yyear%4==0){if(yday>29)yday=1;}if(yyear%4!=0){if(yday>28)yday=1;}}if(ymonth==3){if(yday>31)yday=1;}if(ymonth==4) {if(yday>30)yday=1;}if(ymonth==5) {if(yday>31)yday=1;}if(ymonth==6) {if(yday>30)yday=1;}if(ymonth==7) {if(yday>31)yday=1;}if(ymonth==8) {if(yday>31)yday=1;}if(ymonth==9) {if(yday>30)yday=1;}if(ymonth==10) {if(yday>31)yday=1;}if(ymonth==11) {if(yday>30)yday=1;}if(ymonth==12) {。
Keil C51程序设计中几种精确延时方法 精确延时
Keil C51程序设计中几种精确延时方法2008-04-03 08:48实现延时通常有两种方法:一种是硬件延时,要用到定时器/计数器,这种方法可以提高CPU的工作效率,也能做到精确延时;另一种是软件延时,这种方法主要采用循环体进行。
1 使用定时器/计数器实现精确延时单片机系统一般常选用11.059 2 MHz、12 MHz或6 MHz晶振。
第一种更容易产生各种标准的波特率,后两种的一个机器周期分别为1 μs和2 μs,便于精确延时。
本程序中假设使用频率为12 MHz的晶振。
最长的延时时间可达216=65 536 μs。
若定时器工作在方式2,则可实现极短时间的精确延时;如使用其他定时方式,则要考虑重装定时初值的时间(重装定时器初值占用2个机器周期)。
在实际应用中,定时常采用中断方式,如进行适当的循环可实现几秒甚至更长时间的延时。
使用定时器/计数器延时从程序的执行效率和稳定性两方面考虑都是最佳的方案。
但应该注意,C51编写的中断服务程序编译后会自动加上PUSH ACC、PUSH PSW、POP PSW和POP ACC语句,执行时占用了4个机器周期;如程序中还有计数值加1语句,则又会占用1个机器周期。
这些语句所消耗的时间在计算定时初值时要考虑进去,从初值中减去以达到最小误差的目的。
2 软件延时与时间计算在很多情况下,定时器/计数器经常被用作其他用途,这时候就只能用软件方法延时。
下面介绍几种软件延时的方法。
2.1 短暂延时可以在C文件中通过使用带_NOP_( )语句的函数实现,定义一系列不同的延时函数,如Delay10us( )、Delay25us( )、Delay40us( )等存放在一个自定义的C文件中,需要时在主程序中直接调用。
如延时10 μs 的延时函数可编写如下:void Delay10us( ) {_NOP_( );_NOP_( );_NOP_( )_NOP_( );_NOP_( );_NOP_( );}Delay10us( )函数中共用了6个_NOP_( )语句,每个语句执行时间为1 μs。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
用C51语言实现单片机
高精度定时的新算法
刘 帆1林育兹1戴玉珍2
(1.厦门大学机电工程系,厦门 361005;2.上海电子信息职业技术学院,上海 201411)
摘要为提高8051单片机定时精度,扩展8051系列单片机的用途,本文分析了8051系列单片机定时器溢出中断与CPU响应中断的时间误差。
在此基础上,提出了应用C51高级语言对多个定时器进行精确定时的误差补偿方法,并且使用Keil Uvision2仿真调试软件搭建一段数字显示式倒计时的实例程序,通过分析和调试误差补偿算法,使8051单片机在多个定时器同时使用的情况下,定时误差最终小于3个机器周期,是目前8051单片机高精度定时的一种新算法。
关键词:单片机;定时器;中断误差;C51
A New Arithmetic to Improve the Multi-timer Timing
Accuracy by Using C51 Language with 8051 MCU
Liu Fan1Lin Yuzi1Dai Yuzhen2
(1.Department of Electrical and Mechanical Engineering, Xiamen University, Xiamen 361005;
2. Shanghai Technical Institute of Electronics & Information, Shanghai 201411)
Abstract In order to improve the timing accuracy of 8051MCU and expand the purpose for the series of 8051MCU, this paper analyzed the timing error between the timer interrupt overflow and interrupt response of CPU. It gives the way of solving the multi-timer timing error by using C51 advanced language. Besides it uses the simulate software —— Keil Uvision2 to build a Single chip control digital display of a counting down program, through analyzing and debug the compensate counting of error, it finally makes the error of timing for 8051MCU below 3 machine period in the condition of using the multi-timer timing.
Key words:8051 MCU;timer;timing error;C51
1引言
对于8051系列单片机的定时应用已经有不少讨论,有的利用汇编语言对其定时器的中断时间进行误差补偿[4],但该方法的系统编程繁琐,且开发周期长等;有的利用C51程序实现T0的精确定时[5],但其算法仅适用于某些特定情况,且稳定性不高。
因此,本文针对单片机多个定时器同时使用的定时误差进行分析,提出应用C51高级语言进行高精确定时的新算法,以解决上述定时方法所存在的问题,并给出了应用实例,具有一定的实用性。
2定时误差产生的原因
产生定时误差的主要原因有:①定时器产生溢出中断信号时,CPU正在执行某指令;②定时器溢出中断信号时,CPU正在执行某中断服务程序。
2.1定时器工作方式的分析
8051单片机的2个定时/计数器,有方式0~方式3的4种工作方式。
本文以最常用的定时方式1作讨论,其工作原理如图1所示。
由图1可知,单片机工作在方式1时,若定时/计数值计满溢出后,则定时/计数器的初值将被置零,并继续从THx=0x00,TLx=0x00开始计数。
由于定时初值不会被重新装入,所以需要利用程序重新对THx、TLx赋值。
但赋值操作需要占用一定时间,如果不补偿,会造成下一次定时/计数溢出中断信号的时间与理想值不符(即误差)。
2008年第3期 34
2008年第3期 35
图1 工作方式1(16位定时/计数器)示意图
2.2 影响定时器误差的因素
由单片机原理可知,定时器的定时最大误差max t Δ为
()max 0123osc 12
t T T T T f Δ=+++× (1)
式中,T 0为中断转移指令周期;T 1为中断服务程序的执行时间;T 2为中断返回指令的执行周期;T 3为中断返回断点后执行下一条指令的周期;osc f 为系统基准频率(MHz );12是8051系列单片机的固定分频倍数。
2.3 定时误差的非固定性特点
由式(1)看出,对于不同指令对应的T 0~T 3
有很大差异,即定时器的定时误差具有非固定性特点。
在多个定时器同时使用时,误差的非固定性更明显。
如第一个循环CPU 正在执行某中断服务程序a ,第二个循环CPU 正要执行中断服务程序b ,这时误差将远远大于CPU 执行一条指令(如MOV )时的误差。
误差的非固定性不仅给误差分析带来不便,而且对于高精度定时任务将产生不良影响,如造成转速测试不准、定时误差过大。
3 定时误差的修正算法
由于定时器溢出中断与处理器响应中断的时间误差的非固定性,所以不能简单依靠设置一个固定补偿参数来修正。
为此,本文尝试采用对每次中断进行动态补偿的方式进行修正(简称新算法)
,其算法流程如图2所示。
若单片机工作于方式1(或方式0),当中断溢出信号产生时,THx 、TLx 重新由0开始计数。
因为在用户对定时器赋初值前的一刻,THx 、TLx 中的计数值就代表误差012
3T T T T Δ+Δ+Δ+Δ的时间。
所以,将THx 、TLx 从溢出到用户对其重新赋值时已有的计数值读出,并补偿到计数初值当中,就可以消除定时/计数器的连续定时误差。
图2 修正算法流程
4 新算法的应用实例
现代工业通常使用的低频脉冲发生器、低速测速系统、时间控制系统等,均需要高定时精度、周期长的时钟信号作为时间基准。
使用新算法,可使上述系统得到很高的定时精度。
图3给出了一个由单片8051 MCU 应用新算法构成倒计时系统的实例。
图3 单芯片控制数码管显示倒计时系统实例框图
期。