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