1 /*
2 * Copyright (c) 2006-2025, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2020-07-26 supperthomas first version
9 *
10 */
11
12
13 #include <board.h>
14 #include "rtdevice.h"
15 #include "rtservice.h"
16
17 #ifdef RT_USING_PWM
18
19 #include <nrfx_pwm.h>
20
21 struct mcu_pwm
22 {
23 struct rt_device_pwm pwm_device;
24
25 nrfx_pwm_t *pwm_handle;
26 nrf_pwm_values_individual_t m_demo1_seq_values;
27 nrf_pwm_sequence_t m_demo1_seq;
28
29 rt_uint8_t channel;
30 char *name;
31 rt_uint64_t pwm_src_clk;
32 uint8_t channel_0_pin;
33 uint8_t channel_1_pin;
34 uint8_t channel_2_pin;
35 uint8_t channel_3_pin;
36 };
37
38 enum
39 {
40 #ifdef BSP_USING_PWM0
41 PWM0_INDEX,
42 #endif
43 #ifdef BSP_USING_PWM1
44 PWM1_INDEX,
45 #endif
46 #ifdef BSP_USING_PWM2
47 PWM2_INDEX,
48 #endif
49 #ifdef BSP_USING_PWM3
50 PWM3_INDEX,
51 #endif
52 };
53 #ifdef BSP_USING_PWM0
54 static nrfx_pwm_t m_pwm0 = NRFX_PWM_INSTANCE(0);
55 #define PWM0_CONFIG \
56 { \
57 .pwm_handle = &m_pwm0, \
58 .name = "pwm0", \
59 .pwm_src_clk = 1000000, \
60 }
61 #endif
62
63 #ifdef BSP_USING_PWM1
64 static nrfx_pwm_t m_pwm1 = NRFX_PWM_INSTANCE(1);
65 #define PWM1_CONFIG \
66 { \
67 .pwm_handle = &m_pwm1, \
68 .name = "pwm1", \
69 .pwm_src_clk = 1000000, \
70 }
71 #endif
72
73 #ifdef BSP_USING_PWM2
74 static nrfx_pwm_t m_pwm2 = NRFX_PWM_INSTANCE(2);
75 #define PWM2_CONFIG \
76 { \
77 .pwm_handle = &m_pwm2, \
78 .name = "pwm2", \
79 .pwm_src_clk = 1000000, \
80 }
81 #endif
82
83 #ifdef BSP_USING_PWM3
84 static nrfx_pwm_t m_pwm3 = NRFX_PWM_INSTANCE(3);
85 #define PWM3_CONFIG \
86 { \
87 .pwm_handle = &m_pwm3, \
88 .name = "pwm3", \
89 .pwm_src_clk = 1000000, \
90 }
91 #endif
92
93 static struct mcu_pwm mcu_pwm_obj[] =
94 {
95 #ifdef BSP_USING_PWM0
96 PWM0_CONFIG,
97 #endif
98 #ifdef BSP_USING_PWM1
99 PWM1_CONFIG,
100 #endif
101
102 #ifdef BSP_USING_PWM2
103 PWM2_CONFIG,
104 #endif
105
106 #ifdef BSP_USING_PWM3
107 PWM3_CONFIG,
108 #endif
109 };
110
111 static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg);
112 static struct rt_pwm_ops drv_ops =
113 {
114 drv_pwm_control
115 };
116
drv_pwm_enable(struct mcu_pwm * p_mcu,struct rt_pwm_configuration * configuration,rt_bool_t enable)117 static rt_err_t drv_pwm_enable(struct mcu_pwm *p_mcu, struct rt_pwm_configuration *configuration, rt_bool_t enable)
118 {
119 if (!enable)
120 {
121 nrfx_pwm_stop(p_mcu->pwm_handle, true);
122 }
123 else
124 {
125 (void)nrfx_pwm_simple_playback(p_mcu->pwm_handle, &p_mcu->m_demo1_seq, 1, NRFX_PWM_FLAG_LOOP);
126 }
127
128 return RT_EOK;
129 }
130
mcu_get_channel_number(uint8_t channel)131 uint8_t mcu_get_channel_number(uint8_t channel)
132 {
133 if (channel & 0x01)
134 {
135 return 0;
136 }
137 else if (channel & 0x02)
138 {
139 return 1;
140 }
141 else if (channel & 0x04)
142 {
143 return 2;
144 }
145 else if (channel & 0x08)
146 {
147 return 3;
148 }
149 return 0;
150 }
151
drv_pwm_get(struct mcu_pwm * pwm_handle,struct rt_pwm_configuration * configuration)152 static rt_err_t drv_pwm_get(struct mcu_pwm *pwm_handle, struct rt_pwm_configuration *configuration)
153 {
154 rt_uint8_t channel_number = mcu_get_channel_number(configuration->channel);
155 uint8_t tick_pscond;
156
157 tick_pscond = pwm_handle->pwm_src_clk / 1000000UL;
158 configuration->period = pwm_handle->pwm_handle->p_registers->COUNTERTOP * 1000UL / tick_pscond;
159 configuration->pulse = pwm_handle->pwm_handle->p_registers->SEQ[channel_number].PTR / tick_pscond;
160
161 return RT_EOK;
162 }
163
nrfx_set_prioid(nrfx_pwm_t * pwm_handle,uint32_t perioid)164 static void nrfx_set_prioid(nrfx_pwm_t *pwm_handle, uint32_t perioid)
165 {
166 pwm_handle->p_registers->COUNTERTOP = perioid;
167 }
168
drv_pwm_set(struct mcu_pwm * p_mcu,struct rt_pwm_configuration * configuration)169 static rt_err_t drv_pwm_set(struct mcu_pwm *p_mcu, struct rt_pwm_configuration *configuration)
170 {
171 rt_uint32_t period, pulse;
172 uint8_t tick_pscond;
173 tick_pscond = p_mcu->pwm_src_clk / 1000000UL;
174
175 p_mcu->pwm_handle->p_registers->COUNTERTOP = (unsigned long long)configuration->period * tick_pscond;
176 if (configuration->channel & 0x01)
177 {
178 p_mcu->m_demo1_seq_values.channel_0 = configuration->pulse;
179 }
180
181 if (configuration->channel & 0x02)
182 {
183 p_mcu->m_demo1_seq_values.channel_1 = configuration->pulse;
184 }
185
186 if (configuration->channel & 0x04)
187 {
188 p_mcu->m_demo1_seq_values.channel_2 = configuration->pulse;
189 }
190
191 if (configuration->channel & 0x08)
192 {
193 p_mcu->m_demo1_seq_values.channel_3 = configuration->pulse;
194 }
195 return RT_EOK;
196 }
197
drv_pwm_control(struct rt_device_pwm * device,int cmd,void * arg)198 static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
199 {
200 struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
201 void *pwm_handle = (void *)device->parent.user_data;
202 nrfx_pwm_t *p_handle = (nrfx_pwm_t *)pwm_handle;
203 struct mcu_pwm *p_mcu = rt_container_of(p_handle, struct mcu_pwm, pwm_handle);
204 switch (cmd)
205 {
206 case PWM_CMD_ENABLE:
207 return drv_pwm_enable(p_mcu, configuration, RT_TRUE);
208 case PWM_CMD_DISABLE:
209 return drv_pwm_enable(p_mcu, configuration, RT_FALSE);
210 case PWM_CMD_SET:
211 return drv_pwm_set(p_mcu, configuration);
212 case PWM_CMD_GET:
213 return drv_pwm_get(p_mcu, configuration);
214 default:
215 return -RT_EINVAL;
216 }
217 }
218
mcu_hw_pwm_init(struct mcu_pwm * device)219 static rt_err_t mcu_hw_pwm_init(struct mcu_pwm *device)
220 {
221 #define NRFX_PWM_PIN_INVERTED 0x80
222 #define _PRIO_APP_LOWEST 7
223 nrfx_pwm_config_t config0 =
224 {
225 .irq_priority = _PRIO_APP_LOWEST,
226 .base_clock = NRF_PWM_CLK_1MHz, /* default value */
227 .count_mode = NRF_PWM_MODE_UP,
228 .top_value = 5000, /* default vaule */
229 .load_mode = NRF_PWM_LOAD_INDIVIDUAL,
230 .step_mode = NRF_PWM_STEP_AUTO
231 };
232 rt_err_t result = RT_EOK;
233 if (device->pwm_src_clk == 1000000)
234 {
235 config0.base_clock = NRF_PWM_CLK_1MHz;
236 }
237 else if (device->pwm_src_clk == 2000000)
238 {
239 config0.base_clock = NRF_PWM_CLK_2MHz;
240 }
241 else if (device->pwm_src_clk == 8000000)
242 {
243 config0.base_clock = NRF_PWM_CLK_8MHz;
244 }
245 else
246 {
247 config0.base_clock = NRF_PWM_CLK_1MHz;
248 }
249
250 if (device->channel & 0x01)
251 {
252 config0.output_pins[0] = device->channel_0_pin | NRFX_PWM_PIN_INVERTED;
253 }
254
255 if (device->channel & 0x02)
256 {
257 config0.output_pins[1] = device->channel_1_pin | NRFX_PWM_PIN_INVERTED;
258 }
259
260 if (device->channel & 0x04)
261 {
262 config0.output_pins[2] = device->channel_2_pin | NRFX_PWM_PIN_INVERTED;
263 }
264
265 if (device->channel & 0x08)
266 {
267 config0.output_pins[3] = device->channel_3_pin | NRFX_PWM_PIN_INVERTED;
268 }
269 device->m_demo1_seq.values.p_individual = &device->m_demo1_seq_values;
270 device->m_demo1_seq.length = NRF_PWM_VALUES_LENGTH(device->m_demo1_seq_values),
271 nrfx_pwm_init(device->pwm_handle, &config0, NULL, NULL);
272 return result;
273 }
274
pwm_get_channel(void)275 static void pwm_get_channel(void)
276 {
277 #ifdef BSP_USING_PWM0_CH0
278 mcu_pwm_obj[PWM0_INDEX].channel |= 1 << 0;
279 mcu_pwm_obj[PWM0_INDEX].channel_0_pin = BSP_USING_PWM0_CH0;
280 #endif
281 #ifdef BSP_USING_PWM0_CH1
282 mcu_pwm_obj[PWM0_INDEX].channel |= 1 << 1;
283 mcu_pwm_obj[PWM0_INDEX].channel_1_pin = BSP_USING_PWM0_CH1;
284 #endif
285 #ifdef BSP_USING_PWM0_CH2
286 mcu_pwm_obj[PWM0_INDEX].channel |= 1 << 2;
287 mcu_pwm_obj[PWM0_INDEX].channel_2_pin = BSP_USING_PWM0_CH2;
288 #endif
289 #ifdef BSP_USING_PWM0_CH3
290 mcu_pwm_obj[PWM0_INDEX].channel |= 1 << 3;
291 mcu_pwm_obj[PWM0_INDEX].channel_3_pin = BSP_USING_PWM0_CH3;
292 #endif
293 #ifdef BSP_USING_PWM1_CH0
294 mcu_pwm_obj[PWM1_INDEX].channel |= 1 << 0;
295 mcu_pwm_obj[PWM1_INDEX].channel_0_pin = BSP_USING_PWM1_CH0;
296 #endif
297 #ifdef BSP_USING_PWM1_CH1
298 mcu_pwm_obj[PWM1_INDEX].channel |= 1 << 1;
299 mcu_pwm_obj[PWM1_INDEX].channel_1_pin = BSP_USING_PWM1_CH1;
300 #endif
301 #ifdef BSP_USING_PWM1_CH2
302 mcu_pwm_obj[PWM1_INDEX].channel |= 1 << 2;
303 mcu_pwm_obj[PWM1_INDEX].channel_2_pin = BSP_USING_PWM1_CH2;
304 #endif
305 #ifdef BSP_USING_PWM1_CH3
306 mcu_pwm_obj[PWM1_INDEX].channel |= 1 << 3;
307 mcu_pwm_obj[PWM1_INDEX].channel_3_pin = BSP_USING_PWM1_CH3;
308 #endif
309 #ifdef BSP_USING_PWM2_CH0
310 mcu_pwm_obj[PWM2_INDEX].channel |= 1 << 0;
311 mcu_pwm_obj[PWM2_INDEX].channel_0_pin = BSP_USING_PWM2_CH0;
312 #endif
313 #ifdef BSP_USING_PWM2_CH1
314 mcu_pwm_obj[PWM2_INDEX].channel |= 1 << 1;
315 mcu_pwm_obj[PWM2_INDEX].channel_1_pin = BSP_USING_PWM2_CH1;
316 #endif
317 #ifdef BSP_USING_PWM2_CH2
318 mcu_pwm_obj[PWM2_INDEX].channel |= 1 << 2;
319 mcu_pwm_obj[PWM2_INDEX].channel_2_pin = BSP_USING_PWM2_CH2;
320 #endif
321 #ifdef BSP_USING_PWM2_CH3
322 mcu_pwm_obj[PWM2_INDEX].channel |= 1 << 3;
323 mcu_pwm_obj[PWM2_INDEX].channel_3_pin = BSP_USING_PWM2_CH3;
324 #endif
325 #ifdef BSP_USING_PWM3_CH0
326 mcu_pwm_obj[PWM3_INDEX].channel |= 1 << 0;
327 mcu_pwm_obj[PWM3_INDEX].channel_0_pin = BSP_USING_PWM3_CH0;
328 #endif
329 #ifdef BSP_USING_PWM3_CH1
330 mcu_pwm_obj[PWM3_INDEX].channel |= 1 << 1;
331 mcu_pwm_obj[PWM3_INDEX].channel_1_pin = BSP_USING_PWM3_CH1;
332 #endif
333 #ifdef BSP_USING_PWM3_CH2
334 mcu_pwm_obj[PWM3_INDEX].channel |= 1 << 2;
335 mcu_pwm_obj[PWM3_INDEX].channel_2_pin = BSP_USING_PWM3_CH2;
336 #endif
337 #ifdef BSP_USING_PWM3_CH3
338 mcu_pwm_obj[PWM3_INDEX].channel |= 1 << 3;
339 mcu_pwm_obj[PWM3_INDEX].channel_3_pin = BSP_USING_PWM3_CH3;
340 #endif
341 }
342
mcu_pwm_init(void)343 static int mcu_pwm_init(void)
344 {
345 int i = 0;
346 int result = RT_EOK;
347
348 pwm_get_channel();
349 for (i = 0; i < sizeof(mcu_pwm_obj) / sizeof(mcu_pwm_obj[0]); i++)
350 {
351 /* pwm init */
352 if (mcu_hw_pwm_init(&mcu_pwm_obj[i]) != RT_EOK)
353 {
354 rt_kprintf("\r\n %s init failed", mcu_pwm_obj[i].name);
355 result = -RT_ERROR;
356 goto __exit;
357 }
358 else
359 {
360 rt_kprintf("\r\n %s init success", mcu_pwm_obj[i].name);
361
362 /* register pwm device */
363 if (rt_device_pwm_register(&mcu_pwm_obj[i].pwm_device, mcu_pwm_obj[i].name, &drv_ops, &mcu_pwm_obj[i].pwm_handle) == RT_EOK)
364 {
365 rt_kprintf("\r\n %s register success", mcu_pwm_obj[i].name);
366 }
367 else
368 {
369 rt_kprintf("\r\n %s register failed", mcu_pwm_obj[i].name);
370 result = -RT_ERROR;
371 }
372 }
373 }
374 __exit:
375 return result;
376 }
377 INIT_DEVICE_EXPORT(mcu_pwm_init);
378
379
380
381 /* test example */
382 #define PWM_DEV_NAME "pwm0" /* PWM name*/
383 #define PWM_DEV_CHANNEL 15 /* PWM channel */
384
385 struct rt_device_pwm *pwm_dev;
386
pwm_led_sample(int argc,char * argv[])387 static int pwm_led_sample(int argc, char *argv[])
388 {
389 rt_uint32_t period, pulse, dir;
390
391 period = 50000; /* 50ms*/
392 dir = 1;
393 pulse = 0;
394
395
396 pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
397 if (pwm_dev == RT_NULL)
398 {
399 rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME);
400 return -RT_ERROR;
401 }
402
403 rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
404 rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
405
406 while (1)
407 {
408 rt_thread_mdelay(50);
409 if (dir)
410 {
411 pulse += 500;
412 }
413 else
414 {
415 pulse -= 500;
416 }
417
418 if (pulse >= period)
419 {
420 dir = 0;
421 }
422
423 if (0 == pulse)
424 {
425 dir = 1;
426 }
427
428
429 rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
430 }
431 }
432 MSH_CMD_EXPORT(pwm_led_sample, pwm sample);
433
434 #endif
435
436