controlling pwm output with a timer interrupt

aarbak
Posts: 3
Joined: Thu Nov 08, 2018 2:06 am

controlling pwm output with a timer interrupt

Postby aarbak » Thu Nov 08, 2018 2:20 am

Hi all,

I am new on forum and also in ESP32. I am using "ledcWrite" command to generate constant frequency, variable duty square wave output.It works fine. I also need to control this pwm with a timer. For example pwm should be enable for 1 second then disable for 1 second. When pwm control function is called from timer interrupt function, following error occurs. I really need help in this matter. Thanks in advance

Code: Select all

***ERROR*** A stack overflow in task IDLE has been detected. Guru Meditation Error: Core 1 panic'ed (Unhandled debug exception) Debug exception reason: Stack canary watchpoint triggered (IDLE) Core 1 register dump: PC : 0x40007d7a PS : 0x00060936 A0 : 0x80089327 A1 : 0x3ffc7d90 A2 : 0x3ffc1664 A3 : 0x3ffc7dc0 A4 : 0x00000001 A5 : 0x3ffc7e00 A6 : 0x3ffb0000 A7 : 0xbaad5678 A8 : 0x3ff40000 A9 : 0x0000007d A10 : 0x0000002a A11 : 0x3ffc7f40 A12 : 0x800d0f0d A13 : 0x3ffc7f20 A14 : 0x3ffc24a8 A15 : 0x00000040 SAR : 0x00000013 EXCCAUSE: 0x00000001 EXCVADDR: 0x00000000 LBEG : 0x400014fd LEND : 0x4000150d LCOUNT : 0xfffffffb Core 1 was running in ISR context: EPC1 : 0x40007d7a EPC2 : 0x00000000 EPC3 : 0x400ea4fa EPC4 : 0x40082db3 Backtrace: 0x40007d7a:0x3ffc7d90 0x40089324:0x3ffc7e20 0x40089340:0x3ffc7e40 0x40086684:0x3ffc7e60 0x40087fa4:0x3ffc7e80 0x40088035:0x3ffc7ea0 ⸮
Last edited by aarbak on Thu Nov 08, 2018 3:14 am, edited 1 time in total.

ESP_Sprite
Posts: 1987
Joined: Thu Nov 26, 2015 4:08 am

Re: controlling pwm output with a timer interrupt

Postby ESP_Sprite » Thu Nov 08, 2018 3:07 am

You cannot call functions in an interrupt unless they're specifically marked for it (e.g. freertos *FromISR functions). I'd suggest you switch to the esp-timer subsystem, as the callback for that normally is done from thread context, allowing you to call the led pwm driver functions safely.

aarbak
Posts: 3
Joined: Thu Nov 08, 2018 2:06 am

Re: controlling pwm output with a timer interrupt

Postby aarbak » Thu Nov 08, 2018 3:22 am

@ESP_Sprite

Thanks for your response. I realized that i posted on wrong topic. It should be in ESP32 Arduino topic :oops:

Anyway, i am using Arduino IDE. Can i use high resolution timer functions in Arduino IDE?

ESP_Sprite
Posts: 1987
Joined: Thu Nov 26, 2015 4:08 am

Re: controlling pwm output with a timer interrupt

Postby ESP_Sprite » Thu Nov 08, 2018 9:00 am

I'll move your topic over. And yes, you should be able to call any esp-idf function in the Arduino environment. Do take care not to call any Arduino-specific things from an esp-idf callback without triple-checking that doesn't lead to issues, though, as the esp-idf callbacks can be from another thread context and Arduino libraries in general aren't written to be multithread-aware.

aarbak
Posts: 3
Joined: Thu Nov 08, 2018 2:06 am

Re: controlling pwm output with a timer interrupt

Postby aarbak » Thu Nov 08, 2018 3:41 pm

I will try to switch to esp-timer in the future. I found a quick solution. I am changing duty value with timer interrupt (called duty_timer :D ) Main loop checks all the time if duty_timer has changed or not. I know it is not the best way. But does it sound terrible? Here is code

Code: Select all

#include <Wire.h> #include "SSD1306.h" #define LEDC_CHANNEL_0 0 #define LEDC_TIMER_13_BIT 13 SSD1306 display(0x3C, 5,4); unsigned int pwmpin = 16; unsigned int freq = 900; unsigned int duty_set_min = 0; unsigned int duty_set_max = 100; unsigned int duty_timer = 100; unsigned int duty_last = 100; unsigned int resolution = 10; unsigned int aresolution = pow(2,resolution)-1; unsigned int rduty=map(duty_set_max, 0, aresolution, 0, 100); hw_timer_t * timer = NULL; volatile uint8_t stat = 0; void IRAM_ATTR onTimer(){ stat=1-stat; if (stat==1)duty_timer=duty_set_max; else if (stat==0)duty_timer=duty_set_min; } void setup() { Serial.begin(9600); while (!Serial) { } Serial.println("ESP32 PWM GENERATOR V2.2"); Serial.println("Commands:"); Serial.println("'f' to change frequency"); Serial.println("'d' to change duty"); Serial.println("'r' to change resolution"); Serial.println("'p' to change pin number"); display.init(); ledcSetup(0, freq, resolution); ledcAttachPin(pwmpin, 0); ledcWrite(0, duty_set_max); screen(pwmpin,freq,duty_set_max,rduty); timer = timerBegin(1, 80, true); // timer 0, MWDT clock period = 12.5 ns * TIMGn_Tx_WDT_CLK_PRESCALE -> 12.5 ns * 80 -> 1000 ns = 1 us, countUp timerAttachInterrupt(timer, &onTimer, true); // edge (not level) triggered timerAlarmWrite(timer, 1000000, true); // 1000000 * 1 us = 1 s, autoreload true timerAlarmEnable(timer); // enable } void loop() { if (duty_last==duty_timer){ } else { duty_last=duty_timer; pwmsettings(pwmpin,freq,duty_last,resolution); } if (Serial.available()) { char cin = Serial.read(); byte cin1; switch (cin) { case 'f': Serial.println("input new frequency in Hz"); while (!Serial.available()); freq = Serial.parseInt(); pwmsettings(pwmpin,freq,duty_set_max,resolution); Serial.print("Frequency is:"); Serial.print(freq); Serial.println("Hz"); break; case 'd': Serial.print("input new duty (0-"); Serial.print(aresolution); Serial.println(")"); while (!Serial.available()); duty_set_max = Serial.parseInt(); pwmsettings(pwmpin,freq,duty_set_max,resolution); Serial.print("New duty is:"); Serial.println(duty_set_max); break; case 'r': Serial.println("input new resolution (8-16)"); while (!Serial.available()); resolution = Serial.parseInt(); pwmsettings(pwmpin,freq,duty_set_max,resolution); Serial.print("New resolution is:"); Serial.print(resolution); Serial.println(" bit"); break; case 'p': Serial.println("input new pin number"); while (!Serial.available()); pwmpin = Serial.parseInt(); pwmsettings(pwmpin,freq,duty_set_max,resolution); Serial.print("New pin number is:"); Serial.println(pwmpin); break; } } } void screen(unsigned int pwmpin,unsigned int freq, unsigned int duty_set_max,unsigned int rduty) { display.clear(); display.setTextAlignment(TEXT_ALIGN_CENTER); display.setFont(ArialMT_Plain_10); display.drawString(64, 10, "PWM GENERATOR V2.2"); display.setTextAlignment(TEXT_ALIGN_LEFT); display.drawString(0, 24, "Freq: " + String(freq)+"Hz"); display.drawString(0, 36, "Duty:"+String(duty_set_max)+" / "+String(aresolution)+" ("+String(rduty)+"%)"); display.drawString(0, 48, "Pin:"+String(pwmpin)); display.display(); } void pwmsettings(unsigned int pwmpin,unsigned int freq, unsigned int duty_set_max,unsigned int resolution) { aresolution = pow(2,resolution)-1; rduty=map(duty_set_max, 0, aresolution, 0, 100); ledcSetup(0, freq, resolution); ledcAttachPin(pwmpin, 0); ledcWrite(0, duty_set_max); screen(pwmpin,freq,duty_set_max,rduty); }
Last edited by aarbak on Fri Nov 09, 2018 2:19 pm, edited 1 time in total.

ESP_Sprite
Posts: 1987
Joined: Thu Nov 26, 2015 4:08 am

Re: controlling pwm output with a timer interrupt

Postby ESP_Sprite » Fri Nov 09, 2018 8:12 am

That... will work, but pins your CPU at 100% and possibly anger the task watchdog. In normal esp-idf, you'd use something like a semaphore to handle this; FreeRTOS would suspend your task and do other things (or even sleep, saving power) while waiting for the interrupt to trigger.

Return to “ESP32 Arduino”

Who is online

Users browsing this forum: No registered users and 8 guests