drv_pwm.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /*
  2. * Copyright (c) 2006-2024, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2024-02-26 Yilin Sun Initial version.
  9. */
  10. #include <rtthread.h>
  11. #include <rtdevice.h>
  12. #include "fsl_ctimer.h"
  13. #define MCX_PWM_MATCH_COUNT 4
  14. #ifdef RT_USING_PWM
  15. typedef struct
  16. {
  17. struct rt_device_pwm pwm_device;
  18. CTIMER_Type *ct_instance;
  19. uint32_t counter_period_ps;
  20. } mcx_pwm_obj_t;
  21. static CTIMER_Type *mcx_pwm_instances[] = CTIMER_BASE_PTRS;
  22. static mcx_pwm_obj_t mcx_pwm_list[ARRAY_SIZE(mcx_pwm_instances)];
  23. static int mcx_pwm_current_period_channel(mcx_pwm_obj_t *pwm)
  24. {
  25. CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
  26. uint32_t mcr = ct->MCR;
  27. for (uint8_t i = 0; i < 4; i++)
  28. {
  29. if (mcr & (1U << (3 * i + CTIMER_MCR_MR0R_SHIFT)))
  30. {
  31. return i;
  32. }
  33. }
  34. return -1;
  35. }
  36. static int mcx_pwm_first_free_channel(mcx_pwm_obj_t *pwm)
  37. {
  38. CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
  39. int pc = mcx_pwm_current_period_channel(pwm);
  40. uint32_t pwmc = ct->PWMC;
  41. for (uint8_t i = 0; i < 4; i++)
  42. {
  43. if (pwmc & (1U << i))
  44. {
  45. /* Skip this channel if there's an active PWM output */
  46. continue;
  47. }
  48. if (i == pc)
  49. {
  50. /* Skip this channel if this channel is the current period channel */
  51. continue;
  52. }
  53. return i;
  54. }
  55. /* There are no free channels left. */
  56. return -1;
  57. }
  58. static int mcx_pwm_period_set(mcx_pwm_obj_t *pwm, uint32_t period_ns)
  59. {
  60. CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
  61. int p_channel = mcx_pwm_current_period_channel(pwm);
  62. if (p_channel < 0)
  63. {
  64. return -EINVAL;
  65. }
  66. /* Store new values in shadow registers */
  67. ct->MSR[p_channel] = period_ns * 1000 / pwm->counter_period_ps;
  68. /* Enable period channel interrupt to check a reload event occurs.
  69. * Since interrupts are not configured from NVIC, so no ISR will occur.
  70. * Check IR[MRnINT] for reload point.
  71. */
  72. uint32_t mcr_mask = (CTIMER_MCR_MR0RL_MASK << p_channel) | (CTIMER_MCR_MR0I_MASK << (3 * p_channel));
  73. for (uint8_t i = 0; i < 4; i++)
  74. {
  75. if (ct->PWMC & (1U << i))
  76. {
  77. /* Channel PWM output is enabled, calculate new values and store into shadow registers */
  78. uint32_t new_mr = ct->MR[i] * ct->MSR[p_channel] / ct->MR[p_channel];
  79. ct->MSR[i] = new_mr;
  80. /* Update MRnRL map */
  81. mcr_mask |= CTIMER_MCR_MR0RL_MASK << i;
  82. }
  83. }
  84. /* Reload MRs on next counter reset, enable reload MR interrupt */
  85. ct->MCR |= mcr_mask;
  86. while ((ct->IR & (CTIMER_IR_MR0INT_MASK << p_channel)) == 0U)
  87. {
  88. /* -- */
  89. }
  90. /* Disable reload channel interrupt and MSR synchronization */
  91. ct->MCR &= ~mcr_mask;
  92. /* Clear interrupt flags. */
  93. ct->IR |= (CTIMER_IR_MR0INT_MASK << p_channel);
  94. return 0;
  95. }
  96. static int mcx_pwm_period_get(mcx_pwm_obj_t *pwm, uint32_t *period_ns)
  97. {
  98. CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
  99. int p_channel = mcx_pwm_current_period_channel(pwm);
  100. if (p_channel < 0)
  101. {
  102. return -1;
  103. }
  104. *period_ns = ct->MR[p_channel] * pwm->counter_period_ps / 1000;
  105. return 0;
  106. }
  107. static int mcx_pwm_pulse_set(mcx_pwm_obj_t *pwm, uint8_t channel, uint32_t pulse_ns)
  108. {
  109. CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
  110. int p_channel = mcx_pwm_current_period_channel(pwm);
  111. if (p_channel < 0)
  112. {
  113. return -1;
  114. }
  115. /* Up-counting counters, the polarity is inversed */
  116. ct->MSR[channel] = ct->MR[p_channel] - pulse_ns * 1000 / pwm->counter_period_ps;
  117. /* Reload MRn on the next cycle */
  118. ct->MCR |= (CTIMER_MCR_MR0RL_MASK << channel);
  119. /* Wait for new duty cycle loaded into the MRn */
  120. while (ct->MR[channel] != ct->MSR[channel])
  121. {
  122. /* -- */
  123. }
  124. /* Disable shadow register updates */
  125. ct->MCR &= ~(CTIMER_MCR_MR0RL_MASK << channel);
  126. return 0;
  127. }
  128. static int mcx_pwm_pulse_get(mcx_pwm_obj_t *pwm, uint8_t channel, uint32_t *pulse_ns)
  129. {
  130. CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
  131. int p_channel = mcx_pwm_current_period_channel(pwm);
  132. if (p_channel < 0)
  133. {
  134. return -1;
  135. }
  136. /* Up-counting counters, the polarity is inversed */
  137. *pulse_ns = (ct->MR[p_channel] - ct->MR[channel]) * pwm->counter_period_ps / 1000;
  138. return 0;
  139. }
  140. static rt_err_t mcx_drv_pwm_get(mcx_pwm_obj_t *pwm, struct rt_pwm_configuration *configuration)
  141. {
  142. if (mcx_pwm_period_get(pwm, &configuration->period) < 0)
  143. {
  144. return -RT_EFAULT;
  145. }
  146. if (mcx_pwm_pulse_get(pwm, configuration->channel, &configuration->pulse) < 0)
  147. {
  148. return -RT_EFAULT;
  149. }
  150. return RT_EOK;
  151. }
  152. static rt_err_t mcx_drv_pwm_set(mcx_pwm_obj_t *pwm, struct rt_pwm_configuration *configuration)
  153. {
  154. CTIMER_Type *ct = pwm->ct_instance;
  155. uint32_t period = configuration->period * 1000 / pwm->counter_period_ps;
  156. uint8_t channel = configuration->channel;
  157. if ((ct->TCR & CTIMER_TCR_CEN_MASK) == 0U)
  158. {
  159. /* There's two conditions for a all-zero TCR: either a reset condition or timer is stopped. */
  160. /* In either case, we need to initialize the timer instance (AHB RST CTRL). */
  161. /* TODO: Do not use SDK functions */
  162. ctimer_config_t ct_cfg =
  163. {
  164. .mode = kCTIMER_TimerMode,
  165. .prescale = 1U,
  166. };
  167. /* Frequency: 150MHz max., we got 32bit counters, we can take that. */
  168. /* Approx. maximum period: 28.6 seconds. */
  169. CTIMER_Init(ct, &ct_cfg);
  170. /* Current timer is not running, we are the first channel being configured. */
  171. ct->TC = 0U; /* Reset counter */
  172. ct->PC = 0U; /* Reset prescaler counter */
  173. ct->PR = 0U; /* Prescaler, divide by 1 to get best resolution */
  174. ct->MCR = 0U; /* Reset interrupt and reset condition */
  175. ct->EMR = 0U; /* Do nothing on match event and output 0 as default state */
  176. ct->PWMC = 0U; /* Disable all PWM channels, outputs will be controlled by EMn */
  177. /* Here, we have a favoritism of using channel 3 as period channel, unless channel 3 is used for output */
  178. if (channel != 3)
  179. {
  180. ct->MR[3] = period;
  181. ct->MCR |= CTIMER_MCR_MR3R_MASK;
  182. }
  183. else
  184. {
  185. /* Use channel 2 as period channel. */
  186. ct->MR[2] = period;
  187. ct->MCR |= CTIMER_MCR_MR2R_MASK;
  188. }
  189. /* Start counter */
  190. ct->TCR |= CTIMER_TCR_CEN_MASK;
  191. }
  192. else
  193. {
  194. /*
  195. * Due to the nature of the CTimer, one of the 4 match channels is needed for period control (frequency)
  196. * To find out which one is the current period channel, check the MRxR bit for each match output.
  197. * If we are configuring the same match being used as periodic channel, configure the next free match as period
  198. * then current channel can be re-used. If all 4 channels are in use then the function will fail with an errno.
  199. */
  200. /* The timer is running, check whether we need to re-locate the period channel */
  201. int p_channel = mcx_pwm_current_period_channel(pwm);
  202. if (p_channel < 0)
  203. {
  204. return -RT_EINVAL;
  205. }
  206. if (p_channel == channel)
  207. {
  208. /* We need to re-locate the period channel */
  209. int f_channel = mcx_pwm_first_free_channel(pwm);
  210. if (f_channel < 0)
  211. {
  212. /* There's no free channel, bail out. */
  213. return -RT_EBUSY;
  214. }
  215. /* Transfer the period channel to first free channel */
  216. /* Step 1: Copy current period to first free channel */
  217. ct->MR[f_channel] = ct->MR[p_channel];
  218. /* Step 2: Enable reset for new period channel */
  219. /* Note: it's safe doing it here since both old and new channel MRs contains same value */
  220. ct->MCR |= (CTIMER_MCR_MR0R_MASK << (3 * f_channel));
  221. /* Step 3: Disable reset for old period channel */
  222. ct->MCR &= ~(CTIMER_MCR_MR0R_MASK << (3 * p_channel));
  223. /* The old period channel is now available for PWM output */
  224. p_channel = f_channel;
  225. }
  226. if (mcx_pwm_period_set(pwm, configuration->period) < 0)
  227. {
  228. return -RT_EINVAL;
  229. }
  230. }
  231. if (mcx_pwm_pulse_set(pwm, channel, configuration->pulse) < 0)
  232. {
  233. return -RT_EINVAL;
  234. }
  235. return 0;
  236. }
  237. static rt_err_t mcx_drv_pwm_enable(mcx_pwm_obj_t *pwm, struct rt_pwm_configuration *configuration)
  238. {
  239. CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
  240. ct->PWMC |= (1U << configuration->channel);
  241. return 0;
  242. }
  243. static rt_err_t mcx_drv_pwm_disable(mcx_pwm_obj_t *pwm, struct rt_pwm_configuration *configuration)
  244. {
  245. CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
  246. ct->PWMC &= ~(1U << configuration->channel);
  247. return 0;
  248. }
  249. static rt_err_t mcx_drv_pwm_control(struct rt_device_pwm *device, int cmd, void *args)
  250. {
  251. mcx_pwm_obj_t *pwm = device->parent.user_data;
  252. struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)args;
  253. switch (cmd)
  254. {
  255. case PWM_CMD_ENABLE:
  256. return mcx_drv_pwm_enable(pwm, configuration);
  257. case PWM_CMD_DISABLE:
  258. return mcx_drv_pwm_disable(pwm, configuration);
  259. case PWM_CMD_SET:
  260. return mcx_drv_pwm_set(pwm, configuration);
  261. case PWM_CMD_GET:
  262. return mcx_drv_pwm_get(pwm, configuration);
  263. default:
  264. return -RT_EINVAL;
  265. }
  266. return RT_EOK;
  267. }
  268. static struct rt_pwm_ops mcx_pwm_ops =
  269. {
  270. .control = mcx_drv_pwm_control,
  271. };
  272. int mcx_pwm_init(void)
  273. {
  274. rt_err_t ret;
  275. char name_buf[8];
  276. for (uint8_t i = 0; i < ARRAY_SIZE(mcx_pwm_instances); i++)
  277. {
  278. mcx_pwm_list[i].ct_instance = mcx_pwm_instances[i];
  279. mcx_pwm_list[i].counter_period_ps = 1000000000000ULL / CLOCK_GetCTimerClkFreq(i);
  280. rt_snprintf(name_buf, sizeof(name_buf), "pwm%d", i);
  281. ret = rt_device_pwm_register(&mcx_pwm_list[i].pwm_device, name_buf, &mcx_pwm_ops, &mcx_pwm_list[i]);
  282. if (ret != RT_EOK)
  283. {
  284. return ret;
  285. }
  286. }
  287. return RT_EOK;
  288. }
  289. INIT_DEVICE_EXPORT(mcx_pwm_init);
  290. #endif /* RT_USING_PWM */