-->
Page 1 of 2

Timer inaccurate. Possibly sdk background activity?

PostPosted: Sun Aug 21, 2016 4:12 pm
by frischevollmilch
Hello,

I am currently working on a ESP8266 driven 4x4x4 RGB LED cube. It works with two TLC5947 led drivers in series and quick cycling through all 4 planes to get a static image.

It works fine so far but the image seems to flicker every ~0.75 seconds or so. I tried disabling every other component in my sketch but the problem persists. I tried disabling Wifi with
Code: Select allWiFi.mode(WIFI_OFF);
completely but it didn't help. I even considered my heartbeat to be the cause when looking at fast flickering LEDs but my heart didn't beat every .75 seconds so that also can't be it.

I feel like the ESP8266 SDK does something in the background that I am not aware of. I heard that the SDK handles some Wifi functions in the background that might stop my timer from firing exactly every 1500µs. I will just post the relevant part of the code here.

Code: Select all
xyz current_led = xyz { 0, 1, 0 };
rgb rgb_data[4][4][4];
os_timer_t tlcTimer;

void tlc_init()
{
   SPI.begin();
   SPI.setFrequency(1000000);

   pinMode(PLANE_1_PIN, OUTPUT);
   pinMode(PLANE_2_PIN, OUTPUT);
   pinMode(PLANE_3_PIN, OUTPUT);
   pinMode(PLANE_4_PIN, OUTPUT);
   pinMode(BLANK_AND_LATCH_PIN, OUTPUT);

   start_tlc_timer();
}

void start_tlc_timer()
{
   // initialize transmitter timer
   system_timer_reinit();

   // set callback function
   os_timer_setfn(&tlcTimer, transmit, NULL);

   // enable timer (fourth argument: 0=us, 1=ms)
   ets_timer_arm_new(&tlcTimer, 1500, true, 0);
}

void reinit_timer(uint16_t timeout)
{
   ets_timer_disarm(&tlcTimer);
   ets_timer_arm_new(&tlcTimer, timeout, true, 0);
}

void transmit(void *pArg)
{
   // Write SPI data
   uint8_t data_array[72];
   for(uint8_t i = 0; i < 8; i++)
   {
      data_array[i * 9 + 0] = rgb_data[current_led.x][current_led.y][current_led.z].blue;
      data_array[i * 9 + 1] = rgb_data[current_led.x][current_led.y][current_led.z].green >> 4;
      data_array[i * 9 + 2] = rgb_data[current_led.x][current_led.y][current_led.z].green << 4;
      data_array[i * 9 + 3] = rgb_data[current_led.x][current_led.y][current_led.z].red;
      select_next_led();
      data_array[i * 9 + 4] = rgb_data[current_led.x][current_led.y][current_led.z].blue >> 4;
      data_array[i * 9 + 5] = rgb_data[current_led.x][current_led.y][current_led.z].blue << 4;
      data_array[i * 9 + 6] = rgb_data[current_led.x][current_led.y][current_led.z].green;
      data_array[i * 9 + 7] = rgb_data[current_led.x][current_led.y][current_led.z].red >> 4;
      data_array[i * 9 + 8] = rgb_data[current_led.x][current_led.y][current_led.z].red << 4;
      select_next_led();
   }
   SPI.writeBytes(data_array, 72);

   // Reset enable pins
   digitalWrite(PLANE_1_PIN, 0);
   digitalWrite(PLANE_2_PIN, 0);
   digitalWrite(PLANE_3_PIN, 0);
   digitalWrite(PLANE_4_PIN, 0);

   // Latch
   digitalWrite(BLANK_AND_LATCH_PIN, 1);
   digitalWrite(BLANK_AND_LATCH_PIN, 0);
   
   // Set enable pin
   switch(current_led.z)
   {
      case 2:
         digitalWrite(PLANE_1_PIN, 1);
         current_led.z = 3;
      break;
      case 3:
         digitalWrite(PLANE_2_PIN, 1);
         current_led.z = 0;
      break;
      case 0:
         digitalWrite(PLANE_3_PIN, 1);
         current_led.z = 1;
      break;
      case 1:
         digitalWrite(PLANE_4_PIN, 1);
         current_led.z = 2;
      break;
   }
}

void select_next_led()
{
   switch(current_led.y)
   {
      case 0:
         current_led.x--;
         if(current_led.x < 0)
         {
            current_led.x = 0;
            current_led.y = 3;
         }
      break;
      case 1:
         current_led.x++;
         if(current_led.x > 3)
         {
            current_led.x = 3;
            current_led.y = 0;
         }
      break;
      case 2:
         current_led.x--;
         if(current_led.x < 0)
         {
            current_led.x = 0;
            current_led.y = 1;
         }
      break;
      case 3:
         current_led.x++;
         if(current_led.x > 3)
         {
            current_led.x = 3;
            current_led.y = 2;
         }
      break;
   }
}



As you can see the timer should fire every 1500µs:
Code: Select allets_timer_arm_new(&tlcTimer, 1500, true, 0);


Does anybody know why I get that flickering/inaccurate timing? My loop function is empty while the setup function just calls the tlc_init function and populates the rgb_data array with an example image.

Thanks for reading!

Edit:

I just tested the timing with the system_get_time() function like this inside the transmit function:

Code: Select allthis_time = system_get_time();
difference = this_time - last_time;
if(difference > 1500+200 || difference < 1500-200)
   Serial.println(difference);
last_time = this_time;


The Serial output matched the flicker frequency I can see with my eyes. It was always inaccurate by about +/-270µs. Now my question is: How can I make the timer more accurate?

Re: Timer inaccurate. Possibly sdk background activity?

PostPosted: Mon Aug 22, 2016 1:48 am
by schufti
Hi,
yes there is definitely sth "going on in the background".
I'm not sure if WIFI_OFF is sufficient to stop all wifi activities.
I'd try to go through one deepsleep cycle with RF_DISABLED.
But it might also be the watchdog or pwm timer ...

Re: Timer inaccurate. Possibly sdk background activity?

PostPosted: Mon Aug 22, 2016 9:26 am
by mrburnette
From my simple tests, disabling WiFi does not disable the Watch Dog. So, unless the WDT is disabled, you still need to yield() in your sketch or ensure the loop() recycles in under 50mS.

http://www.hackster.io/rayburne/esp8266-turn-off-wifi-reduce-current-big-time-1df8ae

Personally, I have no issues just leaving in a few well-placed "delay(0);" as it keeps me remembering that I must do this when WiFi is enabled.

Ray

Re: Timer inaccurate. Possibly sdk background activity?

PostPosted: Tue Aug 23, 2016 3:30 pm
by frischevollmilch
I only tried disabling wifi for debugging purposes. Actually I really want to keep using wifi for my led cube.

I tried disabling the watchdog timer by calling system_soft_wdt_stop() in the setup() function and system_soft_wdt_feed() inside my os_timer_t timer callback function. It ran fine that way but the lights kept flickering. When I leave out the system_soft_wdt_feed() call it keeps crashing. Did I really disable the WDT that way?

So I still couldn't get my timer to run accurately. I tried calling the transmit() function from the loop() function which got called every 600µs that way but also sometimes gets delayed by ~200µs causing the lights to flicker.

I don't know what to do. Is it even possible to get the timing right with the ESP8266 using Arduino?