drv_pwm.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. #include <rtconfig.h>
  13. #if defined(BSP_USING_PWM)
  14. #define LOG_TAG "drv.pwm"
  15. #define DBG_ENABLE
  16. #define DBG_SECTION_NAME LOG_TAG
  17. #define DBG_LEVEL DBG_INFO
  18. #define DBG_COLOR
  19. #include <rtdbg.h>
  20. #include <stdint.h>
  21. #include <rtdevice.h>
  22. #include <rthw.h>
  23. #include "NuMicro.h"
  24. enum
  25. {
  26. PWM_START = -1,
  27. #if defined(BSP_USING_PWM0)
  28. PWM0_IDX,
  29. #endif
  30. #if defined(BSP_USING_PWM1)
  31. PWM1_IDX,
  32. #endif
  33. PWM_CNT
  34. };
  35. struct nu_pwm
  36. {
  37. struct rt_device_pwm dev;
  38. char *name;
  39. PWM_T *pwm_base;
  40. rt_int32_t pwm_period_time;
  41. };
  42. typedef struct nu_pwm *nu_pwm_t;
  43. static struct nu_pwm nu_pwm_arr [] =
  44. {
  45. #if defined(BSP_USING_PWM0)
  46. {
  47. .name = "pwm0",
  48. .pwm_base = PWM0,
  49. },
  50. #endif
  51. #if defined(BSP_USING_PWM1)
  52. {
  53. .name = "pwm1",
  54. .pwm_base = PWM1,
  55. },
  56. #endif
  57. {0}
  58. }; /* pwm nu_pwm */
  59. static rt_err_t nu_pwm_control(struct rt_device_pwm *device, int cmd, void *arg);
  60. static struct rt_pwm_ops nu_pwm_ops =
  61. {
  62. .control = nu_pwm_control
  63. };
  64. static rt_err_t nu_pwm_enable(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration, rt_bool_t enable)
  65. {
  66. rt_err_t result = RT_EOK;
  67. PWM_T *pwm_base = ((nu_pwm_t)device)->pwm_base;
  68. rt_uint32_t pwm_channel = ((struct rt_pwm_configuration *)configuration)->channel;
  69. if (enable == RT_TRUE)
  70. {
  71. PWM_EnableOutput(pwm_base, 1 << pwm_channel);
  72. PWM_Start(pwm_base, 1 << pwm_channel);
  73. }
  74. else
  75. {
  76. PWM_DisableOutput(pwm_base, 1 << pwm_channel);
  77. PWM_ForceStop(pwm_base, 1 << pwm_channel);
  78. }
  79. return result;
  80. }
  81. static rt_err_t nu_pwm_set(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
  82. {
  83. if ((((struct rt_pwm_configuration *)configuration)->period) <= 0)
  84. return -(RT_ERROR);
  85. rt_uint8_t pwm_channel_pair;
  86. rt_uint32_t pwm_freq, pwm_dutycycle ;
  87. PWM_T *pwm_base = ((nu_pwm_t)device)->pwm_base;
  88. rt_uint8_t pwm_channel = ((struct rt_pwm_configuration *)configuration)->channel;
  89. rt_uint32_t pwm_period = ((struct rt_pwm_configuration *)configuration)->period;
  90. rt_uint32_t pwm_pulse = ((struct rt_pwm_configuration *)configuration)->pulse;
  91. //rt_uint32_t pre_pwm_prescaler = PWM_GET_PRESCALER(pwm_base, pwm_channel);
  92. if ((pwm_channel % 2) == 0)
  93. pwm_channel_pair = pwm_channel + 1;
  94. else
  95. pwm_channel_pair = pwm_channel - 1;
  96. if (PWM_GET_CNR(pwm_base, pwm_channel_pair!= 0))
  97. {
  98. pwm_period = ((nu_pwm_t)device)->pwm_period_time;
  99. LOG_I("%s output frequency is determined, user can only change the duty\n", ((nu_pwm_t)device)->name);
  100. }
  101. else
  102. {
  103. ((nu_pwm_t)device)->pwm_period_time = pwm_period;
  104. }
  105. pwm_freq = 1000000000 / pwm_period;
  106. pwm_dutycycle = (pwm_pulse * 100) / pwm_period;
  107. PWM_ConfigOutputChannel(pwm_base, pwm_channel, pwm_freq, pwm_dutycycle) ;
  108. return RT_EOK;
  109. }
  110. static rt_uint32_t nu_pwm_clksr(struct rt_device_pwm *device)
  111. {
  112. rt_uint32_t u32Src, u32PWMClockSrc;
  113. PWM_T *pwm_base = ((nu_pwm_t)device)->pwm_base;
  114. if (pwm_base == PWM0)
  115. {
  116. u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_PWM0SEL_Msk;
  117. }
  118. else /* (pwm == PWM1) */
  119. {
  120. u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_PWM1SEL_Msk;
  121. }
  122. if (u32Src == 0U)
  123. {
  124. /* clock source is from PLL clock */
  125. u32PWMClockSrc = CLK_GetPLLClockFreq();
  126. }
  127. else
  128. {
  129. /* clock source is from PCLK */
  130. SystemCoreClockUpdate();
  131. if (pwm_base == PWM0)
  132. {
  133. u32PWMClockSrc = CLK_GetPCLK0Freq();
  134. }
  135. else /* (pwm == PWM1) */
  136. {
  137. u32PWMClockSrc = CLK_GetPCLK1Freq();
  138. }
  139. }
  140. return u32PWMClockSrc;
  141. }
  142. static rt_err_t nu_pwm_get(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
  143. {
  144. rt_uint32_t pwm_real_period, pwm_real_duty, time_tick, u32PWMClockSrc ;
  145. PWM_T *pwm_base = ((nu_pwm_t)device)->pwm_base;
  146. rt_uint32_t pwm_channel = ((struct rt_pwm_configuration *)configuration)->channel;
  147. rt_uint32_t pwm_prescale = PWM_GET_PRESCALER(pwm_base, pwm_channel);
  148. rt_uint32_t pwm_period = PWM_GET_CNR(pwm_base, pwm_channel);
  149. rt_uint32_t pwm_pulse = PWM_GET_CMR(pwm_base, pwm_channel);
  150. u32PWMClockSrc = nu_pwm_clksr(device);
  151. time_tick = 1000000000000 / u32PWMClockSrc;
  152. pwm_real_period = (((pwm_prescale + 1) * (pwm_period + 1)) * time_tick) / 1000;
  153. pwm_real_duty = (((pwm_prescale + 1) * pwm_pulse * time_tick)) / 1000;
  154. ((struct rt_pwm_configuration *)configuration)->period = pwm_real_period;
  155. ((struct rt_pwm_configuration *)configuration)->pulse = pwm_real_duty;
  156. LOG_I("%s %d %d %d\n", ((nu_pwm_t)device)->name, configuration->channel, configuration->period, configuration->pulse);
  157. return RT_EOK;
  158. }
  159. static rt_err_t nu_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
  160. {
  161. struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
  162. RT_ASSERT(device != RT_NULL);
  163. RT_ASSERT(configuration != RT_NULL);
  164. if (((((struct rt_pwm_configuration *)configuration)->channel) + 1) > PWM_CHANNEL_NUM)
  165. return -(RT_ERROR);
  166. switch (cmd)
  167. {
  168. case PWM_CMD_ENABLE:
  169. return nu_pwm_enable(device, configuration, RT_TRUE);
  170. case PWM_CMD_DISABLE:
  171. return nu_pwm_enable(device, configuration, RT_FALSE);
  172. case PWM_CMD_SET:
  173. return nu_pwm_set(device, configuration);
  174. case PWM_CMD_GET:
  175. return nu_pwm_get(device, configuration);
  176. }
  177. return -(RT_EINVAL);
  178. }
  179. int rt_hw_pwm_init(void)
  180. {
  181. rt_err_t ret;
  182. int i;
  183. for (i = (PWM_START + 1); i < PWM_CNT; i++)
  184. {
  185. ret = rt_device_pwm_register(&nu_pwm_arr[i].dev, nu_pwm_arr[i].name, &nu_pwm_ops, RT_NULL);
  186. RT_ASSERT(ret == RT_EOK);
  187. }
  188. return 0;
  189. }
  190. INIT_DEVICE_EXPORT(rt_hw_pwm_init);
  191. #ifdef RT_USING_FINSH
  192. #include <finsh.h>
  193. #ifdef FINSH_USING_MSH
  194. static int xpwm_get(int argc, char **argv)
  195. {
  196. int result = 0;
  197. struct rt_device_pwm *device = RT_NULL;
  198. struct rt_pwm_configuration configuration = {0};
  199. if (argc != 3)
  200. {
  201. rt_kprintf("Usage: pwm_get pwm1 1\n");
  202. result = -RT_ERROR;
  203. goto _exit;
  204. }
  205. device = (struct rt_device_pwm *)rt_device_find(argv[1]);
  206. if (!device)
  207. {
  208. result = -RT_EIO;
  209. goto _exit;
  210. }
  211. configuration.channel = atoi(argv[2]);
  212. result = rt_device_control(&device->parent, PWM_CMD_GET, &configuration);
  213. _exit:
  214. return result;
  215. }
  216. MSH_CMD_EXPORT(xpwm_get, xpwm_get <pwm_dev> <channel>);
  217. #endif /* FINSH_USING_MSH */
  218. #endif /* RT_USING_FINSH */
  219. #endif