//--------------------------------------------------------------------------// //针对论坛近期讨论激烈的“用51做实时时钟,不能有算法上带来的误差”问题特整理此贴// // 以供供初学者学习和行家参考 // //--------------------------------------------------------------------------// // 软件无累积误差的定时中断程序,修改晶振频率定义FREQ即可直接使用 // // 可以 时间周期 或 频率 为单位直接定义 // // Edit By xwj // //--------------------------------------------------------------------------// #include <REGX52.H> #define uchar unsigned char #define uint unsigned int #define ulong unsigned long
#define FREQ 12000000 // 晶振频率12MHz,换不同晶振改这个参数即可// #define MODIFY 14 // 从变量加载时的修正值// //#define MODIFY 13 // 从常量加载时的修正值//
//按时间计算定时重载值// // f单位:100uS,f<=655 // #define TIMEdata(f) (65536-(uint)((ulong)(1200000000)/FREQ*(f))+MODIFY) #define TIMEN(f) (500/f) // x次50mS// //按频率计算定时重载值// // f单位:Hz, f>25// #define FREQdata(f) (65536-(FREQ/12)/(f)+MODIFY) #define FREQdataH(f) ((FREQdata(f))/256) // 定时重载值高,f单位:Hz // #define FREQdataL(f) ((FREQdata(f))%256) // 定时重载值低,f单位:Hz // #define FREQN(f) (f/20) // x次50mS,有误差,f单位:Hz // #define FREQdata3(f) FREQdataH(f),FREQdataL(f),FREQN(f) /************************************** / unsigned char code FREQ_TAB[]= //频率 -- 定时器初值 { FREQdata3(450), //450Hz FREQdata3(500), //500Hz FREQdata3(1000), //1KHz FREQdata3(1300), //1.3KHz }; /**************************************/
// 全局变量 // unsigned int timeload; //定时器重载值 unsigned char timen_50mS; //50mS计数次数 unsigned char timeload_50mS; //50mS需要的次数 unsigned char time1s; //1S计数次数
/************************************/ void ext_int0() interrupt 0 { ; //原 中断 }
/**************************************/ void ext_int1() interrupt 2 { ; //原 中断 }
/**************************************/ void time1() interrupt 3 { ; //原 中断 }
/**************************************/ //--------//串口中断处理 void serial_int () interrupt 4 { ; }
/************************************/ void time0() interrupt 1 using 1 //25mS或1mS { unsigned int t0buf; EA=0; TR0=0; t0buf = timeload+TL0; //蜂鸣频率表 // t0buf = TIMEdata(250)+TL0; //蜂鸣频率表 TH0 = t0buf/256; TL0 = t0buf%256; TR0=1; EA=1; if((--timen_50mS)==0) //50mS计数次数 //50mS { timen_50mS=timeload_50mS; //重载50mS需要的次数 // timen_50mS=TIMEN(250); //重载50mS需要的次数
// KeyScan(); //键扫描,自己去增加你要的功能
if(--time1s==0) //1S { time1s=20; //改成10,就是每秒2次(500mS) // sec_add(); //加1秒,自己去增加你要的功能 } } }
/**************************************/ void system_init(void) //系统初始化 {
//------------------------------------------------//自己去根据需要修改 // SCON = 0x50; // SCON: 设串口模式1,并打开接受:1起始位,8数据位,1停止位// TMOD = 0x21; // TMOD: 定时器1:模式2:8位自加载;定时器2:模式1:16位 // TH1 = 0xEA; // TH1: 2400波特串口的自加载值,晶振频率: 20MHz // IP = 0x10; // IP: 串口中断优先 // IE = 0x92; // IE: 开串口中断,开定时器 1中断 // // TCON = 0x50; // TCON: 定时器 1运行;定时器 2运行 // TR1=1; timeload_50mS=TIMEN(250); //50mS需要的次数 timeload=TIMEdata(250); //定时器重载值 TH0=TIMEdata(250)/256;TL0=TIMEdata(250)%256; //25ms TR0=1; timen_50mS=timeload_50mS; //50mS计数次数 time1s=20; //1S计数次数 }
/**************************************/ void main (void) { P0=0xff; //自己去根据需要修改 // P1=0xff; //自己去根据需要修改 // P2=0xff; //自己去根据需要修改 // P3=0xff; //自己去根据需要修改 // system_init(); //系统初始化 // beepn(1);
while(1) { ; //改成你自己的程序 // if (key_ok) //按键处理程序 // do_key(); // if (inbufsign) // do_serial(getbyte ()); //串口数据处理程序 // delayms(10); //延时n mS } } /**************************************/
/************************************** / //汇编指令分析: //从TR0=0;到TR0=1;之间漏计14指令周期,故#define MODIFY 14--这就是没有误差的原理了 ; unsigned int t0buf; ; EA=0; ; SOURCE LINE # 76 CLR EA ; TR0=0; ; SOURCE LINE # 77 CLR TR0 ; t0buf = timeload+TL0; //蜂鸣频率表 ; SOURCE LINE # 78 MOV R7,TL0 MOV R6,#00H MOV A,timeload+01H ADD A,R7 MOV R7,A MOV A,R6 ADDC A,timeload ;---- Variable 't0buf?440' assigned to register 'R4/R5' ---- MOV R5,AR7 ; // t0buf = TIMEdata(250)+TL0; //蜂鸣频率表 ; TH0 = t0buf/256; ; SOURCE LINE # 80 MOV TH0,A ; TL0 = t0buf%256; ; SOURCE LINE # 81 MOV TL0,R7 ; TR0=1; ; SOURCE LINE # 82 SETB TR0 ; EA=1; ; SOURCE LINE # 83 SETB EA ;
//本程序由xwj设计的UltraEdit脚本加亮显示,如需要脚本访问我的Blog 或发送邮件至:xwjfile@21cn.com(xwjfile@21cn.com) |