打印

如何减少误差:外部中断INT0触发定时器T0,计数器T1的频率计

[复制链接]
10971|19
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
ANSI_gao|  楼主 | 2010-8-3 10:25 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 ANSI_gao 于 2010-8-3 10:26 编辑

这几天我做了一个外部中断信号INT0中断触发Timer0计时,Timer1计数 的仿真。误差很大。不知各位大侠有何减少误差的方法。
     关于LCD显示的函数和初始化函数、延时函数太繁琐就不一一列出了
     现在我把主函数和三个中断函数列出来:
/*********************************************************
程序功能:计数两个负边缘触发信号时间间隔T内的外部脉冲个数P
              并在LCD上显示出时间间隔T和脉冲数P
**********************************************************/
.................
/*******************************************************
函数功能:主函数
********************************************************/
void main(void)
{
   LcdInitiate();            //调用LCD初始化函数
  display_sym();             //在LCD上显示时间和脉冲提示符“T=”和“P=”
  display_val1(000);         //初始时在LCD上显示器工作正常标志“0.000”和“00000”
  display_val2(00000);
  display_unit();            //LCD显示时间单位“S”      
   TMOD=0x51;                //定时器T1工作于计数模式1,定时器T0工作于计时模式1;
    EA=1; EX0=1;               //开总中断,
    ET0=1;  
    IT0=1;
     while(1)                 //无限循环
      {
      while(flag==0);    //第一个下降沿启动T0,T1
          TH1=0;            //定时器T1高8位赋初值0
          TL1=0;            //定时器T1低8位赋初值0
          TR1=1;            //定时器T1启动 INT0下降沿触发
      count=0;                    //再赋初值
      TH0=-0x03;
        TL0=-0xe8;
      TR0=1;                    //启动定时器T0         
         while(flag==1);     //第二个下降沿关闭T0,T1
         TR0=0;                         //终止Timer0
         TR1=0;
         flag=0;
         pulse=(unsigned int)(TH1*256+TL1);
         pulse=65536*T1count+pulse;  //脉冲数
         display_val2(pulse);        //显示脉冲数
         display_val1(count);         //显示时间
         T1count=0;           
    }           
}
/*******************************************************
函数功能:定时器T0的中断服务函数
********************************************************/
void Time0(void ) interrupt 1  //定时器T0的中断编号为1,使用第1组工作寄存器
  {
    count++;          //T0每中断1次,count加1
    TH0=-0x03;       /1ms中断一次;
    TL0=-0xe8;
    }

void Int0(void) interrupt 0
  {
    flag++;
   }

void Time1(void) interrupt 3
  {
    T1count++;
    }
------------------------------------------------------------------------------------------------------
用脉冲数pulse除以(countX0.001)S得到脉冲的频率
但是仿真结果误差很大,而且我发现把TH0=-0x03;TL0=-0xe8;改为TH0=TH_M1;TL0=TL_M1;
(#define TH_M1 (65536-1000)/256
#define TL_M1 (65536-1000)%256   结果又不一样)
请各位大侠给点建议怎样才能减小误差。

相关帖子

沙发
欧阳青云| | 2010-8-3 10:28 | 只看该作者
我也想知道答案

使用特权

评论回复
板凳
bald| | 2010-8-3 11:12 | 只看该作者
INT0中断服务程序内把数据读出

没必要用那么多中断
T0和T1也无需重置
只需计算出中断间隔内T0和TI的增量值即可。
建议读出顺序为 TL0,TL1,TH0,TH1
还需重读TL0和TL1判断是否溢出并做相应的处理

许多活,在中断服务程序内做会简单一些。
无论用什么方式,首先应该确保中断服务程序内外处理同一数据不会发生竞争,依据这一原则再查一下你的程序吧。

使用特权

评论回复
地板
ANSI_gao|  楼主 | 2010-8-3 12:59 | 只看该作者
根据我要模拟的物理过程,相邻两个外部中断间大概有12000个脉冲,0.3秒。TL1是会溢出的。
我先按3楼的方法试试,如果不行再来讨教。

使用特权

评论回复
5
ANSI_gao|  楼主 | 2010-8-3 18:58 | 只看该作者
改了程序,误差减少了一些,但是还没达到要求。
再来请教。
我把程序改为这样:
/*******************************************************
函数功能:主函数
********************************************************/
void main(void)
{
   LcdInitiate();            //调用LCD初始化函数
  display_sym();             //显示时间和脉冲提示符
  display_val1(000);         //显示器工作正常标志
  display_val2(00000);
  display_unit();            //显示速度单位      
   TMOD=0x52;                //定时器T1工作于计数模式1,定时器T0工作于计时模式2;
    EA=1; EX0=1;               //开总中断
        ET0=1;  
        IT0=1;
        TH0=-0x64; TL0=-0x64;
        TH1=0;     TL1=0;
        while(flag==0);
        TR0=1;     TR1=1;
     while(1)                 //无限循环
      {
         display_val2(pulse);        //显示脉冲数
         display_val1(time);                 //显示时间                    
       }           
}
/*******************************************************
函数功能:定时器T0的中断服务函数
********************************************************/
void Time0(void ) interrupt 1  //定时器T0的中断编号为1,使用第1组工作寄存器
  {
    count++;          //T0每中断1次,count加1
        }

void Int0(void) interrupt 0
  {
    flag++;
        if(flag==1)                         //第一个外部中断到来
        {
          count=0;                          //初始化计时变量
          T1count=0;                       
          p=TH1*256+TL1;              //先读出TH1,TL1
          }
        if(flag==2)                           //第二个外部中断到来
        {
          time=count;                        //取出计时值
          T1count=((TF1==1)?1:0);   
          TF1=0;
          pulse=65536*T1count+TH1*256+TL1-p;      //计算两次外部中断之间的脉冲个数
          flag=0;                                                           //重置flag
          }
   }

使用特权

评论回复
6
ANSI_gao|  楼主 | 2010-8-3 19:00 | 只看该作者
顶起!不要这贴沉了。

使用特权

评论回复
7
流行音乐| | 2010-8-3 21:07 | 只看该作者
误差是指令执行时间造成的,建议使用定时器的捕获功能。

使用特权

评论回复
8
流行音乐| | 2010-8-3 21:21 | 只看该作者
问题太多了,
1:
        if(flag==1)                         //第一个外部中断到来
        {
          count=0;                          //初始化计时变量
        ......

    在将 count0 清零时,th1和tl1并没有清零。

2:
pulse=65536*T1count+TH1*256+TL1-p;   
上面这个表达式在执行时也会有较大的概率得到错误结果,因为count、th1、tl1有可能会在此时发生变化。
......

使用特权

评论回复
9
ANSI_gao|  楼主 | 2010-8-3 22:17 | 只看该作者
楼上说得对。
1.在将 count0 清零时,th1和tl1并没有清零。
但是个问题在相邻的两个外部中断之间引起的时间误差不会超过1ms。因为T0是1ms中断一次的。我是拿(count-0)来求时间间隔的。而仿真结果的误差绝对比这点误差大得多。
2.pulse=65536*T1count+TH1*256+TL1-p;   
上面这个表达式在执行时也会有较大的概率得到错误结果,因为count、th1、tl1有可能会在此时发生变化。

这个确实有可能,我是为了不中断T0,T1而这样写的。那该怎样写程序才好呢?
希望楼上说详细点,以解我之惑。

使用特权

评论回复
10
流行音乐| | 2010-8-4 11:08 | 只看该作者
建议换一个方法。请把你的要求说明一下:测量的频率范围,允许的误差范围。

使用特权

评论回复
11
ANSI_gao|  楼主 | 2010-8-4 12:12 | 只看该作者
频率40kHZ,误差:50HZ以内。

使用特权

评论回复
12
airwill| | 2010-8-4 12:17 | 只看该作者
我有一法, 测稳定信号的周期可以稳定精确到一个机器周期.

加 D 触发器, 将信号二分频.
将分频后的信号送 INT0, 设置 T1 的 GATE 位, 只允许 INT0 高电平时计数.
允许 INT0 下降沿中断. 在中断中读取 T1 的计数值, 就是信号的周期, 再清零准备下次计数.

用这个办法, 软件非常简单, 而且精度做到了最高.

使用特权

评论回复
13
airwill| | 2010-8-4 12:22 | 只看该作者
另外, 开 T0 的中断, 用于高位计数(高于16位), 这样计时长度可以扩展到 24bit, 乃至 32bit..., 要求 T0 中断优先级要高于 INT0

使用特权

评论回复
14
ANSI_gao|  楼主 | 2010-8-4 12:37 | 只看该作者
我看不懂啊。
D触发器是什么?将信号二分频是什么意思?是将信号分成两路还是将频率/2?
楼上可以解析一下吗?

使用特权

评论回复
15
bald| | 2010-8-4 13:31 | 只看该作者
本帖最后由 bald 于 2010-8-4 13:33 编辑

频率40kHZ,误差:50HZ以内

建议你直接用T0做gate,间隔用62.5mS,40KHz读数2500,误差在16Hz左右,

这样外部中断时只需读取即时数据后稍做处理就行了,程序又省事(读出乘16就行了)。至于冗余数据就不用去管它了。

使用特权

评论回复
16
流行音乐| | 2010-8-4 18:19 | 只看该作者
根据楼主的要求,我给出一个方案:
定时器1设置为外部计数模式,用于对被测信号计数。
用定时器0或定时器2做一个1ms的时钟中断。
每次测量800ms即可满足楼主的误差要求,因为:50/40000=1ms/800ms
在800ms内,40000Hz的信号有32000个周期计数,小于16位定时器1的计数上限,所以不需扩展定时器高字节。

需要主意的是,在1ms定时器中断中读取定时器1的外部计数值TH1和TL1时,需要防止误读,因为有可能在读取时TL1正好在向TH1进位。防止误读的方法如下:
CountH=TH1;
CountL=TL1;
if(CountH!=TH1)
{
  CountH=TH1;
  CountL=TL1;
}

使用特权

评论回复
17
ANSI_gao|  楼主 | 2010-8-5 12:43 | 只看该作者
问题解决了,谢谢各位!

使用特权

评论回复
18
16777216| | 2013-5-21 17:42 | 只看该作者
ANSI_gao 发表于 2010-8-5 12:43
问题解决了,谢谢各位!

楼主是怎么解决的 能告知一下么 我也在苦恼类似的问题

使用特权

评论回复
19
16777216| | 2013-5-21 20:12 | 只看该作者
airwill 发表于 2010-8-4 12:17
我有一法, 测稳定信号的周期可以稳定精确到一个机器周期.

加 D 触发器, 将信号二分频.

版主 你好 我现在用stcA60s2的51芯片 用了内部的pca的pwm产生 这该死的用了定时器0 (由于我的pwm频率需要在200HZ)

现在我只剩下一个T1 和一个能当一般定时的pca的一个模块了 怎么做到精准测频率

使用特权

评论回复
20
airwill| | 2013-5-21 21:03 | 只看该作者
T1 配合 INT1 也一样可以用 GATE 实现精确时间

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

0

主题

11

帖子

1

粉丝