跳转至

timer开发指南

本文档以 timer_demo 为例,带你在 HiDiTing 开发板上快速跑通第一个 timer(定时器)应用,并了解如何基于它构建自己的应用。


timer 驱动背景知识

timer 工作原理

timer 是用于生成周期性或单次定时信号的硬件模块,主要功能包括:

  • 生成周期性中断(用于系统心跳、任务调度)
  • 生成单次定时事件(用于延时、超时检测)

操作流程

本示例支持定时器的创建、开始、结束、删除步骤,步骤如下。

timer定时器的工作全流程:

  1. 反初始化定时器:调用uapi_timer_deinit();将timer进行反初始化,消除对后面timer初始化的影响。
  2. 初始化定时器:调用uapi_timer_init();将timer进行初始化。
  3. 适配定时器配置:调用uapi_timer_adapter();配置定时器索引、定时器中断号和中断优先级。
  4. 创建定时器:调用uapi_timer_create();创建一个高精度定时器,函数参数句柄为唯一定时器标 识。

  5. 开启定时器:调用uapi_timer_start();设置超时时间、超时回调函数、回调函数入参以及启动定 时器。

  6. 停止定时器:调用uapi_timer_stop();停止当前定时器计时。

  7. 删除定时器:调用uapi_timer_delete();删除当前定时器。

timer 模式

timer 支持以下工作模式:

模式 特点 适用场景
周期模式 定期触发中断 系统心跳、周期性任务调度
单次模式 触发一次中断后停止 延时、超时检测
自由运行模式 计数器模式 时间测量、性能分析

API 接口列表

本文档示例代码中使用的接口:

接口函数 说明
uapi_timer_init 初始化 Timer
uapi_timer_deinit 去初始化 Timer
uapi_timer_adapter 配置定时器参数
uapi_timer_create 创建定时器
uapi_timer_start 启动定时器
uapi_timer_stop 停止定时器
uapi_timer_delete 删除定时器

完整 API 列表

更多 Timer 接口请参考:Timer API 参考


快速跑通 timer_demo

配置说明

timer_demo 支持通过AT指令进行 timer 定时器的创建、开启、结束和删除功能。相应的AT指令如下:

AT+TIMERTASK=1,1,0,0,1000,0,0,2000
AT指令参数说明 | 参数 | 默认值 | 说明 | |---|---|---| | timer_pority | 1 | 定时器中断优先级 | | timer_index_no | 1 | 定时器索引 | | start_time1 | 0 | timer1定时开始时间(ms) | | end_time1 | 0 | timer1定时结束时间(ms) | | delay_time1 | 1000 | timer1定时间隔时间(ms) | | start_time2 | 0 | timer2定时开始时间(ms) | | end_time2 | 0 | timer2定时结束时间(ms) | | delay_time2 | 2000 | timer2定时间隔时间(ms) |

编译

完成一站式 CLI 环境配置后执行:

# 编译固件,编译生成的固件从 output/3322/fwpkgt 中获取 diting-community.fwpkg
fbb set-target pack_diting_community
fbb build

构建成功后,使用一站式 CLI 烧写固件并打开 UART2 串口监视器。以下为 Windows USB DFU 示例;将 COM3 替换为实际日志串口,其他平台和串口烧写参数参见一站式 CLI 开发环境使用指南

fbb flash -f "$env:FBB_SDK_DIR\output\3322\fwpkg\diting-community.fwpkg" --chip 3322 -d --timeout 180
fbb monitor --port COM3 --baud 750000

使用方式

  1. 烧录固件并启动设备
  2. 通过串口发送 AT 命令,格式如下:
    # TIMER 设置使用示例:设置定时器中断优先级和定时器索引值,以及定时器1和定时器2开始、结束、延时时间
    AT+TIMERTASK=1,1,0,0,1000,0,0,2000
    

通过串口发送 AT 命令,格式如下

预期结果

预期结果


文件结构与代码走读

文件职责

文件 作用
samples/native_samples/timer/CMakeLists.txt 定义 timer_sample 组件及依赖。
samples/native_samples/timer/timer_demo.h 定义测试参数、范围约束和命令表。
samples/native_samples/timer/timer_demo.c 实现定时器创建、启动、回调、计时校验和释放。
samples/native_samples/timer/README.md 给出完整命令和预期日志。

核心数据与常量

名称 作用 开发注意事项
timer_info_t 保存每个定时器的开始、结束和设定延时。 回调与业务线程共享数据时应考虑并发可见性。
timer_handle_t 标识创建成功的定时器实例。 每个成功创建的句柄都要停止并删除。
TIMER_NUM Sample 同时创建的定时器数量。 数组容量、回调索引和等待条件必须保持一致。
TIMER_MS_2_US 将毫秒与微秒参数进行换算。 调用接口前确认单位,避免把日志单位直接作为接口单位。
timer_task_args_t 承载 AT 测试选择的索引和中断优先级。 自定义应用应由产品配置提供这些值,并在调用前校验范围。

核心业务流程

  1. 调用 uapi_timer_init(),配置定时器索引、中断号和优先级。
  2. 为每个业务定时器调用 uapi_timer_create(),记录开始时间并用 uapi_timer_start() 注册回调。
  3. 回调只记录结束时间、更新轻量状态或通知任务,避免在中断上下文执行阻塞操作。
  4. 业务任务收到完成条件后,依次停止、删除所有句柄,再调用 uapi_timer_deinit()
  5. 任一步失败时只释放已经成功获取的资源,并避免等待一个未启动的定时器。

AT 命令仅用于传入索引和优先级并触发上述流程。下一节将向你展示 以app_run 为示例入口的实现过程。

基于 timer_demo 开发自己的应用

上面的demo是使用AT指令触发运行,HiDiTing还支持app_run方式触发应用在系统启动时自动运行,以下示例将以app_run的方式开发一个开发者自己的应用

  • app_run(func) 是HiDiTing中应用层注册应用函数的宏,基于 GCC 编译器属性和自定义段区(section)自动注册来实现集中调用应用函数,系统启动时会自动遍历所有用 app_run 注册过的函数并执行,无需在 系统 main 函数里逐个调用函数。

代码清单

新建一个 timer 应用(以 my_timer_app 为例)通常只需要以下改动:

  • 新建 my_timer_app.c 源文件
  • 新建 my_timer_app.h 源文件
  • 新建 CMakeLists.txt 源文件
  • my_timer_demo.c 实现timer的初始化,创建、开始操作
  • my_timer_demo.h 中对void my_timer_demo(void)声明
  • CMakeLists.txt 中添加源文件编译规则

my_timer_demo文件结构:

/samples/native_samples/my_timer_demo/
├── my_timer_demo.c
├── my_timer_demo.h
└── CMakeLists.txt

CMakeLists.txt 修改示例

# 在新建的 CMakeLists.txt 中添加应用的源文件
set(SOURCES
    ${CMAKE_CURRENT_SOURCE_DIR}/my_timer_demo.c
)

set(PUBLIC_HEADER
    ${CMAKE_CURRENT_SOURCE_DIR}
)

set(PRIVATE_HEADER

)

set(PUBLIC_DEFINES
    MY_TIMER_DEMO_ENABLE
)

build_component()

关键代码片段

my_timer_app.c —— timer 示例

#include "timer.h"
#include "tcxo.h"
#include "app_init.h"
#include "common_def.h"

#define MY_TIMER_DELAY_MS   10
#define MY_TIMER_PRIO       2

static errcode_t my_timer_callback(uintptr_t data)
{
    UNUSED(data);
    osal_printk("timer timeout!\r\n");
    return ERRCODE_SUCC;
}

void my_timer_demo(void)
{
    osal_printk("my_timer_app start\n");
    uint32_t g_timer_int_count = 0;
    // step1:初始化 timer
    uapi_timer_init();
    uapi_timer_adapter(0, TIMER_1_IRQN, MY_TIMER_PRIO);

    // step2:创建定时器
    timer_handle_t timer_handle;
    uapi_timer_create(0, &timer_handle);

    // step3:启动定时器
    uapi_timer_start(timer_handle, MY_TIMER_DELAY_MS * 1000, my_timer_callback, 0);

    // step4:等待定时器结束
    do {
        osal_msleep(1000);
        osal_printk("Main task running...\r\n");
        g_timer_int_count++;
    }while (g_timer_int_count < MY_TIMER_DELAY_MS);

    // step5: 停止定时器
    uapi_timer_stop(timer_handle);
    // step6: 删除定时器
    uapi_timer_delete(timer_handle);
    // step7:反初始化 timer
    uapi_timer_deinit();
}

app_run运行配置

app_run应用默认是关闭的,如需启用此应用,需用户手动在acore.prelds文件中添加 KEEP (*(SORT(.zinitcall.app_run*.init))) 具体参考示意图如下:

apprun运行配置

acore.prelds

测试验证

新demo完成后,逐项验收:

  • 设备启动后串口输出 my_timer_app start
  • 每隔1秒输出一次定时器超时信息
  • 主任务每隔500ms输出一次运行信息

注意事项

时钟源注意事项:

  • timer是基于外部32MHz晶体为系统提供高精度定时器的功能
  • 高精度场景请使用高精度硬件定时器

优先级注意事项:

  • 中断优先级过高可能影响系统稳定性
  • 定时器回调函数应尽量简短

资源说明:

  • timer 通常为硬件模块,无需额外资源分配

常见错误

错误现象 原因 解决方法
undefined reference to 'uapi_timer_init' 未链接 timer 驱动库 检查 CMakeLists.txt 中是否正确添加了源文件
TIMER_INDEX not defined 未定义定时器索引 确保在代码中正确定义了定时器索引
undefined reference to 'uapi_at_cmd_table_register' 未链接 AT 命令库 检查 CMakeLists.txt 中是否正确添加了 AT 相关头文件路径
编译报 at_ret_t 未定义 未包含 at.h 头文件 添加 #include "at.h"
串口无输出 AT 命令未注册 确认调用了 at_diting_timer_example_cmd_register()
定时器不触发 中断优先级设置过高或未正确初始化 检查中断优先级和初始化代码