Page 1 of 1

Difficulty setting up an LEDC interrupt tied to LEDC_OVF_CNT_CH0_INT

Posted: Wed Sep 27, 2023 1:51 am
by HikariFaith
Unfortunately, ESP-IDF doesn't seem to have built-in support for LEDC interrupts that aren't tied to fade completion, so I've been trying to write code for it myself that doesn't rely on all the levels of abstraction used in ESP-IDF. I've gone through a bunch of iterations of the code I'm sharing already, and none of it has been successful at generating a single successful interrupt. The aim of the interrupt is to do the following:
1. trigger every X number cycles (to give time for later steps to work effectively)
2. use a callback function (or something of the sort) to trigger an ADC read (with the ADC set to operate at [LEDC_FREQ] / [CONSTANT] Hz in order to ensure the reads are perfectly synchronized with the PWM)
3. follow the ADC read, likely on completion, with a PID loop that adjusts the PWM duty cycle toward a given target

My most current code is as follows (I'm using the ESP32-S2). It's able to properly run the PWM, but the interrupt is still completely unresponsive:

Code: Select all

#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <driver/ledc.h>
#include <esp_intr_alloc.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#include <esp_err.h>


#define HVCTL_PIN               1


/* LEDC DEFINITIONS */
#define DR_REG_LEDC_BASE        0x3F419000
#define LEDC_CH0_CONF0_REG      (DR_REG_LEDC_BASE + 0x00)
#define LEDC_TIMER0_CONF_REG    (DR_REG_LEDC_BASE + 0xA0)
#define LEDC_INT_ST_REG         (DR_REG_LEDC_BASE + 0xC4)
#define LEDC_INT_ENA_REG        (DR_REG_LEDC_BASE + 0xC8)
#define LEDC_INT_CLR_REG        (DR_REG_LEDC_BASE + 0xCC)
#define LEDC_CONF_REG           (DR_REG_LEDC_BASE + 0xD0)

#define LEDC_CH0_CONF0          (*((volatile uint32_t *)LEDC_CH0_CONF0_REG))
#define LEDC_TIMER0_CONF        (*((volatile uint32_t *)LEDC_TIMER0_CONF_REG))
#define LEDC_INT_ENA            (*((volatile uint32_t *)LEDC_INT_ENA_REG))
#define LEDC_INT_CLR            (*((volatile uint32_t *)LEDC_INT_CLR_REG))
#define LEDC_APB_CLK_SEL        (*((volatile uint32_t *)LEDC_CONF_REG))


/* GPIO DEFINITIONS */
#define DR_REG_GPIO_BASE        0x3F404000
#if (HVCTL_PIN < 32)
#define GPIO_ENABLE_W1TS_REG    (DR_REG_GPIO_BASE + 0x24)
#else
#define GPIO_ENABLE_W1TS_REG    (DR_REG_GPIO_BASE + 0x30)
#endif

#define GPIO_OUT_ENABLE         (*((volatile uint32_t *)GPIO_ENABLE_W1TS_REG))


/* IO MUX DEFINITIONS */
#define DR_REG_IO_MUX_BASE      0x3F409000
#define GPIO_FUNC_OUT_SEL_REG   (DR_REG_IO_MUX_BASE + 0x0554 + 4*HVCTL_PIN)

#define GPIO_FUNC_SEL           (*((volatile uint32_t *)GPIO_FUNC_OUT_SEL_REG))


#define LEDC_CLK_SEL            (1)         // use APB_CLK (80 MHz)

#define LEDC_OVF_NUM            (9 << 5)    // 10-count overflow
#define LEDC_OVF_CNT_EN         (1 << 15)   // enable overflow counter

#define LEDC_DUTY_RES           (8)         // 8-bit duty resolution
#define LEDC_CLK_DIV            (234 << 4)  // 342 kHz duty cycle (actually 341880 Hz)
#define LEDC_TIMER_PARA_UP      (1 << 25)   // update LEDC timer settings

#define LEDC_INT_MASK           (1 << 12)
#define LEDC_INT_FLAGS          (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM)


ledc_timer_config_t t_cfg =
{
  .duty_resolution = LEDC_TIMER_8_BIT,
  .timer_num = LEDC_TIMER_0,
  .freq_hz = 10000,
  .clk_cfg = LEDC_APB_CLK
};


ledc_channel_config_t c_cfg =
{
  .gpio_num = 1,
  .channel = LEDC_CHANNEL_0,
  .timer_sel = LEDC_TIMER_0,
  .duty = 0
};


static TaskHandle_t s_task_handle;
static bool callback = false;

void IRAM_ATTR ledc_cb()
{
  LEDC_INT_CLR = LEDC_INT_MASK;
  vTaskNotifyGiveFromISR(s_task_handle, NULL);
  callback = true;
}


void app_main(void)
{
  s_task_handle = xTaskGetCurrentTaskHandle();

  ledc_timer_config(&t_cfg);
  ledc_channel_config(&c_cfg);

  printf("%ld\n", ledc_get_freq(0, LEDC_TIMER_0));

  /*set LEDC signal in gpio matrix*/
  GPIO_FUNC_SEL = 1;
  GPIO_OUT_ENABLE = (1 << (HVCTL_PIN % 32));
  esp_rom_gpio_connect_out_signal(HVCTL_PIN, 79, 0, 0);

  LEDC_APB_CLK_SEL = LEDC_CLK_SEL;
  LEDC_CH0_CONF0 |= LEDC_OVF_NUM | LEDC_OVF_CNT_EN;
  LEDC_TIMER0_CONF = LEDC_DUTY_RES | LEDC_CLK_DIV | LEDC_TIMER_PARA_UP;

  printf("%ld\n", ledc_get_freq(0, LEDC_TIMER_0));

  ESP_ERROR_CHECK(esp_intr_alloc_intrstatus(45, LEDC_INT_FLAGS, LEDC_INT_ST_REG, LEDC_INT_MASK, ledc_cb, NULL, NULL));
  LEDC_INT_ENA = LEDC_INT_MASK;

  while(1)
  {
    ESP_ERROR_CHECK(ulTaskNotifyTake(pdTRUE, portMAX_DELAY));
    if(callback) return;
    vTaskDelay(1);
  }
}