VxWorks几种常用的延时方法

2023-05-16

转载于:https://www.vxworks.net/app/144-time-facilities-of-vxworks 自用

VxWorks系统提供灵活多样的定时器机制,有多种接口可以方便地实现延时,文章列举几种常用的延时方法,并对这几种延时方式的适用条件和注意事项作了说明。

嵌入式系统中,一个任务往往需要在特定的延时之后去执行一个指定的动作,比如等待外设以确保数据可靠,控制扬声器发声时间,串口通信超时重发等等。这就需要利用定时器机制来计量特定长度的时间段。VxWorks作为实时嵌入式系统,提供多样的定时接口函数。以下列举一些常用的定时方式及其注意事项。

taskDelay

taskDelay(n) 使调用该函数的任务延时n个tick(内核时钟周期),该任务在指定的时间内主动放弃CPU,除了taskDelay(0) 专用于任务调度(将CPU交给同一优先级的其他任务)外,任务延时经常也用于等待某一外部事件,作为一种定时/延时机制。在没有中断触发的时候,taskDelay能很方便实现又不影响系统整体性能。比如需要写数据至EEPROM,EEPROM需要一个内部擦除时间(最大擦除时间为10ms)。以下文章所提及的一个tick都假设为16.67ms(1/60s),我们可以简单地调用taskDelay(2)以保证数据擦写完成,按理说taskDelay(1)就足以保证,为什么需要taskDelay(2)呢?

VxWorks Timer taskDelay

这也是taskDelay使用的一个缺陷,使用时需要注意。taskDelay(n)表示任务延时至第n个系统时钟到来的时刻,如图1所示:如果在A时刻调用taskDelay(1)仅仅延时5ms,而在B时刻taskDelay(1)就是刚好一个tick周期。可见要10ms的延时就必须taskDelay(2)才能保证。taskDelay有接近-1个tick的误差存在,taskDelay(n)实际上是延时(n-1)tick ~ n tick 之间的时间。延时精度为1/n,延时1s就是taskDelay(60)的误差极限为1.6%,而taskDelay(1)的误差极限将是100%。

使用taskDelay需注意的另外一点是:即使经过n个tick,调用延时的任务也不保证返回执行状态,可能有更高或相同优先级的任务占用了CPU。如果不希望CPU被其他任务占用,我们可以让程序在此处执行一个无效的耗时代码即可,这样又可以实现延时,CPU又不会被其他任务抢占。

watchDog

VxWorks提供了一个通用的看门狗定时器机制,利用提供的函数,任何任务都可以创建一个看门狗定时器,经过指定的延时后,实现在系统时钟ISR的上下文中运行指定的程序。需要注意的是,看门狗定时触发的程序是在中断级别上执行,而不是在任务的上下文。因此,看门狗定时挂接的程序编写有一定的限制,这个限制条件与中断服务程序的约束是一样的。比如,不能使用获取信号量的语句,不能使用像printf()这样的I/O 系统函数。

通过wdCreate( )可以创建一个看门狗定时器。调用wdStart()启动定时器,延时参数同taskDelay一样以tick为单位,同时还要指定定时完成后要调用的程序。如果应用程序同时需要多个看门狗函数,应使用wdCreate( )产生多个独立的看门狗ID。因为对于给定的看门狗ID,通过wdStart()只能关联一个看门狗函数。在指定的tick计数到达之前,要取消一个看门狗计时器,可以调用wdCancel()实现。每调用一次wdStart(),看门狗定时器只执行一次。对于一些要求周期性执行的应用程序。要获得该效果,定时器函数本身必须通过递归调用wdStart()来重新启动定时器。

如果利用看门狗定时器实现延时,存在与taskDelay一样的精度上的缺陷,以tick为基准。并且看门狗关联的函数受的限制很大,也是使用不便的一个方面。不过启动看门狗的任务不会被阻塞,因为wdStart()调用立即返回并继续执行。

sleep/nanosleep

sleep() 和nanosleep() 是VxWorks提供的延时函数接口,sleep以秒为单位,nanosleep提供更精确的延时,传参是时钟的结构体,参数可以精确到ns,但实际上只能做到大于或等于这个时间。因为sleep或nanosleep函数延时的时间基准仍是tick,调用此函数的任务处于任务延时状态,这些点与taskDelay() 一致。不同的地方是,taskDelay() 是用于任务调度,taskDelay(0)有他自身的含义,sleep(0) 是没有意义的。前面提过,taskDelay(n) 延时时间(n-1) tick ~ n tick,而sleep/nanosleep保证实际延时时间大于或等于设定的时间参数。这一点我们可以通过编写一个测试程序试验证明。代码如下:

void testTimer(int sec,int nsec)  // 测试函数,输入参数为秒和纳秒
{
	struct timespec tm;			// 声明一个时钟结构体,包括秒和纳秒成员
	tm.tv_sec = sec;           // 根据传参赋值
	tm.tv_nsec = nsec;
	nanosleep(&tm,NULL); 	// 执行延时程序
}

在tornado的windshell环境下执行该函数和输出结果如下:

-> sp testTimer,0,1000*1000*16; i
  NAME        ENTRY       TID    PRI   STATUS      PC       SP     ERRNO  DELAY
---------- ------------ -------- --- ---------- -------- -------- ------- -----
tExcTask   _excTask      2318d10   0 PEND         412cd8  2318c14       0     0
tLogTask   _logTask      23131e0   0 PEND         412cd8  23130e4       0     0
tWdbTask   _wdbTask      230e920   3 READY        412cd8  230e7c8       0     0
s2u24      _testTimer    23092b8 100 DELAY        412cd8  23091b4       0     1
value = 0 = 0x0
-> sp testTimer,0,1; i
  NAME        ENTRY       TID    PRI   STATUS      PC       SP     ERRNO  DELAY
---------- ------------ -------- --- ---------- -------- -------- ------- -----
tExcTask   _excTask      2318d10   0 PEND         412cd8  2318c14       0     0
tLogTask   _logTask      23131e0   0 PEND         412cd8  23130e4       0     0
tWdbTask   _wdbTask      230e920   3 READY        412cd8  230e7c8       0     0
s2u25      _testTimer    23092b8 100 DELAY        412cd8  23091b4       0     1
value = 0 = 0x0

可见延时1ns和16ms的结果都是一样的,都相当于taskDelay(1)。

高精度时钟sysTimeStamp

sysTimeStamp()也称作时间戳,是通过系统时钟实现的,刚开始也觉得费解,系统时钟的定时周期就是tick,怎么实现高精度时钟呢?通过走读BSP底层代码发现,sysTimeStamp其实是通过读取该定时器的当前计数值来获取高精度的定时。通过sysTimestampFreq)函数可以得到系统时间戳的频率,它往往反映的是CPU定时器的基准频率。当然如此高的分辨率只能是一个理想值,对于不同的系统不一定都能实现。毕竟该时间戳的实现方式就有一个致命的弱点:通过查询方式。系统时钟定时中断是以tick为单位的,进一步提高分辨率读取定时器计数值(CPU的一个特殊功能寄存器),只能是查询方式实现。如下代码示例。

void msDelay( int ms )
{  
int  t,  t1,  t2;
t1  =  sysTimestamp();     // 记录上一轮时间戳
do
{
t = 0;                 //计数清零
     	while(t <sysTimestampFreq()/1000) // 时间戳小于1ms
{
    		t2 = sysTimestamp();           // 读取当前时间戳
    		if( t2>t1)  t += (t2-t1);          // 根据前后时间戳比较算出经历时间
		else t+=t2; // 发生定时中断清零,应该为t+=(t2+计数最大值-t1),这里简化计算
     		t1 = t2;       // 当前时间戳保存到下一轮计算
     	}
}while(ms--)        // 循环ms次
}

这种方式定时比较占用系统资源,但是能方便实现并只适用于短时间的定时。为确保定时准确,应该在锁定中断情况下调用sysTimestamp,否则应该考虑使用sysTimestampLock函数。

辅助时钟

辅助时钟是利用目标板上CPU的另一个定时器(除了系统时钟之外)中断实现的,它可以灵活配置实现高分辨率的定时,而且容易实现ms级甚至us级定时。VxWorks也提供一系列与系统时钟相同的操作接口,用户可以方便挂接自己的中断处理函数,时钟分辨率的高低取决于硬件定时器的精度和用户中断函数的长短。要将辅助时钟作为精确的延时机制(比如毫秒级延时),我们可以通过这种方式实现。初始化程序先调用SysAuxClkRateSet()函数设置辅助时钟中断周期为1ms(一般在config.h文件中对中断频率作了限定,在AUX_CLK_RATE_MIN和AUX_CLK_RATE_MAX之间,如果需要可以对此宏定义修改),通过SysAuxClkConnect() 将用户处理函数连接到辅助时钟中断上,用户处理函数可以为SemGive(semTimer)释放一个同步信号量。编写一个msDelay(int ms)作为其他任务调用接口,函数代码如下:

void msDelay(int ms)
{
	int i;
	sysAuxClkEnable();    //启动辅助定时器
	for(i=0;i<ms;i++)      //循环ms次
		semTake(semTimer); //等待定时中断释放信号量
sysAuxClkDisable(); //关闭辅助定时器
}

这种方式能实现十分精确的定时,调用延时的任务处于任务阻塞状态。但是使用上任存在缺陷,不能实现多个任务同时调用,并且需要CPU的一个时钟资源,如果没有多余的时钟,这一方法就不能实现。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aaFwDVNA-1636160296577)(https://www.vxworks.net/images/app/timer-spy.jpg)]

另外需要注意一点:Tornado的调试工具Browser ->Spy Chart的实现原理是利用辅助定时器产生中断,并记录当前被中断的任务,抽样数据反映各任务CPU占用率的情况。因此如果调试程序中使用了辅助定时器,使用Spy Chart时,定时处理函数会被重新挂接,原有定时挂接的程序将得不到进行。反过来,如果在Spy Chart运行之后挂接辅助定时处理函数,Spy Chart的运行将出现问题。试验发现,运行Spy Chart后重现挂接辅助定时处理函数,Spy Chart即使选中自动刷新,各任务状态也不会更新,如图2所示。

VxWorks提供的定时接口(不一定专门用于定时,可以间接实现)远不只这些,具体使用哪种方式,应根据其精度要求,资源状态和优先级要求而定。


后面介绍一下重要函数的使用方法

taskDelay( )

NAME

*taskDelay*( ) - delay a task from executing

SYNOPSIS

STATUS taskDelay
    (
    int ticks /* number of ticks to delay task */
    )

DESCRIPTION

This routine causes the calling task to relinquish the CPU for the duration specified (in ticks). This is commonly referred to as manual rescheduling, but it is also useful when waiting for some external condition that does not have an interrupt associated with it.

If the calling task receives a signal that is not being blocked or ignored, *taskDelay*( ) returns ERROR and sets errno to EINTR after the signal handler is run.

RETURNS

OK, or ERROR if called from interrupt level or if the calling task receives a signal that is not blocked or ignored.

ERRNO

S_intLib_NOT_ISR_CALLABLE, EINTR

SEE ALSO

taskLib

VxWorks中的sysClkRateGet()返回系统时钟详解

操作系统总是基于某个时钟节拍来跑的,这个节拍的得到往往是通过硬件时钟中断得到,一般来说这个中断的优先级就比NMI低一点点,比其他的都高。

这个中断是供给操作系统用的,操作系统用他来进行调度等各种处理。而在VxWorks中的一个重要参数就是SYS_CLK_RATE这个参数,也就是系统时钟率。它的含义是:系统时钟滴嗒在一秒钟之内发生多少次。

比如说,你定义为 60,那么系统时钟在1s中将发生60次中断,两次之间的时间差就是1/60s。发生中断后,操作系统可以进行任务切换。也就是说,如果你有一个任务被挂起,则至少要过1/60s后被激活(其它中断除外)。

又假如你设置为1000,那么系统时钟1秒发生1000次中断,两个时间差就是1ms。而函数sysClkRateGet就是用来获取系统时钟率的,如果你没有调用sysClkRateSet() 函数对系统时钟率进行重新设置的 话,其返回值应该是你在config.h中定义的SYS_CLK_RATE宏的值。而函数taskDelay()是以tick数目为单位的,比如 taskDelay(1) 是指将调用该函数的任务延迟1个tick。那么时间是多少呢,根据你的SYS_CLK_RATE的值,其实际时间不同,但具体 时间是1/SYS_CLK_RATE。假如SYS_CLK_RATE是1000,那么就是1ms。如果是60那么就大约是16.67ms。

通常来讲,VxWorks手册建议不要将时钟率设得太高,否则它就由硬实时变得趋向于软实时了。因为过高的时钟率使得内核调度频繁进入,可能导致一些低优先 级的硬件中断不能得到及时响应。当然,也不要太担心,在x86系统中完全可以设置为1000,这样比较好使,1个tick就是1ms,跟Windows一 样了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

VxWorks几种常用的延时方法 的相关文章

随机推荐

  • 并行计算之OpenMP入门简介

    转载于 xff1a https www cnblogs com kuliuheng p 4059133 html OpenMp提供了对于并行描述的高层抽象 xff0c 降低了并行编程的难度和复杂度 xff0c 这样程序员可以把更多的精力投入
  • PHP + Apache + Mysql集成环境部署及简要教程

    文章目录 PHP运行原理和机制PHP 的设计理念及特点PHP 的四层体系1 Zend 引擎 xff08 核心 xff09 2 Extensions xff08 扩展 xff09 3 SAPI xff08 服务器应用程序编程接口 xff09
  • 不同行业公司工资对比,计算机YYDS

    一 纳税标准 推荐一篇文章 xff1a 扣除社保 公积金年后 社保和公积金的扣除比例是22 左右 xff0c 工资在扣完社保和公积金的基础上再进行个税的扣除 税前19 2w xff0c 税前平均每月1 6w xff0c 扣除社保 公积金后年
  • 对实时操作系统多任务的一些理解

    一 什么是优先级反转 优先级反转 xff0c 是指在使用信号量时 xff0c 可能会出现的这样一种不合理的现象 xff0c 即 xff1a 优先级反转是指一个低优先级的任务持有一个被高优先级任务所需要的共享资源 高优先任务由于因资源缺乏而处
  • 【LeetCode刷题日记】数组和链表性质总结

    一 数据结构的存储方式 数据结构的存储方式只有两种 xff1a 数组 xff08 顺序存储 xff09 和链表 xff08 链式存储 xff09 这句话怎么理解 xff0c 不是还有散列表 栈 队列 堆 树 图等等各种数据结构吗 xff1f
  • C语言面向对象实现滑动均值滤波与平均值滤波

    文章目录 一 背景二 平均值滤波1 算法介绍2 代码实现3 实例 三 滑动均值滤波 xff08 Moving Average xff09 四 C语言面向面向对象实现滑动均值滤波 一 背景 在实际的数据采集中 xff0c 我们经常会取多次数据
  • 【LeetCode刷题日记】常用算法基础和理解及运用

    在我们LeetCode刷题过程中 xff0c 如果我们只是了解数据结构 xff08 数组 xff0c 链表 xff0c 数 xff09 的使用方法 xff0c 那我们在面对复杂的题目时 xff0c 是很难很好的解决问题的 xff0c 因此我
  • 【LeetCode刷题日记】数组类题目常见题型

    文章目录 303 区域和检索 数组不可变 https leetcode cn com problems range sum query immutable 304 二维区域和检索 矩阵不可变 https leetcode cn com pr
  • 【LeetCode刷题日记】队列类题目常见题型

    文章目录 225 用队列实现栈 https leetcode cn com problems implement stack using queues 剑指 Offer 09 用两个栈实现队列 https leetcode cn com p
  • 【LeetCode刷题日记】栈类题目常见题型

    文章目录 20 有效的括号 https leetcode cn com problems valid parentheses 225 用队列实现栈 https leetcode cn com problems implement stack
  • 回顾 nexus maven-snapshots 401 Unauthorized

    1 修改maven settings 文件 私库的用户名和密码 lt server gt lt id gt maven releases lt id gt lt username gt admin lt username gt lt pas
  • 【LeetCode刷题日记】树类题目常见题型

    文章目录 树基础知识 104 二叉树的最大深度 https leetcode cn com problems maximum depth of binary tree 102 二叉树的层序遍历 https leetcode cn com p
  • 【LeetCode刷题日记】常用算法基础和理解及运用

    文章目录 递归和迭代递归迭代迭代和递归的关系和区别 xff08 敲黑板 xff09 二分法典型的二分法二分法的变种找出第一个与key相等的元素的位置找出最后一个与key相等的元素的位置查找第一个等于或者大于Key的元素的位置查找第一个大于k
  • 编程常用快捷键,学完保证和大神一样件字如飞

    每次看到一下大神在编程时 xff0c 那打字的速度简直是恐怖 xff0c 而且几乎不会用到鼠标 xff0c 手不用离开键盘 xff0c 因此打字效率也高 而菜鸡的我 xff0c 每次写代码时 xff0c 有时候要在不同行之间跳动 xff0c
  • Clion中编译C/C++混合代码

    今天在Clion中编译C C 43 43 语言时 xff0c 出现了一个不容易发现的错误 项目文件包括三个文件 三个文件其实都是用C语言写的 xff0c 只是第一个文件的后缀是 cpp 如果在这种情况下直接编译 xff0c 会出现一下报错
  • 【LeetCode刷题日记】[447. 回旋镖的数量](https://leetcode-cn.com/problems/number-of-boomerangs/)

    LeetCode刷题日记 447 回旋镖的数量 题目 给定平面上 n 对 互不相同 的点 points xff0c 其中 points i 61 xi yi 回旋镖 是由点 i j k 表示的元组 xff0c 其中 i 和 j 之间的距离和
  • 【LeetCode刷题日记】[162. 寻找峰值]

    LeetCode刷题日记 162 寻找峰值 题目 峰值元素是指其值严格大于左右相邻值的元素 给你一个整数数组 nums xff0c 找到峰值元素并返回其索引 数组可能包含多个峰值 xff0c 在这种情况下 xff0c 返回 任何一个峰值 所
  • 基于Junit4+Mockito+PowerMock实现Controller+Service的单元测试

    一 导入的依赖 lt test gt lt dependency gt lt groupId gt org mockito lt groupId gt lt artifactId gt mockito core lt artifactId
  • 嵌入式编程之状态机

    文章目录 1 什么是状态机 xff1f 2 状态机编程的优点 1 提高CPU使用效率 2 逻辑完备性 3 程序结构清晰 3 状态机的三种实现方法switch case 法表格驱动法函数指针法小节 摘要 xff1a 不知道大家有没有这样一种感
  • VxWorks几种常用的延时方法

    转载于 xff1a https www vxworks net app 144 time facilities of vxworks 自用 VxWorks系统提供灵活多样的定时器机制 xff0c 有多种接口可以方便地实现延时 xff0c 文