piątek, 6 października 2017

[4] ESP32 - Konfiguracja PWM

W tym poście przedstawię w jaki sposób skonfigurować PWM dla ESP32.

[Źródło: www.banggood.com]

Opis:


Kontroler PWM może wygenerować do 16 kanałów. Do każdego z nich można osobno wygenerować sygnał o podanym okresie i wypełnieniu. Układ działa na 80 MHz zegarze APB. Osiem z nich może wykorzystywać 8 MHz oscylator. Każdy z kanałów może być konfigurowalny z 20 bitowego timera z ustawianym zakresem odliczania. Dokładność obliczeń wykonana jest na 16 bitach z 1ms okresem.

Programowanie:

Przedstawiony kod testowałem na dwóch wyprowadzeniach (GPIO19 oraz GPIO16) jednak powinien on działać bez problemu na innych wyprowadzeniach. Spowodowane jest to tym, ze można jego wyprowadzenia jak i kanały PWM bezpośrednio przerzucać pomiędzy pinami.

Program poniżej pozwala na uruchomienie wybranych timerów wraz z wybranym kanałem PWM.

  1. static void pwmInitial(uint8_t timer, uint8_t channel, uint8_t gpioPinNumber)
  2. {
  3.     /* Config timer, in else if to have different settings in define */
  4.     if(timer == LEDC_TIMER_0){
  5.         timer_Conf(timer, LEDC_TIMER_12_BIT, 1000, LEDC_HIGH_SPEED_MODE);
  6.     }
  7.     else if(timer == LEDC_TIMER_1){
  8.         timer_Conf(timer, LEDC_TIMER_12_BIT, 1000, LEDC_HIGH_SPEED_MODE);
  9.     }
  10.     else if(timer == LEDC_TIMER_2){
  11.         timer_Conf(timer, LEDC_TIMER_12_BIT, 1000, LEDC_HIGH_SPEED_MODE);
  12.     }
  13.     else if(timer == LEDC_TIMER_3){
  14.         timer_Conf(timer, LEDC_TIMER_12_BIT, 1000, LEDC_HIGH_SPEED_MODE);
  15.     }
  16.     /* Config PWM, in else if functions for set different settings for different channels */
  17.     if(channel ==  LEDC_CHANNEL_0){
  18.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  19.     }
  20.     else if(channel ==  LEDC_CHANNEL_1){
  21.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  22.     }
  23.     else if(channel ==  LEDC_CHANNEL_2){
  24.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  25.     }
  26.     else if(channel ==  LEDC_CHANNEL_3){
  27.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  28.     }
  29.     else if(channel ==  LEDC_CHANNEL_4){
  30.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  31.     }
  32.     else if(channel ==  LEDC_CHANNEL_5){
  33.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  34.     }
  35.     else if(channel ==  LEDC_CHANNEL_6){
  36.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  37.     }
  38.     else if(channel ==  LEDC_CHANNEL_7){
  39.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  40.     }
  41. }

Funkcja wywołuje pod funkcje statyczne które ustawiają przesłane parametry. Zostały one przedstawione poniżej:

  1. static void timer_Conf(ledc_timer_t  timer, ledc_timer_bit_t bit_num, uint32_t freq, ledc_mode_t speed_mode)
  2. {
  3.     ledc_timer_config_t timer_conf = {0};
  4.     timer_conf.bit_num = bit_num;
  5.     timer_conf.freq_hz = freq;
  6.     timer_conf.speed_mode = speed_mode;
  7.     timer_conf.timer_num = timer;
  8.     ledc_timer_config(&timer_conf);
  9. }
  10. static void pwmChannel_Conf(ledc_channel_t channel, uint32_t duty, uint8_t gpioPinNumber, ledc_intr_type_t intr_type,
  11.                             ledc_mode_t speed_mode, ledc_timer_t timer)
  12. {
  13.     ledc_channel_config_t ledc_conf;
  14.     ledc_conf.channel = channel;
  15.     ledc_conf.duty = duty;
  16.     ledc_conf.gpio_num = (int)gpioPinNumber;
  17.     ledc_conf.intr_type = intr_type;
  18.     ledc_conf.speed_mode = speed_mode;
  19.     ledc_conf.timer_sel = timer;
  20.     ledc_channel_config(&ledc_conf);
  21.     ledc_set_duty(speed_mode, channel, 0);
  22.     ledc_update_duty(speed_mode, channel);
  23. }

Pierwsza funkcja powyżej odpowiada za konfiguracje timera podanymi parametrami. Najpierw tworzy się strukturę do której dane zostają przypisane po czym zapisuje się ustawienia po przez podanie do funkcji ledc_timer_config informacji o ustawieniach. Podobnie działa funkcja następna czyli pwmChannel_Conf. Odpowiada ona za konfiguracje kanału PWM zadanymi parametrami. 

Ustawienie wartosci na konretnym porcie PWM odbywa sie po wywolaniu nastepujacej funkcji:

  1. void pwmSetValue(uint8_t channel, uint16_t duty)
  2. {
  3.     ledc_set_duty(LEDC_HIGH_SPEED_MODE, channel, duty);
  4.     ledc_update_duty(LEDC_HIGH_SPEED_MODE, channel);
  5. }

Ponizej zalaczam plik .c oraz plik .h:

.c:

  1. /*
  2.  * Calculations:
  3.  * f = (Clock Speed)/(Divisor * precision)
  4.  * Divisor = (Clock Speed)/(f * precision)
  5.  */
  6. #include "pwm_ctrl.h"
  7. static void pwmInitial(uint8_t timer, uint8_t channel, uint8_t gpioPinNumber);
  8. static void timer_Conf(ledc_timer_t  timer, ledc_timer_bit_t bit_num, uint32_t freq, ledc_mode_t speed_mode);
  9. static void pwmChannel_Conf(ledc_channel_t channel, uint32_t duty, uint8_t gpioPinNumber, ledc_intr_type_t intr_type,
  10.                             ledc_mode_t speed_mode, ledc_timer_t timer);
  11. void pwmInit(void)
  12. {
  13.     pwmInitial(LEDC_TIMER_0, LEDC_CHANNEL_0, 19);
  14. }
  15. void pwmSetValue(uint8_t channel, uint16_t duty)
  16. {
  17.     ledc_set_duty(LEDC_HIGH_SPEED_MODE, channel, duty);
  18.     ledc_update_duty(LEDC_HIGH_SPEED_MODE, channel);
  19. }
  20. /* ================= STATIC FUNCTIONS ================= */
  21. static void pwmInitial(uint8_t timer, uint8_t channel, uint8_t gpioPinNumber)
  22. {
  23.     /* Config timer, in else if to have different settings in define */
  24.     if(timer == LEDC_TIMER_0){
  25.         timer_Conf(timer, LEDC_TIMER_12_BIT, 1000, LEDC_HIGH_SPEED_MODE);
  26.     }
  27.     else if(timer == LEDC_TIMER_1){
  28.         timer_Conf(timer, LEDC_TIMER_12_BIT, 1000, LEDC_HIGH_SPEED_MODE);
  29.     }
  30.     else if(timer == LEDC_TIMER_2){
  31.         timer_Conf(timer, LEDC_TIMER_12_BIT, 1000, LEDC_HIGH_SPEED_MODE);
  32.     }
  33.     else if(timer == LEDC_TIMER_3){
  34.         timer_Conf(timer, LEDC_TIMER_12_BIT, 1000, LEDC_HIGH_SPEED_MODE);
  35.     }
  36.     /* Config PWM, in else if functions for set different settings for different channels */
  37.     if(channel ==  LEDC_CHANNEL_0){
  38.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  39.     }
  40.     else if(channel ==  LEDC_CHANNEL_1){
  41.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  42.     }
  43.     else if(channel ==  LEDC_CHANNEL_2){
  44.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  45.     }
  46.     else if(channel ==  LEDC_CHANNEL_3){
  47.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  48.     }
  49.     else if(channel ==  LEDC_CHANNEL_4){
  50.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  51.     }
  52.     else if(channel ==  LEDC_CHANNEL_5){
  53.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  54.     }
  55.     else if(channel ==  LEDC_CHANNEL_6){
  56.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  57.     }
  58.     else if(channel ==  LEDC_CHANNEL_7){
  59.         pwmChannel_Conf(channel, 1024, gpioPinNumber, LEDC_INTR_DISABLE, LEDC_HIGH_SPEED_MODE, timer);
  60.     }
  61. }
  62. static void timer_Conf(ledc_timer_t  timer, ledc_timer_bit_t bit_num, uint32_t freq, ledc_mode_t speed_mode)
  63. {
  64.     ledc_timer_config_t timer_conf = {0};
  65.     timer_conf.bit_num = bit_num;
  66.     timer_conf.freq_hz = freq;
  67.     timer_conf.speed_mode = speed_mode;
  68.     timer_conf.timer_num = timer;
  69.     ledc_timer_config(&timer_conf);
  70. }
  71. static void pwmChannel_Conf(ledc_channel_t channel, uint32_t duty, uint8_t gpioPinNumber, ledc_intr_type_t intr_type,
  72.                             ledc_mode_t speed_mode, ledc_timer_t timer)
  73. {
  74.     ledc_channel_config_t ledc_conf;
  75.     ledc_conf.channel = channel;
  76.     ledc_conf.duty = duty;
  77.     ledc_conf.gpio_num = (int)gpioPinNumber;
  78.     ledc_conf.intr_type = intr_type;
  79.     ledc_conf.speed_mode = speed_mode;
  80.     ledc_conf.timer_sel = timer;
  81.     ledc_channel_config(&ledc_conf);
  82.     ledc_set_duty(speed_mode, channel, 0);
  83.     ledc_update_duty(speed_mode, channel);
  84. }

.h:

  1. #ifndef MAIN_PWM_CTRL_H_
  2. #define MAIN_PWM_CTRL_H_
  3. #include <driver/ledc.h>
  4. #include <esp_err.h>
  5. #include <esp_event.h>
  6. #include <esp_event_loop.h>
  7. #include <esp_log.h>
  8. #include <esp_system.h>
  9. void pwmInit(void);
  10. void pwmSetValue(uint8_t channel, uint16_t duty);
  11. #endif /* MAIN_PWM_CTRL_H_ */

Wszystkie dodatkowe informacje można przeczytać pod tym linkiem.

Bibliotekę można znaleźć w folderze ESP32 na dysku Google pod tym linkiem.