drv_pwm.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /*
  2. * Copyright (c) 2006-2021, 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. *
  11. */
  12. #include <rtthread.h>
  13. #ifdef RT_USING_PWM
  14. #if !defined(BSP_USING_CTIMER2_MAT0) && !defined(BSP_USING_CTIMER2_MAT1) && \
  15. !defined(BSP_USING_CTIMER2_MAT2)
  16. #error "Please define at least one BSP_USING_CTIMERx_MATx"
  17. #else
  18. #define BSP_USING_CTIMER2
  19. #endif
  20. #define LOG_TAG "drv.pwm"
  21. #include <drv_log.h>
  22. #include <rtdevice.h>
  23. #include "fsl_ctimer.h"
  24. #include "drv_pwm.h"
  25. #define DEFAULT_DUTY 50
  26. #define DEFAULT_FREQ 1000
  27. static rt_err_t lpc_drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg);
  28. static struct rt_pwm_ops lpc_drv_ops =
  29. {
  30. .control = lpc_drv_pwm_control
  31. };
  32. static rt_err_t lpc_drv_pwm_enable(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration, rt_bool_t enable)
  33. {
  34. CTIMER_Type *base;
  35. base = (CTIMER_Type *)device->parent.user_data;
  36. if (!enable)
  37. {
  38. /* Stop the timer */
  39. CTIMER_StopTimer(base);
  40. }
  41. else
  42. {
  43. /* Start the timer */
  44. CTIMER_StartTimer(base);
  45. }
  46. return RT_EOK;
  47. }
  48. static rt_err_t lpc_drv_pwm_get(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
  49. {
  50. uint8_t get_duty;
  51. uint32_t get_frequence;
  52. uint32_t pwmClock = 0;
  53. CTIMER_Type *base;
  54. base = (CTIMER_Type *)device->parent.user_data;
  55. #ifdef BSP_USING_CTIMER2
  56. /* get frequence */
  57. pwmClock = CLOCK_GetFreq(kCLOCK_CTimer2) ;
  58. #endif
  59. get_frequence = pwmClock / (base->MR[kCTIMER_Match_3] + 1);
  60. if(configuration->channel == 1)
  61. {
  62. /* get dutycycle */
  63. get_duty = (100*(base->MR[kCTIMER_Match_3] + 1 - base->MR[kCTIMER_Match_1]))/(base->MR[kCTIMER_Match_3] + 1);
  64. }
  65. /* get dutycycle */
  66. /* conversion */
  67. configuration->period = 1000000000 / get_frequence;
  68. configuration->pulse = get_duty * configuration->period / 100;
  69. rt_kprintf("*** PWM period %d, pulse %d\r\n", configuration->period, configuration->pulse);
  70. return RT_EOK;
  71. }
  72. static rt_err_t lpc_drv_pwm_set(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
  73. {
  74. RT_ASSERT(configuration->period > 0);
  75. RT_ASSERT(configuration->pulse <= configuration->period);
  76. ctimer_config_t config;
  77. CTIMER_Type *base;
  78. base = (CTIMER_Type *)device->parent.user_data;
  79. uint32_t pwmPeriod, pulsePeriod;
  80. /* Run as a timer */
  81. config.mode = kCTIMER_TimerMode;
  82. /* This field is ignored when mode is timer */
  83. config.input = kCTIMER_Capture_0;
  84. /* Timer counter is incremented on every APB bus clock */
  85. config.prescale = 0;
  86. if(configuration->channel == 1)
  87. {
  88. /* Get the PWM period match value and pulse width match value of DEFAULT_FREQ PWM signal with DEFAULT_DUTY dutycycle */
  89. /* Calculate PWM period match value */
  90. pwmPeriod = (( CLOCK_GetFreq(kCLOCK_CTimer2) / (config.prescale + 1) ) / DEFAULT_FREQ) - 1;
  91. /* Calculate pulse width match value */
  92. if (DEFAULT_DUTY == 0)
  93. {
  94. pulsePeriod = pwmPeriod + 1;
  95. }
  96. else
  97. {
  98. pulsePeriod = (pwmPeriod * (100 - DEFAULT_DUTY)) / 100;
  99. }
  100. /* Match on channel 3 will define the PWM period */
  101. base->MR[kCTIMER_Match_3] = pwmPeriod;
  102. /* This will define the PWM pulse period */
  103. base->MR[kCTIMER_Match_1] = pulsePeriod;
  104. }
  105. return RT_EOK;
  106. }
  107. static rt_err_t lpc_drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
  108. {
  109. struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
  110. switch (cmd)
  111. {
  112. case PWM_CMD_ENABLE:
  113. return lpc_drv_pwm_enable(device, configuration, RT_TRUE);
  114. case PWM_CMD_DISABLE:
  115. return lpc_drv_pwm_enable(device, configuration, RT_FALSE);
  116. case PWM_CMD_SET:
  117. return lpc_drv_pwm_set(device, configuration);
  118. case PWM_CMD_GET:
  119. return lpc_drv_pwm_get(device, configuration);
  120. default:
  121. return RT_EINVAL;
  122. }
  123. }
  124. int rt_hw_pwm_init(void)
  125. {
  126. rt_err_t ret = RT_EOK;
  127. #ifdef BSP_USING_CTIMER2
  128. static struct rt_device_pwm pwm1_device;
  129. ctimer_config_t config;
  130. uint32_t pwmPeriod, pulsePeriod;
  131. /* Use 12 MHz clock for some of the Ctimers */
  132. CLOCK_AttachClk(kMAIN_CLK_to_CTIMER2);
  133. /* Run as a timer */
  134. config.mode = kCTIMER_TimerMode;
  135. /* This field is ignored when mode is timer */
  136. config.input = kCTIMER_Capture_0;
  137. /* Timer counter is incremented on every APB bus clock */
  138. config.prescale = 0;
  139. CTIMER_Init(CTIMER2, &config);
  140. #ifdef BSP_USING_CTIMER2_MAT1
  141. /* Get the PWM period match value and pulse width match value of DEFAULT_FREQ PWM signal with DEFAULT_DUTY dutycycle */
  142. /* Calculate PWM period match value */
  143. pwmPeriod = (( CLOCK_GetFreq(kCLOCK_CTimer2) / (config.prescale + 1) ) / DEFAULT_FREQ) - 1;
  144. /* Calculate pulse width match value */
  145. if (DEFAULT_DUTY == 0)
  146. {
  147. pulsePeriod = pwmPeriod + 1;
  148. }
  149. else
  150. {
  151. pulsePeriod = (pwmPeriod * (100 - DEFAULT_DUTY)) / 100;
  152. }
  153. CTIMER_SetupPwmPeriod(CTIMER2, kCTIMER_Match_1 , pwmPeriod, pulsePeriod, false);
  154. #endif
  155. ret = rt_device_pwm_register(&pwm1_device, "pwm1", &lpc_drv_ops, CTIMER2);
  156. if (ret != RT_EOK)
  157. {
  158. LOG_E("%s register failed", "pwm1");
  159. }
  160. #endif /* BSP_USING_CTIMER2 */
  161. return ret;
  162. }
  163. INIT_DEVICE_EXPORT(rt_hw_pwm_init);
  164. #endif /* RT_USING_PWM */