找回密码
 立即注册

QQ登录

只需一步,快速开始

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

一个关于单片机硬件延时的疑惑

  [复制链接]
ID:518902 发表于 2021-11-21 21:54 | 显示全部楼层 |阅读模式
本帖最后由 laopihappy123 于 2021-11-22 20:21 编辑

关于单片机的定时,在我刚刚学习的时候,都是用软件实现的.但在软件进行延时的过程中,cpu时间被白白浪费了,此时能利用cpu的只有中断.


随着使用的深入,我越发觉得软件定时这是不是个好方法,虽然它很简单.

随后我对硬件定时进行了了解,发现替代的方法有个用定时器来搞的:
在定时器中断里设置一个标志位,每当定时器运行到设定的时间,就将该位置置位(定时短)或+1(定时时间较长),然后用外部程序去查询这个标志位,做判断.

但是我又有了个疑惑,对于后者,假如我要定时30毫秒,定时器溢出时间为1ms,标志位用8位无符号
那到了240的时候,240+30=14 ,溢出了~ 这个怎么处理?
难道我把最大阈值设定为225么? 那假如我有多个不同的延时需要进行处理呢?.... 没办法呀~ 难道要设置最大延时限制值么?...

让人很是摸不着头脑,求大家帮助一下我呀~

---------------分割线---------------
一天后更新,感谢大家的回复与指教,我看了感觉无地自容.....愧对坛龄,愧对老师....
大家的回复我都有好好看,我会好好学习的,谢谢大家
回复

使用道具 举报

ID:690448 发表于 2021-11-21 23:53 | 显示全部楼层
标志位可以随时清零。
等到了30ms 执行操作后,顺便清零标志位,



可以设置多个变量 例如 flag_300ms = 0;  上面每次到30ms 就加一;
然后大循环里面判断flag_300ms ==9,执行操作,清零  flag_300ms
回复

使用道具 举报

ID:161164 发表于 2021-11-22 01:30 | 显示全部楼层
标志位用完要清零
void timer0_int (void) interrupt 1  //1ms
{
        if(!TMR_01_DN)
          if(++TMR_01_OT>30)
            {
               TMR_01_DN = 1;
               TMR_01_OT = 0;  //clear count
            }
}
void main()
{
        if(TMR_01_DN)
          {TMR_01_DN = 0;  //clear flag
            //your code
          }
}
回复

使用道具 举报

ID:276663 发表于 2021-11-22 01:36 | 显示全部楼层
看样子,您还在单片机门槛上,没有了解有时间片轮,RTX-Tiny系统
  1. unsigned short TimeMs;//全局变量 short 65536一般够用了

  2. void TIM0_Init(void)                //设置定时器1毫秒一个中断,代码省略
  3. {}

  4. void TIM0_Int (void) interrupt 1
  5. {
  6.   if(TimeMs>0)//如果有延时
  7.       TimeMs--;
  8. }

  9. void Delay(unsigned short t)//short 65536一般够用了
  10. {
  11.         TimeMs = t;
  12.         while(TimeMs);
  13. }

  14. void main(void)
  15. {
  16.         while(1)
  17.         {
  18.                 P1^0 = 0X00;

  19.                 Delay(1000);
  20.                
  21.                 P1^0 = 0XFF;

  22.                 Delay(1000);
  23.         }
  24. }
复制代码

回复

使用道具 举报

ID:276663 发表于 2021-11-22 01:44 | 显示全部楼层
还有
  1. unsigned short TimeMs;//全局变量 short 65536一般够用了
  2. unsigned short Systick;//全局变量 short 65536一般够用了

  3. void TIM0_Init(void)                //设置定时器1毫秒一个中断,代码省略
  4. {}

  5. void TIM0_Int (void) interrupt 1
  6. {
  7.   if(TimeMs>0)//如果有延时
  8.       TimeMs--;
  9.   Systick++;
  10. }

  11. void Delay(unsigned short t)//short 65536一般够用了
  12. {
  13.         TimeMs = t;
  14.         while(TimeMs);
  15. }

  16. void Delay2(unsigned short t)//short 65536一般够用了
  17. {
  18.         unsigned short start;

  19.         start = Systick;
  20.         while((Systick-start) < t);
  21. }

  22. void main(void)
  23. {
  24.         while(1)
  25.         {
  26.                 P1^0 = 0X00;

  27.                 Delay(1000);
  28.                
  29.                 P1^0 = 0XFF;

  30.                 Delay(1000);
  31.         }
  32. }
复制代码
回复

使用道具 举报

ID:276663 发表于 2021-11-22 01:45 | 显示全部楼层
附上代码

p.zip

647 Bytes, 下载次数: 6

回复

使用道具 举报

ID:883242 发表于 2021-11-22 02:49 | 显示全部楼层
没有任何规定让你必须要用8位标志,255不够用换16位数据好了。
回复

使用道具 举报

ID:213173 发表于 2021-11-22 04:56 | 显示全部楼层
用软件延时的时间长度是无限值,定时器溢出时间为1ms,定时30毫秒只要计数30。使用后清0,怎么会溢出?240+30的算法又从何谈起?楼主提这个问题明显与坛龄不符。
回复

使用道具 举报

ID:891686 发表于 2021-11-22 08:12 | 显示全部楼层
定时器溢出时间为1ms,另外用一个变量做(加法或减法)计数器。计数到 30 ms 时,计数大器复位并执行相应的操作。

多个定时的情形,最容易理解的是实时时钟。定时器溢出时间为1ms,然后设置多个计数器变量:秒(两位)、分(两位)、时(两位)……以此类推。
回复

使用道具 举报

ID:57657 发表于 2021-11-22 08:54 | 显示全部楼层
无论是什么程序,至少开启一个定时器中断(通常为1mS),如果没有打开就是新人写的,按键消抖、流水灯、数码管动态扫描、延时任意毫秒什么,一心多用,全部交给定时器中断实现。
软件延时少用或不用,一般只有驱动DS18B20、WS2812B等芯片才使用,其他代码几乎没有,误差不好计算且会被中断打乱,如果大量使用就是乱成一锅粥。

评分

参与人数 1黑币 +7 收起 理由
laopihappy123 + 7 回帖助人

查看全部评分

回复

使用道具 举报

ID:88256 发表于 2021-11-22 09:16 | 显示全部楼层
所谓的软件延时,是利用它的循环、计数等特征完成的,因为需要靠编写程序来达到目的,又需要有别于NE555、CD4060之类的方法,所以才有这种说法的,而单片机本身是不具有直接延时的功能,就是定时器延时也是需要编写程序的,所以单片机是不具有硬件延时功能的。
回复

使用道具 举报

ID:367934 发表于 2021-11-22 09:25 | 显示全部楼层
解决阀值问题。使用合适的进制就行。比如说定时器中断为1ms,我可以设置100进1,变量i满100使j+1,这样就可以,j=10为1秒,再设置一个变量y,J设置为60进1。以此类推就可以。也不会导致变量溢出。
对于延时的使用,硬件延时和软件延时没有那个好那个不好的说法,只要合不合适用的问题。如果定时器多当然使用硬件延时,如果定时器少你用来做延时那你其他的就不够用。软件延时调试合适了也可以达到比较好的精准度。
回复

使用道具 举报

ID:160500 发表于 2021-11-22 09:59 | 显示全部楼层
这个问题不在于溢出,而是你定义的这个变量类型,如果是8位的就是最大255ms,如果程序在这个时间内不能处理,那就会造成定时不准。对于溢出,直接抛弃就好。另外,8位无符号数240+30=14,不是=15
回复

使用道具 举报

ID:983385 发表于 2021-11-22 12:42 | 显示全部楼层
对于延时的使用,硬件延时和软件延时没有那个好那个不好的说法,只要合不合适用的问题。如果定时器多当然使用硬件延时,如果定时器少你用来做延时那你其他的就不够用。软件延时调试合适了也可以达到比较好的精准度。
回复

使用道具 举报

ID:624769 发表于 2021-11-22 13:27 | 显示全部楼层
先强调一个定义, 至少目前你在玩的单片机, 是不存在硬件延时的。
在给你区分两个概念, 延时,和定时。
延时: 啥也不干(表面上,其实干的很欢),等消磨一定时间后再继续下一步。
定时: 先干别的,等到时间了再干指定的事情。

延时的实现: 通常有两种方式,
一、通过延时函数,进行累加或者累减,达到消耗时间的目的,达成延时目的,常见用法有:delay(50); 等。
二、通过定时器延时,利用判断定时器标志来达到消耗时间的目的,常见用法有: while(!TF0); 这里我们可以看到,在定时器延时的情况下,其实单片机也是在不停的判断 TF0 的状态,繁忙程度和用延时函数没有什么区别,无非是又搭上一个定时器而已。

定时的实现: 通常也有两种方式
一、通过定时器实现, 通过定时器实现对精度要求比较高的,需要定期执行的程序。通过这也分两种。
1) 精度要求极高,这一类的,我们会直接在定时器中断里面执行,比如每10微秒读取一次比较器的状态位这种,
2) 精度要求不高,只是隔一段时间需要执行一下,比如每0.5秒读取一次RTC的时间,那么,我们通常在定时器中断里面设置一个标志为,比如: GetTime_Flag = 1;  然后主程序里面,通过判断标志位决定是否执行, if(GetTime_Flag) {GetTime_Flag = 0; 其他执行语句;  }

二,通过计数方式定期执行对时间精度几乎没有要求的程序。
比如,在主程序中,有些项目是每次循环都要执行的有些不是,还是拿时钟举例子吧,在主循环体内,平时通过RTC芯片,获取时间。但是为了精度定期要联网校对时间,这个定期,对时间要求不高,我们可以每循环65536次,执行一次网络对时,那么一般会声明一个u16的变量,Adjusttime_Count,然后这么在主函数中写:
if(--Adjusttime_Count ==0) { 需要执行的语句 }
这样也就达到了,定期执行的目的。

所以,认清自己的目的,选择合适的方式是最重要的。

最后再强调一下: 用定时器来“延时”是毫无意义的。仅仅增加了“功耗”。

评分

参与人数 1黑币 +9 收起 理由
laopihappy123 + 9 乐于助人,谢谢啦

查看全部评分

回复

使用道具 举报

ID:57657 发表于 2021-11-22 16:11 | 显示全部楼层
比如某游乐设施,一边数码管精准计秒、流水灯的同时控制蜂鸣器放音乐和按键识别处理等,都离不开 定时器中断 。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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