http://bbs.21ic.com/club/bbs/ShowAnnounce.asp?v=&ID=2458545
gdtyy 发表于 2007-3-6 15:51 ZLG-ARM ←返回版面
******************* * 第十讲 时间管理 * ******************* 2007/03/06 asdjf@163.com www.armecos.com 时间管理是操作系统的一项重要功能,硬件必须提供一个周期性的时钟或者定时器,以支持系统中与时间相关的功能部件。一般,大多数CPU都有一个或多个内置定时器,提供周期性的中断,即使没有内置定时器,也必须使用外部定时器或时钟。 ecos抽象了时间相关硬件的本质,提供了通用的时间管理机制。这些定时机制包括:计数器(Counter)、时钟(Clock)、告警器(Alarm)和定时器(Timer)。有了这些抽象概念,我们就不必做一些搭架子的重复性工作了,也不必在硬件细节上浪费时间,而是集中精力在功能实现上。针对抽象平台接口编程的好处是移植性好,以不变应万变。 计数器---对指定事件进行单调递增计数。 时钟-----对具有一定周期性的时间滴答(tick)进行计数的计数器(对时间进行计数)。 告警器---在计数器的基础上增加一个提示功能的机制,或者基于计数器的值产生具有周期性的事件。 定时器---简单地附加在时钟上的告警器。 这些时间相关API函数的定义都在<cyg/kernel/kapi.h>内,使用时必须包含该头文件。 ========== * 计数器 * ========== 创建 void cyg_counter_create(cyg_handle_t *handle, cyg_handle_t *counter) 该函数创建一个性的计数器,新计数器的句柄通过handle返回。 cyg_handle_t---cyg表示cygnus公司定义,handle是句柄,t是type的缩写(类型),合起来的意思是cygnus公司定义的句柄类型,很好理解吧。所有计数器函数都是cyg_counter开头的,只要看到有counter,就知道和计数器有关。 counter是应用程序提供给该计数器所需的内存,从而减少了在内核中进行动态内存分配的需求。ecos里很多函数都是由应用程序提供目标对象所需内存,使用静态内存分配而不是动态分配符合嵌入式系统的特点。 删除 void cyg_counter_delete(cyg_handle_t counter) 读指定计数器当前值 cyg_tick_count_t cyg_counter_current_value(cyg_handle_t counter) 设置指定计数器值 void cyg_counter_set_value(cyg_handle_t counter, cyg_tick_count_t new_value) 计数器加1 void cyg_counter_tick(cyg_handle_t counter) 创建一个新的计数器不会自动将事件源连接到该计数器上,无论相对应的时间何时发生,都需要调用cyg_counter_tick函数使计数器加1计数。一般在事件中断的DSR里调用此函数。 计数器值增加指定tick数 void cyg_counter_multi_tick(cyg_handle_t counter, cyg_tick_count_t ticks) ======== * 时钟 * ======== 内核级的所有与时钟相关的操作包括延时、超时和告警等都是以时钟滴答为单位进行工作的,而不是以秒或微秒为单位。如果应用程序或其他软件需要使用秒或微秒这样的时间单位,则需要将这些时间单位转换为时钟滴答。这是因为只有时钟滴答才能精确地反映出硬件的支持条件。而且如果使用传统意义上的时间单位如纳秒(ns),则硬件有可能不能提供支持。另一个原因是在时钟滴答和传统意义上的时间单位之间的转换需要浪费很多代码和数据,采用时钟滴答为单位可以节省代码量和CPU时间。 时钟单位间的转换需要一个分辨率,如100Hz运行的时钟,1秒内产生100次tick,则分辨率为1000000000(ns) : 100(tick)。同样,60Hz的分辨率为1000000000 : 60。 将一个以纳秒为单位的延时除以分辨率就可以转换为对应的时钟滴答。如一个50ms的延时(50000000ns),时钟频率为100Hz(即分辨率1000000000 : 100),那么可以通过下面的计算转换为时钟滴答: 50000000 ÷ (1000000000/100) = 5 即:50ms的延时在100Hz时钟分辨率下对应5个tick。 创建 void cyg_clock_create(cyg_resolution_t resolution, cyg_handle_t *handle, cyg_clock *clock) 使用给定分辨率创建一个新的时钟,新时钟的句柄由handle带回。 删除 void cyg_clock_delete(cyg_handle_t clock) 调用此函数前应确保系统中已经没有使用该时钟的组件。 将时钟转换为计数器 void cyg_clock_to_counter(cyg_handle_t clock, cyg_handle_t *counter) 设置时钟分辨率 void cyg_clock_set_resolution(cyg_handle_t clock, cyg_resolution_t resolution) 获取时钟分辨率 cyg_resolution_t cyg_clock_get_resolution(cyg_handle_t clock) 获取系统的实时时钟(RTC,不是外部RTC,是内核抽象的clock) cyg_handle_t cyg_real_time_clock(void) 获取当前的系统时间,单位为时钟滴答。系统时间用64位的数字表示。 cyg_tick_count_t cyg_current_time(void) ========== * 告警器 * ========== 内核告警器与计数器一起使用,使得当某种时间发生了一定的次数时采取响应的动作。如果与计数器一起使用的是时钟,那么当时间滴答的个数达到适当的值也就是在一定的时间周期后将发生告警行为。 创建 void cyg_alarm_create ( cyg_handle_t counter, //和告警器一起使用的计数器 cyg_alarm_t *alarmfn, //告警时的回调函数 cyg_addrword_t data, //传给回调函数的参数 cyg_handle_t *handle, //返回的告警器句柄 cyg_alarm *alarm //告警器对象 ) 其中alarmfn对应的回调函数格式如下: void alarm_fun(cyg_handle_t alarm, cyg_addrword_t data) { ...... } 删除 void cyg_alarm_delete(cyg_handle_t alarm) 初始化并启动一个告警器 void cyg_alarm_initialize ( cyg_handle_t alarm, //告警器句柄 cyg_tick_count_t trigger, //绝对触发时间 cyg_tick_count_t interval //触发间隔 ) 返回告警器的下一次触发的绝对时间和它的触发间隔 void cyg_alarm_get_times ( cyg_handle_t alarm, //待查询的告警器的句柄 cyg_tick_count_t trigger, //下一次触发时的绝对时间 cyg_tick_count_t interval //当前触发间隔 ) 使能告警器 void cyg_alarm_enable(cyg_handle_t alarm) 禁止告警器 void cyg_alarm_disable(cyg_handle_t alarm) =========================================================================================== 下面用一个具体实验来说明时间相关API函数的用法。 使用时钟函数统计程序执行时间,在程序开始执行前和执行完毕后分别调用cyg_current_time获得开始时间和结束时间,将tick单位转换为毫秒单位,计算单位时间内执行循环次数和单个循环执行时间。 使用告警器,以2秒为周期,让蜂鸣器交替发声和静音。 注意:cyg_current_time得到的时间是64位的,printf显示64位整数时使用“%lld”格式(MSVC中使用“I64d”);另外不要在告警器回调函数里使用printf函数。 测试SmartARM2200的结果为(循环0x80000次):每秒执行195527.8次循环,每循环执行时间为5us。蜂鸣器响2秒,停2秒,周而复始。 测试EasyARM2200的结果为(循环0x80000次):每秒执行344926.3次循环,每循环执行时间为3us。 由此可见EasyARM2200比SmartARM2200速度快,具体原因是前者用的SRAM比后者用的PSRAM快。 有了时间相关函数后,我们就可以定时执行某些任务、统计bit速率。例如:在图象处理中能够比较方便地定时插入I帧,码源速率控制...... //测试程序 #include <cyg/kernel/kapi.h> #include <cyg/hal/hal_io.h> #include <cyg/hal/plf_io.h> #define STACK_SIZE 4096 #define ALARM_INTERVAL 200 //2秒 #define BEEPCON 0x0000080 #define LOOPNUM 0x80000 char stack[STACK_SIZE]; static cyg_thread thread_data; static cyg_handle_t thread_handle; cyg_handle_t counter_handle; cyg_handle_t alarm_handle; cyg_alarm alarm_object; void alarm_func(cyg_handle_t alarm_handle, cyg_addrword_t data) { int i; HAL_READ_UINT32(LPC2XXX_GPIO_IO0SET,i); if((i&BEEPCON) == 0 ) { HAL_WRITE_UINT32(LPC2XXX_GPIO_IO0SET,BEEPCON); } else { HAL_WRITE_UINT32(LPC2XXX_GPIO_IO0CLR,BEEPCON); } } void alarm(cyg_addrword_t data) { cyg_tick_count_t t1, t2, t; int i, sum; printf("/n/n/n"); printf("/t *******************************/n"); printf("/t * Hello! Alarm test. */n"); printf("/t *******************************/n/n/n"); printf("Test speed:/n"); sum = 0; t1 = cyg_current_time(); for(i = 0; i < LOOPNUM; i++) { sum = sum + i; } t2 = cyg_current_time(); t = abs(t2-t1) * 10; printf("start=%lld/nend=%lld/n", t1, t2); printf("Total time = %lldms/n",t); printf("Total loop = %lld/n", LOOPNUM); if(t == 0) { t = 1; } printf("%lf(loop/sec) , %lf(sec/loop)/n", LOOPNUM * 1000.0 / t, t / 1000.0 / LOOPNUM); //设置输出到BEEP HAL_WRITE_UINT32(LPC2XXX_GPIO_IO0DIR,BEEPCON); //禁止刚一开始就BEEP,上电缺省输出是低电平,导致一开始就鸣叫。 HAL_WRITE_UINT32(LPC2XXX_GPIO_IO0SET,BEEPCON); //将时钟转换为计数器。cyg_real_time_clock()函数获取系统实时时钟(RTC)。实时时钟用于系统的延时、阻塞等待等操作。 cyg_clock_to_counter(cyg_real_time_clock(), &counter_handle); //创建警告器 cyg_alarm_create(counter_handle, alarm_func, 0, &alarm_handle, &alarm_object); //初始化警告器 cyg_alarm_initialize(alarm_handle, cyg_current_time() + ALARM_INTERVAL, ALARM_INTERVAL); //使能警告器 cyg_alarm_enable(alarm_handle); while(1) { cyg_thread_delay(2000); } } void cyg_start(void) { // Create a main thread, so we can run the scheduler and have time 'pass' cyg_thread_create(10, // Priority - just a number alarm, // entry 0, // entry parameter "alarm", // Name &stack, // Stack STACK_SIZE, // Size &thread_handle, // Handle &thread_data // Thread data structure ); cyg_thread_resume(thread_handle); // Start it cyg_scheduler_start(); }