当前位置: 首页 > news >正文

OpenVela之 Arch Timer 驱动框架使用指南

一、概述

在嵌入式系统开发中,定时器是实现任务调度、精确延时等功能的核心组件。Arch Timer 作为基于 Timer Driver 实现的间隔定时器,在系统调度中扮演着重要角色。本文将全面介绍 Arch Timer 驱动框架,从基本概念到实际应用,帮助开发者快速掌握其使用与开发技巧。

二、认识 Arch Timer

1. 什么是 Arch Timer

Arch Timer 是基于 Timer Driver 实现的间隔定时器,为操作系统的 sched 模块提供了丰富的 timer 接口。它支持两种工作模式,以满足不同场景的需求:

  • Tickless 模式:允许更灵活高效的系统调度,无周期性时钟中断,系统在无任务执行时进入空闲模式,有效降低功耗。
  • Tick 模式:提供固定时间间隔的调度机制,按照配置的固定周期运行,保证周期性任务的稳定执行。

Arch timer 在系统中的位置框架如下图所示:
在这里插入图片描述

2. Arch Timer 的驱动框架

Arch Timer 的驱动框架分为 upper - half 和 lower - half 两部分:

  • upper - half 部分:由 openvela 提供,其中 up_timer_initialize 接口需由芯片厂商实现。
  • lower - half 部分:需芯片厂商进行适配,实现硬件级别的控制。

应用程序可通过标准的 POSIX API 接口或 ioctl 接口,调用这两部分的接口来完成相应功能。
在这里插入图片描述

三、Arch Timer 接口详解

sched 模块依赖于 arch 模块提供的定时器接口,这些接口定义在 /include/nuttx/arch.h 头文件中。在 Tickless 模式下,接口按时间单位分为基于微秒(us)和基于计时单元(tick)的两组,开发者可通过配置选项 CONFIG_SCHED_TICKLESS_TICK_ARGUMENT 选择启用哪一组。默认支持 tick 接口以确保运行效率,部分主要接口如下:

Arch Timer 接口说明

接口名称功能描述
up_timer_set_lowerhalf初始化 Arch Timer 定时器,设置一个 timer_lowerhalf_s 实例,并启动定时器
up_timer_tick_start启动定时器,仅在 Tickless 模式下使用,参数为超时时间(单位:tick)
up_timer_tick_cancel停止 Arch Timer 定时器,仅在 Tickless 模式中使用,返回当前剩余 tick 数
up_timer_getmask获取定时器支持的时间掩码(mask)的值
up_timer_gettick获取定时器当前已经经过的 tick 数值
up_udelay实现以微秒(us)为单位的延时操作
up_mdelay实现以毫秒(ms)为单位的延时操作

四、Timer Driver 深入了解

1. 配置说明

启用和调整 Timer Driver 功能需配置三个关键选项:

CONFIG_TIMER:启用 Timer Driver 功能。
CONFIG_TIMER_ARCH:启用 arch timer 模块。
CONFIG_SCHED_TICKLESS:启用 Tickless 模式。

相关配置文件路径如下:

sched/Kconfig:包含 SCHED_TICKLESS 等相关配置。

# sched/Kconfig
config SCHED_TICKLESSdepends on ARCH_HAVE_TICKLESSconfig SCHED_TICKLESS_TICK_ARGUMENT
config SCHED_TICKLESS_LIMIT_MAX_SLEEP

drivers/timers/Kconfig:包含 TIMER 和 TIMER_ARCH 等配置。

# drivers/timers/Kconfig
config TIMER
......
if TIMER
config TIMER_ARCHselect ARCH_HAVE_TICKLESSselect ARCH_HAVE_TIMEKEEPINGselect SCHED_TICKLESS_LIMIT_MAX_SLEEP  if SCHED_TICKLESSselect SCHED_TICKLESS_TICK_ARGUMENT  if SCHED_TICKLESS
#endif

可通过以下命令检查配置是否正确:

grep -rE "CONFIG_TIMER|CONFIG_TIMER_ARCH|CONFIG_ARCH_HAVE_TICKLESS|CONFIG_ARCH_HAVE_TIMEKEEPING|CONFIG_SCHED_TICKLESS_TICK_ARGUMENT|CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP" nuttx/.config

2. 初始化

在 board 初始化过程中,需调用具体厂商实现的 ***_timer_initialize 函数,完成分配并初始化 struct timer_lowerhalf_s 结构实例、注册 Timer 驱动等操作,注册后会生成 /dev/timer 设备节点,并绑定相关结构实例。

/***************************************************************************** Name: timer_register** Description:*   This function binds an instance of a "lower half" timer driver with the*   "upper half" timer device and registers that device so that can be used*   by application code.**   When this function is called, the "lower half" driver should be in the*   disabled state (as if the stop() method had already been called).**   NOTE:  Normally, this function would not be called by application code.*   Rather it is called indirectly through the architecture-specific*   initialization.** Input Parameters:*   dev path - The full path to the driver to be registered in the NuttX*     pseudo-filesystem.  The recommended convention is to name all timer*     drivers as "/dev/timer0", "/dev/timer1", etc.  where the driver*     path differs only in the "minor" number at the end of the device name.*   lower - A pointer to an instance of lower half timer driver.  This*     instance is bound to the timer driver and must persists as long as*     the driver persists.** Returned Value:*   On success, a non-NULL handle is returned to the caller.  In the event*   of any failure, a NULL value is returned.*****************************************************************************/FAR void *timer_register(FAR const char *path,FAR struct timer_lowerhalf_s *lower);

3. 上下层接口

  • upper - half 接口:主要供 sched 调用,根据配置可选择以 struct timespec 或 tick 为单位的接口,减少时间转换工作。

  • lower - half 接口:通过 struct timer_ops_s 提供标准化接口,供 upper - half、ioctl 系统调用等使用,按时间单位分为两组,开发者可根据需求选择实现,未实现的接口有默认实现。

struct timer_ops_s
{/* Required methods *******************************************************/CODE int (*start)(FAR struct timer_lowerhalf_s *lower);CODE int (*stop)(FAR struct timer_lowerhalf_s *lower);CODE int (*getstatus)(FAR struct timer_lowerhalf_s *lower,FAR struct timer_status_s *status);CODE int (*settimeout)(FAR struct timer_lowerhalf_s *lower,uint32_t timeout);CODE void (*setcallback)(FAR struct timer_lowerhalf_s *lower,CODE tccb_t callback, FAR void *arg);CODE int (*maxtimeout)(FAR struct timer_lowerhalf_s *lower,FAR uint32_t *maxtimeout);CODE int (*ioctl)(FAR struct timer_lowerhalf_s *lower, int cmd,unsigned long arg);CODE int (*tick_getstatus)(FAR struct timer_lowerhalf_s *lower,FAR struct timer_status_s *status);CODE int (*tick_setttimeout)(FAR struct timer_lowerhalf_s *lower,uint32_t timeout);CODE int (*tick_maxtimeout)(FAR struct timer_lowerhalf_s *lower,FAR uint32_t *maxtimeout);
};

五、调用流程解析

  1. Tickless 模式
  • 模式概述:sched 动态管理软件定时器,选择最短时长的定时器作为下一次超时时间,动态调用启动或停止函数。
  • 调用流程:初始化定时器后,sched 计算超时时间,配置并启动定时器,超时后触发回调函数通知 sched,随后重新分配时长并启动下一个定时器或任务。

以下为 Tickless 模式下的调用流程图

在这里插入图片描述

  1. Tick 模式
  • 模式概述:sched 启用固定时间间隔的周期性定时器,间隔由 CONFIG_USEC_PER_TICK 配置,系统初始化时启动,无需频繁调用启动和停止接口。
  • 流程描述:初始化时启动周期性定时器,按固定周期触发事件,触发调度器调度,保证周期性任务执行。

六、驱动适配实例

以 nrf52(基于 ARMv7 - M 架构)为例,驱动适配主要包括两部分:

1. 实现 up_timer_initialize 接口

在平台特定代码中实现该接口,调用 timer_register 函数注册定时器驱动,生成设备节点。初始化调用流程如下:

nx_start
-> clock_initialize-> up_timer_initialize      #开发者实现-> systick_initialize    #开发者实现-> timer_register-> up_timer_set_lowerhalf

以下为 systick_initialize 的实现参考:

struct timer_lowerhalf_s *systick_initialize(bool coreclk,unsigned int freq, int minor)
{struct systick_lowerhalf_s *lower =(struct systick_lowerhalf_s *)&g_systick_lower;.../* Register the timer driver if need */if (minor >= 0){char devname[32];sprintf(devname, "/dev/timer%d", minor);timer_register(devname, (struct timer_lowerhalf_s *)lower);}return (struct timer_lowerhalf_s *)lower;
}

2. 实现 lower - half 接口

通过定义 struct timer_ops_s 结构的实例,实现其中的方法,如 startstop 等,以控制硬件运行。

在 ARMv7-M 的 Arch Timer 适配中,lower-half 方法的出现形式如下:

文件路径: arch/arm/src/armv7-m/arm_systick.c

/* "Lower half" driver methods */
static const struct timer_ops_s g_systick_ops =
{.start       = systick_start,.stop        = systick_stop,.getstatus   = systick_getstatus,.settimeout  = systick_settimeout,.setcallback = systick_setcallback,.maxtimeout  = systick_maxtimeout,
};

七、POSIX API 与测试实例

1. POSIX API

包括 timer_createtimer_deletetimer_settime 等接口,用于创建、删除、配置定时器等操作。

  1. timer_create
/*
* 函数:timer_create
* 参数:clockid,定时类型;evp,sigevent结构体,用来指定定时器到期时如何相应
*       timerid,返回一个timerid
* 返回:0 success | -1 error
* 说明:创建一个定时器
*/
int timer_create(clockid_t clockid, FAR struct sigevent *evp,FAR timer_t *timerid);
  1. timer_delete
/*
* 函数:timer_delete
* 参数:timerid,执行timer_create返回的timerid
* 返回:0 success | -1 error
* 说明:删除一个定时器
*/
int timer_delete(timer_t timerid);
  1. timer_settime
/* 设置定时器
* 函数:timer_settime
* 参数:timerid:id
*      flags:相对时间/绝对时间
*      value:定时时间和间隔
*      ovalue:若不为NULL,则返回上次定时的剩余到期时间 
* 返回:0 success | -1 error
* 说明:设置定时
*/
int timer_settime(timer_t timerid, int flags,FAR const struct itimerspec *value,FAR struct itimerspec *ovalue);

2. IOCTL 接口

应用级别的程序可以通过 ioctl 函数直接操作定时器(前提是在 bringup 过程中已注册 /dev/timer 设备节点)。

支持的 IOCTL 命令

IOCTL 命令功能描述参数类型参数说明
TCIOC_START启动定时器
TCIOC_STOP停止定时器
TCIOC_GETSTATUS获取当前定时器的状态struct timer_status_s*用于存储定时器状态信息的结构体指针
TCIOC_SETTIMEOUT设置定时器间隔时间(单位:微秒)32位无符号整数定时器的间隔时间值
TCIOC_NOTIFICATION设置定时器超时消息struct timer_notify_s*包含超时通知配置的结构体指针
TCIOC_MAXTIMEOUT获取定时器支持的最大时延(单位:微秒)uint32_t*用于存储最大时延值的指针

3. 测试实例

  1. TIMER API 测试:通过 cmocka 测试框架验证 POSIX API 的工作,包括创建、配置、以及删除定时器的全过程。

此部分介绍如何测试定时器相关功能,通过 cmocka 测试框架(可参考文档:OpenVela之开发自测试框架cmocka进行cmocka相关配置)验证 POSIX API 的工作,包括创建、配置、以及删除定时器的全过程。

  • 代码位置:apps/testing/drivertest/drivertest_posix_timer.c
  • 测试框架:cmocka
  • 依赖配置:
    • TESTING_CMOCKA
    • TESTING_DRIVER_TEST
    • CONFIG_SIG_EVTHREAD

在这里插入图片描述

在这里插入图片描述

  • 运行步骤:
  1. 启用上述配置,并构建固件。
  2. 在 NuttShell(NSH)中运行以下命令:
nsh> cmocka_posix_timer

在这里插入图片描述

  1. IOCTL 测试:通过 IOCTL 命令控制定时器设备,测试启动、停止、设置间隔等功能。
  • 代码路径:apps/testing/drivertest/drivertest_timer.c
  • 测试框架:cmocka
  • 依赖配置:
    • TESTING_CMOCKA
    • TESTING_DRIVER_TEST

在这里插入图片描述

在这里插入图片描述

  • 测试步骤:
  1. 启用依赖配置,并构建功能完整的固件。
  2. 在 NuttShell(NSH)中运行以下命令:
nsh> cmocka_driver_timer

在这里插入图片描述

总结

Arch Timer 驱动框架为嵌入式系统提供了灵活高效的定时器解决方案,支持两种工作模式,满足不同调度需求。通过本文的介绍,相信开发者对其原理、接口、配置和应用有了全面的了解,能够根据实际需求进行开发和适配,充分发挥其在系统调度中的作用。

http://www.dtcms.com/a/283571.html

相关文章:

  • GESP2025年6月认证C++四级( 第三部分编程题(2)排序)
  • 机器学习中Precision(查准率)和Recall(查全率)
  • 第2章通用的高并发架构设计——2.3 高并发读场景方案2:本地缓存
  • nftables的配置与使用
  • sqlite3_exec函数练习
  • 多维动态规划题解——最小路径和【LeetCode】记忆化搜索翻译为递推写法
  • # Win11开机卡死?无法进入登录界面?3招强制进安全模式,快速修复系统
  • 亚马逊广告深度优化:如何平衡大词与小词的投放,提升转化率?
  • 初学python的我开始Leetcode题-13
  • webpack将组件vue进行编译混淆,并能正常使用编译之后的文件
  • H3CNE综合实验
  • 2025第二届绿色能源与机电工程国际学术会议(ICGEME 2025)
  • 数据库(five day)——物物而不物于物,念念而不念于念。
  • java基础(day09)
  • Python中的列表list、元组(笔记)
  • BASE64编码通俗介绍
  • 观察者设计模式
  • 嵌入式单片机开发实战指南: 从RISC-V到TinyML全栈技术
  • 测试工作中的质量门禁管理
  • DMC-E 系列总线控制卡----雷赛板卡介绍(十六)
  • ST表及数学归纳法
  • 洛谷 P11247 [GESP202409 六级] 算法学习-普及/提高-
  • IIS网站间歇性打不开暴力解决方法
  • 基于多源时序特征卷积网络(MSTFCN)的光伏功率预测模型
  • 对称二叉树、二叉树直径
  • 云测试提前定位和解决问题 萤火故事屋上架流程
  • LLM指纹底层技术——模型架构
  • IPD核心思想之一:业务与能力并重
  • 专业文档盖章软件,批量处理高效
  • Qt开发环境搭建指南:从零开始构建跨平台应用基石