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