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: 2378
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: 2378
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: 2378
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