PX4模块设计之十二:High Resolution Timer设计

PX4飞控整体软件设计在嵌入式平台(platform)层面增加了一个公共层(common)来处理work_queue、uORB、event,并统一接口对外提供服务。而这些对外服务的基础又是基于HRT模块。

这里针对Nuttx系统的Flat Build和Protected Build对HRT模块进行研读。

1. HRT模块特性

支持特性:

  1. 高精度(32bit毫秒)
  2. 支持循环、延迟、及定时触发机制
  3. 支持Flat/Protected Build两种编译模式
  4. 支持ticket精度latency检测

2. HRT模块基本功能

基本模块功能是通过hrt_call_internal函数的不同入参来区分的:

static void hrt_call_internal(struct hrt_call *entry, hrt_abstime deadline, hrt_abstime interval, hrt_callout callout, void *arg)
  • HRT列表:struct hrt_call *entry
  • 开始时间:hrt_abstime delay
  • 触发间隔:hrt_abstime interval
  • 被调接口:hrt_callout callout
  • 被调参数:void *arg
hrt_call_internal
 ├──> px4_enter_critical_section
 ├──> <entry->deadline != 0>
 │   └──> sq_rem(&entry->link, &callout_queue);
 ├──> entry->deadline = deadline;
 ├──> entry->period = interval;
 ├──> entry->callout = callout;
 ├──> entry->arg = arg;
 ├──> hrt_call_enter(entry);
 └──> px4_leave_critical_section(flags);
hrt_call_enter
 ├──> call = (struct hrt_call *)sq_peek(&callout_queue);
 ├──> <(call == NULL) || (entry->deadline < call->deadline)>
 │   ├──> sq_addfirst(&entry->link, &callout_queue);
 │   └──> hrt_call_reschedule();
 └──> <else><do><(call = next) != NULL>
     ├──> next = (struct hrt_call *)sq_next(&call->link);
     ├──> <(next == NULL) || (entry->deadline < next->deadline)>
     ├──> sq_addafter(&call->link, &entry->link, &callout_queue);
     └──> break

2.1 循环触发接口

void hrt_call_every(struct hrt_call *entry, hrt_abstime delay, hrt_abstime interval, hrt_callout callout, void *arg)
  • HRT列表:struct hrt_call *entry
  • 开始时间:hrt_abstime delay
  • 触发间隔:hrt_abstime interval
  • 被调接口:hrt_callout callout
  • 被调参数:void *arg

2.2 延迟触发接口

void hrt_call_after(struct hrt_call *entry, hrt_abstime delay, hrt_callout callout, void *arg)
  • HRT列表:struct hrt_call *entry
  • 开始时间:hrt_abstime delay = hrt_absolute_time() + delay
  • 触发间隔:hrt_abstime interval = 0
  • 被调接口:hrt_callout callout
  • 被调参数:void *arg

2.3 定时触发接口

void hrt_call_at(struct hrt_call *entry, hrt_abstime calltime, hrt_callout callout, void *arg)
  • HRT列表:struct hrt_call *entry
  • 开始时间:hrt_abstime delay
  • 触发间隔:hrt_abstime interval = 0
  • 被调接口:hrt_callout callout
  • 被调参数:void *arg

2.4 其他功能

bool hrt_called(struct hrt_call *entry) //已经被调用,且从HRT列表中删除
void hrt_cancel(struct hrt_call *entry) //从HRT列表中删除

void hrt_call_init(struct hrt_call *entry) //初始化HRT节点
void hrt_call_delay(struct hrt_call *entry, hrt_abstime delay) //将该HRT节点时间从当前时刻再延迟delay毫秒

void hrt_init(void) //初始化高精度模块

hrt_abstime hrt_absolute_time(void) //返回值获取当前绝对时间,单位:ms
void hrt_store_absolute_time(volatile hrt_abstime *t) //传址获取当前绝对时间,单位:ms

3. HRT模块精度

3.1 精度粒度

HRT底层进度通过hrt_tim_isr可以分析出,基本在cpu tick级别;

hrt_tim_isr
 ├──> latency_actual = rCNT; //cpu tick精度
 ├──> status = rSR; //copy interrupt status
 ├──> rSR = ~status; //ack the interrupts we just read
 └──> <status & SR_INT_HRT>
     ├──> hrt_latency_update  //do latency calculations
     ├──> hrt_call_invoke     //run any callouts that have met their deadline
     └──> hrt_call_reschedule //and schedule the next interrupt

而实际使用过程timer要求的精度在毫秒级,主要转换函数见hrt_call_invoke里面的hrt_absolute_time函数。

hrt_call_invoke
 └──> <while (true)>
     ├──> hrt_abstime now = hrt_absolute_time();
     ├──> call = (struct hrt_call *)sq_peek(&callout_queue);
     ├──> <call == NULL>
     │   └──> break
     ├──> <call->deadline > now>
     │   └──> break
     ├──> sq_rem(&call->link, &callout_queue); //remove and execute
     ├──> deadline = call->deadline; //save the intended deadline for periodic calls
     ├──> call->deadline = 0; //zero the deadline, as the call has occurred
     ├──> <call->callout>
     │   └──> call->callout(call->arg); //invoke the callout (if there is one)
     └──> <all->period != 0>
         ├──> <call->deadline <= now>
         │   └──> call->deadline = deadline + call->period;
         └──> hrt_call_enter(call);
hrt_call_reschedule
 ├──> hrt_abstime	now = hrt_absolute_time();
 ├──> struct hrt_call	*next = (struct hrt_call *)sq_peek(&callout_queue);
 ├──> hrt_abstime	deadline = now + HRT_INTERVAL_MAX;
 ├──> <next != NULL>
 │   ├──> <next->deadline <= (now + HRT_INTERVAL_MIN)>
 │   │   └──> deadline = now + HRT_INTERVAL_MIN;
 │   └──> <next->deadline < deadline>
 │       └──> deadline = next->deadline;
 └──> rCCR_HRT = latency_baseline = deadline & 0xffff;

3.2 精度误差

实际误差在hrt_latency_update中有latency 统计数据,分别对应1tick,2tick,5tick,,,100tick,1000tick,这个和CPU的性能以及编译使用的模式有关。

hrt_latency_update
 ├──> uint16_t latency = latency_actual - latency_baseline;
 └──> <for (index = 0; index < LATENCY_BUCKET_COUNT; index++)>
     └──> <latency <= latency_buckets[index]>
         ├──> latency_counters[index]++;
         └──> return
注:const uint16_t latency_buckets[LATENCY_BUCKET_COUNT] = { 1, 2, 5, 10, 20, 50, 100, 1000 };

4. 编译模式

4.1 Flat Build

略。前面介绍的都是内核态的代码,这里不再细说。

4.2 Protected Build

关于boardctl注册机制,可以参考board_ctrl.c,有兴趣的朋友可以直接阅读。

4.2.1 内核态代码

通过px4_register_boardct_ioctl对hrt_ioctl进行注册。

src/drivers/drv_hrt.h

#define HRT_WAITEVENT		_HRTIOC(1)
#define HRT_ABSOLUTE_TIME	_HRTIOC(2)
#define HRT_CALL_AFTER		_HRTIOC(3)
#define HRT_CALL_AT		_HRTIOC(4)
#define HRT_CALL_EVERY		_HRTIOC(5)
#define HRT_CANCEL		_HRTIOC(6)
#define HRT_GET_LATENCY		_HRTIOC(7)
#define HRT_RESET_LATENCY	_HRTIOC(8)

platforms/nuttx/src/px4/stm/stm32_common/hrt/hrt.cp第737-760行。

/**
 * Initialise the high-resolution timing module.
 */
void
hrt_init(void)
{
	sq_init(&callout_queue);
	hrt_tim_init();

#ifdef HRT_PPM_CHANNEL
	/* configure the PPM input pin */
	px4_arch_configgpio(GPIO_PPM_IN);
#endif

#if !defined(CONFIG_BUILD_FLAT)
	/* Create a semaphore for handling hrt driver callbacks */
	px4_sem_init(&g_wait_sem, 0, 0);
	/* this is a signalling semaphore */
	px4_sem_setprotocol(&g_wait_sem, SEM_PRIO_NONE);

	/* register ioctl callbacks */
	px4_register_boardct_ioctl(_HRTIOCBASE, hrt_ioctl);
#endif
}

platforms/nuttx/src/px4/stm/stm32_common/hrt/hrt.cp第1005-1095行。

#if !defined(CONFIG_BUILD_FLAT)
/* These functions are inlined in all but NuttX protected/kernel builds */

latency_info_t get_latency(uint16_t bucket_idx, uint16_t counter_idx)
{
	latency_info_t ret = {latency_buckets[bucket_idx], latency_counters[counter_idx]};
	return ret;
}

void reset_latency_counters(void)
{
	for (int i = 0; i <= get_latency_bucket_count(); i++) {
		latency_counters[i] = 0;
	}
}

/* board_ioctl interface for user-space hrt driver */
int
hrt_ioctl(unsigned int cmd, unsigned long arg)
{
	hrt_boardctl_t *h = (hrt_boardctl_t *)arg;

	switch (cmd) {
	case HRT_WAITEVENT: {
			irqstate_t flags;
			px4_sem_wait(&g_wait_sem);
			/* Atomically update the pointer to user side hrt entry */
			flags = px4_enter_critical_section();

			/* This should be always true, but check it anyway */
			if (hrt_entry_queued > 0) {
				*(struct hrt_call **)arg = next_hrt_entry[--hrt_entry_queued];
				next_hrt_entry[hrt_entry_queued] = NULL;

			} else {
				hrt_entry_queue_error = true;
			}

			px4_leave_critical_section(flags);

			/* Warn once for entry queue being full */
			if (hrt_entry_queue_error && !suppress_entry_queue_error) {
				PX4_ERR("HRT entry error, queue size now %d", hrt_entry_queued);
				suppress_entry_queue_error = true;
			}
		}
		break;

	case HRT_ABSOLUTE_TIME:
		*(hrt_abstime *)arg = hrt_absolute_time();
		break;

	case HRT_CALL_AFTER:
		hrt_call_after(h->entry, h->time, (hrt_callout)hrt_usr_call, h->entry);
		break;

	case HRT_CALL_AT:
		hrt_call_at(h->entry, h->time, (hrt_callout)hrt_usr_call, h->entry);
		break;

	case HRT_CALL_EVERY:
		hrt_call_every(h->entry, h->time, h->interval, (hrt_callout)hrt_usr_call, h->entry);
		break;

	case HRT_CANCEL:
		if (h && h->entry) {
			hrt_cancel(h->entry);

		} else {
			PX4_ERR("HRT_CANCEL called with NULL entry");
		}

		break;

	case HRT_GET_LATENCY: {
			latency_boardctl_t *latency = (latency_boardctl_t *)arg;
			latency->latency = get_latency(latency->bucket_idx, latency->counter_idx);
		}
		break;

	case HRT_RESET_LATENCY:
		reset_latency_counters();
		break;

	default:
		return -EINVAL;
	}

	return OK;
}
#endif

4.2.2 用户态代码

platforms/nuttx/src/px4/common/usr_hrt.cpp 主要通过boardctl,最终调用hrt_ioctl来执行命令。

5. 参考资料

【1】PX4开源软件框架简明简介

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
boost::asio::high_resolution_timer是一个定时器类,用于在boost::asio库中进行高分辨率的定时操作。 boost::asio库是一个用于网络和异步编程的C++库。它提供了丰富的功能,包括异步IO操作、定时器、socket通信等。其中,boost::asio::high_resolution_timer是其中的一个定时器类,它使用了高分辨率的时钟来进行精确的定时操作。 使用boost::asio::high_resolution_timer,我们可以创建一个定时器对象,并设置定时的时间间隔。可以使用成员函数expires_from_now()指定定时的时间间隔,参数为一个duration类型的对象,表示时间间隔的长度。 例如,以下代码创建了一个定时器对象timer,设置了定时时间间隔为1秒: boost::asio::high_resolution_timer timer(io_context); timer.expires_from_now(boost::posix_time::seconds(1)); 然后,我们可以调用定时器对象的async_wait()函数来启动定时器,并指定一个回调函数,在定时器超时时被调用。回调函数可以是一个lambda函数,也可以是一个函数对象。 例如,以下代码定义了一个lambda函数作为回调函数: timer.async_wait([](const boost::system::error_code& ec) { if (!ec) { // 定时器超时,执行相应操作 } }); 在定时器超时时,回调函数会被触发,并执行相应操作。 总之,boost::asio::high_resolution_timer是一个用于高分辨率定时操作的定时器类,可以帮助我们在异步编程中进行精确的定时操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值