脉冲宽度调制(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_apply_might_sleep()
的最后一次调用的PWM状态,使用pwm_get_state()
。请注意,这与驱动程序实际实现的不同,如果无法通过正在使用的硬件精确满足请求。目前,使用者无法获取实际实现的设置。
除了PWM状态外,PWM API还公开了PWM参数,这些参数是应该在此PWM上使用的参考PWM配置。PWM参数通常是特定于平台的,并允许PWM用户只关心相对于整个周期的占空比(例如,duty = 周期的 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芯片支持更改极性时,对此属性的写入才起作用。值为字符串“normal”或“inversed”。
- enable
启用/禁用PWM信号(读/写)。
0 - 禁用
1 - 启用
实现PWM驱动程序¶
目前有两种方法可以实现pwm驱动程序。传统上只有裸机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()
执行任何锁定,因此调用上下文目前是驱动程序特定的。这是一个由以前的简易 API 导致的问题,应该尽快修复。
辅助函数¶
目前,PWM 只能使用 period_ns 和 duty_ns 进行配置。对于某些用例,freq_hz 和 duty_percent 可能更好。请考虑向框架添加适当的辅助函数,而不是在驱动程序中进行此计算。