STM下纯软件实现精确定时

2019-10-15 17:56发布

不用定时器得到精确定时

先上测试代码(普通版)

#include "stm32f10x.h"
#include "core_cm3.h"

__asm int Dellayus(u32 usec)
{
     ALIGN
     USH.W {r1}  //2时钟周期
     MOV r1,#18  //1时钟周期
     MUL r0,r1  //1时钟周期
     SUB r0,#3  //1时钟周期
loop
     SUBS r0,#1   //1时钟周期
     BNE loop   //如果跳转则为3个周期,不跳则只有1个周期
     OP {r1}   //2时钟周期
     BX lr    //3个时钟周期
                  //总共所用周期为(usec*4)-4,此处减4主要用于抵消调用此函数的消耗时钟周期(传参1时钟,BLX跳转3时钟)
}

int main(void)
{
     while(1)
     {
          //运行到此处时32.93us,第1379时钟周期
          Dellayus(100);
          //运行到此处时132.93us,第8579时钟周期,运行上条指令共花8579-1379=7200时钟周期
          //因系统时钟为72MHz,所以每72时钟周期所花时间为1微妙,上条指令所花时间
          //等于7200 ÷ 72 = 100微妙,延时完全准确
          Dellayus(100);
          //运行到此处时232.93us,第15779时钟周期,同样验证上面的结论
     }
}


调试:

第一个100微妙延时函数运行前


第一个100微妙延时函数运行后



所花系统时钟周期8579-1379=7200个系统时钟,时间精确为0.00013293-0.0003293=0.0001秒,
也就是100微秒。延时函数通过软件模拟以及硬件调试同样精确,如果延时参数大于255微秒,调用
时会多耗两个时钟周期(0.028微秒)用于从代码段加载常数。可以修改代码修正误差。最大定时不能
超过(232-1) ÷  18 ≈ 238.6秒。

以上方法可以节约系统定时器资源,避免和其他代码争用定时器。通常用于微秒級延时。




版主的建议非常好,是该增加时钟参数以适应不同的时钟频率,改了一下代码,目前软件仿真测试没问题,等晚上再用JLink
测试一下(理论上应该没问题,已经考虑了闪存预取优化)。

#include "stm32f10x.h"
#include "core_cm3.h"

__asm void Dellayus(u32 usec,u8 freq)  //freq参数为系统时钟频率(SYSCLK),单位MHz,且必须能被4整除,且    等于16,常用的
{           //有24,36,48,56,72,如果一定要使用8MHz则必须满足延时时间大于2微秒,但freq一定不要
           //小于8MHz,否则函数将出现混乱。条件为 ((usec >= 1) && (freq >=16)) ||  ((usec >= 2) && (freq >= 8))
 ALIGN
 LSR r1,r1,#2  //1时钟周期,除以单次循环所用的时钟数(4个)即得到延时1微妙所需的循环数
 MUL r0,r1  //1时钟周期
 SUB r0,#3  //1时钟周期
 NOP    //用于匹配延时周期以及使loop循环处指令在8字节边界对齐,提高精度(因为指令预取单元一次预取8字节指令)
 NOP    //所以循环时都不用再从闪存内取指令,避免闪存延时影响延时精度
loop
 SUBS r0,#1   //1时钟周期
 BNE loop   //延时循环,每次4个时钟周期,最后一次只需两个时钟周期(如果跳转则为3个周期,不跳则只有1个周期)
 NOP
 BX lr    //3个时钟周期
      //本函数内总共所用周期为usec*(freq/4)-2 +9,调用此函数的消耗5个时钟周期(传参2时钟,BLX跳转3时钟)
}      //函数最低耗时11个时钟周期,上面usec*(freq/4) - 2为循环代码的耗时(此处减2是因为最后一次循环BNE没有跳转,只消耗1个时钟比跳转的3个时钟节约2个所以减去2才是最终循环的耗时),9就是其它代码的耗时

//比如频率为8MHz时延时2微秒所需延时周期数为2*8=16个时钟,将值带入上面的公式即为 2 * (8 / 4) - 2 + 9 =11            即为函数体耗时,再加上5个周期的调用开销最终消耗16个时钟周期,同时这也是最低延时周期数。

int main(void)
{
 while(1)
 {
  //运行到此处时32.93us,第1379时钟周期
  Dellayus(100,72);
  //运行到此处时132.93us,第8579时钟周期,运行上条指令共花8579-1379=7200时钟周期
  //因系统时钟为72MHz,所以每72时钟周期所花时间为1微妙,上条指令所花时间
  //等于7200 ÷ 72 = 100微妙,延时完全准确
  Dellayus(100,72);
  //运行到此处时232.93us,第15779时钟周期,同样验证上面的结论
 }
}




示例代码见附件

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
40条回答
aa2206
2019-10-17 08:10
//加个编译项就行了.  /*--------------------------------------------------------------------------------  *   * 延时函数  * input: t 延时 t*10us  * 例:延时100us  *    Delay_us(10)  *--------------------------------------------------------------------------------*/  #define OSC     (8)                                 //定义为8M   #define OSC_D   ((OSC*144)/8)  #pragma O3  void Delay_us(unsigned int t)  {                   int i; 
         for(i=0; i<OSC_D*t; i++){                      ;            }  }
//测试 //运行时间为100.014ms /*-------------------------------------------------------------------------------- * for(i=0; i<1000; i++){ * Delay_us(10); * } *--------------------------------------------------------------------------------*/

呵呵,纯为游戏之作.在这种CPU,延时最好是用原子的方法了.

 

一周热门 更多>