1 /**************************************************************************//**
2 *
3 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Change Logs:
8 * Date Author Notes
9 * 2021-02-04 klcheng First version
10 *
11 ******************************************************************************/
12
13 #include <rtconfig.h>
14
15 #if defined(BSP_USING_PWM)
16
17 #define LOG_TAG "drv.pwm"
18 #define DBG_ENABLE
19 #define DBG_SECTION_NAME LOG_TAG
20 #define DBG_LEVEL DBG_INFO
21 #define DBG_COLOR
22 #include <rtdbg.h>
23
24 #include <stdint.h>
25 #include <rtdevice.h>
26 #include <rthw.h>
27 #include "NuMicro.h"
28
29 enum
30 {
31 PWM_START = -1,
32 #if defined(BSP_USING_PWM0)
33 PWM0_IDX,
34 #endif
35 #if defined(BSP_USING_PWM1)
36 PWM1_IDX,
37 #endif
38 PWM_CNT
39 };
40
41 struct nu_pwm
42 {
43 struct rt_device_pwm dev;
44 char *name;
45 PWM_T *pwm_base;
46 rt_int32_t pwm_period_time;
47 };
48
49 typedef struct nu_pwm *nu_pwm_t;
50
51 static struct nu_pwm nu_pwm_arr [] =
52 {
53 #if defined(BSP_USING_PWM0)
54 {
55 .name = "pwm0",
56 .pwm_base = PWM0,
57 },
58 #endif
59
60 #if defined(BSP_USING_PWM1)
61 {
62 .name = "pwm1",
63 .pwm_base = PWM1,
64 },
65 #endif
66 {0}
67 }; /* pwm nu_pwm */
68
69 static rt_err_t nu_pwm_control(struct rt_device_pwm *device, int cmd, void *arg);
70
71 static struct rt_pwm_ops nu_pwm_ops =
72 {
73 .control = nu_pwm_control
74 };
75
nu_pwm_enable(struct rt_device_pwm * device,struct rt_pwm_configuration * configuration,rt_bool_t enable)76 static rt_err_t nu_pwm_enable(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration, rt_bool_t enable)
77 {
78 rt_err_t result = RT_EOK;
79
80 PWM_T *pwm_base = ((nu_pwm_t)device)->pwm_base;
81 rt_uint32_t pwm_channel = ((struct rt_pwm_configuration *)configuration)->channel;
82
83 if (enable == RT_TRUE)
84 {
85 PWM_EnableOutput(pwm_base, 1 << pwm_channel);
86 PWM_Start(pwm_base, 1 << pwm_channel);
87 }
88 else
89 {
90 PWM_DisableOutput(pwm_base, 1 << pwm_channel);
91 PWM_ForceStop(pwm_base, 1 << pwm_channel);
92 }
93
94 return result;
95 }
96
nu_pwm_set(struct rt_device_pwm * device,struct rt_pwm_configuration * configuration)97 static rt_err_t nu_pwm_set(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
98 {
99 if ((((struct rt_pwm_configuration *)configuration)->period) <= 0)
100 return -(RT_ERROR);
101 rt_uint8_t pwm_channel_pair;
102 rt_uint32_t pwm_freq, pwm_dutycycle ;
103 PWM_T *pwm_base = ((nu_pwm_t)device)->pwm_base;
104 rt_uint8_t pwm_channel = ((struct rt_pwm_configuration *)configuration)->channel;
105 rt_uint32_t pwm_period = ((struct rt_pwm_configuration *)configuration)->period;
106 rt_uint32_t pwm_pulse = ((struct rt_pwm_configuration *)configuration)->pulse;
107
108 //rt_uint32_t pre_pwm_prescaler = PWM_GET_PRESCALER(pwm_base, pwm_channel);
109
110 if ((pwm_channel % 2) == 0)
111 pwm_channel_pair = pwm_channel + 1;
112 else
113 pwm_channel_pair = pwm_channel - 1;
114
115 if (PWM_GET_CNR(pwm_base, pwm_channel_pair!= 0))
116 {
117 pwm_period = ((nu_pwm_t)device)->pwm_period_time;
118 LOG_I("%s output frequency is determined, user can only change the duty\n", ((nu_pwm_t)device)->name);
119 }
120 else
121 {
122 ((nu_pwm_t)device)->pwm_period_time = pwm_period;
123 }
124
125 pwm_freq = 1000000000 / pwm_period;
126 pwm_dutycycle = (pwm_pulse * 100) / pwm_period;
127 PWM_ConfigOutputChannel(pwm_base, pwm_channel, pwm_freq, pwm_dutycycle) ;
128
129 return RT_EOK;
130 }
131
nu_pwm_clksr(struct rt_device_pwm * device)132 static rt_uint32_t nu_pwm_clksr(struct rt_device_pwm *device)
133 {
134 rt_uint32_t u32Src, u32PWMClockSrc;
135 PWM_T *pwm_base = ((nu_pwm_t)device)->pwm_base;
136 if (pwm_base == PWM0)
137 {
138 u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_PWM0SEL_Msk;
139 }
140 else /* (pwm == PWM1) */
141 {
142 u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_PWM1SEL_Msk;
143 }
144
145 if (u32Src == 0U)
146 {
147 /* clock source is from PLL clock */
148 u32PWMClockSrc = CLK_GetPLLClockFreq();
149 }
150 else
151 {
152 /* clock source is from PCLK */
153 SystemCoreClockUpdate();
154 if (pwm_base == PWM0)
155 {
156 u32PWMClockSrc = CLK_GetPCLK0Freq();
157 }
158 else /* (pwm == PWM1) */
159 {
160 u32PWMClockSrc = CLK_GetPCLK1Freq();
161 }
162 }
163 return u32PWMClockSrc;
164 }
165
nu_pwm_get(struct rt_device_pwm * device,struct rt_pwm_configuration * configuration)166 static rt_err_t nu_pwm_get(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
167 {
168 rt_uint32_t pwm_real_period, pwm_real_duty, time_tick, u32PWMClockSrc ;
169
170 PWM_T *pwm_base = ((nu_pwm_t)device)->pwm_base;
171 rt_uint32_t pwm_channel = ((struct rt_pwm_configuration *)configuration)->channel;
172 rt_uint32_t pwm_prescale = PWM_GET_PRESCALER(pwm_base, pwm_channel);
173 rt_uint32_t pwm_period = PWM_GET_CNR(pwm_base, pwm_channel);
174 rt_uint32_t pwm_pulse = PWM_GET_CMR(pwm_base, pwm_channel);
175
176 u32PWMClockSrc = nu_pwm_clksr(device);
177 time_tick = 1000000000000 / u32PWMClockSrc;
178
179 pwm_real_period = (((pwm_prescale + 1) * (pwm_period + 1)) * time_tick) / 1000;
180 pwm_real_duty = (((pwm_prescale + 1) * pwm_pulse * time_tick)) / 1000;
181 ((struct rt_pwm_configuration *)configuration)->period = pwm_real_period;
182 ((struct rt_pwm_configuration *)configuration)->pulse = pwm_real_duty;
183
184 LOG_I("%s %d %d %d\n", ((nu_pwm_t)device)->name, configuration->channel, configuration->period, configuration->pulse);
185
186 return RT_EOK;
187 }
188
nu_pwm_control(struct rt_device_pwm * device,int cmd,void * arg)189 static rt_err_t nu_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
190 {
191 struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
192
193 RT_ASSERT(device != RT_NULL);
194 RT_ASSERT(configuration != RT_NULL);
195
196 if (((((struct rt_pwm_configuration *)configuration)->channel) + 1) > PWM_CHANNEL_NUM)
197 return -(RT_ERROR);
198
199 switch (cmd)
200 {
201 case PWM_CMD_ENABLE:
202 return nu_pwm_enable(device, configuration, RT_TRUE);
203 case PWM_CMD_DISABLE:
204 return nu_pwm_enable(device, configuration, RT_FALSE);
205 case PWM_CMD_SET:
206 return nu_pwm_set(device, configuration);
207 case PWM_CMD_GET:
208 return nu_pwm_get(device, configuration);
209 }
210 return -(RT_EINVAL);
211 }
212
rt_hw_pwm_init(void)213 int rt_hw_pwm_init(void)
214 {
215 rt_err_t ret;
216 int i;
217
218 for (i = (PWM_START + 1); i < PWM_CNT; i++)
219 {
220 ret = rt_device_pwm_register(&nu_pwm_arr[i].dev, nu_pwm_arr[i].name, &nu_pwm_ops, RT_NULL);
221 RT_ASSERT(ret == RT_EOK);
222 }
223
224 return 0;
225 }
226
227 INIT_DEVICE_EXPORT(rt_hw_pwm_init);
228
229 #ifdef RT_USING_FINSH
230 #include <finsh.h>
231
232 #ifdef FINSH_USING_MSH
233
xpwm_get(int argc,char ** argv)234 static int xpwm_get(int argc, char **argv)
235 {
236 int result = 0;
237 struct rt_device_pwm *device = RT_NULL;
238 struct rt_pwm_configuration configuration = {0};
239
240 if (argc != 3)
241 {
242 rt_kprintf("Usage: pwm_get pwm1 1\n");
243 result = -RT_ERROR;
244 goto _exit;
245 }
246
247 device = (struct rt_device_pwm *)rt_device_find(argv[1]);
248 if (!device)
249 {
250 result = -RT_EIO;
251 goto _exit;
252 }
253
254 configuration.channel = atoi(argv[2]);
255 result = rt_device_control(&device->parent, PWM_CMD_GET, &configuration);
256
257 _exit:
258 return result;
259 }
260
261 MSH_CMD_EXPORT(xpwm_get, xpwm_get <pwm_dev> <channel>);
262
263 #endif /* FINSH_USING_MSH */
264 #endif /* RT_USING_FINSH */
265
266 #endif
267