Teensy 3.6 clock speed after EEPROM write

wb8nbs

Active member
I'm seeing something odd. The project is a dot matrix clock using the Pixelmatix SmartMatrix adapter to a 64x64 LED display. It's working great but I added an option to set the default matrix brightness. It's saved in byte 0 of the EEPROM, read in the setup() section.

What I'm seeing is a large change in frame rate after writing a new default brightness value to EEPROM. I added some counters and it drops from 120 FPS to 49 FPS with noticeable flicker. I read a post here that says Teensy lowers the clock rate while writing to EEPROM. Apparently, it doesn't set the rate back when it's done.

If I change the compile clock time down to 120 Mhz it will write without a frame rate hit. Here is a table of what I observed:
CPU Clock, FPS before write, FPS after write
180, 120, 49
168, 120, 44
144, 119 , 37
120, 77, 77

The offending statement is:
Code:
EEPROM.update(0, (uint8_t)defaultBrightness);
The problem is definitely here, frame rate does not change if I comment that out. I'm going to leave the compile at 120 Mhz as it looks OK at 77 FPS but I think there's a bug.
 
Indeed, on a T_3.6 over 120 MHz the clock speed is dropped to 120 MHz but has always reverted to speed after the writes are complete.

While the write is in progress the clock change can affect other system elements timing if they use that same clock.

The function kinetis_hsrun_disable() drops the clock when needed and in checking each is followed by kinetis_hsrun_enable().

That code is in: {local install}\hardware\teensy\avr\cores\teensy3\eeprom.c

As needed, that is only over the duration of the write and clocks for F_CPU speed are restored before return.

If there is a side effect not seen before it will need to be discovered and presented in a reproducible sketch.
 
I tried but could not reproduce this problem. Usually I don't investigate unless code is shown to reproduce the problem, but in this case I wrote a little test program which blinks the LED on pin 13 using a busy loop that depends on CPU speed.

Code:
#include <EEPROM.h>

void setup() {
  pinMode(13, OUTPUT);
  while (!Serial) ; // wait for Arduino serial monitor
  Serial.println("setup");
}

void blink_cpu_speed_dependent(int num) {
  num *= 2;
  elapsedMicros usec = 0;
  for (int i=0; i < num; i++) {
    digitalToggle(13);
    for (volatile int j=0; j < 2500000; j++) ; // busy loop wait
  }
  float hz = (float)num * 0.5f / ((float)usec * 1.0e-6);
  Serial.print("blink rate = ");
  Serial.print(hz, 3);
  Serial.println(" Hz");
}

void loop() {
  static unsigned int count=0;
  Serial.print("loop ");
  Serial.println(++count);
  blink_cpu_speed_dependent(10);
  if (count == 3) {
    int n = EEPROM.read(0);
    n = n + 1;
    Serial.print("EEPROM Write, addr0 = ");
    Serial.println(n);  
    EEPROM.write(0, n);
  }
}

When Teensy 3.6 runs at 180 MHz, my oscilloscope sees the pin oscillating at 3.5976 Hz. It remains constant, same before and after the EEPROM write on the 3rd time loop() runs.

file.png

Here's the result I see in the Arduino serial monitor.

screenshot.png

Maybe you could try running this test program on your Teensy 3.6?

I can't say what's going wrong with your display refresh rate. But I'm pretty sure the cause isn't Teensy remaining at the slower speed used temporarily while writing to the EEPROM (on-chip FlexNVM hardware).
 
The offending statement is:
Code:
EEPROM.update(0, (uint8_t)defaultBrightness);
The problem is definitely here, frame rate does not change if I comment that out. I'm going to leave the compile at 120 Mhz as it looks OK at 77 FPS but I think there's a bug.

Paul: Thanks for looking at this. From your test code, I see the same number before and after the write at 120 through 180 Mhz compiles.

I don't think posting the whole sketch here would be useful unless you had the same hardware set up.
https://www.youtube.com/watch?v=5LKxAbJSIZk
https://www.youtube.com/watch?v=XJ2DcbBWEiw
I'll see if I can cut it down to something that works on the serial port. My teensy loader is 1.56, IDE is 1.8.19.

Is there a way to retrieve the running CPU clock speed? Must be in some register somewhere.
 
There is no single register to read the CPU speed in a simple way.

You could read registers to determine the PLL config, and whether the CPU runs from the PLL (or is running in some other modes, which we don't generally use), and of course read the clock dividers.
 
I narrowed this down to interaction with the SmartMatrix library. I added matrix code to your test sketch to display "test" on the LED matrix, then commented out stuff back to "matrix.begin()" which triggers the problem. The loop actually gets faster after the EEPROM write, with 180 MHz clock 1.214 per loop, 2.626 after the write. Commenting out "matrix.begin()" gives 3.598 per loop before and after the write. At 120 MHz, there is no change with or without removing matrix.begin(). You can probably duplicate these results on a bare Teensy 3.6. I will ask Louis Beaudoin to look at it.


Code:
#include <MatrixHardware_Teensy3_ShieldV4.h> // SmartLED Shield for Teensy 3 (V4)
#include <SmartMatrix.h>

#include <EEPROM.h>


#define COLOR_DEPTH 24                  // Choose the color depth used for storing pixels in the layers: 24 or 48 (24 is good for most sketches - If the sketch uses type `rgb24` directly, COLOR_DEPTH must be 24)
const uint16_t kMatrixWidth = 64;       // Set to the width of your display, must be a multiple of 8
const uint16_t kMatrixHeight = 64;      // Set to the height of your display
const uint8_t kRefreshDepth = 36;       // Tradeoff of color quality vs refresh rate, max brightness, and RAM usage.  36 is typically good, drop down to 24 if you need to.  On Teensy, multiples of 3, up to 48: 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48.  On ESP32: 24, 36, 48
const uint8_t kDmaBufferRows = 4;       // known working: 2-4, use 2 to save RAM, more to keep from dropping frames and automatically lowering refresh rate.  (This isn't used on ESP32, leave as default)
const uint8_t kPanelType = SM_PANELTYPE_HUB75_64ROW_MOD32SCAN;   // Choose the configuration that matches your panels.  See more details in MatrixCommonHub75.h and the docs: https://github.com/pixelmatix/SmartMatrix/wiki
const uint32_t kMatrixOptions = (SM_HUB75_OPTIONS_NONE);         // see docs for options: https://github.com/pixelmatix/SmartMatrix/wiki
const uint8_t kBackgroundLayerOptions = (SM_BACKGROUND_OPTIONS_NONE);

SMARTMATRIX_ALLOCATE_BUFFERS(matrix, kMatrixWidth, kMatrixHeight, kRefreshDepth, kDmaBufferRows, kPanelType, kMatrixOptions);
SMARTMATRIX_ALLOCATE_BACKGROUND_LAYER(backgroundLayer, kMatrixWidth, kMatrixHeight, COLOR_DEPTH, kBackgroundLayerOptions);

const rgb24 White = {0xFF, 0xFF, 0xFF};
const int defaultBrightness = 204;


void setup() {
  pinMode(13, OUTPUT);
  while (!Serial) ; // wait for Arduino serial monitor
  Serial.println("setup");

  // Setup SmartMatrix
  matrix.addLayer(&backgroundLayer);
  matrix.begin();
  Serial.println("Matrix begin()");
//  matrix.setBrightness(defaultBrightness);
//  backgroundLayer.setFont(gohufont11b);  // Font is 11 pixels high

}

void blink_cpu_speed_dependent(int num) {
  num *= 2;
  elapsedMicros usec = 0;
  for (int i = 0; i < num; i++) {
    digitalToggle(13);
    for (volatile int j = 0; j < 2500000; j++) ; // busy loop wait
  }
  float hz = (float)num * 0.5f / ((float)usec * 1.0e-6);
  Serial.print("blink rate = ");
  Serial.print(hz, 3);
  Serial.println(" Hz");
}

void loop() {
  static unsigned int count = 0;


  Serial.print("loop ");
  Serial.println(++count);

//  backgroundLayer.drawString(20, 25, White, "TEST");
//  backgroundLayer.swapBuffers(true);

  blink_cpu_speed_dependent(10);

  if (count == 3) {
    int n = EEPROM.read(0);
    n = n + 1;
    Serial.print("EEPROM Write, addr0 = ");
    Serial.println(n);
    EEPROM.write(0, n);
  }

}
 
Back
Top