1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2018-05-07     aozima       the first version
9  * 2022-05-14     Stanley Lwin add pwm function
10  * 2022-07-25     liYony       fix complementary outputs and add usage information in finsh
11  * 2022-08-31     liYony       Add complementary output section to framework for management
12  * 2022-09-24     qiyu         Add dead-time and phase configuration
13  * 2023-12-23     1ridic       Add second-level command completion
14  */
15 
16 #include <rtdevice.h>
17 
_pwm_control(rt_device_t dev,int cmd,void * args)18 static rt_err_t _pwm_control(rt_device_t dev, int cmd, void *args)
19 {
20     rt_err_t result = RT_EOK;
21     struct rt_device_pwm *pwm = (struct rt_device_pwm *)dev;
22     struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)args;
23 
24     switch (cmd)
25     {
26     case PWMN_CMD_ENABLE:
27         configuration->complementary = RT_TRUE;
28         break;
29     case PWMN_CMD_DISABLE:
30         configuration->complementary = RT_FALSE;
31         break;
32     default:
33         if (pwm->ops->control)
34             result = pwm->ops->control(pwm, cmd, args);
35         break;
36     }
37 
38     return result;
39 }
40 
41 /*
42 pos: channel
43 void *buffer: rt_uint32_t pulse[size]
44 size : number of pulse, only set to sizeof(rt_uint32_t).
45 */
_pwm_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)46 static rt_ssize_t _pwm_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
47 {
48     rt_err_t result = RT_EOK;
49     struct rt_device_pwm *pwm = (struct rt_device_pwm *)dev;
50     rt_uint32_t *pulse = (rt_uint32_t *)buffer;
51     struct rt_pwm_configuration configuration = {0};
52 
53     configuration.channel = (pos > 0) ? (pos) : (-pos);
54 
55     if (pwm->ops->control)
56     {
57         result = pwm->ops->control(pwm, PWM_CMD_GET,  &configuration);
58         if (result != RT_EOK)
59         {
60             return 0;
61         }
62 
63         *pulse = configuration.pulse;
64     }
65 
66     return size;
67 }
68 
69 /*
70 pos: channel
71 void *buffer: rt_uint32_t pulse[size]
72 size : number of pulse, only set to sizeof(rt_uint32_t).
73 */
_pwm_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)74 static rt_ssize_t _pwm_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
75 {
76     rt_err_t result = RT_EOK;
77     struct rt_device_pwm *pwm = (struct rt_device_pwm *)dev;
78     rt_uint32_t *pulse = (rt_uint32_t *)buffer;
79     struct rt_pwm_configuration configuration = {0};
80 
81     configuration.channel = (pos > 0) ? (pos) : (-pos);
82 
83     if (pwm->ops->control)
84     {
85         result = pwm->ops->control(pwm, PWM_CMD_GET, &configuration);
86         if (result != RT_EOK)
87         {
88             return 0;
89         }
90 
91         configuration.pulse = *pulse;
92 
93         result = pwm->ops->control(pwm, PWM_CMD_SET, &configuration);
94         if (result != RT_EOK)
95         {
96             return 0;
97         }
98     }
99 
100     return size;
101 }
102 
103 #ifdef RT_USING_DEVICE_OPS
104 static const struct rt_device_ops pwm_device_ops =
105 {
106     RT_NULL,
107     RT_NULL,
108     RT_NULL,
109     _pwm_read,
110     _pwm_write,
111     _pwm_control
112 };
113 #endif /* RT_USING_DEVICE_OPS */
114 
rt_device_pwm_register(struct rt_device_pwm * device,const char * name,const struct rt_pwm_ops * ops,const void * user_data)115 rt_err_t rt_device_pwm_register(struct rt_device_pwm *device, const char *name, const struct rt_pwm_ops *ops, const void *user_data)
116 {
117     rt_err_t result = RT_EOK;
118 
119     rt_memset(device, 0, sizeof(struct rt_device_pwm));
120 
121 #ifdef RT_USING_DEVICE_OPS
122     device->parent.ops = &pwm_device_ops;
123 #else
124     device->parent.init = RT_NULL;
125     device->parent.open = RT_NULL;
126     device->parent.close = RT_NULL;
127     device->parent.read  = _pwm_read;
128     device->parent.write = _pwm_write;
129     device->parent.control = _pwm_control;
130 #endif /* RT_USING_DEVICE_OPS */
131 
132     device->parent.type         = RT_Device_Class_PWM;
133     device->ops                 = ops;
134     device->parent.user_data    = (void *)user_data;
135 
136     result = rt_device_register(&device->parent, name, RT_DEVICE_FLAG_RDWR);
137 
138     return result;
139 }
140 
rt_pwm_enable(struct rt_device_pwm * device,int channel)141 rt_err_t rt_pwm_enable(struct rt_device_pwm *device, int channel)
142 {
143     rt_err_t result = RT_EOK;
144     struct rt_pwm_configuration configuration = {0};
145 
146     if (!device)
147     {
148         return -RT_EIO;
149     }
150 
151     /* Make it is positive num forever */
152     configuration.channel = (channel > 0) ? (channel) : (-channel);
153 
154     /* If channel is a positive number (0 ~ n), it means using normal output pin.
155      * If channel is a negative number (0 ~ -n), it means using complementary output pin. */
156     if (channel > 0)
157     {
158         result = rt_device_control(&device->parent, PWMN_CMD_DISABLE, &configuration);
159     }
160     else
161     {
162         result = rt_device_control(&device->parent, PWMN_CMD_ENABLE, &configuration);
163     }
164 
165     result = rt_device_control(&device->parent, PWM_CMD_ENABLE, &configuration);
166 
167     return result;
168 }
169 
rt_pwm_disable(struct rt_device_pwm * device,int channel)170 rt_err_t rt_pwm_disable(struct rt_device_pwm *device, int channel)
171 {
172     rt_err_t result = RT_EOK;
173     struct rt_pwm_configuration configuration = {0};
174 
175     if (!device)
176     {
177         return -RT_EIO;
178     }
179 
180     /* Make it is positive num forever */
181     configuration.channel = (channel > 0) ? (channel) : (-channel);
182 
183     /* If channel is a positive number (0 ~ n), it means using normal output pin.
184      * If channel is a negative number (0 ~ -n), it means using complementary output pin. */
185     if (channel > 0)
186     {
187         result = rt_device_control(&device->parent, PWMN_CMD_DISABLE, &configuration);
188     }
189     else
190     {
191         result = rt_device_control(&device->parent, PWMN_CMD_ENABLE, &configuration);
192     }
193 
194     result = rt_device_control(&device->parent, PWM_CMD_DISABLE, &configuration);
195 
196     return result;
197 }
198 
rt_pwm_set(struct rt_device_pwm * device,int channel,rt_uint32_t period,rt_uint32_t pulse)199 rt_err_t rt_pwm_set(struct rt_device_pwm *device, int channel, rt_uint32_t period, rt_uint32_t pulse)
200 {
201     rt_err_t result = RT_EOK;
202     struct rt_pwm_configuration configuration = {0};
203 
204     if (!device)
205     {
206         return -RT_EIO;
207     }
208 
209     configuration.channel = (channel > 0) ? (channel) : (-channel);
210     configuration.period = period;
211     configuration.pulse = pulse;
212     result = rt_device_control(&device->parent, PWM_CMD_SET, &configuration);
213 
214     return result;
215 }
216 
rt_pwm_set_period(struct rt_device_pwm * device,int channel,rt_uint32_t period)217 rt_err_t rt_pwm_set_period(struct rt_device_pwm *device, int channel, rt_uint32_t period)
218 {
219     rt_err_t result = RT_EOK;
220     struct rt_pwm_configuration configuration = {0};
221 
222     if (!device)
223     {
224         return -RT_EIO;
225     }
226 
227     configuration.channel = (channel > 0) ? (channel) : (-channel);
228     configuration.period = period;
229     result = rt_device_control(&device->parent, PWM_CMD_SET_PERIOD, &configuration);
230 
231     return result;
232 }
233 
rt_pwm_set_pulse(struct rt_device_pwm * device,int channel,rt_uint32_t pulse)234 rt_err_t rt_pwm_set_pulse(struct rt_device_pwm *device, int channel, rt_uint32_t pulse)
235 {
236     rt_err_t result = RT_EOK;
237     struct rt_pwm_configuration configuration = {0};
238 
239     if (!device)
240     {
241         return -RT_EIO;
242     }
243 
244     configuration.channel = (channel > 0) ? (channel) : (-channel);
245     configuration.pulse = pulse;
246     result = rt_device_control(&device->parent, PWM_CMD_SET_PULSE, &configuration);
247 
248     return result;
249 }
250 
rt_pwm_set_dead_time(struct rt_device_pwm * device,int channel,rt_uint32_t dead_time)251 rt_err_t rt_pwm_set_dead_time(struct rt_device_pwm *device, int channel, rt_uint32_t dead_time)
252 {
253     rt_err_t result = RT_EOK;
254     struct rt_pwm_configuration configuration = {0};
255 
256     if (!device)
257     {
258         return -RT_EIO;
259     }
260 
261     configuration.channel = (channel > 0) ? (channel) : (-channel);
262     configuration.dead_time = dead_time;
263     result = rt_device_control(&device->parent, PWM_CMD_SET_DEAD_TIME, &configuration);
264 
265     return result;
266 }
267 
268 
rt_pwm_set_phase(struct rt_device_pwm * device,int channel,rt_uint32_t phase)269 rt_err_t rt_pwm_set_phase(struct rt_device_pwm *device, int channel, rt_uint32_t phase)
270 {
271     rt_err_t result = RT_EOK;
272     struct rt_pwm_configuration configuration = {0};
273 
274     if (!device)
275     {
276         return -RT_EIO;
277     }
278 
279     configuration.channel = (channel > 0) ? (channel) : (-channel);
280     configuration.phase = phase;
281     result = rt_device_control(&device->parent, PWM_CMD_SET_PHASE, &configuration);
282 
283     return result;
284 }
285 
rt_pwm_get(struct rt_device_pwm * device,struct rt_pwm_configuration * cfg)286 static rt_err_t rt_pwm_get(struct rt_device_pwm *device, struct rt_pwm_configuration *cfg)
287 {
288     rt_err_t result = RT_EOK;
289 
290     if (!device)
291     {
292         return -RT_EIO;
293     }
294 
295     result = rt_device_control(&device->parent, PWM_CMD_GET, cfg);
296 
297     return result;
298 }
299 
300 #ifdef RT_USING_FINSH
301 #include <stdlib.h>
302 #include <string.h>
303 #include <finsh.h>
304 
305 enum pwm_list_parameters
306 {
307     PWM_LIST_PROBE = 1,
308     PWM_LIST_ENABLE,
309     PWM_LIST_DISABLE,
310     PWM_LIST_GET,
311     PWM_LIST_SET,
312     PWM_LIST_PHASE,
313     PWM_LIST_DEAD_TIME,
314 };
315 
CMD_OPTIONS_STATEMENT(pwm_list)316 CMD_OPTIONS_STATEMENT(pwm_list)
317 
318 static int pwm_list(int argc, char **argv)
319 {
320     rt_err_t result = -RT_ERROR;
321     char *result_str;
322     static struct rt_device_pwm *pwm_device = RT_NULL;
323     struct rt_pwm_configuration cfg = {0};
324 
325     if (argc > 1)
326     {
327         if (MSH_OPT_ID_GET(pwm_list) == PWM_LIST_PROBE)
328         {
329             if (argc == 3)
330             {
331                 pwm_device = (struct rt_device_pwm *)rt_device_find(argv[2]);
332                 result_str = (pwm_device == RT_NULL) ? "failure" : "success";
333                 rt_kprintf("probe %s %s\n", argv[2], result_str);
334                 return (pwm_device == RT_NULL) ? -RT_ERROR : RT_EOK;
335             }
336             else
337             {
338                 rt_kprintf("pwm probe <device name>                  - probe pwm by name\n");
339                 return -RT_EINVAL;
340             }
341         }
342         else if (pwm_device == RT_NULL)
343         {
344             rt_kprintf("Please using 'pwm probe <device name>' first.\n");
345             return -RT_ERROR;
346         }
347 
348         switch (MSH_OPT_ID_GET(pwm_list))
349         {
350         case PWM_LIST_ENABLE:
351             if (argc == 3)
352             {
353                 result = rt_pwm_enable(pwm_device, atoi(argv[2]));
354                 result_str = (result == RT_EOK) ? "success" : "failure";
355                 rt_kprintf("%s channel %d is enabled %s \n", pwm_device->parent.parent.name, atoi(argv[2]), result_str);
356             }
357             else
358             {
359                 rt_kprintf("pwm enable <channel>                     - enable pwm channel\n");
360                 rt_kprintf("    e.g. MSH >pwm enable  1              - PWM_CH1  nomal\n");
361                 rt_kprintf("    e.g. MSH >pwm enable -1              - PWM_CH1N complememtary\n");
362             }
363             break;
364 
365         case PWM_LIST_DISABLE:
366             if (argc == 3)
367             {
368                 result = rt_pwm_disable(pwm_device, atoi(argv[2]));
369             }
370             else
371             {
372                 rt_kprintf("pwm disable <channel>                    - disable pwm channel\n");
373             }
374             break;
375 
376         case PWM_LIST_GET:
377             cfg.channel = atoi(argv[2]);
378             result = rt_pwm_get(pwm_device, &cfg);
379             if (result == RT_EOK)
380             {
381                 rt_kprintf("Info of device [%s] channel [%d]:\n", pwm_device, atoi(argv[2]));
382                 rt_kprintf("period      : %d\n", cfg.period);
383                 rt_kprintf("pulse       : %d\n", cfg.pulse);
384                 rt_kprintf("Duty cycle  : %d%%\n", (int)(((double)(cfg.pulse) / (cfg.period)) * 100));
385             }
386             else
387             {
388                 rt_kprintf("Get info of device: [%s] error.\n", pwm_device);
389             }
390             break;
391 
392         case PWM_LIST_SET:
393             if (argc == 5)
394             {
395                 result = rt_pwm_set(pwm_device, atoi(argv[2]), atoi(argv[3]), atoi(argv[4]));
396                 rt_kprintf("pwm info set on %s at channel %d\n", pwm_device, (rt_base_t)atoi(argv[2]));
397             }
398             else
399             {
400                 rt_kprintf("Set info of device: [%s] error\n", pwm_device);
401                 rt_kprintf("Usage: pwm set <channel> <period> <pulse>\n");
402             }
403             break;
404 
405         case PWM_LIST_PHASE:
406             if (argc == 4)
407             {
408                 result = rt_pwm_set_phase(pwm_device, atoi(argv[2]), atoi(argv[3]));
409                 result_str = (result == RT_EOK) ? "success" : "failure";
410                 rt_kprintf("%s phase is set %d \n", pwm_device->parent.parent.name, (rt_base_t)atoi(argv[3]));
411             }
412             break;
413 
414         case PWM_LIST_DEAD_TIME:
415             if (argc == 4)
416             {
417                 result = rt_pwm_set_dead_time(pwm_device, atoi(argv[2]), atoi(argv[3]));
418                 result_str = (result == RT_EOK) ? "success" : "failure";
419                 rt_kprintf("%s dead_time is set %d \n", pwm_device->parent.parent.name, (rt_base_t)atoi(argv[3]));
420             }
421             break;
422 
423         default:
424             goto _usage;
425         }
426     }
427     else
428     {
429         goto _usage;
430     }
431     return result;
432 
433 _usage:
434     rt_kprintf("Usage: \n");
435     rt_kprintf("pwm probe      <device name>                - probe pwm by name\n");
436     rt_kprintf("pwm enable     <channel>                    - enable pwm channel\n");
437     rt_kprintf("pwm disable    <channel>                    - disable pwm channel\n");
438     rt_kprintf("pwm get        <channel>                    - get pwm channel info\n");
439     rt_kprintf("pwm set        <channel> <period> <pulse>   - set pwm channel info\n");
440     rt_kprintf("pwm phase      <channel> <phase>            - set pwm phase\n");
441     rt_kprintf("pwm dead_time  <channel> <dead_time>        - set pwm dead time\n");
442     result = -RT_ERROR;
443     return result;
444 }
445 CMD_OPTIONS_NODE_START(pwm_list)
446 CMD_OPTIONS_NODE(PWM_LIST_PROBE, probe, probe pwm by name)
447 CMD_OPTIONS_NODE(PWM_LIST_ENABLE, enable, enable pwm channel)
448 CMD_OPTIONS_NODE(PWM_LIST_DISABLE, disable, disable pwm channel)
449 CMD_OPTIONS_NODE(PWM_LIST_GET, get, get pwm channel info)
450 CMD_OPTIONS_NODE(PWM_LIST_SET, set, set pwm channel info)
451 CMD_OPTIONS_NODE(PWM_LIST_PHASE, phase, set pwm phase)
452 CMD_OPTIONS_NODE(PWM_LIST_DEAD_TIME, dead_time, set pwm dead time)
453 CMD_OPTIONS_NODE_END
454 MSH_CMD_EXPORT_ALIAS(pwm_list, pwm, control pwm device, optenable);
455 
456 #endif /* RT_USING_FINSH */
457