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]