drv_pwm.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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. * 2019-04-28 tyustli first version
  9. * 2019-07-15 Magicoe The first version for LPC55S6x, we can also use SCT as PWM
  10. * 2023-02-28 Z8MAN8 Update docking to the RT-Thread device frame
  11. */
  12. #include <rtthread.h>
  13. #ifdef RT_USING_PWM
  14. #if !defined(BSP_USING_CTIMER1_MAT0) && !defined(BSP_USING_CTIMER1_MAT1) && \
  15. !defined(BSP_USING_CTIMER1_MAT2)
  16. #error "Please define at least one BSP_USING_CTIMERx_MATx"
  17. #else
  18. #define BSP_USING_CTIMER1
  19. #endif
  20. #if !defined(BSP_USING_CTIMER2_MAT0) && !defined(BSP_USING_CTIMER2_MAT1) && \
  21. !defined(BSP_USING_CTIMER2_MAT2)
  22. #error "Please define at least one BSP_USING_CTIMERx_MATx"
  23. #else
  24. #define BSP_USING_CTIMER2
  25. #endif
  26. #define LOG_TAG "drv.pwm"
  27. #include <drv_log.h>
  28. #include <rtdevice.h>
  29. #include "fsl_ctimer.h"
  30. #include "drv_pwm.h"
  31. #define DEFAULT_DUTY 50
  32. #define DEFAULT_FREQ 1000
  33. enum
  34. {
  35. #ifdef BSP_USING_CTIMER1
  36. PWM1_INDEX,
  37. #endif
  38. #ifdef BSP_USING_CTIMER2
  39. PWM2_INDEX,
  40. #endif
  41. };
  42. struct lpc_pwm
  43. {
  44. struct rt_device_pwm pwm_device;
  45. CTIMER_Type * tim;
  46. uint32_t channel;
  47. char *name;
  48. };
  49. static struct lpc_pwm lpc_pwm_obj[] =
  50. {
  51. #if defined(BSP_USING_CTIMER1_MAT0) || defined(BSP_USING_CTIMER1_MAT1) || \
  52. defined(BSP_USING_CTIMER1_MAT2)
  53. {
  54. .tim = CTIMER1,
  55. .name = "pwm1",
  56. .channel = RT_NULL
  57. },
  58. #endif
  59. #if defined(BSP_USING_CTIMER2_MAT0) || defined(BSP_USING_CTIMER2_MAT1) || \
  60. defined(BSP_USING_CTIMER2_MAT2)
  61. {
  62. .tim = CTIMER2,
  63. .name = "pwm2",
  64. .channel = RT_NULL
  65. },
  66. #endif
  67. };
  68. static void pwm_get_channel(void)
  69. {
  70. #ifdef BSP_USING_CTIMER1_MAT0
  71. lpc_pwm_obj[PWM1_INDEX].channel |= 1 << 0;
  72. #endif
  73. #ifdef BSP_USING_CTIMER1_MAT1
  74. lpc_pwm_obj[PWM1_INDEX].channel |= 1 << 1;
  75. #endif
  76. #ifdef BSP_USING_CTIMER1_MAT2
  77. lpc_pwm_obj[PWM1_INDEX].channel |= 1 << 2;
  78. #endif
  79. #ifdef BSP_USING_CTIMER2_MAT0
  80. lpc_pwm_obj[PWM2_INDEX].channel |= 1 << 0;
  81. #endif
  82. #ifdef BSP_USING_CTIMER2_MAT1
  83. lpc_pwm_obj[PWM2_INDEX].channel |= 1 << 1;
  84. #endif
  85. #ifdef BSP_USING_CTIMER2_MAT2
  86. lpc_pwm_obj[PWM2_INDEX].channel |= 1 << 2;
  87. #endif
  88. }
  89. static rt_err_t lpc_drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg);
  90. static struct rt_pwm_ops lpc_drv_ops =
  91. {
  92. .control = lpc_drv_pwm_control
  93. };
  94. static rt_err_t lpc_drv_pwm_enable(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration, rt_bool_t enable)
  95. {
  96. CTIMER_Type *base;
  97. base = (CTIMER_Type *)device->parent.user_data;
  98. if (!enable)
  99. {
  100. /* Stop the timer */
  101. CTIMER_StopTimer(base);
  102. }
  103. else
  104. {
  105. /* Start the timer */
  106. CTIMER_StartTimer(base);
  107. }
  108. return RT_EOK;
  109. }
  110. static rt_err_t lpc_drv_pwm_get(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
  111. {
  112. uint8_t get_duty;
  113. uint32_t get_frequence;
  114. uint32_t pwmClock = 0;
  115. CTIMER_Type *base;
  116. base = (CTIMER_Type *)device->parent.user_data;
  117. /* get frequence */
  118. if (base == CTIMER1)
  119. {
  120. pwmClock = CLOCK_GetCTimerClkFreq(1U) ;
  121. }
  122. else if(base == CTIMER2)
  123. {
  124. pwmClock = CLOCK_GetCTimerClkFreq(2U) ;
  125. }
  126. get_frequence = pwmClock / (base->MR[kCTIMER_Match_3] + 1);
  127. if(configuration->channel == 0)
  128. {
  129. /* get dutycycle */
  130. get_duty = (100*(base->MR[kCTIMER_Match_3] + 1 - base->MR[kCTIMER_Match_0]))/(base->MR[kCTIMER_Match_3] + 1);
  131. }
  132. else if(configuration->channel == 1)
  133. {
  134. /* get dutycycle */
  135. get_duty = (100*(base->MR[kCTIMER_Match_3] + 1 - base->MR[kCTIMER_Match_1]))/(base->MR[kCTIMER_Match_3] + 1);
  136. }
  137. else if(configuration->channel == 2)
  138. {
  139. /* get dutycycle */
  140. get_duty = (100*(base->MR[kCTIMER_Match_3] + 1 - base->MR[kCTIMER_Match_2]))/(base->MR[kCTIMER_Match_3] + 1);
  141. }
  142. /* get dutycycle */
  143. /* conversion */
  144. configuration->period = 1000000000 / get_frequence;
  145. configuration->pulse = get_duty * configuration->period / 100;
  146. rt_kprintf("*** PWM period %d, pulse %d\r\n", configuration->period, configuration->pulse);
  147. return RT_EOK;
  148. }
  149. static rt_err_t lpc_drv_pwm_set(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
  150. {
  151. RT_ASSERT(configuration->period > 0);
  152. RT_ASSERT(configuration->pulse <= configuration->period);
  153. ctimer_config_t config;
  154. CTIMER_Type *base;
  155. base = (CTIMER_Type *)device->parent.user_data;
  156. uint32_t pwmPeriod, pulsePeriod;
  157. /* Run as a timer */
  158. config.mode = kCTIMER_TimerMode;
  159. /* This field is ignored when mode is timer */
  160. config.input = kCTIMER_Capture_0;
  161. /* Timer counter is incremented on every APB bus clock */
  162. config.prescale = 0;
  163. /* Get the PWM period match value and pulse width match value of DEFAULT_FREQ PWM signal with DEFAULT_DUTY dutycycle */
  164. /* Calculate PWM period match value */
  165. double tmp = configuration->period;
  166. /* Target frequence. */
  167. tmp = 1000000000/tmp;
  168. if (base == CTIMER1)
  169. {
  170. pwmPeriod = (( CLOCK_GetCTimerClkFreq(1U) / (config.prescale + 1) ) / (uint32_t)tmp) - 1;
  171. }
  172. else if (base == CTIMER2)
  173. {
  174. pwmPeriod = (( CLOCK_GetCTimerClkFreq(2U) / (config.prescale + 1) ) / (uint32_t)tmp) - 1;
  175. }
  176. /* Calculate pulse width match value */
  177. tmp = configuration->pulse;
  178. pulsePeriod = (1.0 - tmp / configuration->period) * pwmPeriod;
  179. /* Match on channel 3 will define the PWM period */
  180. base->MR[kCTIMER_Match_3] = pwmPeriod;
  181. /* This will define the PWM pulse period */
  182. if(configuration->channel == 0)
  183. {
  184. base->MR[kCTIMER_Match_0] = pulsePeriod;
  185. }
  186. else if(configuration->channel == 1)
  187. {
  188. base->MR[kCTIMER_Match_1] = pulsePeriod;
  189. }
  190. else if(configuration->channel == 2)
  191. {
  192. base->MR[kCTIMER_Match_2] = pulsePeriod;
  193. }
  194. return RT_EOK;
  195. }
  196. static rt_err_t lpc_drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
  197. {
  198. struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
  199. switch (cmd)
  200. {
  201. case PWM_CMD_ENABLE:
  202. return lpc_drv_pwm_enable(device, configuration, RT_TRUE);
  203. case PWM_CMD_DISABLE:
  204. return lpc_drv_pwm_enable(device, configuration, RT_FALSE);
  205. case PWM_CMD_SET:
  206. return lpc_drv_pwm_set(device, configuration);
  207. case PWM_CMD_GET:
  208. return lpc_drv_pwm_get(device, configuration);
  209. default:
  210. return -RT_EINVAL;
  211. }
  212. }
  213. static rt_err_t rt_hw_pwm_init(struct lpc_pwm *device)
  214. {
  215. rt_err_t ret = RT_EOK;
  216. CTIMER_Type *tim = RT_NULL;
  217. uint32_t channel = RT_NULL;
  218. static struct rt_device_pwm pwm_device;
  219. ctimer_config_t config;
  220. uint32_t pwmPeriod, pulsePeriod;
  221. tim = device->tim;
  222. channel = device->channel;
  223. if(tim == CTIMER1)
  224. {
  225. /* Use 12 MHz clock for some of the Ctimers */
  226. CLOCK_AttachClk(kMAIN_CLK_to_CTIMER1);
  227. /* Run as a timer */
  228. config.mode = kCTIMER_TimerMode;
  229. /* This field is ignored when mode is timer */
  230. config.input = kCTIMER_Capture_0;
  231. /* Timer counter is incremented on every APB bus clock */
  232. config.prescale = 0;
  233. CTIMER_Init(CTIMER1, &config);
  234. /* Get the PWM period match value and pulse width match value of DEFAULT_FREQ PWM signal with DEFAULT_DUTY dutycycle */
  235. /* Calculate PWM period match value */
  236. pwmPeriod = (( CLOCK_GetCTimerClkFreq(1U) / (config.prescale + 1) ) / DEFAULT_FREQ) - 1;
  237. }
  238. else if (tim == CTIMER2)
  239. {
  240. /* Use 12 MHz clock for some of the Ctimers */
  241. CLOCK_AttachClk(kMAIN_CLK_to_CTIMER2);
  242. /* Run as a timer */
  243. config.mode = kCTIMER_TimerMode;
  244. /* This field is ignored when mode is timer */
  245. config.input = kCTIMER_Capture_0;
  246. /* Timer counter is incremented on every APB bus clock */
  247. config.prescale = 0;
  248. CTIMER_Init(CTIMER2, &config);
  249. /* Get the PWM period match value and pulse width match value of DEFAULT_FREQ PWM signal with DEFAULT_DUTY dutycycle */
  250. /* Calculate PWM period match value */
  251. pwmPeriod = (( CLOCK_GetCTimerClkFreq(2U) / (config.prescale + 1) ) / DEFAULT_FREQ) - 1;
  252. }
  253. /* Calculate pulse width match value */
  254. if (DEFAULT_DUTY == 0)
  255. {
  256. pulsePeriod = pwmPeriod + 1;
  257. }
  258. else
  259. {
  260. pulsePeriod = ((pwmPeriod + 1) * (100 - DEFAULT_DUTY)) / 100;
  261. }
  262. if (channel & 0x01)
  263. {
  264. CTIMER_SetupPwmPeriod(tim, kCTIMER_Match_3 , kCTIMER_Match_0, pwmPeriod, pulsePeriod, false);
  265. }
  266. if (channel & 0x02)
  267. {
  268. CTIMER_SetupPwmPeriod(tim, kCTIMER_Match_3 , kCTIMER_Match_1, pwmPeriod, pulsePeriod, false);
  269. }
  270. if (channel & 0x04)
  271. {
  272. CTIMER_SetupPwmPeriod(tim, kCTIMER_Match_3 , kCTIMER_Match_2, pwmPeriod, pulsePeriod, false);
  273. }
  274. return ret;
  275. }
  276. static int lpc_pwm_init(void)
  277. {
  278. int i = 0;
  279. int result = RT_EOK;
  280. pwm_get_channel();
  281. for (i = 0; i < sizeof(lpc_pwm_obj) / sizeof(lpc_pwm_obj[0]); i++)
  282. {
  283. /* pwm init */
  284. if (rt_hw_pwm_init(&lpc_pwm_obj[i]) != RT_EOK)
  285. {
  286. LOG_E("%s init failed", lpc_pwm_obj[i].name);
  287. result = -RT_ERROR;
  288. goto __exit;
  289. }
  290. else
  291. {
  292. LOG_D("%s init success", lpc_pwm_obj[i].name);
  293. /* register pwm device */
  294. if (rt_device_pwm_register(&lpc_pwm_obj[i].pwm_device, lpc_pwm_obj[i].name, &lpc_drv_ops, lpc_pwm_obj[i].tim) == RT_EOK)
  295. {
  296. LOG_D("%s register success", lpc_pwm_obj[i].name);
  297. }
  298. else
  299. {
  300. LOG_E("%s register failed", lpc_pwm_obj[i].name);
  301. result = -RT_ERROR;
  302. }
  303. }
  304. }
  305. __exit:
  306. return result;
  307. }
  308. INIT_DEVICE_EXPORT(lpc_pwm_init);
  309. #endif /* RT_USING_PWM */