关于delay_us函数的一个小小的却又重大的发现,大部分人应该没注意到吧

2019-08-16 21:52发布

今天在做DHT11实验时,许多函数都调用了delay_us函数,原子哥写的源码是:
[mw_shl_code=c,true]//延时nus //nus为要延时的us数. void delay_us(u32 nus) { u32 temp; SysTick->LOAD=nus*fac_us; //时间加载 SysTick->VAL=0x00; //清空计数器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do { temp=SysTick->CTRL; } while(temp&0x01&&!(temp&(1<<16)));//等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器 SysTick->VAL =0X00; //清空计数器 }[/mw_shl_code] 大家在调用延时函数时都认为它是非常精准的,的确,延时达到ms级时它真的很准。
可是在调用delay_us函数进行几us的延时时,它的误差却非常大!经测试,delay_us(1)所花的时间约为2us,误差达100% !
我写了个函数用USMART进行了测试:
void delayus(u32 n)
{
while(n--) delay_us(1);
}
调用delayus(10000)测出所花时间为19.6ms,而理论值为10ms,多出近1倍的时间!
这是因为delay_us函数中的初始化及数据处理也花了时间,这个时间大概为1us。
delay_us函数的代码量为68字节,不仅仅是延时等待要时间,运行这些代码也要时间。
所以,调用
delay_us(n)时,用的时间为(n+1)us,而不是nus!
这1us的误差看起来好像没什么大不了,可某些对时序(时间)要求非常苛刻的场合说不定能导致实验失败,得不到预期的结果。
原子哥应该也没注意到这点吧?哈哈~
唉,强迫症又犯了。。。。。注定一辈子只能当程序猿。。。。
[mw_shl_code=c,true][/mw_shl_code]
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
9条回答
正点原子
1楼-- · 2019-08-17 02:35
这个我也注意到了,谢谢提醒.
as564335sa
2楼-- · 2019-08-17 06:30
几微秒的延时软件延时一下就好,就不去倒腾systick了
jiaozhu
3楼-- · 2019-08-17 11:29
楼主分析的很透彻    装载初值的时候应该是 nus*fac_us-1
钩月黄昏
4楼-- · 2019-08-17 15:40
 精彩回答 2  元偷偷看……
漆黑丨夜
5楼-- · 2019-08-17 17:19
我初始化了delay函数后,刚开始运行delay_us()时没问题的,不会卡死。但是等我后面用到串口中断收发数据后就会卡在那个do while()循环内,跳不过去。我后来将延时us小的用__nop();来代替,大一点的us用delay_ms(1);代替就没有这种情况了。所以我想问问原子哥给的那个delay源代码的delay_us()函数是不是有什么BUG?
谨慎的狂地精
6楼-- · 2019-08-17 19:15
void delay_us(u32 nus)
{               
        u32 temp;                     
        SysTick->LOAD=nus*fac_us-9;                                         //时间加载          
        SysTick->VAL=0x00;                                                //清空计数器
        SysTick->CTRL=0x01 ;        //开始倒数          
        do
        {
                temp=SysTick->CTRL;
        }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达   
        SysTick->CTRL=0x00;        //关闭计数器
        SysTick->VAL =0X00;                                               //清空计数器         
}


这里应该写成 nus*fac_us-9  假设时钟是72Mhz

单纯延时1us这个delay_us好像做不到了,要重新写个函数。

void delay_us1()
{               
        u32 temp;                     
        SysTick->LOAD=fac_us;                                         //时间加载          
        SysTick->VAL=0x00;                                                //清空计数器
        SysTick->CTRL=0x01 ;        //开始倒数          
        do
        {
                temp=SysTick->CTRL;
        }while(((temp+1)&0x01)&&!(temp&(1<<16)));                //等待时间到达   
        SysTick->CTRL=0x00;        //关闭计数器
        SysTick->VAL =0X00;                                               //清空计数器
}

这里好像没有实际的意义,只是刚好是1us而已。。。。
原子哥看下~~~

一周热门 更多>