搜索
bottom↓
回复: 52

8051计时精度很低,怎么回事呢

[复制链接]

出0入0汤圆

发表于 2012-5-29 21:27:40 | 显示全部楼层 |阅读模式
本帖最后由 boxindang 于 2012-5-30 09:44 编辑

用定时器0计时,外部中断触发,外部触发信号是方波,相当于测量方波的周期,当方波周期为1ms以下时,基本没误差,周期越长误差越大,到40ms时误差到225.3us,40ms时理论计时数为60000,实际计数值是60332(这是用串口将数据发送到电脑上显示的),因为是用于精密测量,精度要求高,这个误差太大了,不知道是我编程的原因,还是单片机本身功能限制,不明白为什么周期越长误差越大,恳请各位高手解惑!方波先后用过信号发生器、另一块单片机发送、单片机+AD9833发送,都是这个规律。曾怀疑是不是时间基准不准确,用示波器测量方波周期,40ms以下还是正确的,不知道是不是示波器本身精度也达不到。这是代码:
# include <C8051f340.h>
# include <stdio.h>
# include <intrins.h>
#include <string.h>
#define SYSCLK      6000000           // SYSCLK frequency in Hz
#define BAUDRATE        9600           // Baud rate of UART in bps
                                                         
int a,b;                                                                       //存储定时器0寄存器的数据
int flag;                                                                      //是否向串口传送数据标志
void Timer0_Init(void);                                        //用来计时
void Ext_Interrupt_Init (void);                        //外部中断初始化
void UART0_Init (void);                                               //串口初始化
void senddata(int a);
//端口初始化
void PORT_Init (void)
{  
XBR0= 0x01;//端口I/O交叉开关寄存器0,UART TX0, RX0 连到端口引脚 P0.4 和 P0.5            
XBR1= 0x40;//端口I/O交叉开关寄存器1,交叉开关使能                     
P0MDOUT= 0x10;//P0.4为推挽输出,其他的为漏极开路输出
}
//时钟初始化
void SYSCLK_Init (void)
{
  OSCICN |= 0x82;                     // Configure internal oscillator for        2分频        
   RSTSRC  = 0x04;                     // Enable missing clock detector
}
//主程序
void main(void)
{
flag=0;
PCA0MD &= ~0x40;                    // WDTE = 0 (clear watchdog timer                                       
PORT_Init ();
SYSCLK_Init ();          
Timer0_Init();                                        //用来计时
Ext_Interrupt_Init ();                        //外部中断初始化
UART0_Init ();
EA=1;
while(1)
{
while(flag==1)
{
     senddata(b);
     senddata(a);
     flag=0;
}       
   }
//串口初始化
void UART0_Init (void)
{
   SCON0 = 0x10;                       // SCON0: 8-bit variable bit rate
                                       //        level of STOP bit is ignored
                                       //        RX enabled
                                       //        ninth bits are zeros
                                       //        clear RI0 and TI0 bits
   if (SYSCLK/BAUDRATE/2/256 < 1) {
      TH1 = -(SYSCLK/BAUDRATE/2);
      CKCON &= ~0x0B;                  // T1M = 1; SCA1:0 = xx
      CKCON |=  0x08;
   } else if (SYSCLK/BAUDRATE/2/256 < 4) {
      TH1 = -(SYSCLK/BAUDRATE/2/4);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 01                  
      CKCON |=  0x01;
   } else if (SYSCLK/BAUDRATE/2/256 < 12) {
      TH1 = -(SYSCLK/BAUDRATE/2/12);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 00
   } else {
      TH1 = -(SYSCLK/BAUDRATE/2/48);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 10
      CKCON |=  0x02;
   }

   TL1 = TH1;                          // Init Timer1
   TMOD &= ~0xf0;                      // TMOD: timer 1 in 8-bit autoreload
   TMOD |=  0x20;                       
   TR1 = 1;                            // START Timer1
   TI0 = 1;                            // Indicate TX0 ready
}
//定时器0初始化,计时定时器         
void Timer0_Init(void)
{
   TMOD |= 0x01;                        // Timer0 in 16-bit mode
      CKCON |= 0x01;          // Timer0 uses a 1:4 prescaler
}
//外部中断0初始化
void Ext_Interrupt_Init (void)
{
  TCON |= 0x01;                        // /INT 0 are edge triggered
  IT01CF = 0x08;                      // /INT0 active high; /INT0 on P0.0                                 
  EX0 = 1;                            // Enable /INT0 interrupts

}
//外部中断0程序
void INT0_ISR (void) interrupt 0
{EA=0;
  TR0=0;                    // Timer0 Off         当收到外部中断信号时停止计时
  while(TR0==0)
{ a=TL0;                                                  //存放TL0的值
  b=TH0;                                                 //存放TH0的值
flag=1;
TL0=0;                                                          //0定时器清0
  TH0=0;
  TCON |= 0x10;              // Timer0 ON
  }
  EA=1;
}
//串口传送数据程序
void senddata(int a)
{
SBUF0=a;
while(!TI0)
{;}
TI0=0;
}                  

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2012-5-29 21:37:16 | 显示全部楼层
不明白为什么周期越长误差越大,跪求...


your code is wrong.

but since you 跪求, you don't deserve to be helped.

出0入25汤圆

发表于 2012-5-29 21:51:34 | 显示全部楼层
检查计数器是否溢出。

单片机的硬件计数器精度是非常高的,理论误差是正负1个计数时钟。

出0入0汤圆

 楼主| 发表于 2012-5-29 21:54:16 | 显示全部楼层
本帖最后由 boxindang 于 2012-5-29 22:02 编辑
millwood0 发表于 2012-5-29 21:37
your code is wrong.

but since you 跪求, you don't deserve to be helped.


好吧,这只是一个网络用语,表达一下急切的心情,和恳切的态度,如果让这位大神厌恶,那我也只能说不好意思。不过还是想向这位大神提下建议,不要这么随意的说一个人"don't deserve "。。。

出0入25汤圆

发表于 2012-5-29 21:57:53 | 显示全部楼层
boxindang 发表于 2012-5-29 21:54
好吧,这只是一个网络,表达一下急切的心情,和恳切的态度,如果让这位大神厌恶,那我也只能说不好意思。 ...

这个论坛不需要你用这种方式表达急切的心情,心平气和的发帖就行。

本来技术就是需要积累的,急急急,是急不来的。

出0入0汤圆

发表于 2012-5-29 22:15:03 | 显示全部楼层
恩,以前也“跪求”了一次,結果被各种鄙视,以后就不敢了
话说单片机的硬件计数器的确是很高精度的,你的时钟有多高精度,基本上计数器和定时器就有这么高的精度,应该还是程序哪里不对。
看看是不是设置的16为计数器,

出0入0汤圆

发表于 2012-5-29 22:53:58 | 显示全部楼层
详细看下规格书。定时器还是非常准的。

出0入0汤圆

发表于 2012-5-29 23:21:23 | 显示全部楼层
我看不懂楼主的C代码
但是我想说:
你的
信号要加到INT0/1上
定时器将GATE和TR均设定为“1”
这时候
当输入信号的上升沿让定时器立即启动
下降沿则一箭双雕的停止定时器并申请中断
。。。。。。

出0入0汤圆

发表于 2012-5-29 23:38:00 来自手机 | 显示全部楼层
我的也有这种情况,刚接触这款

出0入0汤圆

 楼主| 发表于 2012-5-30 09:19:31 | 显示全部楼层
本帖最后由 boxindang 于 2012-5-30 09:23 编辑
lxa0 发表于 2012-5-29 23:21
我看不懂楼主的C代码
但是我想说:
你的


我使用了int0,,将外部信号加到int0,上升沿触发中断,进入中断后先关闭定时器,处理好数后,将定时器寄存器清0,再开定时器。像楼上所说将GATE置1,也尝试过,这样测得是方波高电平部分,可是也有误差

出0入0汤圆

发表于 2012-5-30 15:20:25 | 显示全部楼层
boxindang 发表于 2012-5-30 09:19
我使用了int0,,将外部信号加到int0,上升沿触发中断,进入中断后先关闭定时器,处理好数后,将定时器寄 ...

可能你关定时器,处理完后再开,处理数据这里的时间误差.理论上定时要不关,一直跑的

出0入0汤圆

发表于 2012-5-30 15:46:37 | 显示全部楼层
打个比方:你的表按下计时开始后每分钟会闹响一声,随时可以停掉它并重新开始。

正常情况你每听到一次闹响声计1分钟,这样60响后你就计到一小时了。

可是你现在的计方法却是先按开始计时钮,到一分钟闹响后你按停止计时钮,然后找笔计下现在次数,再按开始计时钮……

出0入0汤圆

发表于 2012-5-30 16:14:54 | 显示全部楼层
INT0_ISR 这个函数编译成汇编语言到底是什么样子的?

出0入0汤圆

 楼主| 发表于 2012-5-30 17:43:45 | 显示全部楼层
adcr 发表于 2012-5-30 15:46
打个比方:你的表按下计时开始后每分钟会闹响一声,随时可以停掉它并重新开始。

正常情况你每听到一次闹响 ...

当时有考虑到这个处理数据的时间差,所以我把定时器寄存器的数给a,b后,把标志flag置1,然后到主程序把数给串口,以此希望减少中断中定时器开启和关闭之间的时间。其实也没有指望计时精度真达到机器周期,毕竟进中断出中断保护现场之类的都需要时间,但是绝不会到达几百us,而且不明白为什么受到方波周期长短的影响那么大。按照楼上的说法,是不是可以过了n个(比如10个)上升沿之后一次读出定时器的数呢,我来尝试下

出0入0汤圆

发表于 2012-5-30 18:33:34 | 显示全部楼层
C8051f340有PCA捕获干嘛不用,测周期、测脉宽最方便

出0入0汤圆

发表于 2012-5-30 19:16:52 | 显示全部楼层
用自动重装会不会好点,51的指令周期不等长,所以响应该中断的误差肯定有的,而且有可能会累积

出0入0汤圆

 楼主| 发表于 2012-5-30 19:17:31 | 显示全部楼层
不知道大家有过这样要求高精度计时的经历吗?测试时用的什么信号源呢?会不会和方波本身精度有关系啊

出0入0汤圆

发表于 2012-5-30 19:34:39 | 显示全部楼层
信号源。。。至少也得是个普通的晶振啊,有条件咋也得用温补的。。。

出0入0汤圆

 楼主| 发表于 2012-5-30 19:37:09 | 显示全部楼层
用过信号发生器、单片机、单片机+AD9833三种信号源,同测40ms周期的方波,每一种都不一样,用单片机+AD9833测得误差是最小的,但也有几百us,同一种信号源测得数据比较稳定

出0入0汤圆

 楼主| 发表于 2012-5-30 23:14:51 | 显示全部楼层
huayuliang 发表于 2012-5-30 19:34
信号源。。。至少也得是个普通的晶振啊,有条件咋也得用温补的。。。

按道理说,单片机精度应该很高啊,用单片机控制管脚翻转,产生方波,现在不知道是方波精度有问题,还是计时程序有问题,或者都有问题。。。
这是方波程序:
#include <C8051F340.h>                 // SFR declarations

#define SYSCLK             12000000/8  // SYSCLK in Hz (12 MHz internal
                                       // oscillator / 8)
                                       // the internal oscillator has a
                                       // tolerance of +/- 2%

#define AUX1     2500               //经过计算方波周期为40ms
#define AUX2     -AUX1

#define TIMER2_RELOAD            AUX2  // Reload value for Timer2

sbit fangbo = P2^2;                       

void Port_Init (void);                 // Port initialization routine
void Timer2_Init (void);               // Timer2 initialization routine

sfr16 TMR2RL = 0xCA;                   // Timer2 Reload Register
sfr16 TMR2 = 0xCC;                     // Timer2 Register

//-----------------------------------------------------------------------------
// main() Routine
//-----------------------------------------------------------------------------

void main (void)
{
   PCA0MD &= ~0x40;                    // Clear watchdog timer enable

   Timer2_Init ();                     // Initialize the Timer2
   Port_Init ();                       // Init Ports
   EA = 1;                             // Enable global interrupts

   while (1);                          // Loop forever
}

//-----------------------------------------------------------------------------
void Port_Init (void)
{
   XBR1 = 0x40;                        // Enable crossbar
   P2MDOUT = 0x04;                     // Set P2.2 to push-pull
}

//-----------------------------------------------------------------------------
// Timer2_Init
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void Timer2_Init(void)
{
   CKCON &= ~0x60;                     // Timer2 uses SYSCLK/12
   TMR2CN &= ~0x01;

   TMR2RL = TIMER2_RELOAD;             // Reload value to be used in Timer2
   TMR2 = TMR2RL;                      // Init the Timer2 register

   TMR2CN = 0x04;                      // Enable Timer2 in auto-reload mode
   ET2 = 1;                            // Timer2 interrupt enabled
}

void Timer2_ISR (void) interrupt 5
{
   fangbo = ~ fangbo;                        
   TF2H = 0;                           // Reset Interrupt
}

//-----------------------------------------------------------------------------
// End Of File
//-----------------------------------------------------------------------------

出0入0汤圆

 楼主| 发表于 2012-5-30 23:28:23 | 显示全部楼层
BXAK 发表于 2012-5-30 18:33
C8051f340有PCA捕获干嘛不用,测周期、测脉宽最方便

我的最终目的不是测周期,是计时,这只是个测试程序,想验证精度高不高,不过可以用捕捉试试测方波,确定一下是什么问题,谢谢~

出0入0汤圆

 楼主| 发表于 2012-5-30 23:32:09 | 显示全部楼层
hefq 发表于 2012-5-30 19:16
用自动重装会不会好点,51的指令周期不等长,所以响应该中断的误差肯定有的,而且有可能会累积 ...

因为是计时不是定时,并不是肯定会溢出的。累积误差的话,因为数据很稳定,并没有出现误差越来越大的情况,应该不是吧

出0入0汤圆

发表于 2012-5-31 00:05:44 | 显示全部楼层
boxindang 发表于 2012-5-30 23:28
我的最终目的不是测周期,是计时,这只是个测试程序,想验证精度高不高,不过可以用捕捉试试测方波,确定 ...

如果计时怎么不用定时器的外部计数功能

出0入0汤圆

发表于 2012-5-31 05:58:02 | 显示全部楼层
本帖最后由 millwood0 于 2012-5-31 06:07 编辑

here is how your code, after being corrected, counts a 20Hz pulse train (12Mhz crystal) - before correcting for latency.



Your code has such a poor result because it is so poorly written.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2012-5-31 06:11:21 | 显示全部楼层
本帖最后由 millwood0 于 2012-5-31 06:12 编辑

here is the same code, corrected for the latency, reading a 40Hz pulse train and returning 0x61a8 (=25000us), 0x61a9(=25001us), and 0x51aa(=25002us).

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2012-5-31 15:09:20 | 显示全部楼层
millwood0 发表于 2012-5-31 06:11
here is the same code, corrected for the latency, reading a 40Hz pulse train and returning 0x61a8 (= ...

嗯,感谢~最起码让我确定是代码的问题,这是我第一次试着编写单片机程序,一切都在摸索中,希望能够得到高手指点,都有哪些潜在因素呢?非常感谢~~

出0入0汤圆

 楼主| 发表于 2012-5-31 15:13:00 | 显示全部楼层
BXAK 发表于 2012-5-31 00:05
如果计时怎么不用定时器的外部计数功能

需要计从某时刻开始到一个脉冲到来的时间,计数的话是来一个外部脉冲计个数,这是不一样的吧

出0入0汤圆

发表于 2012-5-31 16:38:40 | 显示全部楼层
没看原文 但是我曾经能做到 一天 误差十几秒

出0入0汤圆

发表于 2012-5-31 18:17:32 | 显示全部楼层
一天 误差十几秒


most crystals are +-20ppm. so you should be missing not much more than 2 seconds / day, if your code is right.

出0入0汤圆

 楼主| 发表于 2012-5-31 19:09:34 | 显示全部楼层
snwuzhisheng 发表于 2012-5-31 16:38
没看原文 但是我曾经能做到 一天 误差十几秒

有哪些要注意的问题吗?

出0入0汤圆

发表于 2012-6-1 14:50:11 | 显示全部楼层
介个 还真忘记了 就是不断的调整 然后对着电脑时间等待测试

出0入0汤圆

 楼主| 发表于 2012-6-1 15:14:43 | 显示全部楼层
snwuzhisheng 发表于 2012-6-1 14:50
介个 还真忘记了 就是不断的调整 然后对着电脑时间等待测试

嗯,好的,十分感谢~我师兄用32位的单片机自己重编了,误差几us,让我接着做后续的,唉,这会不急了,再接着琢磨,真想知道怎么回事

出0入0汤圆

发表于 2012-6-1 16:48:39 | 显示全部楼层
玩32 很有前途 哈啊好

出0入0汤圆

发表于 2012-6-1 20:31:05 | 显示全部楼层
好精确,最少要用晶振,程序也要优化。

出0入0汤圆

 楼主| 发表于 2012-6-1 21:03:35 | 显示全部楼层
airfex 发表于 2012-6-1 20:31
好精确,最少要用晶振,程序也要优化。

不知道大致有哪些问题要注意些呢,今天根据汇编语言,计算了指令周期,考虑了中断响应时间,算出来的误差是几十us,不知道还有哪些需要算进去?

出0入0汤圆

 楼主| 发表于 2012-11-28 17:38:26 | 显示全部楼层
时隔许久,突然想起来没回个结果,程序修改后仿真效果很好,f=20hz,误差9us,但实际从单片机出来的数与示波器测量的数比较,相差40多us,也有可能实际的信号源精度不够,示波器精度不够,反正仿真和实际结果有较大差距

出0入0汤圆

发表于 2012-11-28 19:22:04 | 显示全部楼层
仔细看看啊,扫年,有问题先自己看啊

出0入0汤圆

 楼主| 发表于 2012-11-28 20:03:28 | 显示全部楼层
# include <C8051f340.h>
# include <stdio.h>
# include <intrins.h>
#include <string.h>
//#define uchar  unsigned char
#define SYSCLK      6000000           // SYSCLK frequency in Hz
#define BAUDRATE        9600           // Baud rate of UART in bps
uchar ;a,b,flag;
void Timer0_Init(void);                                        //用来计时
void Ext_Interrupt_Init (void);                        //外部中断初始化
void UART0_Init (void);
void senddata(unsigned char a);
void PORT_Init (void);
void SYSCLK_Init (void);
void delay(unsigned char m);
void main(void)
{
flag=0;
PCA0MD &= ~0x40;                    // WDTE = 0 (clear watchdog timer                                       // enable
PORT_Init ();
SYSCLK_Init ();          
Timer0_Init();                                        //用来计时
Ext_Interrupt_Init ();                        //外部中断初始化
UART0_Init ();
EA=1;
while(1)
{
while(flag==1)
{                                                                                                        
         senddata(b);
        delay(2);
    senddata(a);
    flag=0;
}       
   }
}
void delay(unsigned char m)
{
unsigned char n;
for (n=m;n>0;n--)
{
}
}       
void PORT_Init (void)
{  
XBR0= 0x01;//端口I/O交叉开关寄存器0,UART TX0, RX0 连到端口引脚 P0.4 和 P0.5            
XBR1= 0x40;//端口I/O交叉开关寄存器1,交叉开关使能                     
P0MDOUT= 0x10;//P0.4为推挽输出,其他的为漏极开路输出
}

void SYSCLK_Init (void)
{
  OSCICN |= 0x82;                     // Configure internal oscillator for        2分频        
   RSTSRC  = 0x04;                     // Enable missing clock detector
}

void UART0_Init (void)
{
   SCON0 = 0x10;                       // SCON0: 8-bit variable bit rate
                                       //        level of STOP bit is ignored
                                       //        RX enabled
                                       //        ninth bits are zeros
                                       //        clear RI0 and TI0 bits
   if (SYSCLK/BAUDRATE/2/256 < 1) {
      TH1 = -(SYSCLK/BAUDRATE/2);
      CKCON &= ~0x0B;                  // T1M = 1; SCA1:0 = xx
      CKCON |=  0x08;
   } else if (SYSCLK/BAUDRATE/2/256 < 4) {
      TH1 = -(SYSCLK/BAUDRATE/2/4);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 01                  
      CKCON |=  0x01;
   } else if (SYSCLK/BAUDRATE/2/256 < 12) {
      TH1 = -(SYSCLK/BAUDRATE/2/12);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 00
   } else {
      TH1 = -(SYSCLK/BAUDRATE/2/48);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 10
      CKCON |=  0x02;
   }

   TL1 = TH1;                          // Init Timer1
   TMOD &= ~0xf0;                      // TMOD: timer 1 in 8-bit autoreload
   TMOD |=  0x20;                       
   TR1 = 1;                            // START Timer1
   TI0 = 1;                            // Indicate TX0 ready
}
void Timer0_Init(void)
{
   TMOD |= 0x01;                        // Timer0 in 16-bit;         
   //CKCON |= 0x04;          // Timer0 uses a sysclk
        CKCON |= 0x01;                         //         Timer0 uses a sysclk/4
}
  void Ext_Interrupt_Init (void)
{
  TCON |= 0x01;                        // /INT 0 are edge triggered
  IT01CF = 0x08;                      // /INT0 active high; /INT0 on P0.0                                 
  EX0 = 1;                            // Enable /INT0 interrupts

}
void INT0_ISR (void) interrupt 0
{
TR0=0;                    // Timer0 Off         当收到外部中断信号时停止计时
a=TL0;                                                  //存放TL0的值
b=TH0;                                   //存放TH0的值
flag=1;  
TL0=0;                                                          //0定时器清0
TH0=0;
TR0=1;

     }                               

void senddata(unsigned char a)
{
SBUF0=a;
while(!TI0){; }
TI0=0;
}

出0入0汤圆

 楼主| 发表于 2012-11-28 20:05:51 | 显示全部楼层
用的串口调试助手,用方波触发,方波周期越长,误差越大,换了几种思路,测得值都很稳定,误差也一样

出0入0汤圆

发表于 2012-11-28 20:17:55 来自手机 | 显示全部楼层
定时中断给一个补偿值,也就是cpu压栈到定时器重新开始计数这个时间要补偿。

出0入0汤圆

发表于 2012-11-28 20:52:46 | 显示全部楼层
本帖最后由 bigeblis 于 2012-11-28 20:53 编辑

1、尽量用PCA捕获
2、精确测量尽量用汇编,可以直观操作寄存器,这种理论上不存在,实测却有的时域误差出现的可能性很小
3、几百us的误差你怀疑示波器?你用的是什么年代的古董啊?

出0入0汤圆

 楼主| 发表于 2012-11-29 09:56:26 | 显示全部楼层
korren 发表于 2012-11-28 20:17
定时中断给一个补偿值,也就是cpu压栈到定时器重新开始计数这个时间要补偿。 ...

谢谢!会试着尝试下。现在的问题是误差随着脉宽的增加而明显增加,cpu压栈到定时器重新开始计数这个时间不应该是保持稳定的吗?

出0入0汤圆

 楼主| 发表于 2012-11-29 11:08:44 | 显示全部楼层
本帖最后由 boxindang 于 2012-11-29 11:15 编辑

程序精简了,测得值挺稳定的,误差还是没变,随着周期增大而增大,用汇编试试,1khz的时候,T0理论值0x05DC,实测值0x05DA;50hz的时候,T0理论值0x7530,实测值0x75A0
# include <C8051f340.h>

#define SYSCLK      6000000           // SYSCLK frequency in Hz
uchar ;a,b;

void main(void)
{

PCA0MD &= ~0x40;                    // WDTE = 0 (clear watchdog timer      
                                // enable
XBR1= 0x40;                                                        //端口I/O交叉开关寄存器1,交叉开关使能

OSCICN |= 0x82;                     // Configure internal oscillator for        2分频  

TMOD |= 0x01;                        // Timer0 in 16-bit;         
CKCON |= 0x01;                         //         Timer0 uses a sysclk/4

TCON |= 0x01;                        // /INT 0 are edge triggered
IT01CF = 0x08;                      // /INT0 active high; /INT0 on P0.0                                 
EX0 = 1;                            // Enable /INT0 interrupts

EA=1;
while(1)
{
;       
   }
}

void INT0_ISR (void) interrupt 0
{
TR0=0;                    // Timer0 Off         当收到外部中断信号时停止计时
a=TL0;                  //存放TL0的值
b=TH0;                    //存放TH0的值  
TL0=0;                  //0定时器清0
TH0=0;
TR0=1;

     }                               

出0入0汤圆

发表于 2012-11-29 12:47:39 | 显示全部楼层
TL0不要写零,给个补偿值,也就是这些汇编执行的时间除以顶事器的分频比.
        RSEG  ?PR?INT0_ISR?MAIN
        USING        0
INT0_ISR:
        PUSH         PSW
        MOV          PSW,#00H
        PUSH         AR7
        USING        0
                        ; SOURCE LINE # 29
; {
; TR0=0;                    // Timer0 Off         当收到外部中断信号时停止计时
                        ; SOURCE LINE # 31
        CLR          TR0
; a=TL0;                  //存放TL0的值
                        ; SOURCE LINE # 32
        MOV          R7,TL0
        MOV          a?,#00H
        MOV          a?+01H,R7
; b=TH0;                    //存放TH0的值  
                        ; SOURCE LINE # 33
        MOV          R7,TH0
        MOV          b,#00H
        MOV          b+01H,R7
; TL0=0;                  //0定时器清0
                        ; SOURCE LINE # 34
        MOV          TL0,#00H
; TH0=0;
                        ; SOURCE LINE # 35
        MOV          TH0,#00H
; TR0=1;
                        ; SOURCE LINE # 36
        SETB         TR0

出0入0汤圆

发表于 2012-11-29 13:53:19 | 显示全部楼层
既然用了C8051F340,为什么不用好它自带的硬件功能模块呢?

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2012-11-29 16:56:51 | 显示全部楼层
eduhf_123 发表于 2012-11-29 13:53
既然用了C8051F340,为什么不用好它自带的硬件功能模块呢?

用了pca边沿捕捉,误差同样随着周期变长而变长,且误差比之前还大一些,20ms时误差90us

出0入0汤圆

 楼主| 发表于 2012-11-29 19:02:48 | 显示全部楼层
本帖最后由 boxindang 于 2012-11-29 19:40 编辑
korren 发表于 2012-11-28 20:17
定时中断给一个补偿值,也就是cpu压栈到定时器重新开始计数这个时间要补偿。 ...


谢谢提供的思路!我用pca试过之后发现误差还是随着周期增大而增大,然后就测量一系列数据,matlab建了个图表,发现实测值和理论值呈线性关系,所以觉定增加定时补偿,补偿后效果不错

出0入0汤圆

 楼主| 发表于 2012-11-29 19:48:23 | 显示全部楼层
korren 发表于 2012-11-29 12:47
TL0不要写零,给个补偿值,也就是这些汇编执行的时间除以顶事器的分频比.
        RSEG  ?PR?INT0_ISR?MAIN
        USING        0 ...

十分感谢!了解您说的意思,发现理论值和实测值存在这样的关系:实测值=1.004*理论值-15.53,现在按照这个关系补偿过了,20ms内误差保证在10us以内,已经基本满足要求,但还是很疑惑为什么会出现这种现象?是单片机内部时钟跑得不准的缘故吗?

出0入0汤圆

发表于 2012-11-29 20:16:21 | 显示全部楼层
不要忽略串口发送的时间,可是随着你的周期增加的

出0入0汤圆

 楼主| 发表于 2012-11-29 20:27:25 | 显示全部楼层
STM_FPGA 发表于 2012-11-29 20:16
不要忽略串口发送的时间,可是随着你的周期增加的

为了排除干扰,把程序精简,把串口部分删掉,直接查看寄存器的值,测量结果无变化

出0入0汤圆

发表于 2012-11-30 01:05:28 | 显示全部楼层
boxindang 发表于 2012-11-29 16:56
用了pca边沿捕捉,误差同样随着周期变长而变长,且误差比之前还大一些,20ms时误差90us ...

PCA的捕获功能测量出来的结果基本就能说明问题了——你的测量用时钟(这里就是C8051F340的片内时钟)频率不准。

出0入0汤圆

发表于 2012-11-30 11:21:01 | 显示全部楼层
晶振不准?

出0入0汤圆

发表于 2012-11-30 20:59:48 | 显示全部楼层
楼主  我也不知道说的对不对   你那里写成TR1=1;会不会写错啦  是不是TR0=1;
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-6-17 04:45

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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