More efficient (and compact) code

I am working on direct port manipulation (I talked about it a previous thread). Right now, I have a for loop counting up from 36 (ASCII !) to 96 (ASCII `) and outputting all the possible characters on a 7-segment display.

/*Flashes each segment on a 7 segment display for 250 milliseconds using
 direct port manipulation.
 
 Note that due to the way the display works, you MUST disconnect the display
 from pins 0 and 1 before uploading.
 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Pins:
 DP  0
 C  1
 D  2
 E  3
 B  4
 A  5
 F  6
 G  7
 
 Arrangement:
 |-A-|
 F   B
 |-G-|
 E   C  
 |-D-| DP
 
 */

#include "digits.h"

long time; /*stores elapsed time
 this variable must be long because int overflows at 32768,
 which is ~33 seconds */

void setDigit(int digit); //convers ascii value to 7-segment display value

void setup() {

  DDRB = DDRB & B11110111; //make pin 12 an input, leave everything else alone

  while((PINB & 1 << 4)) { //wait until pin 12 goes low, then start
  }
  DDRD = B11111111; //make port d (pins 0-7) outputs
}

void loop() {

  for(int digit=33; digit <= 96; digit++) { //var digit holds digit to display
    setDigit(digit); //convert value
    time = millis();
    while(millis() - time < 1000) { //wait 250 milliseconds
    }

  }
}


void setDigit(int digit) { //see "digits.h" for more info
  switch (digit) {
  case 33: 
    PORTD = ~_EXCMARK;
    break;
  case 34:
    PORTD = ~_DQUOTE;
    break;
  case 39:
    PORTD = ~_SFQUOTE;
    break;
  case 44:
    PORTD = ~_COMMA;
    break;
  case 45:
    PORTD = ~_HYPHEN;
    break;
  case 46:
    PORTD = ~_DOT;
    break;
  case 47:
    PORTD = ~_FSLASH;
    break;
  case 48:
    PORTD = ~ZERO;
    break;    
  case 49:
    PORTD = ~ONE;
    break; 
  case 50:
    PORTD = ~TWO;
    break; 
  case 51:
    PORTD = ~THREE;
    break; 
  case 52:
    PORTD = ~FOUR;
    break; 
  case 53:
    PORTD = ~FIVE;
    break; 
  case 54:
    PORTD = ~SIX;
    break; 
  case 55:
    PORTD = ~SEVEN;
    break; 
  case 56:
    PORTD = ~EIGHT;
    break; 
  case 57:
    PORTD = ~NINE;
    break;
  case 61:
    PORTD = ~_EQUALS;
    break;
  case 63:
    PORTD = ~_QMARK;
    break;
  case 65:
    PORTD = ~_A;
    break; 
  case 66:
    PORTD = ~_B;
    break; 
  case 67:
    PORTD = ~_C;
    break; 
  case 68:
    PORTD = ~_D;
    break; 
  case 69:
    PORTD = ~_E;
    break; 
  case 70:
    PORTD = ~_F;
    break; 
  case 71:
    PORTD = ~_G;
    break; 
  case 72:
    PORTD = ~_H;
    break; 
  case 73:
    PORTD = ~_I;
    break; 
  case 74:
    PORTD = ~_J;
    break;    
  case 76:
    PORTD = ~_L;
    break; 
  case 78:
    PORTD = ~_N;
    break; 
  case 79:
    PORTD = ~_O;
    break; 
  case 80:
    PORTD = ~_P;
    break;
  case 81:
    PORTD = ~_Q;
    break;  
  case 82:
    PORTD = ~_R;
    break; 
  case 83:
    PORTD = ~_S;
    break; 
  case 84:
    PORTD = ~_T;
    break;    
  case 85:
    PORTD = ~_U;
    break; 
  case 89:
    PORTD = ~_Y;
    break; 
  case 90:
    PORTD = ~_Z;
    break;
  case 92:
    PORTD = ~_BSLASH;
    break;
  case 94:
    PORTD = ~_CARET;
    break;
  case 95:
    PORTD = ~_UNDRSCR;
    break;
  case 96:
    PORTD = ~_SBQUOTE;
    break;
  default:
    PORTD = ~_NULL;
  }
}

And digits.h:

/*
 Digit Definition File. Contain all numbers, letters, and symbols that can
 be displayed. setDigit(int digit) in main file converts ASCII values to below
 7-segment display values.
 
 Pins:
 G  7
 F  6
 A  5
 B  4
 E  3
 D  2
 C  1
 DP  0
 
 Arrangement:
 |-A-|
 F   B
 |-G-|
 E   C  
 |-D-| DP
 
 */
 
//Numbers
#define ZERO  B01111110
#define ONE   B00010010
#define TWO   B10111100
#define THREE B10110110
#define FOUR  B11010010
#define FIVE  B11100110
#define SIX   B11101110
#define SEVEN B01110010
#define EIGHT B11111110
#define NINE  B11110110

//Letters (K, M, V, W, and X cannot be displayed on a 7-segment)
#define _A    B11111010
#define _B    B11001110
#define _C    B01101100
#define _D    B10011110
#define _E    B11101100
#define _F    B11101000
#define _G    B11110110 //Same as "NINE"
#define _H    B11001010
#define _I    B01001000 //Like "ONE," but on other side
#define _J    B00011110
#define _L    B01001100
#define _N    B01111010
#define _O    B10001110
#define _P    B11111000
#define _Q    B11110010
#define _R    B10001000
#define _S    B11100110 //Same as "FIVE"
#define _T    B11001100
#define _U    B01011110
#define _Y    B11010110
#define _Z    B10111100 //Same as "TWO"

//Symbols
#define _EXCMARK B00010011
#define _DQUOTE  B01010000
#define _SFQUOTE B00010000
#define _COMMA   B00000010
#define _HYPHEN  B10000000
#define _DOT     B00000001
#define _FSLASH  B10011000
#define _EQUALS  B10100000
#define _QMARK   B01110011
#define _BSLASH  B11000010
#define _CARET   B01110000
#define _UNDRSCR B00000100
#define _SBQUOTE B01000000
#define _NULL    B00000000 //All off

Now my problem is that the last chunk of the main code (the switch…case) is very long and seems inefficient. Is there a way I can do what the switch…case does (turning ASCII values into defines) in a quicker/neater/more efficient way?

Thanks!
baum

Arrays. Preferably in flash.

static const byte PROGMEM translating_array[96-36] = {
    ~_EXCMARK,
    ~_DQUOTE,
    ~_SFQUOTE,
    ~_HYPHEN,
    :
    ~_SBQUOTE
};

  PORTD = pgm_read_byte(&(translating_array[digit-36]);

So I would have two arrays, one with the symbol, and one with the 7-segment binary value? Or could I have a 2D array? And what is the 96-36? And this part:

&(translating_array[digit-36]);

?

baum

One array, indexed by the symbol, containing the 7-seg binary value. Since you have all the values between 36 and 96, and arrays normally start at 0, we subtract 36 from the symbol value before using it as an index.

"96-36" is a subtraction that gives us the length of the array (might need a +1 there), while making the compiler do the work for us. It's supposed to make it clearer that the array maps values from 36 to 96, rather than 0 to 60. I guess it failed :-)

"&(array[digit - 36])" is the address (in flash) of the 7seg data for "digit". Subtract 36 to re-scale to zero based array, arr[n] is the array element, "&" is the "address of" operator. (pgm_read_byte() wants an address.)

Search for "pgm_read_byte" to find other examples.

I guess it failed :-)

No it didn't. :) I understood that part.

"&(array[digit - 36])" is the address (in flash) of the 7seg data for "digit". Subtract 36 to re-scale to zero based array, arr[n] is the array element, "&" is the "address of" operator. (pgm_read_byte() wants an address.)

Search for "pgm_read_byte" to find other examples.

That makes sense. I know how to use pointers; I just didn't know why you wanted one here.

Thanks!

but how many arrays should I have? Just one, with the #defines as values?

baum

Just one, with the #defines as values?

Yep. I'd normally put the binary constants directly into the array, but since you already have #defines, you might as well use them...

OK. Thanks a lot!!

baum