Teensy 4.1 delay vs timers and digitalRead...

gfrancke

Member
hi all, I have a baffling problem on a vast synthesizer I'm working on.

I have a switch matrix set up to scan 4 columns and 8 rows of switches, to populate an array of 4 bytes.

every 100ms I scan the switches and update the values; this initiates in Loop() just fine.

This switchScan code works 100% fine with delay(1); it does not work AT ALL with the (currentTime - lastSwitchScan > switchScanDelay), no matter the value of switchScanDelay...
(and it doesn't work right WITHOUT the delay either, I'm assuming I need some kind of settling time; the switch values seem to overflow into neighboring bytes)

here's the switch scan code with delay REMmed out, this returns all zeroes...

Code:
void switchScan()
{
  for (byte i = 0; i < 4; i++)
  {
    digitalWrite(switchCols[i], LOW);
    //  delay(1);
    if (currentTime - lastSwitchScan  > switchScanDelay)
    {
      lastSwitchScan = currentTime;
      for (byte j = 0; j < 8; j++)
      {
        bitWrite(switchSettings[i], j, digitalRead(switchRows[j]));
      }
    }
    digitalWrite(switchCols[i], HIGH);
  }
}

here's the switch scan code with delay(); this works 100% fine...

Code:
void switchScan()
{
  for (byte i = 0; i < 4; i++)
  {
    digitalWrite(switchCols[i], LOW);
      delay(1);
 //   if (currentTime - lastSwitchScan  > switchScanDelay)
 //   {
      lastSwitchScan = currentTime;
      for (byte j = 0; j < 8; j++)
      {
        bitWrite(switchSettings[i], j, digitalRead(switchRows[j]));
      }
 //   }
    digitalWrite(switchCols[i], HIGH);
  }
}

and here's the parser code which faithfully prints out the full value of the bytes:

Code:
void parseData()
{
  for (byte i = 0; i < 4; i++)
  {
    Serial.print(" ");
    Serial.print(i);
    Serial.print(": ");
    for (byte j = 0; j < 8; j++)
    {
      Serial.print(bitRead(switchSettings[i], j), BIN);
    }
    if (i == 3)
    {
      Serial.println();
    }
  }
}

Since this is also running the audio library, as well as LED controls for 7 segment displays and switch feedback, and fastLED to animate 32 LEDs, I need to avoid delay() wherever possible.

What am I doing wrong here?

here's the loop(), which works fine:

Code:
void loop()
{
currentTime = millis();
if (currentTime - lastSwitchScan > switchScanPeriod)
{
  lastSwitchScan = currentTime;
  switchScan();
  parseData();
}

}

Sample Serial Monitor output:
without delay:
0: 00000000 1: 00000000 2: 00000000 3: 00000000
0: 00000000 1: 00000000 2: 00000000 3: 00000000
0: 00000000 1: 00000000 2: 00000000 3: 00000000
0: 00000000 1: 00000000 2: 00000000 3: 00000000
0: 00000000 1: 00000000 2: 00000000 3: 00000000

with delay:
0: 11100101 1: 11111111 2: 11011001 3: 11100110
0: 11100101 1: 11111111 2: 11011001 3: 11100110
0: 11100101 1: 11111111 2: 11011001 3: 11100110
0: 11100101 1: 11111111 2: 11011001 3: 11100110
...updated every 100ms per switchScanPeriod being set to 100

any ideas as to how I can sort this out will be greatly appreciated.
 
the switch scanning seems to run OK in either case; populating the serial monitor with actual switch data or all zeroes depending on if delay() is used in switchScan() or not- switchScan() runs either way.
 
CANCEL THAT, this was helpful- now it writes only to the first byte in the array:

0: 11100101 1: 00000000 2: 00000000 3: 00000000
0: 11100101 1: 00000000 2: 00000000 3: 00000000
0: 11100101 1: 00000000 2: 00000000 3: 00000000
0: 11100101 1: 00000000 2: 00000000 3: 00000000


Code:
void switchScan()
{
  for (byte i = 0; i < 4; i++)
  {
    digitalWrite(switchCols[i], LOW);
 //     delay(1);
    if (currentTime - lastSwitchDelay  > switchScanDelay)
    {
      lastSwitchDelay = currentTime;
      for (byte j = 0; j < 8; j++)
      {
        bitWrite(switchSettings[i], j, digitalRead(switchRows[j]));
      }
    }
    digitalWrite(switchCols[i], HIGH);
  }
}
 
Seems to … but doesn’t. The if is always false. Even if it wasn’t already false on entry, it would be false after the first iteration of the outer for loop, because you assign lastSwitchScan again inside the if.
 
Thanks for that! In my quick rewrite I lost my lastSwitchDelay variable- at least now it populates the first byte in the array- now to figure out why the rest remain blank.
 
Still baffled.
this works like a champ:

Code:
void switchScan()
{
  for (byte i = 0; i < 4; i++)
  {
    digitalWrite(switchCols[i], LOW);
    //  if (currentTime - lastSwitchDelay  > switchScanDelay)
    //   {
    delay(1);
    lastSwitchDelay = currentTime;
    for (byte j = 0; j < 8; j++)
    {
      bitWrite(switchSettings[i], j, digitalRead(switchRows[j]));
    }
    //   }
    digitalWrite(switchCols[i], HIGH);
  }
}

0: 10101010 1: 11111111 2: 10011001 3: 01100110
0: 10101010 1: 11111111 2: 10011001 3: 01100110
0: 10101010 1: 11111111 2: 10011001 3: 01100110
0: 10101010 1: 11111111 2: 10011001 3: 01100110

while this only populates the first byte (an improvement from zero bytes)

Code:
void switchScan()
{
  for (byte i = 0; i < 4; i++)
  {
    digitalWrite(switchCols[i], LOW);
    if (currentTime - lastSwitchDelay  > switchScanDelay)
    {
      //   delay(1);
      lastSwitchDelay = currentTime;
      for (byte j = 0; j < 8; j++)
      {
        bitWrite(switchSettings[i], j, digitalRead(switchRows[j]));
      }
    }
    digitalWrite(switchCols[i], HIGH);
  }
}

0: 10101010 1: 00000000 2: 00000000 3: 00000000
0: 10101010 1: 00000000 2: 00000000 3: 00000000
0: 10101010 1: 00000000 2: 00000000 3: 00000000
0: 10101010 1: 00000000 2: 00000000 3: 00000000
 
no matter what I seem to do, this will always print zero, every 100 milliseconds :(

Code:
void loop()
{
  currentTime = millis();
  if (currentTime - lastSwitchScan > switchScanPeriod)
  {
    lastSwitchScan = currentTime;
    switchScan();
    // parseData();
  }
}

Code:
void switchScan()
{
  for (byte i = 0; i < 4; i++)
  {
   if (currentTime - lastSwitchDelay  > switchScanDelay)
    {
      lastSwitchDelay = currentTime;
      Serial.println(i);
    }
  }
}
 
just a glancing guess?

The time to wait after each setting of the 'digitalWrite(switchCols, LOW);' Would need to be some consistent wait on each change of the control/measure lines?

The 1,000 us from delay(1) is too much but makes it work?

Perhaps delayMicroseconds( 100 ); of some appropriate value would be best?

As written it seems maybe the dynamic resetting of the lastSwitchDelay isn't updating properly on the fly between scan passes?
 
thanks for having a look! I have been using the value of 1ms for the switchScanDelay, but I've tried all kinds of values there to no avail. I can't get it to simply step 0-3 when prompted by the timer.

This code:

Code:
void switchScan()
{
  for (byte i = 0; i < 4; i++)
  {
         delay(1);
      Serial.println(i);
  }
}

prints this:

0
1
2
3

While this code:
Code:
void switchScan()
{
  for (byte i = 0; i < 4; i++)
  {
   if (currentTime - lastSwitchDelay  > switchScanDelay)
    {
      lastSwitchDelay = currentTime;
      Serial.println(i);
    }
  }
}


Prints this:

0
0
0
0

...in general testing a delay(1) is sufficient for good solid reads that populate all 4 bytes with switch data- but I can only get it to work with delay(); I prefer to avoid delay() to keep the rest of the code running smoothly... I also have 15 analog pots to read over a 16 to 1 analog MUX so I imagine non-blocking settling time will be useful there too.
 
...simplifying it further, I find:

Code:
void loop()
{
  for (int i = 0; i < 4; i++)
  {
    currentTime = millis();
    long delayTime = 100;         //(byte is OK here too)
    if (currentTime - lastSwitchScan > delayTime)
    {
      lastSwitchScan = currentTime;
      Serial.println(i);
    }
  }
}

yields this on the Serial Monitor:

0
0
0
0
0

While this:

Code:
void loop()
{
  for (int i = 0; i < 4; i++)
  {
    currentTime = millis();
    long delayTime = 100;         //(byte is OK here too)
    delay(delayTime);
     Serial.println(i);
  }
}

yields this:

0
1
2
3

So I'm missing something fundamental here and I'm having a hard time figuring out what that is.
 
Hahaha got it:

Code:
void loop()
{
  currentTime = millis();
  if (currentTime - lastSwitchScan > switchScanPeriod)
  {
    lastSwitchScan = currentTime;
    switchScan();
    parseData();
  }
}

...and this scanner and parser

Code:
void switchScan()
{
  digitalWrite(switchCols[counter], LOW);
  if (currentTime - lastSwitchDelay  > switchScanDelay)
  {
    lastSwitchScan = currentTime;
    for (byte j = 0; j < 8; j++)
    {
      bitWrite(switchSettings[counter], j, digitalRead(switchRows[j]));
    }
    digitalWrite(switchCols[counter], HIGH);
    counter++;
    if (counter > 3)
    {
      counter = 0;
    }
  }
}

void parseData()
{
  for (byte i = 0; i < 4; i++)
  {
    Serial.print(" ");
    Serial.print(i);
    Serial.print(": ");
    for (byte j = 0; j < 8; j++)
    {
      Serial.print(bitRead(switchSettings[i], j), BIN);
    }
    if (i == 3)
    {
      Serial.println();
    }
  }
}

read everything just great:

0: 01010101 1: 10111111 2: 01010101 3: 01100110
0: 01010101 1: 10111111 2: 01010101 3: 01100110
0: 01010101 1: 10111111 2: 01010101 3: 01100110
0: 01010101 1: 10111111 2: 01010101 3: 01100110

The fundamental problem was not having the timer handle the incrementing... thanks for the help everyone! I'll be back to this thread with the rest of it should I run into issues but this should work great with the analog MUX as well, and allow me to continually smoothly animate my LEDs and apply controls to the synth engine...
 
>> Opps - crossposted now - ... watching TV over dinner ...
Not sure everything is clear with short code snippets but seems that the IF(TIME) conditional is simple causing the for loop to skip the "1,2,3"?

That goes along with glance of p#9.

Seems this might show that:
Code:
void switchScan()
{
  for (byte i = 0; i < 4; i++)
  {
   if (currentTime - lastSwitchDelay  > switchScanDelay)
    {
      lastSwitchDelay = currentTime;
    }
  [B][U]Serial.println(i);[/U][/B]
  }
}

Was this tried?
Code:
void switchScan()
{
  for (byte i = 0; i < 4; i++)
  {
      delayMicroseconds(50); // THis is a guess - it shouldn't take but a couple of microseconds for the I/O to change.
      Serial.println(i);
      switchScan();
  }
}

If the above speculation is right this may also work - the problem if the use of the enclosed for loop() not allowing for time to pass when a delay of some sort is not used.

Code:
void switchScan()
{
  static byte i =0; 
//  for ( ; i < 4; i++)
  if (currentTime - lastSwitchDelay  > switchScanDelay)
  {
//      delayMicroseconds(50); // THis is a guess - it shouldn't take but a couple of microseconds for the I/O to change.
      Serial.println(i);
i++;
if ( i > 3 ) i =0;
  }
}
 
Last edited:
so the blocking delay works every time, as the thing increments it runs into the delay() every time... but when it it's incrementing AND there's a time check, we might not be lining up the time check with the increment: so the non-blocking check says 'Nah' during the incrementing somehow, so I don't get to increment my reads.

but tying the increment to a timer, like

Code:
  if (currentTime - lastSwitchDelay  > switchScanDelay)
  {
  lastSwitchScan = currentTime;
  counter++;
  }

means that whenever that small delay expires, the counter increments, allowing me to write to the correct byte...

Now to look at an efficient way of updating the MAX7221 LED driver chips all at once instead of for every status LED.
 
The last code in p#13 matches the change that fixed the noted 'for' issue from your post #12 code where static byte i seems to match the new counter behavior:
Code:
[B]  if (currentTime - lastSwitchDelay  > switchScanDelay)
[/B]  {
    lastSwitchScan = currentTime;
    for (byte j = 0; j < 8; j++)
    {
      bitWrite(switchSettings[counter], j, digitalRead(switchRows[j]));
    }
    digitalWrite(switchCols[counter], HIGH);
[B]    counter++;
    if (counter > 3)
[/B]    {
      counter = 0;
    }
 
yep, incrementing correctly right there. Thanks for your help! mapping switch data to the LEDs was easy-peasy now it all lights up like a christmas tree... I'll map MUXed pot inputs to the 7 segment displays next, and then finally get the synth part of it up and running.
 
Back
Top