ESP, IoT ESP32 LEDC использование прерываний

SashaPetrov

✩✩✩✩✩✩✩
22 Май 2019
54
5
Добрый день
В новом проекте мне потребовалось вызывать прерывание каждый цикл генерации ШИМ. ESP-IDF предоставляет API ledc. Мне удалось настроить работу таймера. Но по неизвестной мне причине вызов прерывания не идёт.
В чём может быть причина? И как её решить?

Прилагаю код:
C++:
ledc_timer_config_t ledc_timer;
ledc_channel_config_t ledc_channel;

void IRAM_ATTR isr_handler(void *param) {
  static bool flag;
  digitalWrite(22, flag);
  flag = !flag;
}
void setup() {
  pinMode(22, OUTPUT);
  delay(100);
 
  ledc_timer.duty_resolution = LEDC_TIMER_4_BIT; // resolution of PWM duty
  ledc_timer.freq_hz = 8000;                     // frequency of PWM signal 00
  ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;  // timer mode
  ledc_timer.timer_num = LEDC_TIMER_0;           // timer index

  ledc_channel.gpio_num = 2;                      //
  ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; //
  ledc_channel.channel = LEDC_CHANNEL_0;          //
  ledc_channel.timer_sel = LEDC_TIMER_0;          //
  ledc_channel.duty = 1;                           //

  int a = ledc_timer_config(&ledc_timer);

  int b = ledc_channel_config(&ledc_channel);

  int c = ledc_fade_func_install(ESP_INTR_FLAG_IRAM);

  int d = ledc_fade_start(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT);

  int e = ledc_isr_register(isr_handler, NULL, ESP_INTR_FLAG_IRAM, NULL);
}
На данный момент мне пока пришёл в голову только костыль. Подключить выход шим к другому пину и генерировать прерывания таким образом
 

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
Нашлось решение ?
LEDC генерирует прерывания по завершению работы функции фейдера, а не каждый период работы ШИМ.
Не оч понятно в чем задача, суть аппаратного ШИМ в том чтобы как раз не генерировать прерывания и не отвлекать процессор. В противном случае можно использовать прерывания от таймера и дергать ногой, аппратный ШИМ тут не нужен.
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
Да банальная задача генерировать звук при помощи ШИМа
Библиотечка ESP32_PWM, как я понял, использует не шимовский таймер, а таймер общего назначения. И вообще похоже она не использует модуль ШИМ вообще.
Полностью настраивать ШИМ по даташиту - гемор.
Нужно пример готовых настроек под звуковые частоты и прерывание от ШИМа на каждом такте.
 

rkit

★★★✩✩✩✩
5 Фев 2021
508
127
Под ШИМ теперь какой-то особый усилиль делают?
 

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
Полностью настраивать ШИМ по даташиту - гемор.
по даташиту не надо, используйте функции ESP-IDF, там все просто

C++:
  ledc_channel_config_t c_cfg = {
    -1,                       // int gpio_num;                   /*!< the LEDC output gpio_num, if you want to use gpio16, gpio_num = 16 */
    LEDC_LOW_SPEED_MODE,      // ledc_mode_t speed_mode;         /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */
    LEDC_CHANNEL_0,           // ledc_channel_t channel;         /*!< LEDC channel (0 - 7) */
    LEDC_INTR_DISABLE,        // ledc_intr_type_t intr_type;     /*!< configure interrupt, Fade interrupt enable  or Fade interrupt disable */
    LEDC_TIMER_0,             // ledc_timer_t timer_sel;         /*!< Select the timer source of channel (0 - 3) */
    DEFAULT_PWM_DUTY,         // uint32_t duty;                  /*!< LEDC channel duty, the range of duty setting is [0, (2**duty_resolution)-1] */
    0,                        // int hpoint;                     /*!< LEDC channel hpoint value, the max value is 0xfffff */
    {0}                       // unsigned int output_invert: 1;/*!< Enable (1) or disable (0) gpio output invert */
  };

  ledc_timer_config_t t_cfg = {
    LEDC_LOW_SPEED_MODE,      // .speed_mode
    DEFAULT_PWM_RESOLUTION,   // .duty_resolution / resolution of PWM duty
    LEDC_TIMER_0,             // .timer_num / timer index
    DEFAULT_PWM_FREQ,         // .freq_hz / frequency of PWM signal
    DEFAULT_PWM_CLK           // .clk_cfg = LEDC_AUTO_CLK,              // Auto select the source clock
  };

ledc_channel_config(c_cfg);
ledc_timer_config(t_cfg);



Нужно пример готовых настроек под звуковые частоты и прерывание от ШИМа на каждом такте.
я звуком не занимался, но все равно не понимаю зачем аппаратный ШИМ и прерывания каждый цикл одновременно?
вы же понимаете что это тоже самое что без всякого шима

C++:
timer_isr(){
    pin_state=!pin_state;
    digitalWrite(_pin, !pin_state);
    do_something_else();
}
как бы то ни было, но LEDC так прерывания не генерирует.
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
по даташиту не надо, используйте функции ESP-IDF, там все просто


как бы то ни было, но LEDC так прерывания не генерирует.
Спасибо, я этот пример видел. Это не работает в Ардуино ИДЕ
А переносить весь проект в ESP-IDF - гемор
 

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
Спасибо, я этот пример видел. Это не работает в Ардуино ИДЕ
А переносить весь проект в ESP-IDF - гемор
работает, ардуино это нахлобучка над IDF.
достаточно в заголовок проекта добавить #include "driver/ledc.h"
только не используйте одновременно с analogwrite, иначе там параметры таймеров/пина перемешаются
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
'DEFAULT_PWM_DUTY' was not declared in this scope

C++:
#include "driver/ledc.h"

void setup() {
  ledc_channel_config_t c_cfg = {
    -1,                       // int gpio_num;                   /*!< the LEDC output gpio_num, if you want to use gpio16, gpio_num = 16 */
    LEDC_LOW_SPEED_MODE,      // ledc_mode_t speed_mode;         /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */
    LEDC_CHANNEL_0,           // ledc_channel_t channel;         /*!< LEDC channel (0 - 7) */
    LEDC_INTR_DISABLE,        // ledc_intr_type_t intr_type;     /*!< configure interrupt, Fade interrupt enable  or Fade interrupt disable */
    LEDC_TIMER_0,             // ledc_timer_t timer_sel;         /*!< Select the timer source of channel (0 - 3) */
    DEFAULT_PWM_DUTY,         // uint32_t duty;                  /*!< LEDC channel duty, the range of duty setting is [0, (2**duty_resolution)-1] */
    0,                        // int hpoint;                     /*!< LEDC channel hpoint value, the max value is 0xfffff */
    {0}                       // unsigned int output_invert: 1;/*!< Enable (1) or disable (0) gpio output invert */
  };

  ledc_timer_config_t t_cfg = {
    LEDC_LOW_SPEED_MODE,      // .speed_mode
    DEFAULT_PWM_RESOLUTION,   // .duty_resolution / resolution of PWM duty
    LEDC_TIMER_0,             // .timer_num / timer index
    DEFAULT_PWM_FREQ,         // .freq_hz / frequency of PWM signal
    DEFAULT_PWM_CLK           // .clk_cfg = LEDC_AUTO_CLK,              // Auto select the source clock
  };


ledc_channel_config(c_cfg);
ledc_timer_config(t_cfg);

}

void loop() {
}
достаточно в заголовок проекта добавить #include "driver/ledc.h"
где-то что-то не сработало
DEFAULT_PWM_DUTY' was not declared in this scope
ардуина иде так и не знает этих слов
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,974
633
45
Использовать ШИМ в качестве аудио - это как ездить на велосипеде с квадратными колесами. Как минимум после ШИМ нужно ставить НЧ-фильтр, а это нужно в разы поднимать частоту ШИМ. Не проще ли тогда чуть доработать схему и использовать нормальную дельта-сигму?
 

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
где-то что-то не сработало
DEFAULT_PWM_DUTY' was not declared in this scope
ардуина иде так и не знает этих слов
вы, наверное, совсем новичек в ардуино? DEFAULT_PWM_DUTY это константа, подставьте свое значение какое вам нужно. Там еще нужно выставить пин, частоту и прочие параметры для движка PWM которые вы должны сами завести. Вы же не думали что эта структура вставленная в setup() вам сразу музыку заиграет?
Я вам привел пример того что функции IDF можно использовать в ардуино как есть, а как вы хотите PWM использовать дальше я не знаю.
 

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
подскажу (писал по памяти, с типом аргументов мог ошибиться)
используйте ledc_channel_config(&c_cfg);

но вы так далеко не уедете. Возмите лучше пример из IDF, вставьте в свой скетч и правьте пока не раберетесь как он работает.
вставляйте как есть, только вместо void app_main(void) у вас будет ардуиновский setup(), loop() ставьте пустой. Если все сделаете правильно у вас должен на светодиод идти меандр.

инициализацию структур с конфигом переделайте как я выше вам писал. Ардуина Сишный формат с точками наверное не съест.
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
Не нашел, где конфигурируется прерывание от таймера.
Там есть только прерывание от фадера.
А как заставить фадер прерывать после каждого цикла не понятно
 

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
А как заставить фадер прерывать после каждого цикла не понятно
Что вы понимаете под циклом фейдера? Фейдер это плавное изменение скважности ШИМ. Когда требуемое изменение завершается - генерируется прерывание. Вот здесь методы апи как использвать прерывания от фейдера. Здесь пример кода.
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
Что вы понимаете под циклом фейдера? Фейдер это плавное изменение скважности ШИМ. Когда требуемое изменение завершается - генерируется прерывание. Вот здесь методы апи как использвать прерывания от фейдера. Здесь пример кода.
да я в даташите прочитал что такое фадер
Похоже у них там ничего не доделано
есть в заголовках перечисление
typedef enum {
LEDC_INTR_DISABLE = 0, /*!< Disable LEDC interrupt */
LEDC_INTR_FADE_END, /*!< Enable LEDC interrupt */
LEDC_INTR_MAX,
} ledc_intr_type_t;
но работает только первый пункт, остальные дают "недопустимое значение"
Есть функция ledc_isr_register
она похоже вообще не работает.
Я уж рад всё настроить напрямую в регистрах по даташиту, но пока не понял, как регистрировать обработчик произвольного прерывания. Не охота и это делать через хардверные регистры.

За примерчики СПАСИБО, я их ещё не находил
 
Изменено:

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
Похоже у них там ничего не доделано
А вы пробовали то запустить хоть что-нибудь, те же примеры из ИДФ или просто так утверждаете? ШИМ работает, фейдер тоже работает. Прерывание по завершению работы вызывает. Я свою библиотеку написал для управления ledc. Всё что заявленно всё работает.

есть в заголовках перечисление
typedef enum {
LEDC_INTR_DISABLE = 0, /*!< Disable LEDC interrupt */
LEDC_INTR_FADE_END, /*!< Enable LEDC interrupt */
LEDC_INTR_MAX,
} ledc_intr_type_t;
но работает только первый пункт, остальные дают "недопустимое значение"
перечиление не может работать или не работать, это набор допустимых значений и их тут два - "прерывание фейдера выключено", "прерывание фейдера включено". LEDC_INTR_MAX - это размер энумератора, т.е. 2 ("два").

Есть функция ledc_isr_register
она похоже вообще не работает.
Работает, без нее не работал бы фейдер. Она, собственно и подключает фейдер к ШИМ каналу. Вот тут она используется.
Хотите свой обработчик прерывания от ШИМ канала реализовать - подключайте его вместо обработчика фейдера и реализуйте там свои циклические хотелки.

Я уж рад всё настроить напрямую в регистрах по даташиту, но пока не понял, как регистрировать обработчик произвольного прерывания.
не знаю что есть обработчик произвольного прерывания, обычно аппаратные прерывания бывают вполне конкретные, какие-то свои уже не придумаешь.
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
А вы пробовали то запустить хоть что-нибудь, те же примеры из ИДФ или просто так утверждаете?
ага, запустил, СПС благодаря Вам
ШИМ работает, фейдер тоже работает. Прерывание по завершению работы вызывает.
у меня фейдер тоже заработал и прерывание вызывает. ОК.
Я свою библиотеку написал для управления ledc. Всё что заявленно всё работает.
круто !
LEDC_INTR_MAX - это размер энумератора, т.е. 2 ("два").
воон оно что !
Работает, без нее не работал бы фейдер. Она, собственно и подключает фейдер к ШИМ каналу. Вот тут она используется.
Хотите свой обработчик прерывания от ШИМ канала реализовать - подключайте его вместо обработчика фейдера и реализуйте там свои циклические хотелки.
тут не всё понятно
я писал ledc_isr_register но у меня почему-то зарегистрированный через неё хэндлер никак не запускался
Я подозреваю, чтобы он запустился, нужно было зарядить и запустить фэйдэр.
А Вы говорите, что она подключает фэйдэр к ШИМ каналу.
На сколько я понял это при инициализации фэйдэра функция ledc_isr_register вешает на фэйдэровское прерывание обработчик.
А мне в идеале надо не фэйдеровское а ШИМ-овское прерывание по переполнению ШИМ-овского таймера.
не знаю что есть обработчик произвольного прерывания, обычно аппаратные прерывания бывают вполне конкретные, какие-то свои уже не придумаешь.
ну есть функции типа xxxAttachInterrupt для конкретной периферии, коих для ШИМ-овских таймеров я не нашел
 

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
Я подозреваю, чтобы он запустился, нужно было зарядить и запустить фэйдэр.
А Вы говорите, что она подключает фэйдэр к ШИМ каналу.
На сколько я понял это при инициализации фэйдэра функция ledc_isr_register вешает на фэйдэровское прерывание обработчик.
фэйдер это fsm автомат. Программная часть настраивает регистры и хранит состояние, обеспечивает блокировки и т.п. При "запуске" фейдера инициализируются аппратные счетчики циклического изменения скважности ШИМ в соответсвующем канале. Т.к. сам процесс асинхронный, то прерывание ему нужно чтобы программная часть "поняла" когда закончится переходный процесс в аппаратной части. Если вы хотите подсунуть свой обработчик в ledc_isr_register() то вы должны также обеспечить и всю программную часть - инициализировать счетчики к соответсвующему каналу, расчитать и инициировать процесс затухания и затем корректно обработать прерывание. Не надейтесь просто подпихнуть что-то свое и при этом ничего не сломать.

А мне в идеале надо не фэйдеровское а ШИМ-овское прерывание по переполнению ШИМ-овского таймера.
ну есть функции типа xxxAttachInterrupt для конкретной периферии, коих для ШИМ-овских таймеров я не нашел
Это прерывание не доступно в пользовательком коде, оно служит для сброса счетчика делителя частоты, потому и не нашли.
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
Это прерывание не доступно в пользовательком коде, оно служит для сброса счетчика делителя частоты, потому и не нашли.
я увидел 3 источника прерываний связаных с ШИМом
#define ETS_PWM0_INTR_SOURCE 39/**< interrupt of PWM0, level,
#define ETS_PWM1_INTR_SOURCE 40/**< interrupt of PWM1, level,
#define ETS_LEDC_INTR_SOURCE 43/*< interrupt of LED PWM, level/

А по даташиту тоже есть 3 источника
Даташит написал(а):
[Прерывания LEDC]

LEDC_OVF_CNT_CHn_INT: сработает, когда счетчик таймера переполнится (LEDC_OVF_NUM_CHn + 1) раз, и регистр LEDC_OVF_CNT_EN_CHn установлен в 1.

LEDC_DUTY_CHNG_END_CHn_INT: сработает, когда завершится фейдинг генератора PWM.
  • переполнение таймера
  • n-кратное переполнение таймера
  • фаде-финиш (как я понял ETS_LEDC_INTR_SOURCE )
Что чему соответствует не понятно

В файле ledc.c используется на сколько я понял только фаде-финиш
значит мне надо как-то вобход функции ledc_isr_register зарегистрировать свой обработчик другого источника прерываний
 
Изменено:

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
Что чему соответствует не понятно
В файле ledc.c используется на сколько я понял только фаде-финиш
у вас всё смешалось в голове (люди, кони...)
ETS_PWM* это прерывания от mcpwm движка, к ledc это никакого отношения не имеет. Если вам так уперлось влезть в шим таймер, ковыряйте mcpwm движек, там можно много разных событий отлавливать, управлять одиночными синфазными/парафазными сигналами и пр., но он несколько сложнее чем ledc. Чтобы там нормально обрабатывать прерывания нужно использовать функции ртос (семафоры, очереди и это всё прочее).

значит мне надо как-то вобход функции ledc_isr_register зарегистрировать свой обработчик другого источника прерываний
ерунду говорите...
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
ETS_PWM* это прерывания от mcpwm движка
модуль захват-сравнение ? Откуда инфа ?
да. там реально мне совершенно ничего не нужно.
Тогда вопрос:
Можно ли заставить прерывание ETS_LEDC_INTR_SOURCE через которое работатет ledc срабатывать после каждого переполнения таймера ?
И всё таки жаль, что у них недоделана возможность подключать все виды прерываний от модуля ШИМ.
Попробовать ли всё же поработать напрямую через регистры
 
Изменено: