Multiplexed 7-segments. Can this code be faster?

I've written a minutes/seconds countdown timer that sounds an audible alarm when the time reaches zero and has a "disarm" button that can halt the countdown. I'm going to use this for "diffuse the bomb" airsoft battles. It works as I want, but the display is a little dim. It's not bad, but I would like it to be brighter.

I know that speeding up loop() is going to get me the brightness that I'd like. Do you have any hints on streamlining the logic in loop() or have I reached the maximum reasonable brightness that one would expect when multiplexing four displays in parallel? What about alloff() ?

loop()

void loop (){

  switch (digitalRead(buttonPin)){ // read disarm button state
  case 0: // button is not held down.
    previousMillis = millis();      // keeps millis in sync
    currentMillis = previousMillis; // when button is open

  case 1: // button is held down
    currentMillis = millis(); // Grows away from previousMillis
                              // while button is held down.
  }

  // catches long press as soon as it crosses interval threshold.
  if(currentMillis - previousMillis > interval) { // long press?
    allOff();     // clear LEDS
    TIMSK1=0x00;  // stop timer interrupts
    while ( true ){ // LEDS off unconditionally.
    }
  }

  if (alarm == 1){ // countdown has reached zero
    PORTD = B1111111; // LED states cleared.
    digitalWrite(leftSecAnode,ON);  // ready for new LED states
    digitalWrite(rightSecAnode,ON); // ready for new LED states
    digitalWrite(rightMinAnode,ON); // ready for new LED states
    digitalWrite(leftMinAnode,ON);  // ready for new LED states
    while ( true ){   // blink G segments forever. Audible alarm comes later.
      digitalWrite(G,!digitalRead(G));
      delay(200);
    }

  }
  writeMinutes();
  writeSeconds();
}

Entire sketch

// Airsoft bomb utilizing timer1 interrupts
// Four digit, down timer. Able to count from 99m99s to 0m0s.

// Circuit description:
// Four 7-segment, common anode LEDS are connected in parallel.
// Multiplexing of display is achieved by switching the anodes
// on in sequence. Button input pins are tied low with 10Kohm 
// resistors and go high when momentary push button (to 5V) is pressed.

// When power is applied initially, the program waits for user
// setup. Pressing the disarm/time-set button adds time, which 
// is displayed on the 7 segment leds. Once the desired time has
// been set, the user presses the arm button and the timer begins
// counting down to zero.

// when the count reaches zero, the alarm goes off
// if the disarm button is depressed for more the defined
// interval, timer1 interrupt is disabled and the LEDs
// are cleared. If count reaches zero without being disarmed,
// then timer interrupt is disabled, LEDS cleared, middle LED
// segments flashes unconditionally.





// Common anode. LED on when pin is low.
// Anodes switched by PNP transistors, so
//   low on the anode is also on.

// Map segments to arduino pins. Not needed once 
// alarm routine uses audible alarm instead of 
// blinking led segment G.
const int A = 0;
const int B = 1;
const int C = 2;
const int D = 3;
const int E = 4;
const int F = 5;
const int G = 6;

const int buttonPin = 12; // Input pin for disarm button.

int alarm = 0; // set to 1 when countdown reaches zero

long previousMillis = 0; // synced with currentMillis if button is up.
unsigned long currentMillis = 0; // grows away from previousMillis 
// when button is down

long interval = 2000; // how long the button must be pressed to disarm

// Common anode;
// on when pin is low
// and off when pin is high
const int ON = LOW;
const int OFF = HIGH;

int leftSecAnode = 8; // PNP switched, so LOW = on
int rightSecAnode = 9; // PNP switched, so LOW = on
int leftMinAnode = 10; // PNP switched, so LOW = on
int rightMinAnode = 11; // PNP switched, so LOW = on

int segBytes[] = {/* seg A is byte 6. seg G is byte 0.
 __ABCDEFG There is no DP or colon on my displays */
  B1000000, // 0
  B1111001, // 1
  B0100100, // 2
  B0110000, // 3
  B0011001, // 4
  B0010010, // 5
  B0000011, // 6
  B1111000, // 7
  B0000000, // 8
  B0010000  // 9
};

// initial state. Time must be added by user.
int minutes = 0;
int seconds = 0;

// These are set by calcDigits()
int secLeft;
int secRight;
int minLeft;
int minRight;


////////////// Timer interrupt routine /////////////////

ISR(TIMER1_OVF_vect) { // initialize timer1
  TCNT1=0x0BDC; // set initial value to remove time error (16bit counter register)
  if (digitalRead(buttonPin) == 0){ // halt countdown while disarm button is down
    switch (seconds || minutes){
    case 0: // seconds and minutes are both zero.
      alarm = 1;
      break;
    default:
      if (seconds == 0){
        minutes--;
        seconds = 59;
        calcDigits();
        break;
      }
      seconds--;
      calcDigits();
      break;
    }
  }
}

///////////////////////////////////////////////////
//////////////// setup () /////////////////////////
///////////////////////////////////////////////////

void setup (){

  int armed = 0; // set to 1 once time to count down has been set
  int armButton = 13; // press this button to start countdown

  DDRD = B11111111; // PORTD pins are outputs.
  PORTD = B11111111; // start with LEDS off.

  pinMode(leftSecAnode,OUTPUT);
  digitalWrite(leftSecAnode,OFF);

  pinMode(rightSecAnode,OUTPUT);
  digitalWrite(rightSecAnode,OFF);
  
  pinMode(leftMinAnode, OUTPUT);
  digitalWrite(leftMinAnode, OFF);
  
  pinMode(rightMinAnode, OUTPUT);
  digitalWrite(rightMinAnode, OFF);

  pinMode(armButton, INPUT); // arm button
  pinMode(buttonPin, INPUT); // time-set/disarm button

  // Time must be set and bomb armed before counting down begins.
  while (armed == 0){  
    calcDigits();
    writeMinutes();
    writeSeconds();
    if ( digitalRead(buttonPin) == 1 ){
      minutes = minutes + 1; // add one minute every time button is pressed.
      delay(200); // simple debounce
    }
    if ( digitalRead(armButton) == 1){
      armed = 1; // exits the arming loop and begins countdown.
    }

  }

  TIMSK1=0x01; // enabled global and timer overflow interrupt;
  TCCR1A = 0x00; // normal operation page 148 (mode0);
  TCNT1=0x0BDC; // set initial value to remove time error (16bit counter register)
  TCCR1B = 0x04; // start timer/ set clock

}

///////////////////////////////////////////////////
/////////////////// loop() ////////////////////////
///////////////////////////////////////////////////
void loop (){

  switch (digitalRead(buttonPin)){ // read disarm button state
  case 0: // button is not held down.
    previousMillis = millis();      // keeps millis in sync
    currentMillis = previousMillis; // when button is open

  case 1: // button is held down
    currentMillis = millis(); // Grows away from previousMillis
                              // while button is held down.
  }

  // catches long press as soon as it crosses interval threshold.
  if(currentMillis - previousMillis > interval) { // long press?
    allOff();     // clear LEDS
    TIMSK1=0x00;  // stop timer interrupts
    while ( true ){ // LEDS off unconditionally.
    }
  }

  if (alarm == 1){ // countdown has reached zero
    PORTD = B1111111; // LED states cleared.
    digitalWrite(leftSecAnode,ON);  // ready for new LED states
    digitalWrite(rightSecAnode,ON); // ready for new LED states
    digitalWrite(rightMinAnode,ON); // ready for new LED states
    digitalWrite(leftMinAnode,ON);  // ready for new LED states
    while ( true ){   // blink G segments forever. Audible alarm comes later.
      digitalWrite(G,!digitalRead(G));
      delay(200);
    }

  }
  writeMinutes();
  writeSeconds();
}

//////// Function definitions: ///////////////
void allOff(){
  digitalWrite(rightSecAnode,OFF);
  digitalWrite(leftSecAnode,OFF);
  digitalWrite(rightMinAnode,OFF);
  digitalWrite(leftMinAnode,OFF);
}

void writeMinutes(){
  PORTD = segBytes[minLeft]; // display left digit
  digitalWrite(leftMinAnode, ON);
  allOff();
  PORTD = segBytes[minRight]; // display right digit
  digitalWrite(rightMinAnode, ON);
  allOff();
}

void writeSeconds(){
  PORTD = segBytes[secLeft]; // display left digit
  digitalWrite(leftSecAnode, ON);
  allOff();
  PORTD = segBytes[secRight]; // display right digit
  digitalWrite(rightSecAnode, ON);
  allOff();
}

void calcDigits(){
  secLeft = seconds / 10; // left digit
  secRight = seconds % 10; // right digit
  minLeft = minutes / 10; // left digit
  minRight = minutes % 10; // right digit
}

To get more brightness, spend more time with the LEDs on:

const unsigned long delayWhileLit = 1;  // Increase this until you get flickering, then reduce a step

void writeMinutes(){
  PORTD = segBytes[minLeft]; // display left digit
  digitalWrite(leftMinAnode, ON);
  delay(delayWhileLit);
  digitalWrite(leftMinAnode, OFF);
  PORTD = segBytes[minRight]; // display right digit
  digitalWrite(rightMinAnode, ON);
  delay(delayWhileLit);
  digitalWrite(leftMinAnode, OFF);
}

void writeSeconds(){
  PORTD = segBytes[secLeft]; // display left digit
  digitalWrite(leftSecAnode, ON);
  delay(delayWhileLit);
  digitalWrite(leftSecAnode, OFF);
  PORTD = segBytes[secRight]; // display right digit
  digitalWrite(rightSecAnode, ON);
  delay(delayWhileLit);
  digitalWrite(rightSecAnode, OFF);
}

Oh, sure. Just go ahead and tackle the problem from the most simple and obvious angle. Sheesh.

Thank you, that was so obvious that it's no wonder I didn't think of it :slight_smile: