armstrong 发表于 2012-5-10 08:34:18

RTOS的精确延时功能扩展

本帖最后由 armstrong 于 2012-5-10 08:43 编辑

大家都知道,OS有一个系统时基,这个时基大多取值为100,200,500,1000。一般都不会大于1000,1000的时候,意味着每1毫秒就产生一次时基中断,OS时基函数也就被执行一次,如果这个时基中断产生的太频繁,对系统性能是有很大影响的。假设我们的系统时基选择100,那就是延时精度为10毫秒。这种精度很让人烦恼不是吗?
那么怎样提供一个精确的RTOS延时而又不增加系统负担呢?
在延时段死循环执行指定数量的指令?不是!因为这是最浪费CPU资源的做法!
把RTOS时基增加?也不是!因为每个时基都会耗费CPU资源,增加时基就意味着增加CPU资源耗费!
这里,我在LPC17xx上使用RIT模块实现了一个精确延时模块,目前精度为10微秒,系统是ucos-ii,供大家参考。
原理很简单,就是维护一个链表结构。有几个线程正在延时,就有几个链表节点,链表节点不占用堆内存,用的是线程局部堆栈。
延时计数硬件不用每10微秒产生中断,中断次数就等于链表的节点数而已!如果不用RIT硬件模块,其他硬件定时器一样可以实现的。
贴上代码先,然后再来分析。

头文件内容:
typedef struct {
    u32_t ticks;
    OS_EVENT *ev;
    void *next;
} hp_tmr_t;

void hp_timer_init(void);
void hp_delay(u32_t ticks);
void hp_delay_ex(hp_tmr_t *tmr);

C文件内容:
static u32_t hpTickBase;
static hp_tmr_t *hpList = NULL;
////////////////////////////////////////////////////////////////////////////////
//|          |
//| 函数名称 |: hp_timer_init
//| 功能描述 |:
//|          |:
//| 参数列表 |:
//|          |:
//| 返    回 |:
//|          |:
//| 备注信息 |: 10uS延时单位。
//|          |:
////////////////////////////////////////////////////////////////////////////////
void hp_timer_init(void)
{
    u32_t utmp;
   
    target_enable_power(PCONP_RIT);
    utmp = target_get_apbclk(PCLK_RIT);
    utmp /= 100000u;    /* 每10uS的APB时钟计数。*/
    hpTickBase = utmp;
   
    NVIC_DisableIRQ(RIT_IRQn);
    LPC_RIT->RIMASK = 0u;
    // 清除中断标志并停止计数器。
    LPC_RIT->RICTRL = ((1<<2)|(1<<0));
    NVIC_EnableIRQ(RIT_IRQn);
}

////////////////////////////////////////////////////////////////////////////////
//|          |
//| 函数名称 |: RIT_IRQHandler
//| 功能描述 |:
//|          |:
//| 参数列表 |:
//|          |:
//| 返    回 |:
//|          |:
//| 备注信息 |:
//|          |:
////////////////////////////////////////////////////////////////////////////////
void RIT_IRQHandler(void)
{
    DECL_CPU_SR;
    u32_t tmval;
    hp_tmr_t *p;
   
    // 清除中断标志并停止计数器。
    LPC_RIT->RICTRL = ((1<<2)|(1<<0));
    OS_ENTER_CRITICAL();
    OSIntNesting++;
    p = hpList;
    while(p && (p->ticks == 0)){
      if(p->ev){
            OSSemPost(p->ev);
      }
      p = (hp_tmr_t *)p->next;
    }
    hpList = p;
    if(p){
      tmval = p->ticks*hpTickBase;
      tmval += LPC_RIT->RICOUNTER;
      LPC_RIT->RICOMPVAL = tmval;
      p->ticks = 0;
      LPC_RIT->RICTRL = ((1<<2)|(1<<3));
    }
    OS_EXIT_CRITICAL();
   
    OSIntExit();
}

////////////////////////////////////////////////////////////////////////////////
//|          |
//| 函数名称 |: __internal_delay
//| 功能描述 |:
//|          |:
//| 参数列表 |:
//|          |:
//| 返    回 |:
//|          |:
//| 备注信息 |: 10uS延时单位。
//|          |:
////////////////////////////////////////////////////////////////////////////////
static void __internal_delay(hp_tmr_t *tmr)
{
    hp_tmr_t *p;
    DECL_CPU_SR;
    INT8U err;
    u32_t cnt;
   
    tmr->next = 0;
    OS_ENTER_CRITICAL();
    // 清除中断标志并停止计数器。
    LPC_RIT->RICTRL = ((1<<2)|(1<<0));
    p = hpList;
    if(p){
      cnt = LPC_RIT->RICOMPVAL;
      cnt -= LPC_RIT->RICOUNTER;
      cnt /= hpTickBase;
      LPC_RIT->RICOMPVAL -= cnt*hpTickBase;
      p->ticks += cnt;
      if(p->ticks > tmr->ticks){
            p->ticks -= tmr->ticks;
            tmr->next = p;
            hpList = tmr;
      }else{
            while(1){
                tmr->ticks -= p->ticks;
                if(p->next == NULL){
                  p->next = tmr;
                  break;
                }
                if(((hp_tmr_t *)p->next)->ticks >= tmr->ticks){
                  ((hp_tmr_t *)p->next)->ticks -= tmr->ticks;
                  tmr->next = p->next;
                  p->next = tmr;
                  break;
                }
                p = p->next;
            }
      }
      cnt = hpList->ticks;
      LPC_RIT->RICOMPVAL += cnt*hpTickBase;
      hpList->ticks = 0;
    }else{
      // 列表为空。
      hpList = tmr;
      cnt = tmr->ticks*hpTickBase;
      cnt += LPC_RIT->RICOUNTER;
      LPC_RIT->RICOMPVAL = cnt;
      tmr->ticks = 0;
    }
    // 启动计数器。
    LPC_RIT->RICTRL = ((1<<2)|(1<<3));
    OS_EXIT_CRITICAL();
   
    OSSemPend(tmr->ev, 0, &err);
}

////////////////////////////////////////////////////////////////////////////////
//|          |
//| 函数名称 |: hp_delay
//| 功能描述 |:
//|          |:
//| 参数列表 |: ticks 延时时间,单位是10uS。
//|          |:
//| 返    回 |:
//|          |:
//| 备注信息 |: 注意:100MHZ主频时,调用该函数会有10uS以上的代码执行开销,因此:
//|          |:       当ticks参数为1时,当函数返回时就已经过去10+10uS时间了。
//|          |:       当ticks参数为100时,实际上延时了1000+10uS,以此类推。
////////////////////////////////////////////////////////////////////////////////
void hp_delay(u32_t ticks)
{
    OS_EVENT *ev = OSSemCreate(0);
    INT8U err;
   
    if(ev && ticks){
      hp_tmr_t tmr;
      tmr.ticks = ticks;
      tmr.ev = ev;
      __internal_delay(&tmr);
      OSSemDel(ev, OS_DEL_ALWAYS, &err);
    }
}

////////////////////////////////////////////////////////////////////////////////
//|          |
//| 函数名称 |: hp_delay_ex
//| 功能描述 |:
//|          |:
//| 参数列表 |:
//|          |:
//| 返    回 |:
//|          |:
//| 备注信息 |: 10uS延时单位。
//|          |:
////////////////////////////////////////////////////////////////////////////////
void hp_delay_ex(hp_tmr_t *tmr)
{
    if(tmr && tmr->ev && tmr->ticks){
      __internal_delay(tmr);
    }
}
页: [1]
查看完整版本: RTOS的精确延时功能扩展