luat_pwm_idf5.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. #include "luat_base.h"
  2. #include "luat_pwm.h"
  3. #include <math.h>
  4. #include "driver/ledc.h"
  5. #include "luat_log.h"
  6. #define LUAT_LOG_TAG "pwm"
  7. typedef struct pwm_cont {
  8. luat_pwm_conf_t pc;
  9. uint8_t is_opened;
  10. }pwm_conf_t;
  11. static pwm_conf_t luat_pwm_idf[LEDC_TIMER_MAX];
  12. #define LEDC_LL_FRACTIONAL_BITS (8)
  13. #define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1)
  14. #define LEDC_TIMER_DIV_NUM_MAX (0x3FFFF)
  15. #define LEDC_IS_DIV_INVALID(div) ((div) <= LEDC_LL_FRACTIONAL_MAX || (div) > LEDC_TIMER_DIV_NUM_MAX)
  16. static inline uint32_t ilog2(uint32_t i)
  17. {
  18. assert(i > 0);
  19. uint32_t log = 0;
  20. while (i >>= 1) {
  21. ++log;
  22. }
  23. return log;
  24. }
  25. static inline uint32_t ledc_calculate_divisor(uint32_t src_clk_freq, int freq_hz, uint32_t precision)
  26. {
  27. uint64_t fp = freq_hz;
  28. fp *= precision;
  29. return ( ( (uint64_t) src_clk_freq << LEDC_LL_FRACTIONAL_BITS ) + (fp / 2 ) ) / fp;
  30. }
  31. // https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#ledpwm
  32. static uint32_t ledc_find_suitable_duty_resolution2(uint32_t src_clk_freq, uint32_t timer_freq)
  33. {
  34. uint32_t div = (src_clk_freq + timer_freq / 2) / timer_freq; // rounded
  35. uint32_t duty_resolution = ilog2(div);
  36. if ( duty_resolution >= SOC_LEDC_TIMER_BIT_WIDTH) {
  37. duty_resolution = SOC_LEDC_TIMER_BIT_WIDTH - 1;
  38. }
  39. uint32_t div_param = ledc_calculate_divisor(src_clk_freq, timer_freq, 1 << duty_resolution);
  40. if (LEDC_IS_DIV_INVALID(div_param)) {
  41. div = src_clk_freq / timer_freq; // truncated
  42. duty_resolution = ilog2(div);
  43. if ( duty_resolution > SOC_LEDC_TIMER_BIT_WIDTH - 1) {
  44. duty_resolution = SOC_LEDC_TIMER_BIT_WIDTH - 1;
  45. }
  46. div_param = ledc_calculate_divisor(src_clk_freq, timer_freq, 1 << duty_resolution);
  47. if (LEDC_IS_DIV_INVALID(div_param)) {
  48. duty_resolution = 0;
  49. }
  50. }
  51. return duty_resolution;
  52. }
  53. int luat_pwm_setup(luat_pwm_conf_t *conf){
  54. int duty_resolution = 0;
  55. int timer = -1;
  56. int ret = -1;
  57. if (conf->channel < 0)
  58. return -1;
  59. for (size_t i = 0; i < LEDC_TIMER_MAX; i++)
  60. {
  61. if (luat_pwm_idf[i].is_opened && luat_pwm_idf[i].pc.channel == conf->channel) {
  62. timer = i;
  63. break;
  64. }
  65. if (!luat_pwm_idf[i].is_opened) {
  66. timer = i;
  67. break;
  68. }
  69. }
  70. if (timer < 0) {
  71. LLOGE("too many PWM!!! only %d channels supported", LEDC_TIMER_MAX);
  72. return -1;
  73. }
  74. if (conf->pulse > conf->precision) {
  75. conf->pulse = conf->precision;
  76. }
  77. duty_resolution = ledc_find_suitable_duty_resolution2(80*1000*1000, conf->period);
  78. int duty = (conf->pulse * (1 << duty_resolution)) / conf->precision;
  79. // LLOGD("freq=%d, pulse=%d, precision=%d, resolution=%08X, duty=%08X", (int)conf->period, (int)conf->pulse, (int)conf->precision, (int)(1 << duty_resolution), (int)duty);
  80. int speed_mode = LEDC_LOW_SPEED_MODE;
  81. // 判断一下是否需要完全重新配置
  82. if (conf->period != luat_pwm_idf[timer].pc.period || // 频率是否相同
  83. conf->precision != luat_pwm_idf[timer].pc.precision || // 占空比精度是否相同
  84. conf->pnum != luat_pwm_idf[timer].pc.pnum // 输出脉冲数是否相同
  85. ) {
  86. // LLOGD("need to reconfig channel %d period %d", conf->channel, conf->period);
  87. ledc_timer_config_t ledc_timer = {
  88. .speed_mode = speed_mode,
  89. .timer_num = timer,
  90. .freq_hz = conf->period,
  91. .clk_cfg = LEDC_AUTO_CLK,
  92. .duty_resolution = duty_resolution
  93. };
  94. // LLOGD("duty_resolution %d %04X", duty_resolution, 1 << duty_resolution);
  95. ret = ledc_timer_config(&ledc_timer);
  96. if (ret) {
  97. return -1;
  98. }
  99. ledc_channel_config_t ledc_channel = {
  100. .speed_mode = speed_mode,
  101. .channel = timer,
  102. .timer_sel = timer,
  103. .intr_type = LEDC_INTR_DISABLE,
  104. .gpio_num = conf->channel,
  105. .duty = duty,
  106. .hpoint = 0,
  107. };
  108. ledc_channel_config(&ledc_channel);
  109. }
  110. ledc_set_duty(speed_mode, timer, duty);
  111. ledc_update_duty(speed_mode, timer);
  112. memcpy( &luat_pwm_idf[timer].pc, conf, sizeof(luat_pwm_conf_t));
  113. luat_pwm_idf[timer].is_opened = 1;
  114. return 0;
  115. }
  116. int luat_pwm_close(int channel){
  117. int timer = -1;
  118. if (channel < 0)
  119. return -1;
  120. for (size_t i = 0; i < LEDC_TIMER_MAX; i++){
  121. if (luat_pwm_idf[i].is_opened && luat_pwm_idf[i].pc.channel == channel){
  122. timer = i;
  123. break;
  124. }
  125. }
  126. if (timer < 0) {
  127. return -1;
  128. }
  129. int ret = ledc_stop(LEDC_LOW_SPEED_MODE, timer, 0);
  130. if (ret) {
  131. return -1;
  132. }
  133. gpio_reset_pin(channel);
  134. luat_pwm_idf[timer].is_opened = 0;
  135. memset(&luat_pwm_idf[timer].pc, 0, sizeof(luat_pwm_conf_t) );
  136. return 0;
  137. }
  138. int luat_pwm_capture(int channel, int freq){
  139. return -1;
  140. }