~~~ 延时不准,请大家来帮忙啊!!!~~~ 我头大了好久了,谢谢先!!

2020-02-04 09:19发布

我用STC89C52RC做的一个测温湿度的节点,测试发现定时不准,我不知道为什么。我想问下面3个问题:
【1】我的程序和实现是否有问题?
【2】这个误差是否会与环境温度有关系? (节点测得的温度就是它的工作的环境温度,传感器和单片机、晶振都在一起。)
【3】除了上面2点,可能造成这个问题的原因还有什么?

我是这样实现的:
【1】用Timer0,计时50ms,晶振11.0592MHz


【2】在Timer0的中断程序里面,计数每20个50ms就是1秒,然后每到一定时间(比如5分钟)就将 sendDataFlag置1一次,


【3】main函数里面是while的死循环,如果sendDataFlag为1,就向上位机发送一次温湿度值,并把sendDataFlag置0。


下面上位机收到的结果,


我们可以看到有段时间7:29至8:19这段时间定时比较准,每5分钟一次
但是6:43至7点23这段时间定时就非常不准。

在我以往的试验中,也有这样不准的情况出现,每5分钟就会有1秒左右的误差。下面是昨晚试验的数据:



请大家指点一下,我迷惑了很久了。谢谢先!
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
53条回答
packer
1楼-- · 2020-02-09 08:14
LZ你把你T0中断服务程序中开始的“TH0 = 0x4C;”改成如下这样试试:
TR0 = 0;
TH0 = 0x4C-TH0;
TR0 = 1;


如果还不行,用方式2吧。
haozi007
2楼-- · 2020-02-09 09:49
 精彩回答 2  元偷偷看……
eduhf_123
3楼-- · 2020-02-09 10:36
回复【48楼】eduhf_123  经历
lz你把你t0中断服务程序中开始的“th0 = 0x4c;”改成如下这样试试:
tr0 = 0;
th0 = 0x4c-th0;
tr0 = 1;
如果还不行,用方式2吧。
-----------------------------------------------------------------------

感谢回复  不过我试了一下,误差比原来的还大。据我分析,应该是定时器被关闭后,没有补偿这3行代码执行时间所致。

不过我受你这个启发,把关闭、打开定时器代码去掉,只重装了th0,下面是执行的效果:(这是logger里面出来的数据,可以看到毫秒)

11:54:07.500 data received: ID 100 1 - 50 0 22 6, Length:5.
11:59:11.734 data received: ID 100 1 - 50 0 22 4, Length:5.
12:04:13.890 data received: ID 100 1 - 49 0 22 5, Length:5.
12:09:14.906 data received: ID 100 1 - 49 0 22 4, Length:5.
12:14:15.062 data received: ID 100 1 - 49 0 22 5, Length:5.
12:19:15.625 data received: ID 100 1 - 49 0 22 5, Length:5.
12:24:15.781 data received: ID 100 1 - 49 0 22 4, Length:5.
12:29:16.250 data received: ID 100 1 - 49 0 22 4, Length:5.


奇怪的是5分钟的定时飘忽不定,再次印证了42楼说的,但是为什么会这样呢?
jiwm
4楼-- · 2020-02-09 11:09
来跟大家汇报一下今天晚上的测试结果,貌似准确了。

方法就是受48楼的朋友启发,重装TH0时考虑已有的计数进位,只是我没有关计数器。

我很惊讶,为什么中断跳转过程难道需要这么长时间?

下面是刚刚出炉的测试数据:
haozi007
5楼-- · 2020-02-09 16:21
这是我很不喜欢用c的原因,尤其在对延时有较高要求的情况,我都要查看下汇编,是否有隐患
haozi007
6楼-- · 2020-02-09 19:15
回复【51楼】haozi007  
来跟大家汇报一下今天晚上的测试结果,貌似准确了。
方法就是受48楼的朋友启发,重装th0时考虑已有的计数进位,只是我没有关计数器。
我很惊讶,为什么中断跳转过程难道需要这么长时间?
下面是刚刚出炉的测试数据:

-----------------------------------------------------------------------

这个数据现在只能说定时看起来准确了,其实问题很严重:看你在50楼发上来的数据就知道了,每两次收到数据的时间都比5分钟略长一点(头两行间隔的4秒多认为是上位机的问题,其他时间间隔最长的也有2.156秒,2156ms/300s≈72ms/s,快到1%了)。

不是说“中断跳转过程”有这么长,而在于在中断跳转之前还有“中断响应时间”存在(比如有很长一段关中断的时间、比如更高优先级或同级的中断服务程序在运行)。



如果一定要时间很准确,就一定要解决上面两个问题:

1、要减小关中断引起的中断响应延迟,就要尽量减少关中断、及时要关也尽量晚关并尽量早开(关中断过程中只处理临界区代码)。

2、要解决更高级中断服务程序或同优先级中断服务程序引起的中断响应延迟,就只能把T0的中断单独设为高优先级。这时候就只剩低优先级可以用了,如果其余中断服务程序还要区分优先级怎么办呢?
  这时候可以通过类似Linux中对中断服务程序的“上半段+下半段”处理方式给最低优先级的中断服务程序加个“壳”,让它们在主程序优先级上运行。
  具体的实现方式就是,把“中优先级”中断跟“低优先级”中断在IP中的对应位都设为0,但在“低优先级”中断服务程序中只把
实际的中断服务程序的入口地址给压入堆栈就返回,实际的中断服务程序写成一般子程序的形式(函数头部后面不带“interrupt N”),最后禁止这个子程序与其他子程序进行覆盖分析就好(或者把它的局部变量都定义成static的)。
  原理在于,“低优先级”中断服务程序中把实际入口压入堆栈后返回,编译器生成的是RETI指令,这时候就把51的“优先级锁”给复位了,CPU运行在主程序级别上,可以响应任何新的中断请求,但由于我们的压栈操作,这里的“返回”并没有回到主程序中的“断点”,而是“返回”到了实际的中断服务程序入口,在实际的中断服务程序返回的时候才真正返回到原来“断点”——这样就实现了在主程序级别上运行的“低优先级”中断服务程序,当然了,实际的中断服务程序中还是要保护“现场”。

一周热门 更多>