当前位置: 首页 > 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.lryc.cn/news/590730.html

相关文章:

  • UltraISO编辑ISO文件
  • Karate(Java)接口自动化测试框架
  • 二刷 黑马点评 分布式锁-redission
  • 基于Canal实现MySQL数据库数据同步
  • Alamofire 网络请求全流解析,通俗易懂
  • ai 编程工具,简单总结
  • Python脚本批量修复文件时间戳,根据文件名或拍摄日期
  • 达梦数据库CASE_SENSITIVE大小写敏感差异比较
  • 字段级权限控制场景中,RBAC与ABAC的性能差异
  • 【机器学习【6】】数据理解:数据导入、数据审查与数据可视化方法论
  • [NOIP][C++] 树的重心
  • 嵌入式单片机开发实战指南: 从RISC-V到TinyML全栈技术
  • 筑牢网络安全防线:DDoS/CC 攻击全链路防护技术解析
  • 权限隔离设计中实现字段级别的动态隐藏
  • 工作第一步建立连接——ssh
  • 【JavaScript】从事件流到事件委托
  • 再探多线程Ⅰ--- (创建思路+核心方法+代码样例)
  • [Mysql] Connector / C++ 使用
  • 二分查找算法(一)
  • 多目标优化|HKELM混合核极限学习机+NSGAII算法工艺参数优化、工程设计优化,四目标(最大化输出y1、最小化输出y2,y3,y4),Matlab完整源码
  • WP Force SSL Pro – HTTPS SSL Redirect Boost Your Website‘s Trust in Minutes!
  • 代码随想录算法训练营完结篇
  • 主流 TOP5 AI智能客服系统对比与推荐
  • Raydium CLMM 协议
  • Gradle vs Maven:构建工具世纪对决 —— 像乐高积木与标准模型之间的选择艺术
  • Transform的重要方法
  • excel分组展示业绩及增长率
  • 归一化与激活函数:深度学习的双引擎
  • 【WRFDA数据教程第一期】LITTLE_R 格式详细介绍
  • opencv 值类型 引用类型