eCos嘀嗒定时器

news/2024/7/5 2:36:41 标签: ecos, tick

mingdu.zheng <at> gmail <dot> com
http://blog.csdn.net/zoomdy/article/details/8869843

 

1. 概述

嘀嗒定时器(Tick)是操作系统的核心部件之一,操作系统使用嘀嗒定时器实现时间片轮、延时、超时判断等特性。 本文介绍eCos系统中的嘀嗒定时器的使用和实现,以及需要考虑的一些问题。

嘀嗒定时器在eCos中称做RealTimeClock(RTC),一般情况下,RTC是指提供年月日时分秒的实时时钟, 在eCos中RTC表示嘀嗒定时器,WallClock(挂钟)才是指提供年月日时分秒的实时时钟,为什么是这样?

通常使用定时器模块为系统提供嘀嗒定时服务,Cortex-M提供了专用的SysTick定时器。

嘀嗒定时器与硬件相关的代码是由HAL提供的,与硬件无关的代码由内核的clock.cxx提供,ISR和DSR也是由clock.cxx提供的。

2. 配置项

共有3个配置项与嘀嗒定时器有关,分别是:Real-time clock numerator(CYGNUM_HAL_RTC_NUMERATOR)、 Real-time clock denominator(CYGNUM_HAL_RTC_DENOMINATOR)、Real-time clock period(CYGNUM_HAL_RTC_PERIOD), 这3个配置项通常在平台层HAL或变体层HAL定义。

CYGNUM_HAL_RTC_NUMERATORCYGNUM_HAL_RTC_DENOMINATOR定义嘀嗒定时器中断间隔时间, 中断间隔时间 =CYGNUM_HAL_RTC_NUMERATOR / CYGNUM_HAL_RTC_DENOMINATOR,单位为纳秒(ns)。 默认情况下CYGNUM_HAL_RTC_NUMERATOR = 1000000000ns = 1s,CYGNUM_HAL_RTC_DENOMINATOR = 100, 中断间隔 = 1000000000 / 100 = 10000000ns = 10ms。在CYGNUM_HAL_RTC_NUMERATOR = 1s 的情况下, CYGNUM_HAL_RTC_DENOMINATOR 等于每秒产生的嘀嗒数。 据eCos参考手册的描述,采用分子分母的形式来定义中断间隔的原因是提高分辨率减少误差, 在CYGNUM_HAL_RTC_NUMERATOR不能被CYGNUM_HAL_RTC_DENOMINATOR整除的情况下,确实是这么一回事。

CYGNUM_HAL_RTC_PERIOD是嘀嗒定时器中断间隔时间内CPU运行的时钟数,这个选项用来初始化定时器硬件, 一般情况下,CYGNUM_HAL_RTC_PERIOD = CPU主频 / CYGNUM_HAL_RTC_DENOMINATOR

3. 内核函数

与嘀嗒定时器相关的内核函数包括cyg_current_timecyg_thread_delay以及支持超时机制的通信和同步函数。

cyg_tick_count_t cyg_current_time(void):读取当前嘀嗒计数器值,返回值类型为cyg_tick_count_t,这是个64位无符号整数类型。

void cyg_thread_delay(cyg_tick_count_t delay):线程延时,delay为需要延时的嘀嗒数。

支持超时机制的通信和同步函数包括cyg_mbox_timed_getcyg_mbox_timed_putcyg_semaphore_timed_waitcyg_flag_timed_waitcyg_cond_timed_wait,这些函数都有一个abstime参数,abstime是嘀嗒计数器值的绝对值, 这是与cyg_thread_delay的delay参数不同的,在使用这些函数前首先要调用cyg_current_time获取当前计数器值,然后加上超时时间再传给abstime参数, 为什么不使用和cyg_thread_delay一样的相对计数器值呢?下面是使用超时等待条件变量的一个简单例子。

res = cyg_cond_timed_wait( cv, cyg_current_time()+10 );

4. HAL代码

HAL为内核实现嘀嗒定时器提供3个硬件相关的宏定义:HAL_CLOCK_INITIALIZEHAL_CLOCK_RESET、 CYGNUM_HAL_INTERRUPT_RTC

HAL_CLOCK_INITIALIZE( _period_ )对定时器进行初始化,通常对定时器模块相关的寄存器进行设置, 但是不会安装ISR和DSR,也不会使能中断,这部分工作由内核在初始化嘀嗒定时器时完成。 _period_为CYGNUM_HAL_RTC_PERIOD

HAL_CLOCK_RESET( _vec_, _period_ )清除定时器中断标志,复位计数器值,在ISR中调用, _vec_为CYGNUM_HAL_INTERRUPT_RTC,_period_为CYGNUM_HAL_RTC_PERIOD

CYGNUM_HAL_INTERRUPT_RTC嘀嗒定时器的中断号,定时器中断的ISR和DSR是由内核提供的, 只有知道中断号的情况下,才能安装ISR和DSR,以及屏蔽和使能中断。

5. 内核代码

与嘀嗒定时器相关的内核代码主要在kernel/.../src/common/clock.cxx中实现。 嘀嗒定时器功能由C++类Cyg_RealTimeClock提供,clock.cxx定义了一个全局的Cyg_RealTimeClock实例对象Cyg_RealTimeClock::rtc, 内核API通过该实例实现嘀嗒定时器相关特性。

class Cyg_RealTimeClock : public Cyg_Clock(1)
{
    Cyg_Interrupt       interrupt;(2)

    static cyg_uint32   isr(cyg_vector vector, CYG_ADDRWORD data);(3)
    static void         dsr(cyg_vector vector, cyg_ucount32 count, CYG_ADDRWORD data);

    Cyg_RealTimeClock();

    static Cyg_RealTimeClock rtc;(4)
};
Cyg_RealTimeClock Cyg_RealTimeClock::rtc CYG_INIT_PRIORITY( CLOCK );(5)
(1)Cyg_RealTimeClock继承自Cyg_Clock, Cyg_Clock继承自Cyg_Counter
(2)嘀嗒定时器的中断实例。
(3)ISR和DSR函数。
(4)全局的Cyg_RealTimeClock实例,这里将其声明为Cyg_RealTimeClock类自身的静态变量,因此通过Cyg_RealTimeClock::rtc引用。
(5)Cyg_RealTimeClock::rtc在这里定义。
Cyg_RealTimeClock::Cyg_RealTimeClock()(1)
    : Cyg_Clock(rtc_resolution),
      interrupt(CYGNUM_HAL_INTERRUPT_RTC,
                CYGNUM_KERNEL_COUNTERS_CLOCK_ISR_PRIORITY,
                (CYG_ADDRWORD)this, isr, dsr)
{
    HAL_CLOCK_INITIALIZE( CYGNUM_KERNEL_COUNTERS_RTC_PERIOD );(2)

    interrupt.attach();(3)

    interrupt.unmask_interrupt(CYGNUM_HAL_INTERRUPT_RTC);(4)

    Cyg_Clock::real_time_clock = this;
}
(1)Cyg_RealTimeClock构造函数,调用基类构造函数以及Cyg_Interrupt实例构造函数。
(2)调用HAL提供的HAL_CLOCK_INITIALIZE初始化硬件。
(3)安装中断服务ISR和DSR。
(4)使能定时器中断,使能中断后不一定就能产生嘀嗒中断,因为CPU那里还有一个中断总开关。
cyg_uint32 Cyg_RealTimeClock::isr(cyg_vector vector, CYG_ADDRWORD data)(1)
{
    HAL_CLOCK_RESET( CYGNUM_HAL_INTERRUPT_RTC, CYGNUM_KERNEL_COUNTERS_RTC_PERIOD );(2)

    Cyg_Interrupt::acknowledge_interrupt(CYGNUM_HAL_INTERRUPT_RTC);

    return Cyg_Interrupt::CALL_DSR|Cyg_Interrupt::HANDLED;
}
(1)嘀嗒定时器的ISR。(已删减非关键代码)
(2)调用HAL提供的定时器复位宏,复位定时器,并重新初始化定时器的计数器值。
void Cyg_RealTimeClock::dsr(cyg_vector vector, cyg_ucount32 count, CYG_ADDRWORD data)(1)
{
    Cyg_RealTimeClock *rtc = (Cyg_RealTimeClock *)data;

    rtc->tick( count );(2)

}
(1)嘀嗒定时器的DSR。(已删减非关键代码)
(2)DSR仅调用Cyg_Counter基类的tick函数,嘀嗒定时器的关键特性是在Cyg_Counter::tick中实现的。
void Cyg_Counter::tick( cyg_uint32 ticks )(1)
{
    // Increment the counter in a loop so we process
    // each tick separately. This is easier than trying
    // to cope with a range of increments.

    while( ticks-- )(2)
    {
        Cyg_Scheduler::lock();(3)

        // increment the counter, note that it is
        // allowed to wrap.
        counter += increment;(4)

        // now check for any expired alarms

        Cyg_Alarm_List *alarm_list_ptr;     // pointer to list
        alarm_list_ptr = &alarm_list;

        // Now that we have the list pointer, we can use common code for
        // both list organizations.
        // With unsorted lists we must scan the whole list for
        // candidates. However, we must be careful here since it is
        // possible for the function of one alarm to add or remove
        // other alarms to/from this list. Having the list shift under
        // our feet in this way could be disasterous. We solve this by
        // restarting the scan from the beginning whenever we call an
        // alarm function.

        cyg_bool rescan = true;

        while( rescan )(5)
        {
            Cyg_DNode_T<Cyg_Alarm> *node = alarm_list_ptr->get_head();

            rescan = false;

            while( node != NULL )
            {
                Cyg_Alarm *alarm = CYG_CLASSFROMBASE( Cyg_Alarm, Cyg_DNode, node );
                Cyg_DNode_T<Cyg_Alarm> *next = alarm->get_next();

                CYG_ASSERTCLASS(alarm, "Bad alarm in counter list" );

                if( alarm->trigger <= counter )(6)
                {
                    alarm_list_ptr->remove(alarm);

                    if( alarm->interval != 0 )
                    {
                        // The alarm has a retrigger interval.
                        // Reset the trigger time and requeue
                        // the alarm.
                        alarm->trigger += alarm->interval;
                        add_alarm( alarm );
                    }
                    else alarm->enabled = false;

                    CYG_INSTRUMENT_ALARM( CALL, this, alarm );

                    // Call alarm function
                    alarm->alarm(alarm, alarm->data);

                    rescan = true;

                    break;
                }

                // If the next node is the head of the list, then we have
                // looped all the way around. The node == next test
                // catches the case where we only had one element to start
                // with.
                if( next == alarm_list_ptr->get_head() || node == next )
                    node = NULL;
                else
                    node = next;
            }
        }
        Cyg_Scheduler::unlock();
    }
}
(1)Cyg_Counter::tick函数,已删减非关键代码,Cyg_Counter::tick将对计数器值进行累加, 然后检查链接到该计数器的所有Cyg_Alarm实例,如果Cyg_Alarm实例的触发值小于等于当前计数器值,那么调用该Cyg_Alarm实例的回调函数。 从这里可以看出Cyg_CounterCyg_Alarm是密切关联的两个类,因此这两个类互相把对方声明为友元类。
(2)根据未处理嘀嗒中断的次数进行多次处理,eCos的DSR机制通过记录中断次数的方式保证中断不会丢失,即使在DSR未及时得到执行的情况下。
(3)对调度器进行加锁,接下来开始访问共享数据,访问之前对调度器加锁以保护共享数据。
(4)累加计数值,对嘀嗒定时器而言,increment = 1。
(5)扫描所有链接到该计数器的Cyg_Alarm实例,链接到同一个计数器的Cyg_Alarm实例以链表的方式组织。
(6)检查Cyg_Alarm实例的触发值是否小于等于当前计数器值,如果是,那么删除该Cyg_Alarm实例并调用该实例对应的回调函数。

6. 计数器溢出

细心的朋友可能已经注意到,在Cyg_Counter::tick函数中没有考虑计数器溢出回滚到0的问题, 溢出是迟早会发生的事情呀!eCos使用了64位的无符号整数来存储计数器,假设1毫秒产生一次嘀嗒中断, 也就是说每1毫秒,Cyg_Count::counter加1,那么经过0×10000000000000000 / 1000秒后将会溢出,换算成天数, 那么是0×10000000000000000 / 1000 / 60 / 60 / 24天,等于213503982334天,等于584942417年, 早晚会溢出的,但是那是差不多6亿年后的事情啦,反正我死都死啦,管不着啦!使用64位整数做计数器虽然占用更多的内存空间,但是算法简单许多,还算划得来。

 


http://www.niftyadmin.cn/n/1108697.html

相关文章

单机性能分析与调优

一、程序优化 1、表单压缩&#xff1a;压缩表单&#xff0c;减少网络的传输量达到提高响应速度的效果。 2、局部刷新&#xff1a;页面中采取局部内容获取的方式&#xff0c;减少向服务器的请求。 3、仅取所需&#xff1a;只向服务器请求必要的内容&#xff0c;只向客户端发送必…

caffe data层_【caffe教程1】 caffe代码导论

本文首发于公众号《有三AI》[caffe解读] caffe从数学公式到代码实现1-导论​mp.weixin.qq.com我这个系列caffe代码解读跟大部分人的思路不一样&#xff0c;一般读caffe代码思路是按照caffe的层级结构来&#xff0c;blob到layer到net各自分层来读&#xff0c;但我想提供一个另外…

Excel 2016新增函数之TEXTJOIN

Excel 2016新增函数之TEXTJOIN在2016年1月功能更新中&#xff0c;EXCEL 2016新增几个常用重要函数&#xff0c;主要有&#xff1a;ifs,switch,maxifs,minifs,concat,textjoin等。注意&#xff0c;要能使用上述函数&#xff0c;Office的版本很重要&#xff0c;并不是所有的Offic…

力扣——体育馆的人流量

X 市建了一个新的体育馆&#xff0c;每日人流量信息被记录在这三列信息中&#xff1a;序号(id)、日期 (date)、 人流量 (people)。 请编写一个查询语句&#xff0c;找出高峰期时段&#xff0c;要求连续三天及以上&#xff0c;并且每天人流量均不少于100。 例如&#xff0c;表 s…

eCos内核API与内核实现的衔接

mingdu.zheng <at> gmail <dot> comhttp://blog.csdn.net/zoomdy/article/details/8884815 eCos内核API是以C函数以及C结构体的形式提供的&#xff0c;eCos的内核是使用C类实现的&#xff0c; kernel/.../src/common/kapi.cxx 将C类实现衔接到C函数及C结构体&#…

python注释码_python编码与代码注释

一、内容编码显然ASCII码无法将世界上的各种文字和符号全部表示&#xff0c;所以&#xff0c;就需要新出一种可以代表所有字符和符号的编码&#xff0c;即&#xff1a;UnicodeUnicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。Unicode 是为了解决传统的字符编码…

学科实践活动感悟50字_学科实践活动感想

最近我们开展了学科实践活动。在这项活动中&#xff0c;我学会了许多新知识和新方法。比如&#xff0c;语文实践活动中&#xff0c;我知道了如何表达&#xff0c;如何选择素材等。在数学实践活动中&#xff0c;我明白了一道题不一定只有一种解法&#xff0c;可能有很多做法&…

eCos的HAL接口采用宏定义形式的优势

mingdu.zheng <at> gmail <dot> comhttp://blog.csdn.net/zoomdy/article/details/8884820 eCos的HAL接口为eCos其它组件提供访问硬件的统一接口&#xff0c;所有的HAL接口都是以宏定义的形式提供的&#xff0c; 采用宏定义的形式有如下优势&#xff1a; 1. 实现接…