Jasonangel

V1

2022/04/05阅读:33主题:默认主题

Linux pwm 子系统

PWM 简介

PWM:Pulse width modulation,脉冲宽度调制,在 IC 中,是使用定时器来实现。

PWM 多用于控制马达、LED、振动器等模拟器件。

PWM 参数:

周期 period
占空比 duty_cycle
极性 polarity 

周期:单位为纳秒,1000000000 ns = 1s
占空比:高低电平的占比
极性:以低电平为开启状态,还是以高电平为开启状态,一般为 normal,也就是高电平为开启状态(inversed、normal)

PWM 软件分析

PWM 驱动比 I2C 等简单,可以理解为不用写,芯片厂商已经为 soc 写了 PWM 驱动,我们在使用过程中,只需要修改设备树,写应用程序即可。

在 kernel 根目录输入 make menuconfig,打开 PWM 配置

-> Device Drivers
  -> Pulse-Width Modulation (PWM) Support
    -> <*> i.MX PWM support  //不同平台名字不同

Linux 内核提供了个 PWM 子系统框架,编写 PWM 驱动的时候一定要符合这个框架。PWM子系统的核心是 pwm_chip 结构体,定义在文件 include/linux/pwm.h 中

struct pwm_chip {
 struct device *dev;
 const struct pwm_ops *ops;
 int base;
 unsigned int npwm;

 struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
     const struct of_phandle_args *args);

 unsigned int of_pwm_n_cells;

 /* only used internally by the PWM framework */
 struct list_head list;
 struct pwm_device *pwms;
};

其中,pwm_ops 结构体就是 PWM 外设的各种操作函数集合,编写 PWM 外设驱动的时候需要开发人员实现。

pwm_ops 结构体也定义在 pwm.h 头文件中,定义如下:

struct pwm_ops {
 int (*request)(struct pwm_chip *chip, struct pwm_device *pwm);
 void (*free)(struct pwm_chip *chip, struct pwm_device *pwm);
 int (*capture)(struct pwm_chip *chip, struct pwm_device *pwm,
         struct pwm_capture *result, unsigned long timeout);
 int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm,
       const struct pwm_state *state);
 void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
     struct pwm_state *state);
 struct module *owner;

 /* Only used by legacy drivers */
 int (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
        int duty_ns, int period_ns);
 int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm,
       enum pwm_polarity polarity);
 int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
 void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
};

pwm_ops 中的这些函数不一定全部实现,但是像 config、enable 和 disable 这些肯定是需要实现的,否则的话打开/关闭 PWM,设置 PWM 的占空比这些就没操作了。

向内核注册 pwm_chip

int pwmchip_add(struct pwm_chip *chip)

从内核移除掉 pwm_chip

int pwmchip_remove(struct pwm_chip *chip)

其他 pwm 相关 API,定义在 kernel/include/linux/pwm.h

struct pwm_device *pwm_request(int pwm_id, const char *label);
void pwm_free(struct pwm_device *pwm);
static inline int pwm_config(struct pwm_device *pwm, int duty_ns,int period_ns)
static inline int pwm_enable(struct pwm_device *pwm)
static inline void pwm_disable(struct pwm_device *pwm)
int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result,unsigned long timeout)
;
static inline enum pwm_polarity pwm_get_polarity(const struct pwm_device *pwm)

调用流程

1、初始化
    pwm_request
    pwm_config
    pwm_set_polarity
    pwm_enable
    
2、修改占空比
    pwm_disable
    pwm_config
    pwm_enable

3、停止 pwm 信号输出
    pwm_disable
    pwm_free

PWM 驱动测试

参考内核文档 Documentation/ABI/testing/sysfs-class-pwm 描述。

PWM 子系统的核心是 pwm_chip 结构体,在 sys/class/pwm/ 下会显示内核注册了几个。

我们进入 pwmchip2,可以看到几个属性

device 
export:导出 pwm 通道,使用前必须导出
npwm:PWM 控制器下共有几路 PWM 输出
power 
subsystem 
uevent 
unexport:取消导出 PWM 通道
  1. 向 export 写 0,导出 pwm0
  2. 向 enable 写 1 使能 pwm
  3. 设置周期值,单位为 ns,比如 20KHz 频率的周期就是 50000ns
  4. 设置 PWM3 的占空比:设置的一个周期的 ON 时间,也就是高电平时间,比如【20KHz 频率下 20%占空比】的 ON 时间就是 10000

查看原理图,看是哪个 GPIO 口,然后使用示波器查看波形是否正确。

在其他外设上添加 PWM 功能

有时候我们需要在某个外设上添加 PWM 功能,比如,LCD 的背光控制就是 PWM 来完成的。

首先肯定是设备树描述,直接看 linux 内核里面关于 backlight(背光)的绑定文档,路径为Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt,此文档描述了如何创建backlight 节点来使用 linux 内核自带的 pwm 背光驱动。

一般买了一块板子,该部分都是做好的,屏幕肯定是亮的,我们可以直接访问节点

/sys/devices/platform/backlight/
actual_brightness:当前亮度
bl_power 
brightness:当前亮度,可以 echo 写节点改变屏幕亮度
device 
max_brightness:最大亮度,一般是255
power 
subsystem 
type 
uevent

扩展

一些 IC 厂商的 PWM 控制器有多种工作模式:

分类:

移动端开发

标签:

移动端开发

作者介绍

Jasonangel
V1