Skip to content

Commit 77067f8

Browse files
authored
ch569w-evt : add pwm driver, and spi_xfer bug fix (#6240)
add PWM driver, output checked with logic analyzer spi_xfer() bug fix for cs_pin and message looping uart pin_mode init moved to uart driver
1 parent 8cd7ee2 commit 77067f8

File tree

16 files changed

+910
-377
lines changed

16 files changed

+910
-377
lines changed

bsp/wch/risc-v/Libraries/ch56x_drivers/SConscript

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ if GetDepend('SOC_SERIES_CH569'):
1212
if GetDepend('RT_USING_WDT'):
1313
src += ['ch56x_wdt.c']
1414

15+
if GetDepend('RT_USING_PWM'):
16+
src += ['ch56x_pwm.c']
17+
1518
if GetDepend('RT_USING_HWTIMER'):
1619
src += ['ch56x_timer.c']
1720

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
/*
2+
* Copyright (c) 2006-2022, RT-Thread Development Team
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Change Logs:
7+
* Date Author Notes
8+
* 2022-08-04 Emuzit first version
9+
*/
10+
#include <rthw.h>
11+
#include <rtdebug.h>
12+
#include <drivers/rt_drv_pwm.h>
13+
#include <drivers/pin.h>
14+
#include "ch56x_pwm.h"
15+
#include "ch56x_sys.h"
16+
17+
#define PWM_CYCLE_MAX 255 // must be 255 for 0%~100% duty cycle
18+
19+
struct pwm_device
20+
{
21+
struct rt_device_pwm parent;
22+
volatile struct pwm_registers *reg_base;
23+
uint32_t period;
24+
};
25+
static struct pwm_device pwmx_device;
26+
27+
static const uint8_t pwmx_pin[] = {PWM0_PIN, PWM1_PIN, PWM2_PIN, PWM3_PIN};
28+
29+
/**
30+
* @brief Enable or disable PWM channel output.
31+
* Make sure PWM clock is ON for writing registers.
32+
*
33+
* @param device is pointer to the rt_device_pwm device.
34+
*
35+
* @param channel is the PWM channel (0~3) to operate on.
36+
*
37+
* @param enable is to enable PWM when RT_TRUE, or disable when RT_FALSE.
38+
*
39+
* @return None.
40+
*/
41+
static void pwm_channel_enable(struct rt_device_pwm *device,
42+
uint32_t channel, rt_bool_t enable)
43+
{
44+
struct pwm_device *pwm_device = (struct pwm_device *)device;
45+
volatile struct pwm_registers *pxreg = pwm_device->reg_base;
46+
47+
uint8_t ctrl_mod, polar;
48+
49+
if (enable)
50+
{
51+
/* set pwm_out_en to allow pwm output */
52+
ctrl_mod = pxreg->CTRL_MOD.reg;
53+
pxreg->CTRL_MOD.reg = ctrl_mod | (RB_PWM0_OUT_EN << channel);
54+
}
55+
else
56+
{
57+
/* ch56x has no disable bit, set pin out to quiesce */
58+
ctrl_mod = pxreg->CTRL_MOD.reg;
59+
polar = ctrl_mod & (RB_PWM0_POLAR << channel);
60+
rt_pin_write(pwmx_pin[channel], polar ? PIN_HIGH : PIN_LOW);
61+
ctrl_mod &= ~(RB_PWM0_OUT_EN << channel);
62+
pxreg->CTRL_MOD.reg = ctrl_mod;
63+
}
64+
}
65+
66+
/**
67+
* @brief Set period of the PWM channel.
68+
* Make sure PWM clock is ON for writing registers.
69+
*
70+
* @param device is pointer to the rt_device_pwm device.
71+
*
72+
* @param channel is the PWM channel (0~3) to operate on.
73+
*
74+
* @param period is PWM period in nanoseconds.
75+
*
76+
* @return RT_EOK if successful.
77+
*/
78+
static rt_err_t pwm_channel_period(struct rt_device_pwm *device,
79+
uint32_t channel, uint32_t period)
80+
{
81+
struct pwm_device *pwm_device = (struct pwm_device *)device;
82+
83+
uint32_t clock_div;
84+
85+
/* All ch56x PWMX channels share the same period, channel ignored.
86+
*
87+
* Max allowed period is when Fsys@2MHz and CLOCK_DIV is 0 (256) :
88+
* (1 / 2MHz) * 256 * PWM_CYCLE_MAX => 32640000 ns
89+
* Note that `period * F_MHz` won't overflow in calculation below.
90+
*/
91+
if (period > (256 * PWM_CYCLE_MAX * 1000 / 2))
92+
return -RT_EINVAL;
93+
94+
if (period != pwm_device->period)
95+
{
96+
uint32_t Fsys = sys_hclk_get();
97+
uint32_t F_MHz = Fsys / 1000000;
98+
uint32_t F_mod = Fsys % 1000000;
99+
100+
/* period = (clock_div / Fsys) * 10^9 * PWM_CYCLE_MAX */
101+
clock_div = period * F_MHz + (1000 * PWM_CYCLE_MAX / 2);
102+
/* Fsys is mostly in integer MHz, likely to be skipped */
103+
if (F_mod != 0)
104+
{
105+
uint64_t u64v = ((uint64_t)period * F_mod) / 1000000;
106+
clock_div += (uint32_t)u64v;
107+
}
108+
clock_div = clock_div / (1000 * PWM_CYCLE_MAX);
109+
if (clock_div > 256)
110+
return -RT_EINVAL;
111+
/* CLOCK_DIV will be 0 if `clock_div` is 256 */
112+
pwm_device->reg_base->CLOCK_DIV = (uint8_t)clock_div;
113+
/* cycle_sel set to PWM_CYCLE_SEL_255 for 0%~100% duty cycle */
114+
pwmx_device.reg_base->CTRL_CFG.cycle_sel = PWM_CYCLE_SEL_255;
115+
pwm_device->period = period;
116+
}
117+
118+
return RT_EOK;
119+
}
120+
121+
/**
122+
* @brief Set pulse duration of the PWM channel.
123+
* Make sure PWM clock is ON for writing registers.
124+
*
125+
* @param device is pointer to the rt_device_pwm device.
126+
*
127+
* @param channel is the PWM channel (0~3) to operate on.
128+
*
129+
* @param pulse is PWM pulse duration in nanoseconds.
130+
*
131+
* @return RT_EOK if successful.
132+
*/
133+
static rt_err_t pwm_channel_pulse(struct rt_device_pwm *device,
134+
uint32_t channel, uint32_t pulse)
135+
{
136+
struct pwm_device *pwm_device = (struct pwm_device *)device;
137+
138+
uint32_t pdata, period;
139+
140+
/* duty cycle is calculated with "raw" period setting */
141+
period = pwm_device->period;
142+
if (!period || pulse > period)
143+
return -RT_EINVAL;
144+
145+
pdata = (pulse * PWM_CYCLE_MAX + (period >> 1)) / period;
146+
pwm_device->reg_base->PWM_DATA[channel] = pdata;
147+
148+
return RT_EOK;
149+
}
150+
151+
/**
152+
* @brief Set period & pulse of the PWM channel, remain disabled.
153+
* Make sure PWM clock is ON for writing registers.
154+
*
155+
* @param device is pointer to the rt_device_pwm device.
156+
*
157+
* @param configuration is the channel/period/pulse specification.
158+
* ch56x PWM has no complementary pin, complementary ignored.
159+
* FIXME: can we specify PWM output polarity somehow ?
160+
*
161+
* @return RT_EOK if successful.
162+
*/
163+
static rt_err_t pwm_device_set(struct rt_device_pwm *device,
164+
struct rt_pwm_configuration *configuration)
165+
{
166+
struct pwm_device *pwm_device = (struct pwm_device *)device;
167+
168+
uint32_t channel = configuration->channel;
169+
170+
rt_err_t res;
171+
172+
res = pwm_channel_period(device, channel, configuration->period);
173+
if (res == RT_EOK)
174+
{
175+
res = pwm_channel_pulse(device, channel, configuration->pulse);
176+
if (res == RT_EOK)
177+
{
178+
rt_pin_mode(pwmx_pin[channel], PIN_MODE_OUTPUT);
179+
/* seems to be kept disabled according to sample code */
180+
pwm_channel_enable(device, channel, RT_FALSE);
181+
}
182+
}
183+
184+
return res;
185+
}
186+
187+
/**
188+
* @brief Get period & pulse of the PWM channel.
189+
* The returned information is calculated with h/w setting.
190+
*
191+
* @param device is pointer to the rt_device_pwm device.
192+
*
193+
* @param configuration->channel specify the PWM channel (0~3).
194+
* configuration->period & pulse return the calculated result.
195+
*
196+
* @return RT_EOK if successful.
197+
*/
198+
static rt_err_t pwm_device_get(struct rt_device_pwm *device,
199+
struct rt_pwm_configuration *configuration)
200+
{
201+
struct pwm_device *pwm_device = (struct pwm_device *)device;
202+
volatile struct pwm_registers *pxreg = pwm_device->reg_base;
203+
204+
uint32_t channel = configuration->channel;
205+
206+
uint32_t Fsys = sys_hclk_get();
207+
208+
uint32_t clock_div;
209+
uint32_t pdata;
210+
uint64_t u64v;
211+
212+
/* clock_div is actually 256 when CLOCK_DIV is 0 */
213+
clock_div = pxreg->CLOCK_DIV;
214+
if (clock_div == 0)
215+
clock_div = 256;
216+
217+
u64v = clock_div;
218+
u64v = (u64v * 1000*1000*1000 * PWM_CYCLE_MAX + (Fsys >> 1)) / Fsys;
219+
configuration->period = (uint32_t)u64v;
220+
221+
/* `pdata` <= PWM_CYCLE_MAX, calculated pulse won't exceed period */
222+
pdata = pxreg->PWM_DATA[channel];
223+
u64v = clock_div;
224+
u64v = (u64v * 1000*1000*1000 * pdata + (Fsys >> 1)) / Fsys;
225+
configuration->pulse = (uint32_t)u64v;
226+
227+
return RT_EOK;
228+
}
229+
230+
static rt_err_t pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
231+
{
232+
struct pwm_device *pwm_device = (struct pwm_device *)device;
233+
234+
struct rt_pwm_configuration *configuration = arg;
235+
uint32_t channel = configuration->channel;
236+
237+
rt_err_t res = RT_EOK;
238+
239+
RT_ASSERT(device != RT_NULL);
240+
241+
if (channel >= PWM_CHANNELS)
242+
return -RT_EINVAL;
243+
244+
/* PWM clock needs to be ON to write PWM registers */
245+
sys_slp_clk_off0(RB_SLP_CLK_PWMX, SYS_SLP_CLK_ON);
246+
247+
switch (cmd)
248+
{
249+
case PWM_CMD_ENABLE:
250+
pwm_channel_enable(device, channel, RT_TRUE);
251+
break;
252+
case PWM_CMD_DISABLE:
253+
pwm_channel_enable(device, channel, RT_FALSE);
254+
break;
255+
case PWM_CMD_SET:
256+
return pwm_device_set(device, configuration);
257+
case PWM_CMD_GET:
258+
return pwm_device_get(device, configuration);
259+
case PWM_CMD_SET_PERIOD:
260+
return pwm_channel_period(device, channel, configuration->period);
261+
case PWM_CMD_SET_PULSE:
262+
return pwm_channel_pulse(device, channel, configuration->pulse);
263+
default:
264+
res = -RT_EINVAL;
265+
}
266+
267+
/* disable PWMX clocking, if all channels are disabled */
268+
if ((pwm_device->reg_base->CTRL_MOD.reg & PWM_OUT_EN_MASK) == 0)
269+
sys_slp_clk_off0(RB_SLP_CLK_PWMX, SYS_SLP_CLK_OFF);
270+
271+
return res;
272+
}
273+
274+
static struct rt_pwm_ops pwm_ops =
275+
{
276+
.control = pwm_control
277+
};
278+
279+
static int rt_hw_pwm_init(void)
280+
{
281+
/* init pwmx_device with code to save some flash space */
282+
pwmx_device.reg_base = (struct pwm_registers *)PWMX_REG_BASE;
283+
/* Note: PWM clock OFF here => PWM registers not writable */
284+
285+
return rt_device_pwm_register(
286+
&pwmx_device.parent, PWM_DEVICE_NAME, &pwm_ops, RT_NULL);
287+
}
288+
INIT_DEVICE_EXPORT(rt_hw_pwm_init);

0 commit comments

Comments
 (0)