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