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

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
модуль захват-сравнение ? Откуда инфа ?
причем здесь "захват-сравнение"? В есп32 два mcpwm движка, это 2 источника их прерываний. Инфа из хвгайда, 16й раздел Motor Control PWM (PWM).

Тогда вопрос:
Можно ли заставить прерывание ETS_LEDC_INTR_SOURCE через которое работатет ledc срабатывать после каждого переполнения таймера ?
вы путаете причину и следствие. ETS_LEDC_INTR_SOURCE это КОД источника прерывания, т.е. какой компонент вообще вызвал прерывание. Все прерывания какие только технически реализованы в железе в ледц движке во время его работы будут генериться с источником ETS_LEDC_INTR_SOURCE. А что именно вызвало прерывание - переполнение счетчика или фейдер и в каком именно канале это уже дело 10е и решать это обработчику.

И всё таки жаль, что у них недоделана возможность подключать все виды прерываний от модуля ШИМ. Попробовать ли всё же поработать напрямую через регистры
Ну хвгайд у вас есть, описание регистров тоже. В документации указывается возможность зарегать несколько обработчиков на один источник. Если дописать в хал ледц работу с необходимыми битами отвечающими за прерывания счетчика таймера, то может и получится что-то. Задача на чисто академический интерес, применительно к вашей задаче со звуком, как по мне, это лишено всякого смысла.
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
А что именно вызвало прерывание - переполнение счетчика или фейдер и в каком именно канале это уже дело 10е и решать это обработчику.
воон оно как !
Пришлось покурить контроллер прерываний.
Это в старых процах каждый несчастный счетчик генерил отдельное прерывание и обрабатывался со своего собственного вектора. А тут на весь модуль даётся всего один вектор.
Задача на чисто академический интерес
нее это не про меня.
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
откопал функцию ledc_ll_get_fade_end_intr_status(ledc_dev_t *hw, ledc_mode_t speed_mode, uint32_t *intr_status)
в файле ledc_ll.h
Подскажите, пожалуйста, что ей передавать в качестве первого аргумента.
 

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
Судя по всему указатель на структуру регистров LEDC движка. Где она объявляется - не знаю, поищите.
 

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
А как вы тогда планируете этим воспользоваться? Ну найду я вам где-то внутре IDF компонента статический объект. Если вы думаете что сможете в ардуино скетче просто так дернуть эту функцию вписав ей в аргумент имя указателя из стороннего файла, то это так не работает. Для начала поставьте IDF, научитесь собирать компоненты и писать код под чистый идф, хотя бы запускать примеры. Тогда сможете приступить к правке кода движка ледц.
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
Избыточное цитирование. Отредактируй или сообщение будет удалено
А как вы тогда планируете этим воспользоваться? Ну найду я вам где-то внутре IDF компонента статический объект. Если вы думаете что сможете в ардуино скетче просто так дернуть эту функцию вписав ей в аргумент имя указателя из стороннего файла, то это так не работает. Для начала поставьте IDF, научитесь собирать компоненты и писать код под чистый идф, хотя бы запускать примеры. Тогда сможете приступить к правке кода движка ледц.
На сколько я понял это статичная цифра это похоже хардверный адрес начала пула памяти, где лежат ШИМовские регистры. И зависит она только от модели чипа.
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
Избыточное цитирование. Отредактируй или сообщение будет удалено
Не получилось. Это слово знает только файл ledc_ll.h
если его инклудить или ledc_hal.h
то возникает ошибка
invalid conversion from 'unsigned int' to 'ledc_slow_clk_sel_t' [-fpermissive]
*slow_clk_sel = hw->conf.slow_clk_sel;
~~~~~~~~~^~~~~~~~~~~~
 
Изменено:

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
Я вам об этом и говорил, так просто в лоб это не заработает. Указатель на адрес регистров ледц и структура этих регистров не взаимозаменяемы. Иначе бы в функции просто вставили макрос. Приводите одно к другому или смотрите как, что и зачем сделано в коде идф.
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
Ошибка возникает даже если я вообще не вызываю никакие функции, ничего не объявляю.
Просто добавляю
#include "hal/ledc_hal.h"
и уже появляется ошибка
C:\Users\b612\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.4/tools/sdk/esp32/include/hal/esp32/include/hal/ledc_ll.h:475:75: error: invalid conversion from 'unsigned int' to 'ledc_timer_t' [-fpermissive]
*timer_sel = hw->channel_group[speed_mode].channel[channel_num].conf0.timer_sel;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
exit status 1

Значит это вообще не связано с неправильной передачей каких-либо аргументов
 

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
Приводите одно к другому или смотрите как, что и зачем сделано в коде идф.
@b612, вам таки шашечки или ехать? Вы просили адрес регистров движка ледц - я вам его дал.
Хотите чтобы компилятор тупо сожрал что-то или вам все-таки нужен какой-то осмысленный результат?
Если 1й вариант, то собирайте с опцией компилятора "-fpermissive", тогда ошибка станет варнингом и он перестанет валится.
Или правьте хедер и приводите rvalue к энумератору. Компилятору не нравится как в заголовке приводят типы и он вам об этом сообщает. Ардуино использует c++ компилятор, идф свое ядро собирает С компилятором. Почему в идф написали как написали это вопрос в идф, там далеко не все идеально и местами корявого кода хватает. Но среда сборки у идф своя и параметры сборки настраиваются по-другому. В ардуино компоненты идф уже включают в скомпилированном виде. Поэтому либо вы таки возметесь за идф если собираетесь править код компонентов либо вам придется реализовать свой хал для ардуины. Вызовом пары функций надерганых из заголовков вы далеко не уедете.
Но я вас ни в коей мере не останавливаю - ковыряйтесь, попутно много чего изучите, даже если своего не добьетесь. :)
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
либо вам придется реализовать свой хал для ардуины. Вызовом пары функций надерганых из заголовков вы далеко не уедете.
ок, там и реализовывать вообщем нечего. Всего те самые пару функций по две строчки каждая.
И работать они должны на самом низком уровне. Они даже прописаны полностью в заголовочном файле.

Просто чтобы в моём проекте работала функция подобная этой

static inline void ledc_ll_set_fade_end_intr(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, bool fade_end_intr_en)
{
uint32_t value = hw->int_ena.val;
uint32_t int_en_base = LEDC_DUTY_CHNG_END_LSCH0_INT_ENA_S;
hw->int_ena.val = fade_end_intr_en ? (value | BIT(int_en_base + channel_num)) : (value & (~(BIT(int_en_base + channel_num))));
}
Для этого мне всего навсего нужно hw->int_ena.val
это вообще-то просто хардверный адрес регистра.

Вы мне сказали про LEDC_LL_GET_HW(), но это макрос, который определён в файлике ledc_ll.h
#define LEDC_LL_GET_HW() &LEDC
Значит мне теперь надо найти, что такое &LEDC ?

Или можно подойти с другой стороны.
У меня не получилось заставить функцию
ledc_isr_register(...
Зарегистрировать мой собственный обработчик прерываний.
Она прекрасно работает внутри ledc_fade_func_install, но не работает у меня.
 
Изменено:

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
ок, там и реализовывать вообщем нечего. Всего те самые пару функций по две строчки каждая. И работать они должны на самом низком уровне.
вас не поймешь, то вам самый низкий уровень нужен, то жалуетесь что функции ИДФ у вас "не работают".
точно хотите всё сами через регистры сделать? тогда это все что вам нужно:
C++:
#include "soc/ledc_struct.h"
ledc_dev_t *hw = (ledc_dev_t*)DR_REG_LEDC_BASE;
дальше давайте сами в пару строк взводите биты, регистрируйте свой обработчик, подключатей базовый тактовый генератор, программируйте ледц таймеры, выставляйте делители, мапьте пины, взводите фейдер - все регистры у вас доступны через hw->*
Если вы думаете что "зарегать свой обработчик" это прицепить свою функцию которая будет просто вызываться по событию, то очень сильно заблуждаетесь. Обработчик прерывания занимается совсем другими задачами, а то что вы хотите это колл-бэк, который может напоследок дернуть обработчик прерывания.
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
дальше давайте сами в пару строк взводите биты, регистрируйте свой обработчик, подключатей базовый тактовый генератор, программируйте ледц таймеры, выставляйте делители, мапьте пины, взводите фейдер - все регистры у вас доступны через hw->*
нее это пусть делает библиотека
мне не нравится только её обработчик прерываний, потому, что он вызывает калбэк только когда заканчивается fade
Да ещё после него надо снова стартовать её ...
Амне до этой фады вообще никакого дела нет.
Я поймаю прерывание, сброшу тупо все флаги, ставлю новый DUTY и спокойно жду следующее прерывание
 

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
Амне до этой фады вообще никакого дела нет.
Я поймаю прерывание, сброшу тупо все флаги, ставлю новый DUTY и спокойно жду следующее прерывание
ну дерзайте если все так просто ) но что-то мне подсказывает что закончится всё тем же "у них там ничего не работает". Асинхронное программирование не такая простая штукак как может показаться
 

b612

✩✩✩✩✩✩✩
26 Окт 2019
37
5
В кои-то веки дошли руки продолжить.
ЗАРАБОТАЛО !
Преогромнейшая благодарность vortigont !!!

Код генерирует синусоиду с частотой регулируемой энкодером
Можно и попросить его генерировать музыкальные ноты.
Соответственно имеем на ESP32 16 почти независимых каналов вывода звука через модуль LEDC
И к стати шумность и разрядность можно получить на порядок лучше чем через бортовой АЦП.

Следующая цель портировать с ардуины полефонию и FM-синтез

C++:
#define LED 2
#include "driver/ledc.h"
/* LEDC (LED Controller) basic example
*/
#include <stdio.h>
#include "soc/ledc_reg.h"
#include "soc/ledc_struct.h"
ledc_dev_t *hw = (ledc_dev_t*)DR_REG_LEDC_BASE;
static ledc_isr_handle_t s_ledc_timer_isr_handle = NULL;

#define LEDC_TIMER              LEDC_TIMER_0
#define LEDC_MODE               LEDC_LOW_SPEED_MODE
#define LEDC_OUTPUT_IO          (25) // Define the output GPIO
#define LEDC_CHANNEL            LEDC_CHANNEL_0
#define LEDC_DUTY_RES           LEDC_TIMER_8_BIT // Set duty resolution to 13 bits
#define LEDC_DUTY               (127) // Set duty to 50%. ((2 ** 8) - 1) * 50% = 4095
#define LEDC_FREQUENCY          (22500) // Frequency in Hertz. Set frequency at 5 kHz

int8_t sine[256];
long oldPosition=0;
#define nch 4 //number of channels that can produce sound simultaneously
uint16_t phase[nch]  = {0, 0, 0, 0};
int          inc[nch]    = {0, 0, 0, 0};
byte         amp[nch]    = {127, 0, 0, 0};

static void example_ledc_init(void)
{
    // Prepare and then apply the LEDC PWM timer configuration
    ledc_timer_config_t ledc_timer = {
        .speed_mode       = LEDC_MODE,
        .duty_resolution  = LEDC_DUTY_RES,
        .timer_num        = LEDC_TIMER,
        .freq_hz          = LEDC_FREQUENCY,  // Set output frequency at 5 kHz
        .clk_cfg          = LEDC_AUTO_CLK
    };
    ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));

    // Prepare and then apply the LEDC PWM channel configuration
    ledc_channel_config_t ledc_channel = {
        .gpio_num       = LEDC_OUTPUT_IO,
        .speed_mode     = LEDC_MODE,
        .channel        = LEDC_CHANNEL,
        .intr_type      = LEDC_INTR_DISABLE,//LEDC_INTR_MAX,
        .timer_sel      = LEDC_TIMER,
        .duty           = 0, // Set duty to 0%
        .hpoint         = 0  
    };
    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
}
static inline void ledc_lll_clear_timer_ovf_intr_status(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num){
    //uint32_t int_en_base = LEDC_LSTIMER0_OVF_INT_ST_S;
    uint32_t int_en_base = LEDC_LSTIMER0_OVF_INT_CLR_S;
    hw->int_clr.val = BIT(int_en_base + channel_num);
}
static inline void ledc_lll_set_timer_ovf_intr(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, bool fade_end_intr_en){
    uint32_t value = hw->int_ena.val;
    uint32_t int_en_base = LEDC_LSTIMER0_OVF_INT_ENA_S;
    hw->int_ena.val = fade_end_intr_en ? (value | BIT(int_en_base + channel_num)) : (value & (~(BIT(int_en_base + channel_num))));
}

void IRAM_ATTR ledc_isr_fnc(void * arg)
{
  //r++;
  digitalWrite(LED,HIGH);        
  ledc_lll_clear_timer_ovf_intr_status(hw,LEDC_MODE,LEDC_CHANNEL);

   int val;
      // //increment the phases of the note
    phase[0] += inc[0];

      // //calculate the output value and set pulse width for timer2
    val = sine[(phase[0]) >> 8] * amp[0];

    hw->channel_group[LEDC_MODE].channel[LEDC_CHANNEL].duty.duty =   ((val/256)+128)<< 4;
   hw->channel_group[LEDC_MODE].channel[LEDC_CHANNEL].conf0.low_speed_update = 1;


}

//set up array with sine values in signed 8-bit numbers
const float pi = 3.14159265;
void setsine() {
  for (int i = 0; i < 256; ++i) {
    sine[i] = (sin(2 * 3.14159265 * (i + 0.5) / 256)) * 128;

  Serial.print(F("sine["));
  Serial.print(i);
  Serial.print(F("] "));
  Serial.println(sine[i],DEC);


    //sine2[i] = (sin(6 * 3.14159265 * (i + 0.5) / 256)) * 128;
    //sine2[i] = sine[i];
  }
}

//setup frequencies/phase increments, starting at C3=0 to B6. (A4 is defined as 440Hz)
unsigned int tone_inc[48];
void settones() {
  for (byte i = 0; i < 48; i++) {
    tone_inc[i] = 1760.0 * pow(2.0, ( (i - 21) / 12.0)) * 65536.0 / (16000000.0 / 512) + 0.5;
  Serial.print(F("tone_inc["));
  Serial.print(i);
  Serial.print(F("] "));
  Serial.println(tone_inc[i]);

  }
}

void FMsetup() {
    pinMode(LED,OUTPUT);
    digitalWrite(LED,LOW);      
  //disable all inerrupts to avoid glitches
  //noInterrupts();
  //Serial.begin(115200);

  //setup the array with sine values
  setsine();

  //setup array with tone frequency phase increments
  settones();

  //Set a fast PWM signal on TIMER1A, 9-bit resolution, 31250Hz
/*   pinMode(9, OUTPUT);
  TCCR1A = 0B10000010; //9-bit fast PWM
  TCCR1B = 0B00001001;
  // включить прерывание Timer1 overflow:
  TIMSK1 = (1 << TOIE1);

// */
delay(1000);
          Serial.println(F("func_install"));
          example_ledc_init();
          Serial.print(F("esp_intr_alloc "));
         esp_err_t ret;                  
         ret = esp_intr_alloc(ETS_LEDC_INTR_SOURCE, ESP_INTR_FLAG_IRAM, ledc_isr_fnc, NULL, &s_ledc_timer_isr_handle);
         Serial.println(ret);  
         ledc_lll_set_timer_ovf_intr(hw, LEDC_MODE,LEDC_CHANNEL, true);
         hw->channel_group[LEDC_MODE].channel[LEDC_CHANNEL].conf0.sig_out_en = true;
          hw->channel_group[LEDC_MODE].channel[LEDC_CHANNEL].conf0.low_speed_update = 1;
          hw->channel_group[LEDC_MODE].channel[LEDC_CHANNEL].hpoint.hpoint = 0;
          hw->channel_group[LEDC_MODE].channel[LEDC_CHANNEL].duty.duty = LEDC_DUTY << 4;
  //Serial.end();

  //interrupts();
}




#include <ESP32Encoder.h> // https://github.com/madhephaestus/ESP32Encoder.git

#define CLK 12 // CLK ENCODER
#define DT 13 // DT ENCODER

ESP32Encoder encoder;

void setup () {
  encoder.attachHalfQuad ( DT, CLK );
  encoder.setCount ( 0 );
  ESP32Encoder::useInternalWeakPullResistors=UP;
  Serial.begin ( 115200 );
  FMsetup();
  inc[0]=tone_inc[10];
  Serial.print(F("inc[0] "));
  Serial.println(inc[0]);
}

void loop () {
    long newPosition = encoder.getCount() / 2;
    if (newPosition < 0)
    {
      newPosition=0;
    }
    if (newPosition > 17000)
    {
      newPosition=17000;
    }
      if (oldPosition != newPosition)
      {
        Serial.print(newPosition);
        oldPosition = newPosition;
        //inc[0]=tone_inc[newPosition/100];
        inc[0]=newPosition+1000;
        Serial.print(F("inc="));
        Serial.println(inc[0]);
       }

}
 
Изменено:
  • Лойс +1
Реакции: vortigont

vortigont

★★★★★★✩
24 Апр 2020
1,022
542
Saint-Petersburg, Russia
похвально! код не глядел, но если всё задуманное получилось, то это прекрасный результат!
понакидайте еще комментариев в код - и самому через какое-то время проще будет и другим легче разобраться