Linux高精度延时

1、能降低CPU占用就行

只要我的进程不出现在高cpu占用榜单上,不被其他组员锤死就行!

#include <unistd.h> // 引入 usleep()

int main(void)
{
    while(1)
    {
        //没有特殊要求延时最好放到前面,避免忘记
        usleep(1000);
        //没有精神的干活
        ;
    }
    return 0;
}

2、听别人说usleep延时不准,还有没有别的方法?

传说中通过select来间接得到准确延时的方法

#include <stdio.h>    // 引入 NULL
#include <sys/time.h> // 引入 struct timeval

void delayUs(long int us)
{
    struct timeval tv;
    tv.tv_sec = us / 1000000;
    tv.tv_usec = us % 1000000;
    select(0, NULL, NULL, NULL, &tv);
}

int main(void)
{
    while(1)
    {
        delayUs(1000);
        //假装有精神的干活
        ;
    }
    return 0;
}

但从我的实际测试结果来看(树莓派4B),usleep和select的延时效果相差不多,他们有一个共同的问题就是实际延时要多几百us,手动矫正一下还是可以用的。后面在其它平台测了一下,准确的说应该是usleep在树莓派里超长发挥了,select还是比较稳定的。

3、严格周期任务

由于干活的内容所花时长不定,导致下一个周期开始时要么提前了一点、要么晚了一点,有没什么办法保证周期固定?比方约好了每天中午12点搬砖,那么周期就是24小时,今天花10个小时把砖搬完,就休息14小时;花13个小时把砖搬完,就休息11个小时。首先要识别当前干活花了多少时间,然后根据时间差进行延时。

#include <stdio.h>    // 引入 NULL
#include <sys/time.h> // 引入 struct timeval

// 获取系统tick可以作为us时长参考
long int getTickUs(void)
{
    struct timeval tv = {0};
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000000u + tv.tv_usec;
}

void delayUs(long int us)
{
    struct timeval tv;
    tv.tv_sec = us / 1000000;
    tv.tv_usec = us % 1000000;
    select(0, NULL, NULL, NULL, &tv);
}

// 延时初始化
#define DELAY_INIT \
long int _tick1 = 0, _tick2;

// 检查时差,差多少延时多少
#define DELAY_US(us)                        \
_tick2 = getTickUs();                       \
if (_tick2 >= _tick1 && _tick2 - _tick1 < us)\
    delayUs(us - (_tick2 - _tick1));        \
_tick1 = getTickUs();

int main(void)
{
    DELAY_INIT;

    while(1)
    {
        DELAY_US(500000);
        //随机时长的干活(我也不知道接下来要干多久,总之500ms以内就是了)
        ;
    }
    return 0;
}

这种延时的特点就是周期固定,当你不知道接下来的内容会花费多长时间,但又希望周期轮询当前内容时就适合这种延时。

4、严格周期任务 - 改

每种延时都是有误差的,如何用有误差的延时搭建稳定的周期任务?可以利用延时偏大的特点,每次延时目标时差的一半,结束后计算误差,再一半,如此循环多次,即使每次延时都不准确,但最后总能把误差压到最小;另外,循环不可能无限次,应设定一个可接受的误差以结束循环。

#include <stdio.h>    // 引入 NULL
#include <sys/time.h> // 引入 struct timeval

// 获取系统tick可以作为us时长参考
long int getTickUs(void)
{
    struct timeval tv = {0};
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000000u + tv.tv_usec;
}

void delayUs(long int us)
{
    struct timeval tv;
    tv.tv_sec = us / 1000000;
    tv.tv_usec = us % 1000000;
    select(0, NULL, NULL, NULL, &tv);
}

// 延时初始化
#define DELAY_INIT2 \
long int _tick = 0, _tickErr;

// 每次延时一半时差,直至延时满us或差值小于err,注意要放在循环开始
#define DELAY_US2(us, err)         \
_tickErr = getTickUs() - _tick;    \
if (_tickErr >= 0 && us > _tickErr) \
{                                  \
    _tickErr = us - _tickErr;      \
    if (_tickErr > err)            \
    {                              \
        delayUs(_tickErr / 2);     \
        continue;                  \
    }                              \
}                                  \
_tick = getTickUs();

int main(void)
{
    DELAY_INIT2;

    while(1)
    {
        //目标延时500ms,接受误差50us
        DELAY_US2(500000, 50);
        //安心的干活
        ;
    }
    return 0;
}

这种延时周期性精度高、抗干扰能力强,可以用在对周期和稳定性要求都较高的场景,如音、视频播放,帧数据传输等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值