一种嵌入式时钟管理器的设计与实现

发布者:温暖心绪最新更新时间:2013-11-09 来源: eccn关键字:时钟管理器  定时器  钩子函数 手机看文章 扫描二维码
随时随地手机看文章

目前,在嵌入式产品的研发中,低档微处理器软件多采用裸机开发模式实现。在这种开发模式中,常有如下需求:

(1)在经历特定的时间段后,执行特定操作;

(2)根据给定周期执行特定操作。

传统的作法是利用前后台方式:设定硬件定时器,使其在后台以特定周期对各相关操作的标志变量作计数操作;前台则不断对各标志变量巡回查询,若发现标志变量达到预定值,则执行特定操作。可见,上述需求需直接操作硬件定时器实现,其过程繁琐,且需要用户对相关硬件有深入了解。因此,本文设计、实现了一种使用方便的低端系统时钟管理器。

本时钟管理器适用于可提供至少一个硬件定时器的处理器。其为用户提供了有益、友好的裁剪途径,以满足不同目标系统的实际需要。通过裁剪,该时钟管理器的目标代码最小可至100B以下,最大也不超过1KB。

时钟管理器在实现中,将与硬件密切相关的部分组成一独立模块(文件)。针对不同的目标系统处理器,更换该模块即可。为使表述不过抽象,本文以8051系列单片机为目标系统处理器、C51为工具语言阐述该嵌入式时钟管理器的设计与实现。

1 设计

该时钟管理器模块(文件)结构如图1所示。

(1)cONfigClk.h定义了有关系统裁剪、配置的可调参数,通过对configClk.h中相关宏参数的配置,即可实现对该时钟管理器系统的配置和裁剪。

(2)clk_impl.*功能模块用来封装目标系统的一个硬件定时器,以屏蔽不同处理器间的硬件差异,起到HAL(HardwareAbSTractLayer)作用。系统时钟在此构建。

(3)clk.*模块在clk_impl.*提供的HAL基础上进一步封装,通过一个钩子(Hook)函数,为系统提供时钟脉冲,且脉冲宽度可调(配置configClk.h中的相关宏参即可)。

(4)WdLib.*模块为用户应用提供多个软件定时器。

2 实现

2.1 硬件定时器的底层封装

硬件定时器底层封装在图1所示的clk_impl.*中实现。其中定义了一个初始化接口函数和一个定时器中断的ISR(Interrupt Service RouTIne)。令选用的硬件时钟为定时器0(可在configClk.h中配置)。

(1)初始化接口函数void_clkInit(void){ }

用户通过调用该接口函数,可周期性地执行相应的ISR—clkTick_ISR,从而形成逻辑上的系统时钟。另外,本接口函数不为用户直接访问,而在上层模块clk.*中被调用。

(2)定时器0的ISR—clkTick_ISR

  void clkTick_ISR (void) interrupt 1 using REG_GRP_FOR_

  SYS_CLK{ }

其中:REG_GRP_FOR_SYS_CLK为定义于configClk.h中的可调参数,用来设定本ISR的工作寄存器组。[page]

2.2 时钟脉冲的提供

时钟脉冲在图1所示的clk.*中实现。

本文提供三个用户接口函数和一个用户可修改、但不可调用的钩子函数(clkTick_ISR_hook仅能在clkTick_ISR中被调用)。其用户接口声明如下:

  extern void constructClk(void);

  extern void destructClk(void);

  extern UINT8 getClkRate(void);

其中:constructClk用以构建系统时钟,要使用本文所述的时钟管理器,需首先通过调用_clkInit(定义于clk_impl.*模块)实现对本函数的调用;destructClk用以解析业已构建的系统时钟;getClkRate用以获取系统当前的时钟节拍率(即定义于configClk.h中的宏SYS_CLK_RATE的当前值)。

clkTick_ISR_hook由系统声明,用户可修改其定义,其最终仅为系统作周期性调用。用户可将自己需进行的周期性操作放于其中,后面叙述的软件定时器的“守护”例程(wdDaemon)正是置于此处而被周期调用。由于置于其中的操作将在中断执行,所以这些操作应尽可能简短、省时。

2.3 软件定时器的提供

本功能在图1所示的wdLib.*中实现。

其为用户提供了可快速、便捷地实现用户定时需求的接口函数和一个被周期性调用的定时器守护例程wdDaemon。

  extern void constructWDOG(void);//为使用定时器系统作初始化操作

  extern void destructWDOG(void)//置定时器系统为初始态

  extern WDOG_ID wdCreate(void);//建立一个定时器,并返回其ID

  extern STATUS wdCancel(WDOG_ID wdId);//终止指定定时器并复位

  extern STATUS wdDelete(WDOG_ID wdId);//删除指定定时器

  extern STATUS wdStart(WDOG_ID wdId,UINT16 ticks,VOIDFUNCPTR wdr);//启动指定定时器,它会在指定时间后触发给定操作

其中:WDOG_ID为定时器ID类型,即UINT8。传送给wdStart的参数“UINT16 ticks”指明定时时间长度,单位为系统时钟节拍,1节拍=1/SYS_CLK_RATE(s)。因该参数的类型定为UINT16,故定时器的最大定时长度为216×(1/SYS_CLK_RATE),即216/SYS_CLK_RATE(s)。

定时器的实现方案有静态数组法和delta列表法两种方法。这两种方法各有优缺点:前者逻辑简单,ROM用量小,但效率较低(与定时器数目相关);后者逻辑复杂,ROM用量大,但效率较高(与定时器数目无关)。应用中使用哪种方案,可在configClk.h中配置选择。

2.3.1 静态数组法

静态数组法的数据结构如下:

  struct wdNode {

  BOOL flag;//标明本结点是否已被使用

  UINT16 ticks;//用以定时的节拍数

  VOIDFUNCPTR rout;//定时到时需执行的操作

  } data wdList[_MAX_WDOG_NUM_];

其中:_MAX_WDOG_NUM_指出了系统中允许的最大定时器数,其值决定于应用需求及系统资源量,可在configClk.h中设定。一个定时器结点占用5B的RAM空间。具有给定数据结构的静态数组是方案实施的基础。

另外,该静态数组作为软件定时器的全局变量而存在,当系统中有多个定时器活动时,它们都将访问该全局静态数组。重要的是:它们的活动是异步的,所以,对该静态数组(临界资源)的访问需作临界保护。对于51系统,应采用开关中断的方式实现,且应确保不会影响关中断前的中断状态。

(1)用户接口定义

上述用户接口皆基于该静态数组进行,限于篇幅,这里给出关键接口wdStart的定义。

  STATUS wdStart(WDOG_ID wdId,UINT16 ticks,

  VOIDFUNCPTR wdr) {

  if(wdId<_MAX_WDOG_NUM_) {

  if(wdList[wdId].flag) {//判断给定定时器ID有效否

  RTX_ENTER_CRITICAL();//进入临界区

  wdList[wdId].ticks=ticks;//操作静态数组中的特定定时结点

  wdList[wdId].rout=wdr;

  RTX_EXIT_CRITICAL();//退出临界区

  return OK;//定时器启动成功

  }

  }

  return ERROR;//给定定时器ID无效

  }

调用该接口函数,即可启动已创建(wdCreate)的软件定时器。当经历ticks节拍后,给定函数wdr将被执行,以完成用户的定时需求。[page]

(2)定时器守护例程

定时器守护例程wdDaemon被置于前述的钩子函数clkTick_ISR_hook中,以使其周期性执行。由于本例程自身的特点,它应作为clkTick_ISR_hook的最后一个调用函数。本例程是软件定时器实现的核心,而其关键又是对系统栈的调整,为说明其实现流程,给出了如图2所示的wdDaemon的栈(stack)结构。

由图2可知:wdDaemon的返回地址没有入栈,因其为clkTick_ISR_hook中的最后一个函数调用,故其返回地址被优化掉。wdDaemon将栈顶的8B数据上移2B,然后将定时器指定函数的地址插入腾出的栈空间(2B)中。如此,该地址将会被IRET弹入IP中。由于IRET指令的执行而使中断系统复位以重新响应外部中断,同时也使定时器指定函数在非中断态执行,从而不过分影响系统的响应速度。

2.3.2 delta列表法

delta列表法仅维护有效定时器的链表,且链表中的定时器结点按定时剩余时间由小到大排列,使距timeout点最近的定时器作为链表的首结点。链表中定时器结点的顺序由其独特的结点插入算法决定:如有5个定时器,其定时长度分别为10、14、21、32和39,当其组成delta列表时,定时值最小的结点为首结点,其定时存储值为10,而后依序排列,其定时存储值分别为4、7、11、7,即后一个定时器的定时存储值由自己的实际定时值与相邻的前一个定时器的实际定时值相减而得。可见,除首结点外的所有定时器的计数操作在其插入delta列表时就已完成。因而当定时器守护例程确定timeout的定时器时,只需对首结点进行减1或删除的操作,而不需遍历整个列表,从而使delta列表的操作与定时器数量无关。这使delta列表法在大量定时器管理中大显其能。

该法在系统中实现的数据结构为一静态双向链表:

  struct wdNode {

  BOOL          flag;

  UINT16         ticks;

  VOIDFUNCPTR rout;

  UINT8         prior;

  UINT8         next;

  } idata wdList[_MAX_WDOG_NUM_];

  UINT8 headIdx; //索引首结点

有了delta列表法的思路及其实现的数据结构,在静态数组法具体实现的基础上,便可得此法的具体实现。

应用中如果目标系统ROM较小,且系统中启用的定时器少,则用静态数组法;若目标系统ROM较大,且系统中用到的定时器较多,则用delta列表法。

3 应用

针对前述的嵌入式系统中的定时需求,利用定时器管理系统给出其实现代码。

假定“特定操作”为void specFunc(void),“特定时间段”长度为10分钟。

(1)在经历特定的时间段后,执行特定操作。

  #include ″clk.h″

  #include ″wdLib.h″

  void main(void ) {

  WDOG_ID wdId;

  constructClk();constructWDOG();

  wdId=wdCreate();

  wdStart(wdId,10*ONE_MINUTE,specFunc);

  while(1);

  }

(2)以给定周期周期性地执行特定操作。

基于前者,只需在void specFunc(void)函数体的最后加入下述代码即可:

wdStart(wdId,10*ONE_MINUTE,specFunc);

注:该给定周期为10分钟。

由于本时钟管理器只需一个硬件定时器的支持,所以其具有广泛的适用性。使用时,只需进行简单的配置,即可为裸露的目标系统加以简单的软件抽象层。其友好的用户接口有效降低了嵌入式系统的开发难度,提高了目标系统的可靠性。笔者已在实际项目中多次使用了该时钟管理器。基于该时钟管理器的目标系统运行稳定、可靠,从而充分说明该时钟管理器设计的实用性和科学性。

关键字:时钟管理器  定时器  钩子函数 引用地址:一种嵌入式时钟管理器的设计与实现

上一篇:SPARC高性能处理器集成开发环境及其编译器设计与实现
下一篇:基于统一功率格式的SoC的低功耗方案设计

推荐阅读最新更新时间:2024-03-16 13:30

stm32-学习经验总结 ———UCOSIII-软件定时器
一、软件定时器简介 UC/OS-III提供了软件定时器服务,定时器的本质就是一个递减的计数器,当计数器减到0的时候可以触发某种动作的执行,这个动作通过回调函数来实现,回调函数是用户自己定义的,可以是简单的打开一个LED灯,或者开启电机等。当定时器计时完成时,定义的回调函数就会被立即调用,应用程序可以有任意数量的定时器。 注意!一定不要在回调函数中使用阻塞调用或者可以阻塞或删除定时器任务的函数,比如:OSTimeDly() OSTimeDlyHMSM() 等等一些函数。 UCOSIII的软件定时器服务的相关代码是在OS_TIMR.c文件中,当设置OS_CFG.H中的OS_CFG_TMR_EN置为1的时候软件定时器服务被使能。 UCO
[单片机]
STM32F407各定时器时钟频率
TIM1、TIM8~TIM11的时钟为APB2时钟的两倍即168M,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟的两倍即84M。
[单片机]
STM32F407各<font color='red'>定时器</font>的<font color='red'>时钟</font>频率
关于STM32定时器16位拓展32位的探讨
STM32的通用定时器可以实现很多功能,例如:定时计数、测量外部信号脉冲宽度、产生PWM波形、测量输入的PWM波形等。在所有这些操作中,定时器的位数主要影响两个参数,一个是定时或测量的精度,另一个是定时的时间长度。下面我们以一个列表看一下定时的精度和定时的长度有多少: 关于各个预分频器的作用请参考下图的右半部分: 从表中可以看出,在最高精度下(14ns)定时长度只有0.91ms,在精度为250ns(即4MHz)时定时长度可达16.38ms。这是仅使用了定时器的独立工作模式的情况。 对于需要高精度并且长延时的应用,16位的定时(上述精度和时间长度)就不够了,这个问题可以有两种解决办法;第一
[单片机]
关于STM32<font color='red'>定时器</font>16位拓展32位的探讨
【STM32 Cotex-M3处理器系列编程】定时器输出PWM波
//使用定时器TIM4的3通道CH3输出占空比为25%的PWM波 #include stm32f10x.h int main(void) { // SystemInit(); //配置IO口 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE,ENABLE);//IO口使能设置 GPIO_InitTypeDef GPIO_InitStructure; //定义结构体 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM3的C
[单片机]
atmega8 例程:定时器T1 普通模式
/***************************************************************** * 函数库说明:ATMEGA8 定时器T1 普通模式 * 版本: v1.00 * 修改: 庞辉 芜湖联大飞思卡尔工作室 * 修改日期: 2011年08月11日 * * 说明: 6M外部晶振 10ms定时 * * 版本更新: 详见readme.txt * * 20110805 V1.02 *
[单片机]
STM8S103之tim4普通定时器
本文以STM8S103F3P6编程为例,介绍STM8S103F3P6定时器4计时驱动PB5脚 电脑平台:Windows7 64位旗舰 编译软件:IAR 硬件平台:STM8S103F3P6 1.系统时钟初始化为内部16M CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); //时钟 初始化时钟为1分频 16M 2.初始化定时器4设置PB5为输出 void Init_Timer4(void) { GPIO_Init(GPIOB, GPIO_PIN_5, GPIO_MODE_OUT_PP_LOW_FAST); //初始化IO口 TIM4_DeInit()
[单片机]
STM32定时器分时操作系统
前后台系统,RTOS与定时器任务管理系统 前后台系统   在裸机上写程序,通常把程序分为两部分:前台系统和后台系统。   简单的小系统通常是前后台系统,这样的程序包括一个死循环和若干个中断服务程序:应用程序是一个无限循环,循环中调用API函数完成所需的操作,这个大循环就叫做后台系统。中断服务程序用于处理系统的异步事件,也就是前台系统。前台是中断级,后台是任务级。 RTOS   RTOS全称为:Real Time OS,就是实时操作系统,强调的是:实时性。实时操作系统又分为硬实时和软实时。硬实时要求在规定的时间内必须完成操作 ,硬实时系统不允许超时,在软实时里面处理过程超时的后果就没有那么严格。在实时操作系统中,我们可以把要
[单片机]
STM32——系统滴答定时器
一、SysTick【内核中】 【风格:先描述一下库对寄存器的封装,再举例实现某些功能】 SysTick定时器被捆绑在NVIC中,用于产生SysTick异常(异常号: 15)。在以前,操作系统还有所有使用了时基的系统,都必须一个硬件定时器来产生需要的“滴答”中断,作为整个系统的时基。滴答中断对操作系统尤其重要。例如,操作系统可以为多个任务许以不同数目的时间片,确保没有一个任务能霸占系统;或者把每个定时器周期的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。 Corte
[单片机]
STM32——系统滴答<font color='red'>定时器</font>
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved