Phase matching timer interrupt sequence with LEDC PWM

aceman
Posts: 3
Joined: Mon Feb 17, 2020 5:30 am

Phase matching timer interrupt sequence with LEDC PWM

Postby aceman » Mon Feb 17, 2020 5:48 am

Hi all,

I am using an ESP32-PICO SoC to drive a display. I am programming it in Arduino framework using PlatformIO for Visual Studio Code. I have managed to set up a bit banging routine to drive my display. The bit bang is very lean and runs inside of a timer interrupt. I've programmed the interrupt to use hardware timer 0.

Code: Select all

  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, 250, true); //250 microseconds = 4khz
  timerAlarmEnable(timer);
Now, I also want to dim my display using the LEDC function which seems perfect for my application. My display driver chips are essentially shift registers (clock, data, latch) inputs, and they also have a "blanking" pin which will turn the active outputs off when low, or on when high. They are intended to be driven with PWM, and varying the duty cycle high or low will dim the display accordingly.

I've set up my LEDC function to also use hardware timer 0.

Code: Select all

  ledcSetup(ledChannel, freq, resolution);
  ledcAttachPin(PWM, ledChannel);
...where ledChannel is set to 0. As defined in the espressif ledc library file esp32-hal-ledc.c:

* LEDC Chan to Group/Channel/Timer Mapping
** ledc: 0 => Group: 0, Channel: 0, Timer: 0

So why is it that when both my interrupt routine and the PWM are running at the same frequency and set to hardware timer 0, that the output signals are out of phase? This is causing visible jitter in my display, as the PWM signal goes high when the shift registers are clocking in new data. If I could control the phase of the PWM with respect to the interrupt, I would be able to make sure the PWM high does not overlap when the display is updating.

I appreciate any insight. Thank you.

idahowalker
Posts: 139
Joined: Wed Aug 01, 2018 12:06 pm

Re: Phase matching timer interrupt sequence with LEDC PWM

Postby idahowalker » Mon Feb 17, 2020 1:31 pm

Have you considered using freeRTOS and event triggers in your ISR to initiate interrupt code?

Code: Select all

#include "esp32-hal-psram.h"
#include "esp_system.h" //This inclusion configures the peripherals in the ESP system.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"

#define evtReceiveSerial_LIDAR       ( 1 << 0 ) //1
#define evtParseLIDAR_ReceivedSerial ( 1 << 1 ) //10
#define evtLIDAR_Watchdog_Inc        ( 1 << 2 ) //100
#define UPDATE_DISPLAY_TASK_BIT      ( 1 << 3 ) //1000
#define evtfLIDAR_Alarm              ( 1 << 4 ) //10000
#define OneSecondGroupBits ( UPDATE_DISPLAY_TASK_BIT | evtDoBMP | evtGPS_Log | evtLIDAR_Watchdog_Inc )
#define OneMilliSecondGroupBits ( evtGPS_Parse | evtSendCAN_Buss )

void IRAM_ATTR onTimer()
{
  BaseType_t xHigherPriorityTaskWoken;
  iTicCount++;
  xEventGroupSetBitsFromISR(eg, OneMilliSecondGroupBits, &xHigherPriorityTaskWoken);
  if ( (iTicCount % 2) == 0 )
  {
    if ( (xSemaphoreTakeFromISR(sema_ReceiveSerial_LIDAR, &xHigherPriorityTaskWoken)) == pdTRUE ) // grab semaphore, no wait
    {
      xEventGroupSetBitsFromISR(eg, evtReceiveSerial_LIDAR, &xHigherPriorityTaskWoken); // trigger every 2mS, if not already processing
    }
  }
  if ( (iTicCount % 3) == 0 )
  {
    if ( (xSemaphoreTakeFromISR(sema_HexaPodAdjustment, &xHigherPriorityTaskWoken)) == pdTRUE ) // grab semaphore, no wait
    {
      xEventGroupSetBitsFromISR(eg1, evtWalk_Forward, &xHigherPriorityTaskWoken);
    }
  }
  if ( iTicCount == OneK )
  {
    xEventGroupSetBitsFromISR(eg, OneSecondGroupBits, &xHigherPriorityTaskWoken); // trigger every 1X a second
    // reset counter to start again
    iTicCount = 0;
  }
  if ( !bLIDAR_OK )
  {
    xSemaphoreTakeFromISR ( sema_iLIDAR_Power_Reset, &xHigherPriorityTaskWoken );
    ++iLIDAR_Power_Reset;
    xSemaphoreGiveFromISR ( sema_iLIDAR_Power_Reset,  &xHigherPriorityTaskWoken);
    if ( iLIDAR_Power_Reset >= 4000 )
    {
      xEventGroupSetBitsFromISR(eg, evtfLIDAR_Power_On, &xHigherPriorityTaskWoken);
    }
  }
} // void IRAM_ATTR onTimer()

setup()
{
 timer = timerBegin( TIMER_FOUR, TimerDivider, true );
  timerAttachInterrupt( timer, &onTimer, true );
  timerAlarmWrite(timer, OneK, true);
  timerAlarmEnable(timer);
}

void fReceiveSerial_LIDAR( void * parameters  )
{
  bool BeginSentence = false;
  char OneChar;
  char *str;
  str = (char *)ps_calloc(300, sizeof(char) ); // put str buffer into PSRAM
  // log_i("Free PSRAM before String: %d", ESP.getFreePsram());
  for ( ;; )
  {
    EventBits_t xbit = xEventGroupWaitBits (eg, evtReceiveSerial_LIDAR, pdTRUE, pdTRUE, portMAX_DELAY);
    if ( LIDARSerial.available() >= 1 )
    {
      while ( LIDARSerial.available() )
      {
        OneChar = LIDARSerial.read();
        if ( BeginSentence )
        {
          if ( OneChar == '>')
          {
            if ( xSemaphoreTake( sema_ParseLIDAR_ReceivedSerial, xSemaphoreTicksToWait10 ) == pdTRUE )
            {
               xQueueOverwrite( xQ_LIDAR_Display_INFO, ( void * ) &str );
              xEventGroupSetBits( eg, evtParseLIDAR_ReceivedSerial );
              //
            }
            BeginSentence = false;
            break;
          }
          strncat( str, &OneChar, 1 );
        }
        else
        {
          if ( OneChar == '<' )
          {
            strcpy( str, ""); // clear string buffer
            BeginSentence = true; // found beginning of sentence
          }
        }
      } //  while ( LIDARSerial.available() )
    } //if ( LIDARSerial.available() >= 1 )
    xSemaphoreGive( sema_ReceiveSerial_LIDAR );
    //        log_i( "fReceiveSerial_LIDAR " );
    //        log_i(uxTaskGetStackHighWaterMark( NULL ));
  }
  free(str);
  vTaskDelete( NULL );
} //void fParseSerial( void * parameters  )

aceman
Posts: 3
Joined: Mon Feb 17, 2020 5:30 am

Re: Phase matching timer interrupt sequence with LEDC PWM

Postby aceman » Mon Feb 17, 2020 10:40 pm

I have not considered this. I am coming from an arduino backround and this is my first time programming an ESP32. Looking at your code example, I'm trying to make sense of it for myself. However, in reference to my original question, would initiating the ISR code using freeRTOS commands change any of the timing issues I've been encountering?

My primary concern is being unable to understand why, when LEDC and my ISR are both tied to the same hardware timer, that they are not initiated in a fixed sequence with one another?

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

Re: Phase matching timer interrupt sequence with LEDC PWM

Postby ESP_Sprite » Tue Feb 18, 2020 8:33 am

My primary concern is being unable to understand why, when LEDC and my ISR are both tied to the same hardware timer
Are you sure they are? I'm not too familiar with the Arduino standard, but I'd say one uses a 'normal' timer and the other uses a LEDC timer. There's no reason they would be in sync.

aceman
Posts: 3
Joined: Mon Feb 17, 2020 5:30 am

Re: Phase matching timer interrupt sequence with LEDC PWM

Postby aceman » Thu Feb 20, 2020 4:31 pm

My understanding is that the ESP32 has a total of 4 hardware timers. From the documentation I found in from espressif https://docs.espressif.com/projects/esp ... /ledc.html, there are two different speed modes which are either hardware or software controlled. Is it incorrect to assume that the clock source for LEDC is an internal hardware timer?

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

Re: Phase matching timer interrupt sequence with LEDC PWM

Postby ESP_Sprite » Mon Feb 24, 2020 11:44 am

The clock source is the same (usually the ABP clock of 80MHz) and as such if you start two timers with the same cycle time, they should be clocked at exactly the same frequency, but they are still two separate timers so any phase difference (=difference in time between starting the two) will be kept. They won't automatically sync that.

Who is online

Users browsing this forum: Bing [Bot] and 38 guests