【RK3588 Android12】高精度定时器hrtimer

前言

Linux内核提供了两种定时器:低精度定时器(timer)和高精度定时器(hrtimer)。hrtimer可以提供纳秒级别的定时精度,适用于对时间精度要求较高的场景,如PWM模拟、精确采样等。本文将详细介绍hrtimer的原理和使用方法。


一、hrtimer与timer对比

1.1 主要区别

特性 timer hrtimer
精度 毫秒级(基于jiffies) 纳秒级
实现 时间轮算法 红黑树算法
执行上下文 软中断 硬中断
适用场景 一般定时任务 高精度定时

1.2 时钟源类型

复制代码
// kernel-5.10/include/uapi/linux/time.h

#define CLOCK_REALTIME           0  // 实时时钟,可被用户修改
#define CLOCK_MONOTONIC          1  // 单调时钟,系统启动后递增
#define CLOCK_PROCESS_CPUTIME_ID 2  // 进程CPU时间
#define CLOCK_THREAD_CPUTIME_ID  3  // 线程CPU时间
#define CLOCK_BOOTTIME           7  // 包含休眠时间的单调时钟

1.3 定时器模式

复制代码
// kernel-5.10/include/linux/hrtimer.h

enum hrtimer_mode {
    HRTIMER_MODE_ABS      = 0x00,  // 绝对时间
    HRTIMER_MODE_REL      = 0x01,  // 相对时间
    HRTIMER_MODE_PINNED   = 0x02,  // 绑定到特定CPU
    HRTIMER_MODE_SOFT     = 0x04,  // 软中断上下文执行
    
    HRTIMER_MODE_ABS_PINNED = HRTIMER_MODE_ABS | HRTIMER_MODE_PINNED,
    HRTIMER_MODE_REL_PINNED = HRTIMER_MODE_REL | HRTIMER_MODE_PINNED,
    HRTIMER_MODE_ABS_SOFT   = HRTIMER_MODE_ABS | HRTIMER_MODE_SOFT,
    HRTIMER_MODE_REL_SOFT   = HRTIMER_MODE_REL | HRTIMER_MODE_SOFT,
};

二、hrtimer API详解

2.1 数据结构

复制代码
// kernel-5.10/include/linux/hrtimer.h

struct hrtimer {
    struct timerqueue_node      node;       // 红黑树节点
    ktime_t                     _softexpires; // 到期时间
    enum hrtimer_restart        (*function)(struct hrtimer *); // 回调函数
    struct hrtimer_clock_base   *base;      // 时钟基准
    u8                          state;      // 状态
    u8                          is_rel;     // 是否相对时间
    u8                          is_soft;    // 是否软中断
};

2.2 初始化

复制代码
/**
 * hrtimer_init - 初始化高精度定时器
 * @timer: 定时器结构体
 * @which_clock: 时钟源类型
 * @mode: 定时器模式
 */
void hrtimer_init(struct hrtimer *timer, 
                  clockid_t which_clock,
                  enum hrtimer_mode mode);

2.3 设置时间

复制代码
/**
 * ktime_set - 设置时间值
 * @secs: 秒
 * @nsecs: 纳秒
 * 返回: ktime_t类型的时间值
 */
ktime_t ktime_set(const s64 secs, const unsigned long nsecs);

// 常用时间宏
#define NSEC_PER_SEC    1000000000L  // 1秒 = 10^9纳秒
#define NSEC_PER_MSEC   1000000L     // 1毫秒 = 10^6纳秒
#define NSEC_PER_USEC   1000L        // 1微秒 = 10^3纳秒

// 示例
ktime_t kt;
kt = ktime_set(0, 1000000);      // 1ms
kt = ktime_set(0, 20000000);     // 20ms
kt = ktime_set(1, 0);            // 1s
kt = ktime_set(0, 500 * NSEC_PER_USEC);  // 500us

2.4 启动定时器

复制代码
/**
 * hrtimer_start - 启动定时器
 * @timer: 定时器结构体
 * @tim: 到期时间
 * @mode: 定时器模式
 */
void hrtimer_start(struct hrtimer *timer, 
                   ktime_t tim,
                   const enum hrtimer_mode mode);

2.5 取消定时器

复制代码
/**
 * hrtimer_cancel - 取消定时器
 * @timer: 定时器结构体
 * 返回: 0表示定时器未激活,1表示定时器已取消
 */
int hrtimer_cancel(struct hrtimer *timer);

/**
 * hrtimer_try_to_cancel - 尝试取消定时器
 * 返回: -1表示回调正在执行,0表示未激活,1表示已取消
 */
int hrtimer_try_to_cancel(struct hrtimer *timer);

2.6 重新设置时间

复制代码
/**
 * hrtimer_forward_now - 从当前时间向前设置
 * @timer: 定时器结构体
 * @interval: 时间间隔
 * 返回: 跳过的周期数
 */
u64 hrtimer_forward_now(struct hrtimer *timer, ktime_t interval);

2.7 回调函数返回值

复制代码
enum hrtimer_restart {
    HRTIMER_NORESTART,  // 不重启定时器
    HRTIMER_RESTART,    // 重启定时器(需先调用hrtimer_forward_now)
};

三、hrtimer使用示例

3.1 基本使用模板

复制代码
// hrtimer_demo.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>

struct hrtimer_demo {
    struct hrtimer timer;
    ktime_t period;
    int count;
};

static struct hrtimer_demo demo_data;

static enum hrtimer_restart hrtimer_callback(struct hrtimer *timer)
{
    struct hrtimer_demo *demo = container_of(timer, struct hrtimer_demo, timer);
    
    demo->count++;
    pr_info("hrtimer callback: count=%d\n", demo->count);
    
    // 重新设置下次触发时间
    hrtimer_forward_now(timer, demo->period);
    
    // 返回HRTIMER_RESTART继续定时
    return HRTIMER_RESTART;
}

static int __init hrtimer_demo_init(void)
{
    pr_info("hrtimer demo init\n");
    
    // 设置定时周期:100ms
    demo_data.period = ktime_set(0, 100 * NSEC_PER_MSEC);
    demo_data.count = 0;
    
    // 初始化定时器
    hrtimer_init(&demo_data.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    demo_data.timer.function = hrtimer_callback;
    
    // 启动定时器
    hrtimer_start(&demo_data.timer, demo_data.period, HRTIMER_MODE_REL);
    
    return 0;
}

static void __exit hrtimer_demo_exit(void)
{
    hrtimer_cancel(&demo_data.timer);
    pr_info("hrtimer demo exit, total count=%d\n", demo_data.count);
}

module_init(hrtimer_demo_init);
module_exit(hrtimer_demo_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("hrtimer demo driver");

3.2 Platform驱动集成

复制代码
// hrtimer_platform.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/hrtimer.h>
#include <linux/of.h>

struct hrtimer_priv {
    struct device *dev;
    struct hrtimer timer;
    ktime_t period;
    bool running;
};

static enum hrtimer_restart hrtimer_handler(struct hrtimer *timer)
{
    struct hrtimer_priv *priv = container_of(timer, struct hrtimer_priv, timer);
    
    // 执行定时任务
    dev_dbg(priv->dev, "timer triggered\n");
    
    if (priv->running) {
        hrtimer_forward_now(timer, priv->period);
        return HRTIMER_RESTART;
    }
    
    return HRTIMER_NORESTART;
}

static ssize_t period_show(struct device *dev,
                           struct device_attribute *attr, char *buf)
{
    struct hrtimer_priv *priv = dev_get_drvdata(dev);
    return sprintf(buf, "%lld\n", ktime_to_ns(priv->period));
}

static ssize_t period_store(struct device *dev,
                            struct device_attribute *attr,
                            const char *buf, size_t count)
{
    struct hrtimer_priv *priv = dev_get_drvdata(dev);
    u64 ns;
    
    if (kstrtou64(buf, 10, &ns))
        return -EINVAL;
    
    priv->period = ns_to_ktime(ns);
    return count;
}
static DEVICE_ATTR_RW(period);

static ssize_t enable_store(struct device *dev,
                            struct device_attribute *attr,
                            const char *buf, size_t count)
{
    struct hrtimer_priv *priv = dev_get_drvdata(dev);
    bool enable;
    
    if (kstrtobool(buf, &enable))
        return -EINVAL;
    
    if (enable && !priv->running) {
        priv->running = true;
        hrtimer_start(&priv->timer, priv->period, HRTIMER_MODE_REL);
    } else if (!enable && priv->running) {
        priv->running = false;
        hrtimer_cancel(&priv->timer);
    }
    
    return count;
}
static DEVICE_ATTR_WO(enable);

static struct attribute *hrtimer_attrs[] = {
    &dev_attr_period.attr,
    &dev_attr_enable.attr,
    NULL,
};
ATTRIBUTE_GROUPS(hrtimer);

static int hrtimer_platform_probe(struct platform_device *pdev)
{
    struct hrtimer_priv *priv;
    u32 period_ms = 100;  // 默认100ms
    
    priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;
    
    priv->dev = &pdev->dev;
    
    // 从设备树读取周期
    of_property_read_u32(pdev->dev.of_node, "period-ms", &period_ms);
    priv->period = ktime_set(0, period_ms * NSEC_PER_MSEC);
    
    // 初始化定时器
    hrtimer_init(&priv->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    priv->timer.function = hrtimer_handler;
    
    platform_set_drvdata(pdev, priv);
    
    // 创建sysfs属性
    return devm_device_add_groups(&pdev->dev, hrtimer_groups);
}

static int hrtimer_platform_remove(struct platform_device *pdev)
{
    struct hrtimer_priv *priv = platform_get_drvdata(pdev);
    
    priv->running = false;
    hrtimer_cancel(&priv->timer);
    
    return 0;
}

static const struct of_device_id hrtimer_of_match[] = {
    { .compatible = "demo,hrtimer" },
    { }
};
MODULE_DEVICE_TABLE(of, hrtimer_of_match);

static struct platform_driver hrtimer_platform_driver = {
    .probe = hrtimer_platform_probe,
    .remove = hrtimer_platform_remove,
    .driver = {
        .name = "hrtimer-demo",
        .of_match_table = hrtimer_of_match,
    },
};
module_platform_driver(hrtimer_platform_driver);

MODULE_LICENSE("GPL");

设备树配置:

复制代码
hrtimer_demo: hrtimer-demo {
    compatible = "demo,hrtimer";
    period-ms = <50>;  // 50ms周期
    status = "okay";
};

3.3 GPIO模拟PWM

复制代码
// gpio_pwm.c - 使用hrtimer模拟PWM输出
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/hrtimer.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>

struct gpio_pwm {
    struct device *dev;
    struct gpio_desc *gpio;
    struct hrtimer timer;
    ktime_t period;
    ktime_t duty;
    bool state;
    bool enabled;
};

static enum hrtimer_restart gpio_pwm_handler(struct hrtimer *timer)
{
    struct gpio_pwm *pwm = container_of(timer, struct gpio_pwm, timer);
    ktime_t next;
    
    if (!pwm->enabled)
        return HRTIMER_NORESTART;
    
    // 切换GPIO状态
    pwm->state = !pwm->state;
    gpiod_set_value(pwm->gpio, pwm->state);
    
    // 计算下次触发时间
    if (pwm->state) {
        next = pwm->duty;  // 高电平持续时间
    } else {
        next = ktime_sub(pwm->period, pwm->duty);  // 低电平持续时间
    }
    
    hrtimer_forward_now(timer, next);
    return HRTIMER_RESTART;
}

static ssize_t duty_store(struct device *dev,
                          struct device_attribute *attr,
                          const char *buf, size_t count)
{
    struct gpio_pwm *pwm = dev_get_drvdata(dev);
    u32 duty_ns;
    
    if (kstrtou32(buf, 10, &duty_ns))
        return -EINVAL;
    
    pwm->duty = ns_to_ktime(duty_ns);
    return count;
}
static DEVICE_ATTR_WO(duty);

static int gpio_pwm_probe(struct platform_device *pdev)
{
    struct gpio_pwm *pwm;
    u32 period_ns = 1000000;  // 默认1ms周期 (1KHz)
    u32 duty_ns = 500000;     // 默认50%占空比
    
    pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
    if (!pwm)
        return -ENOMEM;
    
    pwm->dev = &pdev->dev;
    
    // 获取GPIO
    pwm->gpio = devm_gpiod_get(&pdev->dev, "pwm", GPIOD_OUT_LOW);
    if (IS_ERR(pwm->gpio))
        return PTR_ERR(pwm->gpio);
    
    // 读取参数
    of_property_read_u32(pdev->dev.of_node, "period-ns", &period_ns);
    of_property_read_u32(pdev->dev.of_node, "duty-ns", &duty_ns);
    
    pwm->period = ns_to_ktime(period_ns);
    pwm->duty = ns_to_ktime(duty_ns);
    
    // 初始化定时器
    hrtimer_init(&pwm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    pwm->timer.function = gpio_pwm_handler;
    
    // 启动PWM
    pwm->enabled = true;
    pwm->state = true;
    gpiod_set_value(pwm->gpio, 1);
    hrtimer_start(&pwm->timer, pwm->duty, HRTIMER_MODE_REL);
    
    platform_set_drvdata(pdev, pwm);
    
    dev_info(&pdev->dev, "GPIO PWM started: period=%uns, duty=%uns\n",
             period_ns, duty_ns);
    
    return 0;
}

static int gpio_pwm_remove(struct platform_device *pdev)
{
    struct gpio_pwm *pwm = platform_get_drvdata(pdev);
    
    pwm->enabled = false;
    hrtimer_cancel(&pwm->timer);
    gpiod_set_value(pwm->gpio, 0);
    
    return 0;
}

static const struct of_device_id gpio_pwm_of_match[] = {
    { .compatible = "gpio-pwm" },
    { }
};
MODULE_DEVICE_TABLE(of, gpio_pwm_of_match);

static struct platform_driver gpio_pwm_driver = {
    .probe = gpio_pwm_probe,
    .remove = gpio_pwm_remove,
    .driver = {
        .name = "gpio-pwm",
        .of_match_table = gpio_pwm_of_match,
    },
};
module_platform_driver(gpio_pwm_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("GPIO PWM using hrtimer");

四、注意事项

4.1 回调函数执行时间

hrtimer回调在硬中断上下文执行,必须注意:

  • 不能调用可能睡眠的函数

  • 执行时间要尽量短

  • 复杂任务应使用工作队列

    // 错误示例 - 回调中睡眠
    static enum hrtimer_restart bad_callback(struct hrtimer *timer)
    {
    msleep(10); // 错误!不能在中断上下文睡眠
    return HRTIMER_NORESTART;
    }

    // 正确示例 - 使用工作队列
    static struct work_struct my_work;

    static void work_handler(struct work_struct *work)
    {
    // 可以执行耗时操作
    msleep(10);
    }

    static enum hrtimer_restart good_callback(struct hrtimer *timer)
    {
    schedule_work(&my_work); // 调度工作队列
    return HRTIMER_NORESTART;
    }

4.2 精度限制

虽然hrtimer支持纳秒级精度,但实际精度受限于:

  • 硬件定时器精度

  • 中断延迟

  • 系统负载

    // 测试实际精度
    static ktime_t last_time;

    static enum hrtimer_restart precision_test(struct hrtimer *timer)
    {
    ktime_t now = ktime_get();
    ktime_t diff = ktime_sub(now, last_time);

    复制代码
      pr_info("actual interval: %lld ns\n", ktime_to_ns(diff));
      
      last_time = now;
      hrtimer_forward_now(timer, ns_to_ktime(1000000));  // 1ms
      return HRTIMER_RESTART;

    }

4.3 CPU绑定

对于时间敏感的应用,可以将定时器绑定到特定CPU:

复制代码
// 绑定到当前CPU
hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);

五、调试方法

5.1 查看定时器状态

复制代码
# 查看hrtimer统计
cat /proc/timer_list | grep -A 20 "hrtimer"

# 查看时钟源
cat /sys/devices/system/clocksource/clocksource0/available_clocksource
cat /sys/devices/system/clocksource/clocksource0/current_clocksource

5.2 ftrace跟踪

复制代码
# 启用hrtimer跟踪
echo 1 > /sys/kernel/debug/tracing/events/timer/hrtimer_start/enable
echo 1 > /sys/kernel/debug/tracing/events/timer/hrtimer_expire_entry/enable

# 查看跟踪结果
cat /sys/kernel/debug/tracing/trace

六、常见问题

6.1 定时器不触发

检查步骤:

  1. 确认定时器已启动
  2. 检查回调函数是否正确设置
  3. 检查时间参数是否合理

6.2 定时精度不够

可能原因:

  1. 系统负载过高
  2. 中断被禁用
  3. 回调执行时间过长

解决方法:

  • 使用HRTIMER_MODE_PINNED绑定CPU
  • 减少回调执行时间
  • 使用实时内核(PREEMPT_RT)

七、总结

本文介绍了Linux高精度定时器hrtimer的使用:

  1. 基本概念:纳秒级精度,红黑树实现
  2. API使用:初始化、启动、取消、重设
  3. 实际应用:周期任务、GPIO PWM模拟
  4. 注意事项:中断上下文、精度限制

hrtimer适用于对时间精度要求较高的场景,但需要注意其在硬中断上下文执行的特性。

相关推荐
小痞同学2 小时前
【铁头山羊STM32】HAL库 3.I2C部分
stm32·单片机·嵌入式硬件
蝎蟹居3 小时前
GBT 4706.1-2024逐句解读系列(29) 第7.9~7.10条款:开关,档位应明确标识
人工智能·单片机·嵌入式硬件·物联网·安全
梁洪飞3 小时前
pmu+power控制+pmic
arm开发·嵌入式硬件·arm
nnerddboy4 小时前
嵌入式面试题:2.模电、数电
单片机·嵌入式硬件
爱吃苹果的梨叔4 小时前
NTP 网络时间服务器硬件驯服技术说明(投标技术响应说明)
linux·运维·服务器·网络·嵌入式硬件·tcp/ip
安庆平.Я4 小时前
STM32——定时器:基本定时器
stm32·单片机·嵌入式硬件
Nautiluss4 小时前
一起调试XVF3800麦克风阵列(十六)
人工智能·单片机·音频·语音识别·dsp开发·智能硬件
Hello_Embed4 小时前
串口面向对象封装实例
笔记·stm32·单片机·学习·操作系统
三伏5224 小时前
stm32f103系列手册IIC笔记
笔记·stm32·嵌入式硬件