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 * 2020-5-22 YHKuo First version
10 *
11 ******************************************************************************/
12
13 #include <rtconfig.h>
14
15 #if defined(BSP_USING_BPWM)
16
17 #define LOG_TAG "drv.bpwm"
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 #define DEFAULT_DUTY 50
30 #define DEFAULT_FREQ 1000
31
32 enum
33 {
34 BPWM_START = -1,
35 #if defined(BSP_USING_BPWM0)
36 BPWM0_IDX,
37 #endif
38 #if defined(BSP_USING_BPWM1)
39 BPWM1_IDX,
40 #endif
41 BPWM_CNT
42 };
43
44 struct nu_bpwm
45 {
46 struct rt_device_pwm dev;
47 char *name;
48 BPWM_T *bpwm_base;
49 rt_int32_t pwm_period_time;
50 };
51
52 typedef struct nu_bpwm *nu_bpwm_t;
53
54 static struct nu_bpwm nu_bpwm_arr [] =
55 {
56 #if defined(BSP_USING_BPWM0)
57 {
58 .name = "bpwm0",
59 .bpwm_base = BPWM0,
60 },
61 #endif
62
63 #if defined(BSP_USING_BPWM1)
64 {
65 .name = "bpwm1",
66 .bpwm_base = BPWM1,
67 },
68 #endif
69 {0}
70 }; /* bpwm nu_epwm */
71
72 static rt_err_t nu_bpwm_control(struct rt_device_pwm *device, int cmd, void *arg);
73
74 static struct rt_pwm_ops nu_bpwm_ops =
75 {
76 .control = nu_bpwm_control
77 };
78
nu_bpwm_enable(struct rt_device_pwm * device,struct rt_pwm_configuration * configuration,rt_bool_t enable)79 static rt_err_t nu_bpwm_enable(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration, rt_bool_t enable)
80 {
81 rt_err_t result = RT_EOK;
82
83 BPWM_T *pwm_base = ((nu_bpwm_t)device)->bpwm_base;
84 rt_uint32_t pwm_channel = configuration->channel;
85
86 if (enable == RT_TRUE)
87 {
88 BPWM_EnableOutput(pwm_base, 1 << pwm_channel);
89 BPWM_Start(pwm_base, 1 << pwm_channel);
90 }
91 else if (enable == RT_FALSE)
92 {
93 BPWM_DisableOutput(pwm_base, 1 << pwm_channel);
94 BPWM_ForceStop(pwm_base, 1 << pwm_channel);
95 }
96
97 return result;
98 }
99
nu_bpwm_set(struct rt_device_pwm * device,struct rt_pwm_configuration * configuration)100 static rt_err_t nu_bpwm_set(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
101 {
102 if ((configuration->period) <= 0)
103 return -(RT_ERROR);
104 rt_uint32_t pwm_freq, pwm_dutycycle;
105 BPWM_T *pwm_base = ((nu_bpwm_t)device)->bpwm_base;
106 rt_uint8_t pwm_channel = configuration->channel;
107 rt_uint32_t pwm_period = configuration->period;
108 rt_uint32_t pwm_pulse = configuration->pulse;
109
110 pwm_dutycycle = (pwm_pulse * 100) / pwm_period;
111
112 if (BPWM_GET_CNR(pwm_base, pwm_channel) != 0)
113 {
114 pwm_period = ((nu_bpwm_t)device)->pwm_period_time;
115 LOG_I("%s output frequency is determined, user can only change the duty\n", ((nu_bpwm_t)device)->name);
116 }
117 else
118 {
119 ((nu_bpwm_t)device)->pwm_period_time = pwm_period;
120 }
121
122 pwm_freq = 1000000000 / pwm_period;
123
124
125 BPWM_ConfigOutputChannel(pwm_base, pwm_channel, pwm_freq, pwm_dutycycle) ;
126
127 return RT_EOK;
128 }
129
nu_bpwm_clksr(struct rt_device_pwm * device)130 static rt_uint32_t nu_bpwm_clksr(struct rt_device_pwm *device)
131 {
132 rt_uint32_t u32Src, u32BPWMClockSrc;
133 BPWM_T *pwm_base = ((nu_bpwm_t)device)->bpwm_base;
134 if (pwm_base == BPWM0)
135 {
136 u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_BPWM0SEL_Msk;
137 }
138 else /* (bpwm == BPWM1) */
139 {
140 u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_BPWM1SEL_Msk;
141 }
142
143 if (u32Src == 0U)
144 {
145 /* clock source is from PLL clock */
146 u32BPWMClockSrc = CLK_GetPLLClockFreq();
147 }
148 else
149 {
150 /* clock source is from PCLK */
151 SystemCoreClockUpdate();
152 if (pwm_base == BPWM0)
153 {
154 u32BPWMClockSrc = CLK_GetPCLK0Freq();
155 }
156 else /* (bpwm == BPWM1) */
157 {
158 u32BPWMClockSrc = CLK_GetPCLK1Freq();
159 }
160 }
161 return u32BPWMClockSrc;
162 }
163
nu_bpwm_get(struct rt_device_pwm * device,struct rt_pwm_configuration * configuration)164 static rt_err_t nu_bpwm_get(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
165 {
166 rt_uint32_t pwm_real_period, pwm_real_duty, time_tick, u32BPWMClockSrc ;
167
168 BPWM_T *pwm_base = ((nu_bpwm_t)device)->bpwm_base;
169 rt_uint32_t pwm_channel = configuration->channel;
170 rt_uint32_t pwm_prescale = pwm_base->CLKPSC;
171 rt_uint32_t pwm_period = BPWM_GET_CNR(pwm_base, pwm_channel);
172 rt_uint32_t pwm_pulse = BPWM_GET_CMR(pwm_base, pwm_channel);
173
174 u32BPWMClockSrc = nu_bpwm_clksr(device);
175 time_tick = 1000000000000 / u32BPWMClockSrc;
176
177 pwm_real_period = (((pwm_prescale + 1) * (pwm_period + 1)) * time_tick) / 1000;
178 pwm_real_duty = (((pwm_prescale + 1) * pwm_pulse * time_tick)) / 1000;
179 configuration->period = pwm_real_period;
180 configuration->pulse = pwm_real_duty;
181
182 LOG_I("%s %d %d %d\n", ((nu_bpwm_t)device)->name, configuration->channel, configuration->period, configuration->pulse);
183
184 return RT_EOK;
185 }
186
nu_bpwm_control(struct rt_device_pwm * device,int cmd,void * arg)187 static rt_err_t nu_bpwm_control(struct rt_device_pwm *device, int cmd, void *arg)
188 {
189 struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
190
191 RT_ASSERT(device != RT_NULL);
192 RT_ASSERT(configuration != RT_NULL);
193
194 if (((configuration->channel) + 1) > BPWM_CHANNEL_NUM)
195 return -(RT_ERROR);
196
197 switch (cmd)
198 {
199 case PWM_CMD_ENABLE:
200 return nu_bpwm_enable(device, configuration, RT_TRUE);
201 case PWM_CMD_DISABLE:
202 return nu_bpwm_enable(device, configuration, RT_FALSE);
203 case PWM_CMD_SET:
204 return nu_bpwm_set(device, configuration);
205 case PWM_CMD_GET:
206 return nu_bpwm_get(device, configuration);
207 default:
208 return -RT_EINVAL;
209 }
210 }
211
rt_hw_bpwm_init(void)212 int rt_hw_bpwm_init(void)
213 {
214 rt_err_t ret;
215 rt_uint8_t i;
216
217 for (i = (BPWM_START + 1); i < BPWM_CNT; i++)
218 {
219 ret = rt_device_pwm_register(&nu_bpwm_arr[i].dev, nu_bpwm_arr[i].name, &nu_bpwm_ops, RT_NULL);
220 RT_ASSERT(ret == RT_EOK);
221 }
222
223 return 0;
224 }
225
226 INIT_DEVICE_EXPORT(rt_hw_bpwm_init);
227
228 #endif
229