Copyright © https://mongoose-os.com

Mongoose OS Forum

frame
ATTENTION! This forum has moved to:

https://community.mongoose-os.com

Do not post any new messages.

Guru Meditation on PWM leads to core 0 panic

jstevewoodjstevewood California
edited April 2018 in Mongoose OS

I am trying to turn a high speed PWM on and off with a Hardware Timer (this example slowed down by at least a factor of 10). The included code results in "Guru Meditation Error: Core 0 panic'd (Coprocessor exception). If I comment out the pwm_set call in the HW timer callback function, I get a nicely initialized 70% duty cycle 1000 Hz pulse train. If I comment out both PWM statements and substitute them with the GPIO statements shown immediately above the PWM statements, I get a properly shaped pulse train, high for 2 seconds, and then low for the remainder of the cycle. Any thoughts on what is happening? Thanks, JSW

#include "mgos.h"
#include "mgos_pwm.h"

int i=0;
int gpin =14;
int hpin =12;
int pfreq=1000;
float dcycle=0.0;
int timer0=0;

static void pulse_width(void *arg) {
dcycle=0.0; 
pfreq=1000;
//mgos_gpio_write(hpin,0);
mgos_pwm_set(hpin, pfreq, dcycle);
  (void) arg;
}

static void led_timer_cb(void *arg) {
 timer0=mgos_set_hw_timer(2000000, 0, pulse_width, NULL);
if (i%2==1){dcycle=0.7;pfreq=1000;};
if (i%2==0){dcycle=0.0;pfreq=1000;};
printf("i: %d  dcycle:  %f \r\n",i,dcycle);
i++;    
//mgos_gpio_write(hpin,1);
mgos_pwm_set(hpin,pfreq,dcycle);
bool val = mgos_gpio_toggle(gpin);
 (void) arg;
}

enum mgos_app_init_result mgos_app_init(void) {
    mgos_gpio_set_mode(gpin, MGOS_GPIO_MODE_OUTPUT);
    mgos_gpio_set_mode(hpin, MGOS_GPIO_MODE_OUTPUT);
    mgos_set_timer(5000, MGOS_TIMER_REPEAT, led_timer_cb, NULL);
    return MGOS_APP_INIT_SUCCESS;
}

Comments

  • nliviunliviu Romania

    This seems to be related to the fact the both pwm and mgos_set_hw_timer are using hardware timers (and maybe the same one).
    Replace mgos_set_hw_timer with mgos_set_timer and it should work as expected.

  • jstevewoodjstevewood California

    Hi nliviu, I tried this, and it almost works (i. e. not to speed or accuracy). No errors, but the software timer jitters too much. The ultimate frequency of operation is 500 Hz. (i. e. the PWM will be triggered on or off at 500 Hz.; the code example is the first crude attempt to simulate and observe the action of a PWM controlled by a HW timer.) Is there a way of forcing which counters are dedicated to PWM or timers? Or is there a way of reducing the jitter of a software timer timer (at the time that this 500 Hz. loop will be running, nothing else is active in the system except an emergency shutoff command). Thanks for your help, JSW

  • nliviunliviu Romania
    edited April 2018

    After further research I found out that the hw timers an the ones used by the ledc peripheral are different.

    The root cause of the panic seems to be using float numbers in an interrupt service routine
    Modified locally the pwm library to use double in mgos_pwm_set(int pin, int freq, double duty) and the panic disappeared.

    PS. pulse_width should be in IRAM.

    Thanked by 1jstevewood
  • jstevewoodjstevewood California

    Hi nliviu, great! Will public PWM library be updated in a few days? Is pulse_width automatically placed in IRAM? Thanks, JSW

  • nliviunliviu Romania
    edited April 2018

    I did not file a PR, but the following code works with the stock pwm library

    #include "mgos.h"
    #include "mgos_pwm.h"
    
    #include <xtensa/hal.h>
    #include <math.h>
    
    /* From http://bbs.esp32.com/viewtopic.php?t=1292 
    http://esp-idf.readthedocs.io/en/latest/api-guides/freertos-smp.html#floating-point-aritmetic
    */
    /*
    uint32_t timer0_int = 0;
    float timer0_float = 0.0;
    DRAM_ATTR float timer0_k = 1.111111;
    
    uint32_t cp0_regs[18];
    
    void IRAM_ATTR timer0_intr()  // Interrupt handler for timer 0
    {
    
      // get FPU state
      uint32_t cp_state = xthal_get_cpenable();
    
      if(cp_state) {
        // Save FPU registers
        xthal_save_cp0(cp0_regs);
      } else {
        // enable FPU
        xthal_set_cpenable(1);
      }
    
      timer0_int++;
      timer0_float = timer0_int * timer0_k;
    
      if(cp_state) {
        // Restore FPU registers
        xthal_restore_cp0(cp0_regs);
      } else {
        // turn it back off
        xthal_set_cpenable(0);
      }
    
    }
     */
    
    int gpin = 2;
    volatile int hpin = 12;
    volatile int pfreq = 1000;
    volatile float dcycle = 0.0;
    
    //uint32_t cp0_regs[18];
    
    static IRAM void pulse_width(void *arg) {
      uint32_t cp0_regs[18];
      // get FPU state
      uint32_t cp_state = xthal_get_cpenable();
    
      if (cp_state) {
        // Save FPU registers
        xthal_save_cp0(cp0_regs);
      } else {
        // enable FPU
        xthal_set_cpenable(1);
      }
      dcycle = 0.0;
      pfreq = 1000;
      mgos_pwm_set(hpin, 0, 0);
    
      if (cp_state) {
        // Restore FPU registers
        xthal_restore_cp0(cp0_regs);
      } else {
        // turn it back off
        xthal_set_cpenable(0);
      }
      (void) arg;
    }
    
    static IRAM void pwm() {
      static int i = 0;
      LOG(LL_INFO, ("enter pwm - i: %d  dcycle:  %.2f", i, dcycle));
      if (i % 2 == 1) {
        dcycle = 0.7;
        pfreq = 1000;
      };
      if (i % 2 == 0) {
        dcycle = 0.0;
        pfreq = 1000;
      };
      LOG(LL_INFO, ("            i: %d  dcycle:  %.2f", i, dcycle));
      i++;
      mgos_pwm_set(hpin, pfreq, dcycle);
      mgos_set_hw_timer(2000, 0, pulse_width, NULL);
    }
    
    static IRAM void led_timer_cb(void *arg) {
      pwm();
      mgos_gpio_toggle(gpin);
      static const float prime = 1039;
      static float f = prime - 1;
      f = fmodf(f * esp_random(), prime);
      if (f < 1.0) {
        f = prime - 1;
      }
      LOG(LL_INFO, ("float:  %.2f", f));
      (void) arg;
    }
    
    enum mgos_app_init_result mgos_app_init(void) {
      mgos_gpio_set_mode(gpin, MGOS_GPIO_MODE_OUTPUT);
      //mgos_gpio_set_mode(hpin, MGOS_GPIO_MODE_OUTPUT);
      mgos_set_timer(5000, MGOS_TIMER_REPEAT, led_timer_cb, NULL);
    
      return MGOS_APP_INIT_SUCCESS;
    }
    

    I did reorganize a bit your code and added some float operations in the main timer callback.
    UDP log:

    esp32_xxxxxx 11369 1524595311.753 2|pwm                  enter pwm - i: 2519  dcycle:  0.00
    esp32_xxxxxx 11370 1524595311.764 2|pwm                              i: 2519  dcycle:  0.70
    esp32_xxxxxx 11371 1524595311.770 2|led_timer_cb         float:  91.00
    esp32_xxxxxx 11375 1524595316.753 2|pwm                  enter pwm - i: 2520  dcycle:  0.00
    esp32_xxxxxx 11376 1524595316.763 2|pwm                              i: 2520  dcycle:  0.00
    esp32_xxxxxx 11377 1524595316.770 2|led_timer_cb         float:  888.00
    esp32_xxxxxx 11378 1524595321.753 2|pwm                  enter pwm - i: 2521  dcycle:  0.00
    esp32_xxxxxx 11379 1524595321.764 2|pwm                              i: 2521  dcycle:  0.70
    esp32_xxxxxx 11380 1524595321.771 2|led_timer_cb         float:  610.00
    esp32_xxxxxx 11384 1524595326.753 2|pwm                  enter pwm - i: 2522  dcycle:  0.00
    esp32_xxxxxx 11385 1524595326.763 2|pwm                              i: 2522  dcycle:  0.00
    esp32_xxxxxx 11386 1524595326.770 2|led_timer_cb         float:  432.00
    esp32_xxxxxx 11387 1524595331.753 2|pwm                  enter pwm - i: 2523  dcycle:  0.00
    esp32_xxxxxx 11388 1524595331.764 2|pwm                              i: 2523  dcycle:  0.70
    
    Thanked by 2rojer jstevewood
  • rojerrojer Dublin, Ireland

    great job, @nliviu! i'll try to clarify the official position on the FPU use in ISR handlers. but please do file a bug about it!

  • jstevewoodjstevewood California

    Hi nliviu, thanks for the code. It works perfectly. On the subject of bug vs limitation vs feature, would it not be possible to change the specification of the duty cycle of the PWM to an integer ranged from 0-1023 (or even 0 -255)?

Sign In or Register to comment.