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: 9727
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: 9727
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: 9727
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.

Who is online

Users browsing this forum: jesper and 36 guests