Linux PWM接口概述 【ChatGPT】

发布时间 2023-12-10 19:39:26作者: 摩斯电码

Linux PWM接口概述

PWM(脉冲宽度调制)接口用于控制LED、风扇或手机中的振动器。具有固定目的的PWM无需实现Linux PWM API(尽管它们可以)。然而,在SoC上通常会发现作为离散设备的PWM,它们没有固定的用途。将它们连接到LED或风扇取决于板设计者。为了提供这种灵活性,存在通用PWM API。

PWM的识别

使用传统PWM API的用户使用唯一ID来引用PWM设备。而不是通过其唯一ID引用PWM设备,板设置代码应该注册一个静态映射,可以用于将PWM消费者与提供者匹配,如下例所示:

static struct pwm_lookup board_pwm_lookup[] = {
        PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight", NULL,
                   50000, PWM_POLARITY_NORMAL),
};

static void __init board_init(void)
{
        ...
        pwm_add_table(board_pwm_lookup, ARRAY_SIZE(board_pwm_lookup));
        ...
}

使用PWM

消费者使用pwm_get()函数,并传递消费者设备或消费者名称。pwm_put()用于释放PWM设备。还存在getter的托管变体devm_pwm_get()devm_fwnode_pwm_get()

请求后,PWM必须使用以下方式配置:

int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state);

此API控制PWM周期/占空比配置以及启用/禁用状态。

作为消费者,不要依赖于已禁用的PWM的输出状态。如果可能的话,驱动程序应该发出非活动状态,但有些驱动程序可能无法这样做。如果您依赖于获取非活动状态,请使用.duty_cycle=0, .enabled=true

还有一个usage_power设置:如果设置,PWM驱动程序只需要维持功率输出,但在信号形式方面具有更大的自由度。如果驱动程序支持,信号可以进行优化,例如通过相移芯片的各个通道来改善电磁干扰。

pwm_config()pwm_enable()pwm_disable()函数只是pwm_apply_state()的包装器,如果用户希望一次更改多个参数,则不应使用它们。例如,如果您在同一个函数中看到pwm_config()pwm_{enable,disable}()调用,这可能意味着您应该切换到pwm_apply_state()

PWM用户API还允许查询上一次pwm_apply_state()调用传递的PWM状态,使用pwm_get_state()。请注意,如果请求无法完全满足硬件的实际实现,这与驱动程序实际实现的不同。目前,消费者无法获取实际实现的设置。

除了PWM状态之外,PWM API还公开了PWM参数,这是应该在此PWM上使用的参考PWM配置。PWM参数通常是特定于平台的,并允许PWM用户只关心相对于完整周期的占空比(例如,占空比=周期的50%)。struct pwm_args包含2个字段(周期和极性),应该用于设置初始PWM配置(通常在PWM用户的探测函数中完成)。使用pwm_get_args()检索PWM参数。

所有消费者在恢复时都应该重新配置PWM。这是确保一切按正确顺序恢复的唯一方法。

使用sysfs接口的PWM

如果在内核配置中启用了CONFIG_SYSFS,则提供了一个简单的sysfs接口,用于从用户空间使用PWM。它在/sys/class/pwm/下公开。每个探测到的PWM控制器/芯片将导出为pwmchipN,其中N是PWM芯片的基址。在该目录中,您将找到:

  • npwm:此芯片支持的PWM通道数(只读)。
  • export:导出一个PWM通道供sysfs使用(只写)。
  • unexport:从sysfs取消导出PWM通道(只写)。

PWM通道使用从0到npwm-1的每个芯片索引进行编号。当导出PWM通道时,将在其关联的pwmchipN目录中创建一个pwmX目录,其中X是导出的通道号。然后将可用以下属性:

  • period:PWM信号的总周期(读/写)。值以纳秒为单位,是PWM的活动时间和非活动时间的总和。
  • duty_cycle:PWM信号的活动时间(读/写)。值以纳秒为单位,必须小于周期。
  • polarity:更改PWM信号的极性(读/写)。仅当PWM芯片支持更改极性时,对此属性的写入才有效。只有在PWM未启用时才能更改极性。值为字符串"normal"或"inversed"。
  • enable:启用/禁用PWM信号(读/写)。
    • 0 - 禁用
    • 1 - 启用

实现PWM驱动程序

目前有两种实现PWM驱动程序的方法。传统上只有裸骨API,这意味着每个驱动程序必须自己实现pwm_*()函数。这意味着系统中不可能有多个PWM驱动程序。因此,新驱动程序必须使用通用PWM框架。

可以使用pwmchip_add()添加新的PWM控制器/芯片,并使用pwmchip_remove()将其移除。pwmchip_add()接受填充的struct pwm_chip作为参数,该结构提供了PWM芯片的描述、芯片提供的PWM设备数量以及支持的PWM操作的芯片特定实现。

在PWM驱动程序中实现极性支持时,确保遵守PWM框架中的信号约定。按定义,正常极性表示信号在占空比的持续时间内开始高电平,并在周期的其余时间内变为低电平。相反,具有反转极性的信号在占空比的持续时间内开始低电平,并在周期的其余时间内变为高电平。

鼓励驱动程序实现->apply()而不是传统的->enable()->disable()->config()方法。这样做应该在PWM配置工作流程中提供原子性,这在PWM控制关键设备(如调节器)时是必需的。

也鼓励实现->get_state()(用于检索初始PWM状态)出于同样的原因:让PWM用户了解当前PWM状态将使他避免故障。

驱动程序不应该实现任何电源管理。换句话说,消费者应该按照“使用PWM”部分中描述的方式实现它。

锁定

PWM核心列表操作受互斥锁保护,因此pwm_get()pwm_put()不能从原子上下文中调用。目前,PWM核心不强制对pwm_enable()pwm_disable()pwm_config()进行任何锁定,因此调用上下文目前是特定于驱动程序的。这是从以前的裸骨API衍生出的问题,应该很快修复。

助手

目前,PWM只能使用period_nsduty_ns进行配置。对于几种用例,freq_hzduty_percent可能更好。请考虑向框架添加适当的助手,而不是在驱动程序中计算这些值。