Linux PWM framework简介和API描述

1. 前言

PWM是Pulse Width Modulation(脉冲宽度调制)的缩写,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,其本质是一种对模拟信号电平进行数字编码的方法。在嵌入式设备中,PWM多用于控制马达、LED、振动器等模拟器件。

PWM framework是kernel为了方便PWM driver开发、PWM使用而抽象出来的一套通用API,之所以要分析该framework,原因如下:

1)PWM接口,本质上一种通信协议,和I2C、SPI、USB、WIFI等没有任何差别。因此,本文将会是kernel通信协议有关framework的分析文章的第一篇。

2)它太简单了!但是,虽然简单,思路却大同小异,因而非常适合做第一篇。

3)我计划整理显示子系统的分析文章,而PWM,是显示子系统中最基础的那一个。

闲话少说,言归正传!

2. 软件框架及API汇整

PWM framework非常简单,但它同样具备framework的基本特性:对上,为内核其它driver(Consumer)提供使用PWM功能的统一接口;对下,为PWM driver(Provider)提供driver开发的通用方法和API;内部,抽象并实现公共逻辑,屏蔽技术细节。下面我们通过它所提供的API,进一步认识PWM framework。

2.1 向PWM consumer提供的APIs

对consumer而言,关注PWM的如下参数:

1)频率

PWM的频率决定了所模拟出来的模拟电平的平滑度,通俗的讲,就是逼真度。不同的模拟器件,对期待的频率是有要求的,因此需要具体情况具体对待。

另外,人耳能感知的频率范围是20Hz~16KHz,因此要注意PWM的频率不要落在这个范围,否则可能会产生莫名其妙的噪声。

2)占空比

占空比,决定了一个周期内PWM信号高低的比率,进而决定了一个周期内的平均电压,也即所模拟的模拟电平的电平值。

3)极性

简单的说,一个PWM信号的极性,决定了是高占空比的信号输出电平高,还是低占空比信号输出电平高。假设一个信号的占空比为100%,如果为正常极性,则输出电平最大,如果为翻转的极性,则输出电平为0。

4)开关

控制PWM信号是否输出。

基于上述需求,linux pwm framework向consumer提供了如下API:

  1: /* include/linux/pwm.h */

  2: 

  3: /*

  4:  * pwm_config - change a PWM device configuration

  5:  */

  6: int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);

  7: 

  8: /*

  9:  * pwm_enable - start a PWM output toggling

 10:  */

 11: int pwm_enable(struct pwm_device *pwm);

 12: 

 13: /*

 14:  * pwm_disable - stop a PWM output toggling

 15:  */

 16: void pwm_disable(struct pwm_device *pwm);

 17: 

 18: /*

 19:  * pwm_set_polarity - configure the polarity of a PWM signal

 20:  */

 21: int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity);


pwm_config,用于控制PWM输出信号的频率和占空比,其中频率是以周期(period_ns)的形式配置的,占空比是以有效时间(duty_ns)的形式配置的。

pwm_enable/pwm_disable,用于控制PWM信号输出与否。

pwm_set_polarity,可以更改pwm信号的极性,可选参数包括normal(PWM_POLARITY_NORMAL)和inversed(极性翻转,PWM_POLARITY_INVERSED)两种。

上面的API都以struct pwm_device类型的指针为操作句柄,该指针抽象了一个PWM设备(consumer不需要关心其内部构成),那么怎么获得PWM句柄呢?使用如下的API:

注1:本文只介绍基于DTS的、新的pwm request系列接口,对于那些旧接口,让它随风而去吧。

  1: /* include/linux/pwm.h */

  2: 

  3: struct pwm_device *pwm_get(struct device *dev, const char *con_id);

  4: struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id);

  5: void pwm_put(struct pwm_device *pwm);

  6: 

  7: struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id);

  8: struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,

  9:                                    const char *con_id);

 10: void devm_pwm_put(struct device *dev, struct pwm_device *pwm);

pwm_get/devm_pwm_get,从指定设备(dev)的DTS节点中,获得对应的PWM句柄。可以通过con_id指定一个名称,或者会获取和该设备绑定的第一个PWM句柄。设备的DTS文件需要用这样的格式指定所使用的PWM device(具体的形式,还依赖pwm driver的具体实现,后面会再介绍):
bl: backlight {
        pwms = <&pwm 0 5000000 PWM_POLARITY_INVERTED>;
        pwm-names = "backlight";
};
如果“con_id”为NULL,则返回DTS中“pwms”字段所指定的第一个PWM device;如果“con_id”不为空,如是“backlight”,则返回和“pwm-names ”字段所指定的name对应的PWM device。
上面“pwms”字段各个域的含义如下:
1)&pwm,对DTS中pwm节点的引用;
2)0,pwm device的设备号,具体需要参考SOC以及pwm driver的实际情况;
3)5000000,PWM信号默认的周期,单位是纳秒(ns);
4)PWM_POLARITY_INVERTED,可选字段,是否提供由pwm driver决定,表示pwm信号的极性,若为0,则正常极性,若为PWM_POLARITY_INVERTED,则反转极性。

of_pwm_get/devm_of_pwm_get,和pwm_get/devm_pwm_get类似,区别是可以指定需要从中解析PWM信息的device node,而不是直接指定device指针。



PWM framework使用struct pwm_chip抽象PWM控制器。通常情况下,在一个SOC中,可以同时支持多路PWM输出(如6路),以便同时控制多个PWM设备。这样每一路PWM输出,可以看做一个PWM设备(由上面struct pwm_device抽象),没有意外的话,这些PWM设备的控制方式应该类似。PWM framework会统一管理这些PWM设备,将它们归类为一个PWM chip。

struct pwm_chip的定义如下:

  1: /* include/linux/pwm.h */

  2: 

  3: /**

  4:  * struct pwm_chip - abstract a PWM controller

  5:  * @dev: device providing the PWMs

  6:  * @list: list node for internal use

  7:  * @ops: callbacks for this PWM controller

  8:  * @base: number of first PWM controlled by this chip

  9:  * @npwm: number of PWMs controlled by this chip

 10:  * @pwms: array of PWM devices allocated by the framework

 11:  * @can_sleep: must be true if the .config(), .enable() or .disable()

 12:  *             operations may sleep

 13:  */

 14: struct pwm_chip {

 15:         struct device           *dev;

 16:         struct list_head        list;

 17:         const struct pwm_ops    *ops;

 18:         int                     base;

 19:         unsigned int            npwm;

 20: 

 21:         struct pwm_device       *pwms;

 22: 

 23:         struct pwm_device *     (*of_xlate)(struct pwm_chip *pc,

 24:                                             const struct of_phandle_args *args);

 25:         unsigned int            of_pwm_n_cells;

 26:         bool                    can_sleep;

 27: };


dev,该pwm chip对应的设备,一般由pwm driver对应的platform驱动指定。必须提供!

ops,操作PWM设备的回调函数,后面会详细介绍。必须提供!

npwm,该pwm chip可以支持的pwm channel(也可以称作pwm device由struct pwm_device表示)个数,kernel会根据该number,分配相应个数的struct pwm_device结构,保存在pwms指针中。必须提供!

pwms,保存所有pwm device的数组,kernel会自行分配,不需要driver关心。

base,在将该chip下所有pwm device组成radix tree时使用,只有旧的pwm_request接口会使用,因此忽略它吧,编写pwm driver不需要关心。

of_pwm_n_cells,该PWM chip所提供的DTS node的cell,一般是2或者3,例如:为3时,consumer需要在DTS指定pwm number、pwm period和pwm flag三种信息(如2.1中的介绍);为2时,没有flag信息。

of_xlate,用于解析consumer中指定的、pwm信息的DTS node的回调函数(如2.1中介绍的,pwms =<&pwm 0 5000000 PWM_POLARITY_INVERTED>)。

注2:一般情况下,of_pwm_n_cells取值为3,或者2(不关心极性),of_xlate则可以使用kernel提供的of_pwm_xlate_with_flags(解析of_pwm_n_cells为3的chip)或者of_pwm_simple_xlate(解析of_pwm_n_cells为2的情况)。具体的driver可以根据实际情况修改上述规则,但不到万不得已的时候,不要做这种非标准的、 掏力不讨好的事情!(有关of_xlate的流程,会在下一篇流程分析的文章中介绍。)

can_sleep,如果ops回调函数中,.config(),.enable()或者.disable()操作会sleep,则要设置该变量。

2.2.2 pwm ops

struct pwm_ops结构是pwm device有关的操作函数集,如下:

  1: /**

  2:  * struct pwm_ops - PWM controller operations

  3:  * @request: optional hook for requesting a PWM

  4:  * @free: optional hook for freeing a PWM

  5:  * @config: configure duty cycles and period length for this PWM

  6:  * @set_polarity: configure the polarity of this PWM

  7:  * @enable: enable PWM output toggling

  8:  * @disable: disable PWM output toggling

  9:  * @dbg_show: optional routine to show contents in debugfs

 10:  * @owner: helps prevent removal of modules exporting active PWMs

 11:  */

 12: struct pwm_ops {

 13:         int                     (*request)(struct pwm_chip *chip,

 14:                                            struct pwm_device *pwm);

 15:         void                    (*free)(struct pwm_chip *chip,

 16:                                         struct pwm_device *pwm);

 17:         int                     (*config)(struct pwm_chip *chip,

 18:                                           struct pwm_device *pwm,

 19:                                           int duty_ns, int period_ns);

 20:         int                     (*set_polarity)(struct pwm_chip *chip,

 21:                                           struct pwm_device *pwm,

 22:                                           enum pwm_polarity polarity);

 23:         int                     (*enable)(struct pwm_chip *chip,

 24:                                           struct pwm_device *pwm);

 25:         void                    (*disable)(struct pwm_chip *chip,

 26:                                            struct pwm_device *pwm);

 27: #ifdef CONFIG_DEBUG_FS

 28:         void                    (*dbg_show)(struct pwm_chip *chip,

 29:                                             struct seq_file *s);

 30: #endif

 31:         struct module           *owner;

 32: };


这些回调函数的操作对象是具体的pwm device(由struct pwm_device类型的指针表示),包括:

config,配置pwm device的频率、占空比。必须提供!

enable/disable,使能/禁止pwm信号输出。必须提供!

request/free,不再使用。

set_polarity,设置pwm信号的极性。可选,具体需要参考of_pwm_n_cells的定义。

2.2.3 pwm device

struct pwm_device是pwm device的操作句柄,consumer的API调用,会中转到provider的pwm ops回调函数上,provider(及pwm driver)根据pwm device的信息,进行相应的寄存器操作。如下:

1: struct pwm_device {

2:         const char              *label;

3:         unsigned long           flags;

4:         unsigned int            hwpwm;

5:         unsigned int            pwm;

6:         struct pwm_chip         *chip;

7:         void                    *chip_data;

8:

9:         unsigned int            period;         /* in nanoseconds */

10:         unsigned int            duty_cycle;     /* in nanoseconds */

11:         enum pwm_polarity       polarity;

12: };


pwm driver比较关心的字段是:

hwpwm,该pwm device对应的hardware pwm number,可用于寄存器的寻址操作。

period、duty_cycle、polarity,pwm信号的周期、占空比、极性等信息。

2.2.4 pwmchip_add/pwmchip_remove

初始化完成后的pwm chip可以通过pwmchip_add接口注册到kernel中,之后的事情,pwm driver就不用操心了。该接口的原型如下:

1: int pwmchip_add(struct pwm_chip *chip);

2: int pwmchip_remove(struct pwm_chip *chip);


3. API使用指南

3.1 consumer使用PWM的步骤

基于2.1章节描述的API,可以得到pwm consumer(如pwm backlight driver)使用pwm framework的方法和步骤如下:

1)查看pwm provider所提供的pwm dts binding信息(一般会在“Documentation/devicetree/bindings/pwm”目录中),并以此在该device所在的dts node中添加“pwms ”以及“pwm-names ”相关的配置。例如:

/* arch\arm\boot\dts\imx23-evk.dts */

backlight {

compatible = "pwm-backlight";

pwms =<&pwm 2 5000000>;

brightness-levels =<0 4 8 16 32 64 128 255>;

default-brightness-level =<6>;

};

其中,&pwm,表示对pwm driver的DTS节点的引用,具体可参考下面3.2章节的介绍。

2)在driver的probe接口中,调用devm_pwm_get接口,获取pwm device句柄,并保存起来。

3)devm_pwm_get成功后,该pwm信号已经具备初始的周期和极性。后续根据需要,可以调用pwm_config和pwm_set_polarity更改该pwm信号的周期、占空比和极性。

4)driver可以根据需要,调用pwm_enable/pwm_disable接口,打开或者关闭pwm信号的输出。

3.2 provider编写PWM driver的步骤

基于2.2章节描述的API,可以得到pwm provider(即具体的PWM驱动)使用pwm framework的方法和步骤如下:

1)创建代表该pwm driver的DTS节点,并提供platform device有关的资源信息,例如:

/* arch\arm\boot\dts\imx23.dtsi */
pwm: pwm@80064000 {

compatible = "fsl,imx23-pwm";

reg =<0x80064000 0x2000>;

clocks =<&clks 30>;

#pwm-cells =<2>;

fsl,pwm-number =<5>;

status = "disabled";

};

/* arch\arm\boot\dts\imx23-evk.dts */

pwm: pwm@80064000 {

pinctrl-names = "default";

pinctrl-0 =<&pwm2_pins_a>;

status = "okay";

};

2)定义一个pwm chip变量

3)注册相应的platform driver,并在driver的.probe()接口中,初始化pwm chip变量,至少要包括如下字段:

dev,使用platform device中的dev指针即可;npwm;ops,至少包括config、enable、disable三个回调函数。

如果该pwm chip支持额外的flag(如PWM极性,或者自定义的flag),将PWM cell指定为3(of_pwm_n_cells),of_xlate指定为of_pwm_xlate_with_flags。

初始化完成调用pwmchip_add接口,将chip添加到kernel中。

4)每当consumer有API调用时,kernel会以pwm device为参数,调用pwm driver提供的pwm ops,相应的回调函数可以从pwm device中取出pwm number(该number的意义driver自行解释),并操作对应的寄存器即可。

原文连接http://www.wowotech.net/comm/pwm_overview.htm

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 当使用Linux时,PWM驱动程序是一种非常常见的驱动程序类型,用于控制数字信号,如LED的亮度,风扇的速度等等。 以下是一些在Linux中使用PWM驱动程序的步骤: 1. 确认您的系统支持PWM驱动程序。要检查此功能,请执行以下操作: $ ls /sys/class/pwm 如果输出结果为“pwmchip0”,则您的系统支持PWM驱动程序。 2. 选择一个PWM引脚。要选择PWM引脚,请执行以下操作: $ ls /sys/class/pwm/pwmchip0 这将显示您的系统中可用的所有PWM通道。选择一个通道,并记下其索引号。 3. 设置PWM参数。要设置PWM参数,请执行以下操作: $ cd /sys/class/pwm/pwmchip0/pwm0 $ echo 1000000 > period $ echo 500000 > duty_cycle $ echo 1 > enable 这将设置PWM周期为1秒(1000000微秒),占空比为50%(500000微秒)并启用PWM信号。 4. 您还可以使用PWM驱动程序的API来编写自己的PWM控制程序。这些API包括pwm_request,pwm_config和pwm_enable等。 这些API可用于从用户空间直接控制PWM驱动程序,以实现更高级的PWM控制功能。 希望这些步骤对您有所帮助! ### 回答2: PWM(Pulse Width Modulation)顾名思义是一种脉冲宽度调制技术,在电子工程领域中广泛应用于数字信号处理中。PWM技术可以通过改变脉冲信号的脉冲宽度,来控制电路中的电压、电流或功率大小。PWM控制技术在电机调速、LED亮度调节、UPS、DC-DC变换器等各种电子设备中均有较为广泛的应用。 在linux内核中,PWM是通过相应的PWM驱动程序来控制的。PWM驱动程序所做的工作主要包括以下几个方面: 第一,初始化PWM。在系统启动过程中,通过相应的平台设备驱动程序,向内核注册PWM设备,设置相应的参数,包括PWM的周期和脉冲宽度等信息。 第二,控制PWM输出。PWM输出可以通过向PWM设备文件写入相应的数值来完成。输出数值由占空比和周期组成,通过改变参数可以控制PWM输出的占空比和频率。 第三,提供用户接口。PWM驱动程序同时还提供相应的用户接口文件,用户可以通过读写PWM设备对应的文件来实现对PWM的控制。例如,在/sys/class/pwm目录下,可以找到相应的PWM设备子目录,通过读写相应的文件即可对PWM进行控制。 第四,支持中断处理。在PWM输出过程中,一般需要在到达周期末尾时触发中断,进行相应的处理。PWM驱动程序也需要对中断处理进行支持。 总之,PWM技术在现代电子领域中应用非常广泛,linux PWM驱动程序也是相应的关键组成部分之一。通过PWM驱动程序的实现,我们可以轻松地控制LED亮度、电机转速等各种应用场景。而PWM驱动程序在日后的不断发展和完善中,也将不断地被改进和优化,为现代电子技术的不断进步提供更为稳定可靠的支持。 ### 回答3: PWM(Pulse Width Modulation)或脉宽调制是一种控制电子器件的技术,通过调整脉冲的宽度和周期来达到控制电子设备的功率、速度和亮度的目的。Linux PWM驱动则是一种跨平台的PWM控制器,使用Linux的内核从驱动器中执行PWM协议操作。 Linux PWM驱动是基于Linux系统内核提供的高级驱动程序接口(API)实现对PWM设备的控制和管理。Linux PWM驱动使得PWM设备的编程接口更加简单和便捷,可以自由配置PWM信号的频率、占空比和周期等参数以控制输出信号。 Linux PWM驱动通常被用于控制一些设备,比如LED、电机、声音发生器和传感器等等。例如,我们可以通过PWM控制器驱动LED的亮度和颜色,也可以通过PWM控制器驱动电机来调节速度和方向。 要在Linux中使用PWM驱动程序,需要安装相应的内核模块并配置设备驱动程序参数。LinuxPWM API接口包括PWM子系统,该子系统提供了PWM设备的管理、底层硬件驱动和访问函数等。此外,Linux的设备树(Device Tree)也提供一种类似于BIOS的抽象层,用于描述硬件平台的配置,包括PWM控制器等设备的物理和逻辑地址。 总之,Linux PWM驱动是一种实现PWM控制的程序接口,可用于管理和控制PWM设备。通过LinuxPWM API和设备树,开发人员可以轻松地访问硬件PWM信号并实现有用的控制功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值