脉冲宽度调制 (PWM) 接口¶
提供有关 Linux PWM 接口的概述
PWM 通常用于控制手机中的 LED、风扇或振动器。具有固定用途的 PWM 不需要实现 Linux PWM API(尽管它们可以)。但是,PWM 通常作为 SoC 上的离散设备存在,没有固定用途。由电路板设计人员将它们连接到 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_might_sleep(struct pwm_device *pwm, struct pwm_state *state);
此 API 控制 PWM 周期/占空比配置以及启用/禁用状态。
如果 PWM 不休眠,则可以在原子上下文中使用 PWM 设备。 您可以使用以下方法检查是否是这种情况
bool pwm_might_sleep(struct pwm_device *pwm);
如果为 false,也可以使用以下命令从原子上下文中配置 PWM
int pwm_apply_atomic(struct pwm_device *pwm, struct pwm_state *state);
作为使用者,不要依赖禁用 PWM 的输出状态。 如果可能,驱动程序应该发出非活动状态,但某些驱动程序无法做到。 如果您依赖于获取非活动状态,请使用 .duty_cycle=0,.enabled=true。
还有一个 usage_power 设置:如果设置,PWM 驱动程序只需要维护功率输出,但在信号形式方面有更大的自由度。 如果驱动程序支持,则可以优化信号,例如通过相移芯片的各个通道来改善 EMI。
pwm_config()
、pwm_enable()
和 pwm_disable()
函数只是 pwm_apply_might_sleep()
的包装器,如果用户想要一次更改多个参数,则不应使用。 例如,如果您在同一函数中看到 pwm_config()
和 pwm_{enable,disable}() 调用,这可能意味着您应该切换到 pwm_apply_might_sleep()
。
PWM 用户 API 还允许用户使用 pwm_get_state()
查询传递给 pwm_apply_might_sleep()
的上次调用的 PWM 状态。 请注意,如果使用中的硬件无法完全满足请求,这与驱动程序实际实现的不同。 目前没有使用者可以获取实际实施的设置的方法。
除了 PWM 状态之外,PWM API 还公开了 PWM 参数,这些参数是应在此 PWM 上使用的参考 PWM 配置。 PWM 参数通常是平台特定的,并允许 PWM 用户仅关心相对于完整周期的占空比(例如,占空比 = 周期的 50%)。 struct pwm_args
包含 2 个字段(周期和极性),应用于设置初始 PWM 配置(通常在 PWM 用户的探测函数中完成)。 PWM 参数使用 pwm_get_args() 检索。
所有使用者都应该在恢复时重新配置 PWM。 这是确保一切都以正确的顺序恢复的唯一方法。
将 PWM 与 sysfs 接口一起使用¶
如果在内核配置中启用了 CONFIG_SYSFS,则提供了一个简单的 sysfs 接口,用于从用户空间使用 PWM。 它在 /sys/class/pwm/ 中公开。 每个探测到的 PWM 控制器/芯片都将导出为 pwmchipN,其中 N 是 PWM 芯片的基数。 在目录中,您将找到
- npwm
此芯片支持的 PWM 通道数(只读)。
- 导出
导出 PWM 通道以与 sysfs 一起使用(只写)。
- 取消导出
从 sysfs 取消导出 PWM 通道(只写)。
PWM 通道使用每个芯片的索引从 0 到 npwm-1 进行编号。
导出 PWM 通道时,将在与其关联的 pwmchipN 目录中创建一个 pwmX 目录,其中 X 是导出的通道的编号。 然后可以使用以下属性
- 周期
PWM 信号的总周期(读/写)。 值的单位为纳秒,是 PWM 的活动时间和非活动时间之和。
- 占空比
PWM 信号的活动时间(读/写)。 值的单位为纳秒,并且必须小于或等于周期。
- 极性
更改 PWM 信号的极性(读/写)。 只有当 PWM 芯片支持更改极性时,才能写入此属性。 值是字符串“normal”或“inversed”。
- 启用
启用/禁用 PWM 信号(读/写)。
0 - 禁用
1 - 启用
实现 PWM 驱动程序¶
目前有两种方法可以实现 PWM 驱动程序。 传统上,只有 barebone API,这意味着每个驱动程序都必须自己实现 pwm_*() 函数。 这意味着系统中不可能有多个 PWM 驱动程序。 因此,新的驱动程序必须使用通用的 PWM 框架。
可以使用 pwmchip_alloc() 分配新的 PWM 控制器/芯片,然后使用 pwmchip_add() 注册,并使用 pwmchip_remove()
再次删除。 要撤消 pwmchip_alloc(),请使用 pwmchip_put()。 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()
,因此调用上下文当前是驱动程序特定的。 这是一个源自先前 barebone API 的问题,应尽快修复。
助手¶
目前,只能使用 period_ns 和 duty_ns 配置 PWM。 对于某些用例,freq_hz 和 duty_percent 可能更好。 请考虑将适当的助手添加到框架,而不是在驱动程序中进行计算。