- 了解PWM基础知识。
1.Introduction
Pulse Wide Modulation (PWM) operates like a switch that constantly cycles on and off. It is a hardware feature used to control servomotors, for voltage regulation, and so on. The most wellknown applications of PWM are:
- Motor speed control
- Light dimming
- Voltage regulation
Now, let us introduce PWM with a simple following figure:
The preceding figure describes a complete PWM cycle, introducing some terms we need to clarify prior to getting deeper into the kernel PWM framework:
- Ton: This is the duration during which the signal is high.
- Toff: This is the duration during which the signal is low.
- Period: This is the duration of a complete PWM cycle. It represents the sum of Ton and - Toff of the PWM signal.
- Duty cycle: It is represented as a percentage of the time signal that remains on during the period of the PWM signal.
2.The Linux PWM framework has two interfaces:
- Controller interface: The one that exposes the PWM line. It is the PWM chip, that is, the producer.
- Consumer interface: The device consuming PWM lines exposed by the controller.
Drivers of such devices use helper functions exported by the controller by means of a generic PWM framework.
2.1.PWM controller driver
PWM framework使用struct pwm_chip抽象PWM控制器。通常情况下,在一个SOC中,可以同时支持多路PWM输出(如6路),以便同时控制多个PWM设备。这样每一路PWM输出,可以看做一个PWM设备(由上面struct pwm_device抽象)。PWM framework会统一管理这些PWM设备,将它们归类为一个PWM chip。
2.1.1.struct pwm_chip
1: /* include/linux/pwm.h */
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: This represents the device associated with this chip.
- ops: This is a data structure providing callback functions this chip exposes to consumer drivers.
- base: This is the number of the first PWM controlled by this chip. If chip->base < 0 then, the kernel will dynamically assign a base.
- can_sleep: This should be set to true by the chip driver if .config(), .enable(), or .disable() operations of the ops field may sleep.
- npwm: This is the number of PWM channels (devices) this chip provide.
- pwms: This is an array of PWM devices of this chip, allocated by the framework, to consumer drivers.
- of_xlate: This is an optional callback to request a PWM device given a DT PWM specifier. If not defined, it will be set to of_pwm_simple_xlate by the PWM core, which will force of_pwm_n_cells to 2 as well.
- of_pwm_n_cells: This is the number of cells expected in the DT for a PWM specifier.
2.1.2. pwm ops
Istruct pwm_ops结构是pwm device有关的操作函数集,如下:
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: };
- request: This is an optional hook that, if provided, is executed during a PWM channel
request. - free: This is the same as request, ran during PWM freeing.
- config: This is the PMW configuration hook. It configures duty cycles and period length
for this PWM. - set_polarity: This hook configures the polarity of this PWM.
- enable: This enables the PWM line, starting output toggling.
- disable: This disables the PWM line, stopping output toggling.
- apply: This atomically applies a new PWM config. The state argument should be adjusted with the real hardware config.
- get_state: This returns the current PWM state. This function is only called once per PWM device when the PWM chip is registered.
- owner: This is the module that owns this chip, usually THIS_MODULE
In the probe function of the PWM controller driver, it is good practice to retrieve DT resources, initialize hardware, fill a struct pwm_chip and its struct pwm_ops, and then, add the PWM chip with the pwmchip_add function.
2.1.3. pwmchip_add/pwmchip_remove
初始化完成后的pwm chip可以通过pwmchip_add接口注册到kernel中,之后的事情,pwm driver就不用操心了。该接口的原型如下:
int pwmchip_add(struct pwm_chip *chip);
int pwmchip_remove(struct pwm_chip *chip);
3.PWM consumer interface
A consumer is the device that actually uses PWM channels. A PWM channel is represented in the kernel as an instance of struct pwm_device structure:
3.1.pwm device
struct pwm_device是pwm device的操作句柄,consumer的API调用,会中转到provider的pwm ops回调函数上,provider(及pwm driver)根据pwm device的信息,进行相应的寄存器操作。如下:
struct pwm_device {
const char *label;
unsigned long flags;
unsigned int hwpwm;
unsigned int pwm;
struct pwm_chip *chip;
void *chip_data;
struct pwm_args args;
struct pwm_state state;
}
struct pwm_args {
unsigned int period; /* Device's nitial period */
enum pwm_polarity polarity;
};
struct pwm_state {
unsigned int period; /* PWM period (in nanoseconds) */
unsigned int duty_cycle; /* PWM duty cycle (in nanoseconds) */
enum pwm_polarity polarity; /* PWM polarity */
bool enabled; /* PWM enabled status */
}
3.2.Using PWMs
- struct pwm_device *pwm_request(int pwm_id, const char *label);
- void pwm_free(struct pwm_device *pwm);
- int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
- int pwm_enable(struct pwm_device *pwm);
- void pwm_disable(struct pwm_device *pwm);
Legacy users can request a PWM device using pwm_request() and free it
after usage with pwm_free().
New users should use the pwm_get() function and pass to it the consumer
device or a consumer name. pwm_put() is used to free the PWM device. Managed
variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist.
AAfter being requested, a PWM has to be configured using::
int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state);
AThis API controls both the PWM period/duty_cycle config and the enable/disable state.
The pwm_config(), pwm_enable() and pwm_disable() functions are just wrappers
around pwm_apply_state() and should not be used if the user wants to change
several parameter at once. For example, if you see pwm_config() and
pwm_{enable,disable}() calls in the same function, this probably means you
should switch to pwm_apply_state().
4.Using PWMs with the sysfs interface
The PWM core sysfs root path is /sys/class/pwm/. It is the user space way to manage PWM device. Each PWM controller/chip added to the system creates a pwmchipN directory entry under the sysfs root path, where N is the base of the PWM chip. The directory contains the following files:
- npwm: This is a reads only file printing the number of PWM channels that this chip supports
- Export: This is a write-only file allowing to export a PWM channel for use with sysfs (this functionality is equivalent to GPIO sysfs interface)
- Unexport: Unexports a PWM channel from sysfs (write-only)
The PWM channels are numbered using an index from 0 to pwm. These numbers are local to the chip. Each PWM channel exportation creates a pwmX directory in the pwmchipN, which is the same directory as the one containing the export file used. X is the number of the channel that was exported. Each channel directory contains the following files:
- period: This is a readable/writable file to get/set the total period of the PWM signal. value is in nanoseconds.
- duty_cycle: This is a readable/writable file to get/set the duty cycle of the PWM signal. It represents the active time of the PWM signal. Value is in nanoseconds and must always be less than the period.
- polarity: This is a readable/writable file to use only if the chip of this PWM device
supports polarity inversion. It is better to change the polarity only when this PWM is not enabled. Accepted values are string normal or inversed. - enable: This is a readable/writable file, to enable (start toggling)/disable (stop toggling) the PWM signal.
Debug as shown:(调试必须如下所示)
The following is an example of using PWM from user space through the sysfs interface:
1. Set PWM period:
# echo <value in nanoseconds> > /sys/class/pwm/pwmchip<pwmchipnr>/pwm<pwmnr>/period
2. Set PWM duty cycle: The value of the duty cycle must be less than the value of PWM
period:
# echo <value in nanoseconds> > /sys/class/pwm/pwmchip<pwmchipnr>/pwm<pwmnr>/duty_cycle
3. Enable PWM:
# echo 1 > /sys/class/pwm/pwmchip<pwmchipnr>/pwm<pwmnr>/enable
4. Disable PWM:
# echo 0 > /sys/class/pwm/pwmchip<pwmchipnr>/pwm<pwmnr>/enable
refer to
- https://en.wikipedia.org/wiki/Pulsewidth_modulation
- Documentation/pwm.txt
来源:CSDN
作者:Hacker_Albert
链接:https://blog.csdn.net/weixin_41028621/article/details/103529078