1
0

ch56x_pwm.c 8.6 KB

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