Teensy 4.0 / 74HC595N shift register compatibility issues?

madamAdam

Member
I was using this code with a teensy lc but had to swap it for a teensy 4.0 when I accidentally damaged it. Since then, the shift registers don't seem to shift.

I have tried using a separate shift register and have tested the pins on the teensy. The teensy pins are responding as expected, but the shift register only lights up one pin and regardless of what pin I select, it's always the same one that lights (pin 6).

Before I dig deeper, does anyone know of any compatibility issues between these the teensy 4.0 and 74HC595n shift registers, or anything else I might have missed?

Tia!


20240321_140717.jpg
20240321_140707.jpg





Code:
/*
This is a simplified version of code i've been developing for a midi instrument. This section is for controlling 48 leds via 6 74HC595N shift registers.
I have removed the rest of the code for simplicity/readability.

This code worked on a teensy LC but doesn't work now on a teensy 4.0. I had to replace the LC after it stopped working. I was soldering some piezos onto
the board holding the teensy with the teensy switched on when it seemed to stop responding. I assume this i has something to do with why it stopped working.

I am now testing this code on a single shift register and have noticed that one of the eight leds lights up, but no matter what led[] i choose, it's
always the same one that lights up. It's as if the shift register is not shifting the data along...any suggestions?
*/

///////////////////////////Shift Registers///////////////////

int led[48];

const int outputEnable = 4; // green
const int dataPin = 6;   // SER yellow
const int latchPin = 7;  // RCLK blue
const int clockPin = 8;  // SRCLK white

const int numRegisters = 6;
const int numLedsPerRegister = 8;
const int numLeds = numRegisters * numLedsPerRegister;

byte ledStates[numRegisters] = { 0 };

void setup() {

  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(outputEnable, OUTPUT);
  digitalWrite(outputEnable, HIGH);

  updateShiftRegisters();
}

void loop() {

  setLedState(led[2], HIGH);  //no matter what led[?] goes here, the same slot on the shift register lights up...
  delay(100);
  setLedState(led[2], LOW);
  delay(100);
}

///////////////////////////LED Functions//////////////////////////

void updateShiftRegisters() {

  digitalWrite(outputEnable, HIGH);         // Disable output while updating

  for (int reg = numRegisters - 1; reg >= 0; reg--) {
    digitalWrite(latchPin, LOW);         // Activate the latch to start sending data
    
    for (int bit = numLedsPerRegister - 1; bit >= 0; bit--) {         // Shift out the bits for each LED
      digitalWrite(clockPin, LOW);
      digitalWrite(dataPin, (ledStates[reg] >> bit) & 1);       // Send the data to the shift register
      digitalWrite(clockPin, HIGH);
    }
    delay(1);
    digitalWrite(latchPin, HIGH);       // Deactivate the latch to update the LEDs
  }
  digitalWrite(outputEnable, LOW);        // Enable output after updating
}

void setLedState(int ledIndex, int brightness) {
  if (ledIndex >= 0 && ledIndex < numRegisters * numLedsPerRegister) {
    int reg = ledIndex / numLedsPerRegister;
    int bit = ledIndex % numLedsPerRegister;

    ledStates[reg] = (ledStates[reg] & ~(1 << bit)) | ((brightness > 0) << bit);         // Set the brightness value to the corresponding bit in the array
    updateShiftRegisters();
  }
}
/////////////////////////////////////////////////////////////////
 
Probably the Teensy 4.1 can change pins faster than the 74HC595 can handle. Teensy 4.1 is way faster than a Teensy LC.

Sprinkle a number of delay(1);'s in the updateShiftRegisters() function like so:
C++:
void updateShiftRegisters() {

  digitalWrite(outputEnable, HIGH);  // Disable output while updating
  delay(1);

  for (int reg = numRegisters - 1; reg >= 0; reg--) {
    digitalWrite(latchPin, LOW);  // Activate the latch to start sending data
    delay(1);

    for (int bit = numLedsPerRegister - 1; bit >= 0; bit--) {  // Shift out the bits for each LED
      digitalWrite(clockPin, LOW);
      delay(1);
      digitalWrite(dataPin, (ledStates[reg] >> bit) & 1);  // Send the data to the shift register
      delay(1);
      digitalWrite(clockPin, HIGH);
      delay(1);
    }
    delay(1);
    digitalWrite(latchPin, HIGH);  // Deactivate the latch to update the LEDs
    delay(1);
  }
  digitalWrite(outputEnable, LOW);  // Enable output after updating
  delay(1);
}
It's a bit crude but if the code now shows the LEDs correctly can you can tune in on where it actually goes wrong.

You could also set the Teensy clock frequency to 150MHz as a test.

Paul
 
That was it -

After a bit of experimentation with delays like you suggested, I found that a one microsecond delay in the right place is enough to get everything functioning as it was, with no discernible latency.

Such a simple solution, but I'd have struggled to find it by myself. Many thanks Paul!

Code:
void updateShiftRegisters() {


  digitalWrite(outputEnable, HIGH);  // Disable output while updating


  for (int reg = numRegisters - 1; reg >= 0; reg--) {

    digitalWrite(latchPin, LOW);  // Activate the latch to start sending data


    for (int bit = numLedsPerRegister - 1; bit >= 0; bit--) {  // Shift out the bits for each LED

      digitalWrite(clockPin, LOW);

      digitalWrite(dataPin, (ledStates[reg] >> bit) & 1);  // Send the data to the shift register

      digitalWrite(clockPin, HIGH);

      delayMicroseconds(1); //putting a tiny delay here is enough to help 74hc595s catch up with teensy 4.0

   }

   digitalWrite(latchPin, HIGH);  // Deactivate the latch to update the LEDs

   }

  digitalWrite(outputEnable, LOW);  // Enable output after updating

}

[/CODE]
 
I've removed delayMicroseconds(1) and lowered CPU speed to 450MHz to see what happens.

This has the same effect as the delay: the shift registers work again with no noticeable lowering of performance.

I'm sticking with the delayMicroseconds(1) solution for now though. As I understand it, while I am slowing
updateShiftRegisters() when it's called, the rest of the code should still be as fast as possible.

Thanks again for all help and suggestions!
 
Yes, the delays only where needed is the best way.

If you want to experiment with shorter delays, use delayNanoseconds() rather than delayMicroseconds(). My guess is you probably only need something like 20 to 50 ns.
 
74HCxx family at 3.3V can allegedly clock upto about 40 or 50MHz, though I would play safe and aim for 30MHz max. 74LVC595 would be considerable faster as its designed for 3.3V.
 
This works:

delayNanoseconds(1);

...but so does this (!):

delayNanoseconds(99999999999999999999999999999999999999999999999999999999999999999999999999999);
 
delayNanoseconds() isn't perfect. It can and usually does give slightly longer delay than requested. Internally it uses a hardware timer so interrupts which occur (and finish) while waiting don't lengthen the total wait. But this approach adds overhead in the 10-15ns range. Usually that's a good trade-off. Perhaps future versions will detect when the requested time short and known const at compile time, to use a simpler approach. But today's version is optimized for longer delays like 50 to 500ns.

Even just using it at all can cause the compiler to optimize the surrounding code differently, quite substantially changing its nanosecond scale timing. The CPU pipeline and branch prediction can also give varying results. Each clock cycle is 1.66ns. In any 1 clock, you could have up to 2 instructions executed in parallel (the CPU is in-order dual issue) or potentially a pipeline flush (eg, branch outcome not successfully predicted) which stalls everything for a few cycles. Even if the code were simpler (not compensating for ISR usage) achieving 1ns precision just isn't feasible.

It also has a maximum limit, which isn't very long. The assumption is you would use delayMicroseconds() or regular delay() if you want longer times.
 
Thanks for this explanation Paul.

I've put delayNanoseconds (50) in my code and I assume that it is inserting a delay of about 60-76ns. It's certainly running very fast and smooth now...

It's hard to imagine that small!
 
Back
Top