ESP32 PWM duty cycle measurement from input

1337ralfy
Posts: 3
Joined: Wed Mar 02, 2022 11:55 am

ESP32 PWM duty cycle measurement from input

Postby 1337ralfy » Wed Mar 02, 2022 2:23 pm

I am trying to measure the duty cycle interval (from falling to rising edge) in microseconds of an incoming PWM signal of 20 KHz.

I made a test PWM signal on a GPIO pin (50% duty) and connected it to input pin.

Up to 80 microseconds it is all good and stable, but if I raise the PWM frequency, below 80 µs the readings start to get unstable and unusable, the value starts jumping around and doesn't show a "real" value. Why so?

Code: Select all

static void IRAM_ATTR rising_handler(void* arg) {
uint32_t gpio_num = (uint32_t)arg;
xQueueSendFromISR(rising_evt_queue, &gpio_num, NULL);
}

static void rising_task(void* arg) {
uint32_t io_num;
for (;;) {
    if (xQueueReceive(rising_evt_queue, &io_num, portMAX_DELAY)) {
        // ESP_LOGI(INTERUPT_TAG, "GPIO_INTR_NEGEDGE!!!");
        if (gpio_get_level(io_num)) {
            // ESP_LOGI(INTERUPT_TAG, "True!!!");
            pwm_value = esp_timer_get_time() - prev_time;
        }
        if (!gpio_get_level(io_num)) {
            // ESP_LOGI(INTERUPT_TAG, "False!!!");
            prev_time = esp_timer_get_time();
        }
    }
  }
}

void app_main(void) {
initPeriodicTimer(100000); // func to trigger output pin, so it can be readed by input pin
attachInterupt(4, rising_task, rising_handler, GPIO_INTR_ANYEDGE, true);

while (1) {
    // if (flag) {
    printf("%lu\n", pwm_value);
    // flag = false;
    // }
    vTaskDelay(50 / portTICK_RATE_MS);
  }
}

void attachInterupt(uint8_t gpio, voidFuncPtrArg userTask, voidFuncPtrArg isr_handler, uint8_t, triggerEdge, bool installISR) {
gpio_config_t io_conf;
// interrupt of rising edge
io_conf.intr_type = triggerEdge;
// bit mask of the pins, use GPIO4/5 here
io_conf.pin_bit_mask = (1ULL << gpio);
// set as input mode
io_conf.mode = GPIO_MODE_INPUT;
// enable pull-up mode
io_conf.pull_up_en = 1;
gpio_config(&io_conf);
if (installISR) {
    // install gpio isr service
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
}
rising_evt_queue = xQueueCreate(10, sizeof(uint32_t));
xTaskCreate(rising_task, "gpio_task_example", 2048, NULL, 10, NULL);
// hook isr handler for specific gpio pin
gpio_isr_handler_add(gpio, isr_handler, (void*)gpio);
}

mikemoy
Posts: 626
Joined: Fri Jan 12, 2018 9:10 pm

Re: ESP32 PWM duty cycle measurement from input

Postby mikemoy » Thu Mar 03, 2022 4:34 am

your running in a RTOS environment. There is latency for things to get done.

liebman
Posts: 18
Joined: Wed Dec 09, 2020 7:03 pm

Re: ESP32 PWM duty cycle measurement from input

Postby liebman » Thu Mar 03, 2022 12:36 pm

Maybe use xt_highint4 or xt_highint5? You'll have to write it in assembler but its very low latency. (and the latency is more consistent) https://docs.espressif.com/projects/esp ... rupts.html. I've used it to measure the time offset of two signals to sync a DS3231 with a GPS PPS.

mikemoy
Posts: 626
Joined: Fri Jan 12, 2018 9:10 pm

Re: ESP32 PWM duty cycle measurement from input

Postby mikemoy » Thu Mar 03, 2022 8:24 pm

I was thinking...
How about creating a volatile variable, and in your rising_handler you just increment it.

Then in another task, when a certain amount of time has passed you read that variable, and clear it.
This should allow you to capture more counts, dunno if you can hit the 20k mark though.

SeungKee, Chea
Posts: 1
Joined: Fri May 20, 2022 7:55 am

Re: ESP32 PWM duty cycle measurement from input

Postby SeungKee, Chea » Fri May 20, 2022 8:26 am

Hi,
I doing some project to measure 0~5us of displacement of duty of input signal.
I used 1 input port to be measure, and two capture module,
one captures at rising edge of input signal and the other captures falling edge.
Save capture value in rising isr routine in a variable(preValue), and do it same in other variable(curValue) in falling isr routine.
If you calculate curValue - preValue = DutyValue, then DutyValue may too large to be real duty value.
When you change duty of input signal and calculate DutyValue again and you can notice that the second DutyValue is changed by the amound of the displacement of duty.
I got 10ns of resolution and stable count value to get the displacement of duty in a test condition.

I use esp32 and Arduino IDE.

static void IRAM_ATTR pxxIsr(void*)
{
prePwData = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP0);
MCPWM0.int_clr.val = CAP0_INT_EN;
}

// get 1024 of duty value
static void IRAM_ATTR pxxIsr2(void*)
{
curPwData = mcpwm_capture_signal_get_value(MCPWM_UNIT_1, MCPWM_SELECT_CAP1);
if(!FullFlag) {
PwData[wrPtr] = curPwData - prePwData;
prePwData = curPwData;
wrPtr++;
if(wrPtr == 1024) FullFlag = 1;
}
MCPWM1.int_clr.val = CAP1_INT_EN;
}

void setup() {

//uart
Serial.begin(115200);

//pins and external interrupt
pinMode(ClkIn, INPUT_PULLUP);

//input capture 0
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_0, pins::pxx);
gpio_pulldown_en((gpio_num_t)pins::pxx);
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, MCPWM_POS_EDGE,0); // set 0~255 is get prescaling 1~256
mcpwm_isr_register(MCPWM_UNIT_0, pxxIsr, nullptr, ESP_INTR_FLAG_EDGE, nullptr); //change ESP_INTR_FLAG_IRAM to ESP_INTR_FLAG_EDGE
MCPWM1.int_ena.val = CAP0_INT_EN;

//input capture 1
mcpwm_gpio_init(MCPWM_UNIT_1, MCPWM_CAP_1, pins::pxx);
gpio_pulldown_en((gpio_num_t)pins::pxx);
mcpwm_capture_enable(MCPWM_UNIT_1, MCPWM_SELECT_CAP1, MCPWM_NEG_EDGE,0); // set 0~255 is get prescaling 1~256
mcpwm_isr_register(MCPWM_UNIT_1, pxxIsr2, nullptr, ESP_INTR_FLAG_EDGE, nullptr); //change ESP_INTR_FLAG_IRAM to ESP_INTR_FLAG_EDGE
MCPWM1.int_ena.val = CAP1_INT_EN;

timer0 = timerBegin(0, 40, true); // 2M

// output test clock, the duty of this signal should be counted
ledcAttachPin(PwmPin, 0); // pin, ch
ledcSetup(0, 40000, 8); // ch, freq, 8
ledcWrite(0, MCPWM_DUTY); //MCPWM_DUTY is from 50 to 200 is good, less or more value can be unstable.
}

void loop() {
if(FullFlag){
for(int i=0; i<1024; i++){
Serial.printf("\nD:%09u",PwData);
}
Serial.printf("\nFinished\n\n");

while(1);
}
}

aakash02208
Posts: 1
Joined: Sat Aug 20, 2022 2:51 pm

Re: ESP32 PWM duty cycle measurement from input

Postby aakash02208 » Sat Aug 20, 2022 2:58 pm

Hi, can you please show the initiallization part of code? i am not able to understand type of PwData[wrPtr] it store int data but what is wrPtr, also by searching online i found that CAP0_INT_EN means BIT(27) correct? like : we need to write #define CAP0_INT_EN BIT(27)
Thankyou Very Very Much for Help

Who is online

Users browsing this forum: Corand and 94 guests