当前位置:   article > 正文

MCU中断里使用软延时函数delay_ms(u16 x)问题及实例探讨

MCU中断里使用软延时函数delay_ms(u16 x)问题及实例探讨

原贴有误已删:https://blog.csdn.net/weixin_50007421/article/details/136138221

今天完善如下:

本意:只是想表达“复杂系统中断里当然尽量不用软延时函数,但简单系统只要心中有数逻辑清楚实测无妨就完全可行”。

但后来感觉还是不求甚解,还是想弄清楚究竟怎么回事,就引入了下面2个具体例子

ms软延时函数就用如下最简代码 (注:STC单片机 1T/11.0592MHz,u8 i,j; u16 x):

  1. void delay_ms(u16 x)
  2. { u8 i,j;
  3. while(x-- > 0)
  4. {i = 15, j = 90;
  5. do while (j--); //原为--j
  6. while (i--); //原为--i
  7. }
  8. }

例1:若主程序正在运行delay_ms(100),x=100,已延时50ms时被中断,且中断里有个最简软延时消抖delay_ms(20),x=20,运行结果究竟如何的呢?

有没可能:中断服务执行完,局部变量i=j=x=0了?那么返回主程序delay_ms(100)时,i=j=x=0了?剩余延时时间50ms是否就几乎跳过去了,实际总延时50ms+中断消抖20ms≈70ms?为什么呢?怎么去理解和必要时解决这个问题呢?  本例中:x--、j--、i--为先判再减;若改为--x,--j、--i,则先减再判,x将变为0xFFFF,i、j将变为0xFF,都不为0都要继续做减1循环,那可就麻烦了!要多延时很长很长很长时间了…具体问题具体分析。

有的说:你是用for循环在延时还是while在延时,不管哪一种情况,若中断发生在主程序延时过程中,如果X是局部变量(有个东西叫做栈),两个延时互不干扰,中断返回后继续计数延时。如果X是全局变量,中断返回后,这个X会变得更小(若是从delay函数从0计数)主程序会继续延时...

有的说:进中断之前,会保持当前场景,且delay形参是以变量而非地址的形式,中断里面的x影响不到主程序里面的x相当于调用了两次delay 两次互不影响...

例2:一节课45分钟,30分钟时老师突然“中断”出去接电话10分钟(若中断用同一延时函数,已延时10分钟),老师回来时离下课还有5分钟,那你说他会继续讲几分钟然后下课?

1种可能是+15分钟(30+10+15)延时10分钟下课)?另一种可能30+10+0,回来就下课?究竟该如何才能明确并选择哪种情况?

探讨:

1.中断中延时函数正常退出时,其3个变量的值未必会恢复到它们在主函数中的值。

Keil的C51编译器缺省是函数不可重入的(当然不是所有不可重入函数在后台和中断中同时调用都会发生问题)。对Keil的设置为大模式和不优化,所有的变量均实际存储在XRAM空间,因此延时函数“delay_ms()”是不可重入的。

2.我联想起一件事:有些函数中断同时调用时,Keil的C51编译时会出现L15 重复调用告警!而调用delay_ms()函数从未出现过告警!!进一步的理解及 解决Keil的C51程序中断重入问题的方法:

 请详见下面2个帖子(再次感谢博主):

     1. http://t.csdnimg.cn/D5mXf

     2. http://t.csdnimg.cn/cbsVO

 L15 重复调用告警具体情况:
***WARNING L15: MULTIPLE CALL TO SEGMENT
SEGMENT: ?PR?SPI_RECEIVE_WORD?D_SPI
CALLER1: ?PR?VSYNC_INTERRUPT?MAIN
CALLER2: ?C_C51STARTUP
该警告表示连接器发现有一个函数可能会被主函数和一个中断服务程序 ( 或者调用中断服务程序的函数 ) 同时调用 , 或者同时被多个中断服务程序调用。
出现这种问题的原因之一是这个函数是不可重入性函数 , 当该函数运行时它可能会被一个中断打断 , 从而使得结果发生变化并可能会引起一些变量形式的冲突 ( 即引起函数内一些数据的丢失 , 可重入性函数在任何时候都可以被 ISR 打断 , 一段时间后又可以运行 , 但是相应数据不会丢失 ) 。
原因之二是用于局部变量和变量 ( 暂且这样翻译 ,arguments,[ 自变量 , 变元一数值 , 用于确定程序或子程序的值 ]) 的内存区被其他函数的内存区所覆盖 , 如果该函数被中断 , 则它的内存区就会被使用 , 这将导致其他函数的内存冲突。

例如 , 第一个警告中函数 WRITE_GMVLX1_REG 在 D_GMVLX1.C 或者 D_GMVLX1.A51 被定义 , 它被一个中断服务程序或者一个调用了中断服务程序的函数调用了 , 调用它的函数是 VSYNC_INTERRUPT, 在 MAIN.C 中。

解决方法:
如果你确定两个函数决不会在同一时间执行 ( 该函数被主程序调用并且中断被禁止 ), 并且该函数不占用内存 ( 假设只使用寄存器 ), 则你可以完全忽略这种警告。
如果该函数占用了内存 , 则应该使用连接器 (linker)OVERLAY 指令将函数从覆盖分析 (overlay
analysis) 中除去 , 例如:OVERLAY (?PR?_WRITE_GMVLX1_REG?D_GMVLX1 ! *)
上面的指令防止了该函数使用的内存区被其他函数覆盖 。 如果该函数中调用了其他函数 , 而这些被调用在程序中其他地方也被调用 , 你可能会需要也将这些函数排除在覆盖分析 (overlay analysis) 之外。这种 OVERLAY 指令能使编译器除去上述警告信息。
如果函数可以在其执行时被调用 , 则情况会变得更复杂一些。这时可以采用以下几种方法:
1. 主程序调用该函数时禁止中断 , 可以在该函数被调用时用 #pragma disable 语句来实现禁止中断的目的。必须使用 OVERLAY 指令将该函数从覆盖分析中除去。
2. 复制两份该函数的代码 , 一份到主程序中 , 另一份复制到中断服务程序中。
3. 将该函数设为重入型。例如:void myfunc(void) reentrant {...}
这种设置将会产生一个可重入堆栈 , 该堆栈被被用于存储函数值和局部变量 , 用这种方法时重入堆栈必须在 STARTUP.A51 文件中配置。这种方法消耗更多的 RAM 并会降低重入函数的执行速度。

4.本例delay_ms(u16  x)函数:STC单片机1T/11.0592MHz,变量定义u8 i,j;x--、j--、i--为先判再减;u8=unsigned char,u16=unsigned int;

以上个人目前理解,欢迎探讨!
————————————————

                           

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/243769?site
推荐阅读
相关标签
  

闽ICP备14008679号