找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2998|回复: 15
收起左侧

单片机定时器程序 时间有误差,不知差在哪里?

[复制链接]
ID:678280 发表于 2021-9-7 16:39 | 显示全部楼层 |阅读模式
本意是每分钟响一声,现在出了两个菜鸟搞不明白的问题:
一是第一次响声短暂,符合预期,之后的响声延长了。
二是运行几分钟后时间误差变大,它跑得快了。谢谢你的观看和指正。

单片机源程序如下:
#include<reg52.h>  //GTX-1C学习板,单片机STC89C52RC,晶振11.0592MHz
#define uint unsigned int
sbit led0=P1^0;
sbit beep=P2^3;
uint num;
void delay(uint xms)
{
   uint i,j;
   for(i=xms; i>0; i--)
     for(j=110; j>0; j--); }

void main()
{
  TMOD=0x01;  //设置定时器0为工作方式1(M1M0为01)。
  TH0=(65536-45872)/256;  //装初值。11.0592M晶振,定时50ms数为45872。
  TL0=(65536-45872)%256;
  EA=1;   //开总中断。
  ET0=1;  //开定时器0中断。
  TR0=1;  //启动定时器0。
  while(1)
  {  if(num==1200) //1200X50ms=60000ms÷1000=60s=1分钟。
     {
       num=0;
       beep=0;
       delay(250);
       beep=1;
     }

    led0=0;
} }

void T0_time()interrupt 1
{  TH0=(65536-45872)/256;  //重装初值
   TL0=(65536-45872)%256;
   num++;
}

回复

使用道具 举报

ID:213173 发表于 2021-9-7 18:47 | 显示全部楼层
定时器中断周期越短越容易调整精度
  1. #include<reg52.h>  //GTX-1C学习板,单片机STC89C52RC,晶振11.0592MHz
  2. #define uint unsigned int
  3. #define uchar unsigned char
  4. #define us_100 10000 //+/-us_100调整计时精度
  5. sbit led0=P1^0;
  6. sbit beep=P2^3;
  7. uint num;
  8. uchar sec;
  9. bit min;

  10. void delay(uint xms)
  11. {
  12.         uint i,j;
  13.         for(i=xms; i>0; i--)
  14.                 for(j=110; j>0; j--);
  15. }

  16. void Timer0Init()                //100微秒@11.0592MHz
  17. {
  18.         TMOD= 0x02;                //设置定时器8位自动重载模式
  19.         TL0 = 0xA4;                //设置定时初值
  20.         TH0 = 0xA4;                //设置定时重载值
  21.         TR0 = 1;                        //定时器0开始计时
  22.         EA=1;                           //开总中断。
  23.         ET0=1;                          //开定时器0中断。
  24. }

  25. void main()
  26. {
  27.         Timer0Init();
  28.         while(1)
  29.         {
  30.                 if(min)
  31.                 {
  32.                         min=0;
  33.                         beep=0;
  34.                         delay(250);
  35.                         beep=1;
  36.                 }
  37.         }
  38. }

  39. void T0_time()interrupt 1
  40. {
  41.         num++;
  42.         if(num>=us_100)
  43.         {
  44.                 num=0;
  45.                 led0=~led0;
  46.                 sec++;
  47.                 if(sec>=60)
  48.                 {
  49.                         sec=0;
  50.                         min=1;
  51.                 }
  52.         }
  53. }
复制代码

评分

参与人数 1黑币 +15 收起 理由
一片一城 + 15 很给力!

查看全部评分

回复

使用道具 举报

ID:332444 发表于 2021-9-7 19:05 | 显示全部楼层
受中断响应速度影响,合理的程序可能影响小些也是有影响的,因此才有时钟芯片。
回复

使用道具 举报

ID:624769 发表于 2021-9-7 19:25 | 显示全部楼层
提几个小建议,
1, STC89C52RC  是有 T2 定时器, 并且支持自动重载功能的。STCMCU点COM 上面有手册可以下载。用那个会准的多。
2,STC89C52RC 的T2L, T2H  支持 SFR16 的定义, 可以直接接收 双字节的赋值
3,不知道你用的什么编译器,在51单片机很多编译器中,unsigned int 是 单字节的 等同于 unsigned char, 如果你要确保是双字节的 最好用 unsigned short num; 来声明 num变量。
最后说一下,不要把C++的很多习惯带到单片机里来, 最好用最保险的 CHAR SHORT LONG 来定义 单,双,四字节,绝对不会有歧义。INT这种 可单可双还可四的最好不要用。
回复

使用道具 举报

ID:678280 发表于 2021-9-7 20:10 | 显示全部楼层
wulin 发表于 2021-9-7 18:47
定时器中断周期越短越容易调整精度

试过你改写的代码了,发光管隔1秒一亮,响声始终是短暂的,符合预期。时间精度也高多了,估计是不使用时钟模块所能达到的最高精度。

谢谢。稍后我去理解你写的代码。
回复

使用道具 举报

ID:678280 发表于 2021-9-7 20:12 | 显示全部楼层
xianfajushi 发表于 2021-9-7 19:05
受中断响应速度影响,合理的程序可能影响小些也是有影响的,因此才有时钟芯片。

感谢你的指导。
回复

使用道具 举报

ID:678280 发表于 2021-9-7 20:19 | 显示全部楼层
188610329 发表于 2021-9-7 19:25
提几个小建议,
1, STC89C52RC  是有 T2 定时器, 并且支持自动重载功能的。STCMCU点COM 上面有手册可以下 ...

感谢你的指导

snap.jpg

这是我现在使用的编译器。
回复

使用道具 举报

ID:57657 发表于 2021-9-7 20:47 | 显示全部楼层
尽量少用循环语句实现的软件延时,时间会被中断打乱。
回复

使用道具 举报

ID:57657 发表于 2021-9-7 21:37 | 显示全部楼层
  1. #include "STC89C5xRC.H"
  2. #define u8 unsigned char
  3. sbit led0 = P1 ^ 0;
  4. sbit beep = P2 ^ 3;
  5. void InitTimer0(){
  6.     TMOD = 0x01;
  7.     TH0 = 0x4C;
  8.     TL0 = 0x00;
  9.     EA = 1;
  10.     ET0 = 1;
  11.     TR0 = 1;
  12. }
  13. void main() {
  14.     InitTimer0();
  15.     WDT_CONTR = 0x27;        //开看门狗
  16.     while (1) {
  17.         WDT_CONTR |= 0x10;      //看门狗喂狗
  18.     }
  19. }

  20. void Timer0Interrupt() interrupt 1{     //11.0592Mhz 50ms定时器中断
  21.     static u8 ms50 = 0, sec = 0;
  22.     TH0 = 0x4C;
  23.     TL0 = 0x00;
  24.     if (++ms50 >= 20) {
  25.         ms50 = 0;
  26.         if (++sec >= 60) {
  27.             sec = 0;
  28.         }
  29.     }
  30.     beep = !(sec == 0 && ms50 < 5);
  31.     if (ms50 == 0 || ms50 == 10) {
  32.         led0 = !led0;
  33.     }
  34. }
复制代码

评分

参与人数 1黑币 +15 收起 理由
一片一城 + 15 很给力!

查看全部评分

回复

使用道具 举报

ID:401564 发表于 2021-9-7 23:12 | 显示全部楼层
定时器可以这么用,但你的代码本身就是错误的
if(num==1200); num是全局变量,而且是在中断中增加的,随时会变的
如果是在比较if(num==1200)的过程中num的值刚好增加
而且,你之后有代码又有延时,不知道延时不多长,但定时器可是自动加载工作的,num的值是一直在变化的
错过了if(num==1200),比如大于,那就是会一直加到65535然后,归零之后再加....
你可以改成if(num>=1200)试一下
下面的延时改动一下,它会影响闪灯的时间
回复

使用道具 举报

ID:678280 发表于 2021-9-8 11:52 | 显示全部楼层

我下载了原本没有的“STC89C5xRC.H”,之后试过你改写的代码,精度更高了,谢谢。
回复

使用道具 举报

ID:678280 发表于 2021-9-8 13:44 | 显示全部楼层
Y_G_G 发表于 2021-9-7 23:12
定时器可以这么用,但你的代码本身就是错误的
if(num==1200); num是全局变量,而且是在中断中增加的,随时会 ...

谢谢!稍后我修改代码再试。
回复

使用道具 举报

ID:883242 发表于 2021-9-9 19:48 | 显示全部楼层
delay(250);这句完全多余,if(num==1200)本身就是延迟的意思。
回复

使用道具 举报

ID:853217 发表于 2021-9-9 20:27 | 显示全部楼层
你这个同时用代码延时和中断延时肯定会有误差的,因为当中断到来时,delay语句必定会因为中断停止自加的。每来一次中断delay语句那里就会停止一次,久而久之,蜂鸣器响的时间会越来越长。
回复

使用道具 举报

ID:965160 发表于 2021-9-9 20:42 | 显示全部楼层
可能程序自身有延迟
回复

使用道具 举报

ID:831736 发表于 2021-10-11 17:19 | 显示全部楼层
50Ms初值50000*11.0592/12=46080你算错了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表