1@page page_device_pwm PWM Device
2
3# Introduction to PWM
4
5PWM (Pulse Width Modulation) is a method of digitally encoding the level of an analog signal. The frequency of the square wave is used to encode the level of a specific analog signal by pulses of different frequencies. The output receives a series of pulses of equal magnitude and uses these pulses to replace the device with the desired waveform.
6
7![PWM Schematic Diagram](figures/pwm-f.png)
8
9Above is a simple schematic diagram of PWM. Assuming that the timer works in a up-counter mode. When the count value is less than the threshold, it outputs a level state, such as a high level. When the count value is greater than the threshold, it outputs the opposite, such as a low level.  When the count value reaches the maximum value, the counter recounts from 0 and returns to the original level state. The ratio of the high-level duration (pulse width) to the cycle time is the duty cycle, ranging from 0 to 100%. The high level of the above picture is just half of the cycle time, so the duty cycle is 50%.
10
11One of the common PWM control scenarios is to adjust the brightness of a light or screen. The brightness can be adjusted through changing the duty cycle. The PWM does not adjust the light continuously, rather it constantly turns the screen on and off. When the light is turned on and off fast enough, the naked eye will always think that it is always bright. In the process of switching it on and off, the longer the light is off, the lower the brightness of the screen to the naked eye. Conversely, the longer the light is on, the brighter the screen will appear.
12
13![PWM Brightness Adjustment](figures/pwm-l.png)
14
15# Access to PWM Devices
16
17The application accesses the PWM device hardware through the PWM device management interface provided by RT-Thread. The related interfaces are as follows:
18
19| **Function** | Description   |
20| ----------------- | ---------------------------------- |
21| rt_device_find()  | Find device handles based on the name of PWM device |
22| rt_pwm_set()     | Set PWM period and pulse width |
23| rt_pwm_enable()   | Enable PWM device |
24| rt_pwm_disable()  | Disable the PWM device |
25
26## Find the PWM Device
27
28The application obtains the device handle based on the name of PWM device, which in turn can operate the PWM device. The function is as follows:
29
30```c
31rt_device_t rt_device_find(const char* name);
32```
33
34| Parameter | Description                |
35| -------- | ---------------------------------- |
36| name     | Device             |
37| **Return** | ——                                 |
38| Device handle | Found the corresponding device, will return the corresponding device handle |
39| RT_NULL  | Device not found |
40
41In general, the name of the PWM device registered to the system is pwm0, pwm1, etc. The usage examples are as follows:
42
43```c
44#define PWM_DEV_NAME        "pwm3"  /* name of PWM device */
45struct rt_device_pwm *pwm_dev;      /* PWM device handle */
46/* Search the device */
47pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
48```
49
50## Set PWM Period and Pulse Width
51
52Set the PWM period and duty cycle by using the following function:
53
54```c
55rt_err_t rt_pwm_set(struct rt_device_pwm *device,
56                    int channel,
57                    rt_uint32_t period,
58                    rt_uint32_t pulse);
59```
60
61| Parameter | Description |
62| ---------- | ----------------- |
63| device     |  PWM device handle  |
64| channel    | PWM channel      |
65| period     | PWM period (ns) |
66| pulse     | PWM pulse width time (ns) |
67| **Return** | ——                |
68| RT_EOK     | successful |
69| -RT_EIO | device is null |
70| -RT_ENOSYS | Device operation method is null |
71| Other Errors | Execute failed |
72
73The output frequency of the PWM is determined by the period. For example, the time of a period is 0.5ms (milliseconds), the period value is 500000ns (nanoseconds), the output frequency is 2KHz, the duty cycle is `pulse / period`, and the pulse value cannot exceed period.
74
75An example of use is as follows:
76
77```c
78#define PWM_DEV_NAME        "pwm3"  /* name of PWM device */
79#define PWM_DEV_CHANNEL      4      /* PWM channel */
80struct rt_device_pwm *pwm_dev;      /* PWM device handle */
81rt_uint32_t period, pulse;
82
83period = 500000;    /* The period is 0.5ms, the unit is nanoseconds */
84pulse = 0;          /* PWM pulse width value, the unit is nanoseconds */
85/* Search the device */
86pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
87/* Set the PWM period and pulse width */
88rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
89```
90
91## Enable the PWM Device
92
93After setting the PWM period and pulse width, you can enable the PWM device by the following function:
94
95```c
96rt_err_t rt_pwm_enable(struct rt_device_pwm *device, int channel);
97```
98
99| Parameter | Description            |
100| ---------- | ------------------------------- |
101| device  | PWM device handle            |
102| channel | PWM channel         |
103| **Return** | ——                             |
104| RT_EOK     | Enable device successful |
105| -RT_ENOSYS | Device operation method is null |
106| Other Errors | Enable device failed |
107
108An example of use is as follows:
109
110```c
111#define PWM_DEV_NAME        "pwm3"  /* name of PWM device */
112#define PWM_DEV_CHANNEL      4      /* PWM channel */
113struct rt_device_pwm *pwm_dev;      /* PWM device handle */
114rt_uint32_t period, pulse;
115
116period = 500000;    /* The period is 0.5ms, the unit is nanoseconds */
117pulse = 0;          /* PWM pulse width value, the unit is nanoseconds */
118/* Search the device */
119pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
120/* Set the PWM period and pulse width */
121rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
122/* Enable the device */
123rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
124```
125
126## Disable the PWM device Channel
127
128Use the following function to turn off the corresponding channel of the PWM device.
129
130```c
131rt_err_t rt_pwm_disable(struct rt_device_pwm *device, int channel);
132```
133
134| **Parameter** | Description   |
135| ---------- | ------------------------------- |
136| device  | PWM device handle               |
137| channel | PWM channel    |
138| **Return** | ——                             |
139| RT_EOK     | Turn off device successful |
140| -RT_EIO | Device handle is null |
141| Other Errors | Turn off device failed |
142
143An example of use is as follows:
144
145```c
146#define PWM_DEV_NAME        "pwm3"  /* name of PWM device */
147#define PWM_DEV_CHANNEL      4      /* PWM channel */
148struct rt_device_pwm *pwm_dev;      /* PWM device handle */
149rt_uint32_t period, pulse;
150
151period = 500000;    /* The period is 0.5ms, the unit is nanoseconds */
152pulse = 0;          /* PWM pulse width value, the unit is nanoseconds */
153/* Search the device */
154pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
155/* Set the PWM period and pulse width */
156rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
157/* Enable the device */
158rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
159/* Turn off the device channel */
160rt_pwm_disable(pwm_dev,PWM_DEV_CHANNEL);
161```
162
163# FinSH Command
164
165To set the period and duty cycle of a channel of a PWM device, use the command `pwm_set pwm1 1 500000 5000`. The first parameter is the command, the second parameter is the PWM device name, the third parameter is the PWM channel, and the fourth parameter is PWM period (ns), the fifth parameter is the pulse width (ns).
166
167```c
168msh />pwm_set pwm1 1 500000 5000
169msh />
170```
171
172To enable a channel of the PWM device, use the command `pwm_enable pwm1 1`. The first parameter is the command, the second parameter is the PWM device name, and the third parameter is the PWM channel.
173
174```c
175msh />pwm_enable pwm1 1
176msh />
177```
178
179To disable a channel of the PWM device, use the command `pwm_disable pwm1 1`. The first parameter is the command, the second parameter is the PWM device name, and the third parameter is the PWM channel.
180
181```c
182msh />pwm_disable pwm1 1
183msh />
184```
185
186# PWM Device Usage Example
187
188The following sample code is a  PWM device usage sample . The main steps of the sample code are as follows:
189
1901. Find the PWM device to get the device handle.
1912. Set the PWM period and pulse width.
1923. Enable the PWM device.
1934. The pulse width is modified every 50 milliseconds in the while loop.
1945. Connect the PWM channel to a LED, and you can see that the LED changes from dark to bright gradually, and then from bright to dark.
195
196```c
197/*
198 * Program list: This is PWM device usage example
199 * The routine exports the pwm_led_sample command to the control terminal
200 * Format for Command: pwm_led_sample
201 * Program function: By controlling the brightness of the LED light through the PWM device,
202 * you can see that the LED changes from dark to bright gradually, then from bright to dark.
203 */
204
205#include <rtthread.h>
206#include <rtdevice.h>
207
208#define PWM_DEV_NAME        "pwm3"  /* PWM device name */
209#define PWM_DEV_CHANNEL     4       /* PWM channel */
210
211struct rt_device_pwm *pwm_dev;      /* PWM device handle */
212
213static int pwm_led_sample(int argc, char *argv[])
214{
215    rt_uint32_t period, pulse, dir;
216
217    period = 500000;    /* The period is 0.5ms, the unit is nanoseconds */
218    dir = 1;            /* Increase or decrease direction of PWM pulse width value */
219    pulse = 0;          /* PWM pulse width value, the unit is nanoseconds*/
220
221    /* Search the Device */
222    pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
223    if (pwm_dev == RT_NULL)
224    {
225        rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME);
226        return -RT_ERROR;
227    }
228
229    /* Set PWM period and pulse width defaults */
230    rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
231    /* Enable device */
232    rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
233
234    while (1)
235    {
236        rt_thread_mdelay(50);
237        if (dir)
238        {
239            pulse += 5000;      /* Increase 5000ns each time from 0 */
240        }
241        else
242        {
243            pulse -= 5000;      /* 5000ns reduction from the maximum */
244        }
245        if (pulse >= period)
246        {
247            dir = 0;
248        }
249        if (0 == pulse)
250        {
251            dir = 1;
252        }
253
254        /* Set the PWM period and pulse width */
255        rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
256    }
257}
258/* Export to the msh command list */
259MSH_CMD_EXPORT(pwm_led_sample, pwm sample);
260```
261