Soccer Pinball
     an ME218A Project

ME218A: Soccer Pinball

Dept. of Mechanical Eng. | Stanford University



Back Glow

Bill of Materials
Circuitry
Software

LED Display Driver Module Code

In order to make the scoreboard operation as transparent and readable as possible in the main game module code, a driver module was written to drive the LED display. This module is documented in the header file and provides functions to the main module to do the following:

The actual files for the code are linked below:

Header File

/* Make sure this is imported only once */
#ifndef SCOREBOARDH
#define SCOREBOARDH

/* NOTE: This module controls the use of ports T3-T7 on the C32.
Before using any of the functions provided by the module,
InitializeScoreboard() must be called. This module initializes
the ME218 timer modules to a 1ms clock tick when used.
*/


/* #define TESTMODE */ /* Comment this line out to use it as a module with no main function */
#define INITIAL_TIME 60 /* The initial time to display on the scoreboard */

/* Public Functions */
void InitializeScoreboard(void);      /* Initializes the input and output pins on the scoreboard */
                                      /* and resets the scores to 0 and timer to a default time, defined above*/
int DecrementScoreboardTime(void);    /* Subtracts 1 from the Timer. Returns a 1 if Time is expired, 0 otherwise */
int IncrementPlayerScore(void);       /* Adds 1 to the Player score. Returns a 1 if maxed out, 0 otherwise */
int IncrementComputerScore(void);     /* Adds 1 to the COmputer score. Returns a 1 if maxed out, 0 otherwise */
void SetScoreboardTime(char time[]);  /* Input a new time as a 2 character string of digits e.g. "60" */
void updatedisplay(void);             /* Force refreshes the 7 segment displays */
void updatedisplayfractionally(void); /* Refreshes the 7 segment display every 10 times it's called */

#endif

.C File

/* #includes */
#include <stdio.h>
#include <ME218_C32.h>
#include <timers12.h>                        /* used for TMRS12_X functions */
#include "scoreboard.h"

/* #defines to make the code more readable */
#define CLOCKBITHI      BIT3HI
#define CLOCKBITLO      BIT3LO
#define COMPSCOREBITHI  BIT4HI
#define COMPSCOREBITLO  BIT4LO
#define TIMER1BITHI     BIT5HI
#define TIMER1BITLO     BIT5LO
#define TIMER10BITHI    BIT6HI
#define TIMER10BITLO    BIT6LO
#define USERSCOREBITHI  BIT7HI
#define USERSCOREBITLO  BIT7LO

/* #define string indices such that '1572' indicates time of 57 and scores: user: 1, comp: 2 */
#define COMP            3        /* Computer Score */
#define TIMER1          2        /* 1s digit of timer */
#define TIMER10         1        /* 10s digit of timer */
#define USER            0        /* User score */

/* #defines for what test function to run when running the scoreboard independently */
#define NONE      0
#define CYCLE     1
#define TESTSIG   2
#define SCORETEST 3

 

/* Local Function Prototypes */
static void initialize(void);                                         
static void pulse_clock(void);
static void wait_milliseconds(int milliseconds);
static void load_shift_register(char *stringAtoH[]);
static void display(char *input);

static void cycle_test(void);
static void SetUserScore(char input);                        
static void SetComputerScore(char input);

/* Local Variables */
static char scoreboard[] = "    \0";        /* initialize the module variable 'scoreboard' to track its current contents */

/*********************************/
/******** PUBLIC FUNCTIONS *******/
/*********************************/

/***** Main() *******/
/* This function is for use only in testing purposes, and can be omitted by #define in the header file */
#ifdef TESTMODE
void main( void) {
  int foolwhile = 5;
  int n;
  int mode = SCORETEST;
  int expired = FALSE;
  initialize();

  if(mode == TESTSIG) {
    while(foolwhile) {
        display("2222");
        for (n=0; n<50; n++);   
    }
  }
 
  if(mode == SCORETEST) {
    while(foolwhile) {
      /* test the initialization and decrementing functions */
      printf("Initializing Scoreboard...\r\n"); 
      InitializeScoreboard();
                                    wait_milliseconds(2000);

      printf("Now decrement the timer until it expires\r\n");
      printf("Initial Timer value is %c%c\r\n",scoreboard[TIMER10],scoreboard[TIMER1]);
      /* make sure the timer expired flag works */
      while (expired == FALSE) {
        wait_milliseconds(300);
        expired = DecrementScoreboardTime();
        printf("Timer value is %c%c\r\n",scoreboard[TIMER10],scoreboard[TIMER1]); 
      }
      printf("Timer Expired!!\r\n");
    
      /* Test the score incrementing function */
      expired = FALSE;
      printf("Time to increment the player's score\r\n");
      while (expired == FALSE) {
 
        expired = IncrementPlayerScore();
        printf("Player Score is %c\r\n",scoreboard[USER]); 
        wait_milliseconds(1000);
      }
    }
  }
 
  if(mode == CYCLE) cycle_test();
 
  return;
}
#endif

/***** InitializeScoreboard() *******/
/* Sets the output pins for the C32, sets player and computer scores to 0 */
/* and sets the timer to the value given in the header file.              */
void InitializeScoreboard(void) {
  initialize();
  SetUserScore('0');
  SetComputerScore('0');
  SetScoreboardTime("INITIAL_TIME");
  updatedisplay();
  return; 
}

/***** SetScoreboardTime() *******/
/* Set the scoreboard timer to the time given as an input string of two */
/* digits.  E.G. "60" or "99" or "00"                                   */
void SetScoreboardTime(char time[]) {
  scoreboard[TIMER1]  = time[1];     /* Set 1s digit of timer */
  scoreboard[TIMER10] = time[0];     /* Set 10s digit of timer */
  updatedisplay();                   /* Write to the display */
  return;
}

/***** IncrementPlayerScore() *******/
/* Adds one to the player score and returns a 1 if it is maxed out.    */
/* If the score is still in an ok range (i.e. is below 9) it returns 0 */
int IncrementPlayerScore(void) {
  int maxedout = FALSE;
  /* Use a switch case to handle 'incrementing' a character value */
  switch (scoreboard[USER]) {
    case '0' : SetUserScore('1'); break;
    case '1' : SetUserScore('2'); break;
    case '2' : SetUserScore('3'); break;
    case '3' : SetUserScore('4'); break;
    case '4' : SetUserScore('5'); break;
    case '5' : SetUserScore('6'); break;
    case '6' : SetUserScore('7'); break;
    case '7' : SetUserScore('8'); break;
    case '8' : SetUserScore('9'); maxedout=TRUE; printf("Player Score maxed out\r\n"); break;
    default  : printf("Invalid Player Score value\r\n"); break;
  }
  updatedisplay();
  return maxedout;
}

/***** IncrementComputerScore() *******/
/* Adds one to the computer score and returns a 1 if it is maxed out.   */
/* If the score is still in an ok range (i.e. is below 9) it returns 0  */
int IncrementComputerScore(void) {
  int maxedout = FALSE;
  /* Use a switch case to handle 'incrementing' a character value */
  switch (scoreboard[COMP]) {
    case '0' : SetComputerScore('1'); break;
    case '1' : SetComputerScore('2'); break;
    case '2' : SetComputerScore('3'); break;
    case '3' : SetComputerScore('4'); break;
    case '4' : SetComputerScore('5'); break;
    case '5' : SetComputerScore('6'); break;
    case '6' : SetComputerScore('7'); break;
    case '7' : SetComputerScore('8'); break;
    case '8' : SetComputerScore('9'); maxedout=TRUE; printf("Comp Score maxed out\r\n"); break;
    default  : printf("Invalid Comp Score value\r\n"); break;
  }
  updatedisplay();
  return maxedout;
}

/***** DecrementScoreboardTime() *******/
/* Subtracts one from the current timer and returns TRUE if the timer is expired */int DecrementScoreboardTime(void) {
  int timerexpired = FALSE;
  /* Use switch cases to decrement the timer value stored as a character */
  switch (scoreboard[TIMER1]) {
    case '0' :
      scoreboard[TIMER1] = '9';
      switch (scoreboard[TIMER10]) {
        /* handle rollover into the 10s digit */
        case '0' : scoreboard[TIMER1]  = '0'; SetComputerScore('1'); timerexpired=TRUE; ; break;
        case '1' : scoreboard[TIMER10] = '0'; break;
        case '2' : scoreboard[TIMER10] = '1'; break;
        case '3' : scoreboard[TIMER10] = '2'; break;
        case '4' : scoreboard[TIMER10] = '3'; break;
        case '5' : scoreboard[TIMER10] = '4'; break;
        case '6' : scoreboard[TIMER10] = '5'; break;
        case '7' : scoreboard[TIMER10] = '6'; break;
        case '8' : scoreboard[TIMER10] = '7'; break;
        case '9' : scoreboard[TIMER10] = '8'; break;
        default  : printf("Invalid Value Exists in Timer 10 spot\r\n"); break;
      }
      break;  
    case '1' : scoreboard[TIMER1] = '0'; break;
    case '2' : scoreboard[TIMER1] = '1'; break;
    case '3' : scoreboard[TIMER1] = '2'; break;
    case '4' : scoreboard[TIMER1] = '3'; break;
    case '5' : scoreboard[TIMER1] = '4'; break;
    case '6' : scoreboard[TIMER1] = '5'; break;
    case '7' : scoreboard[TIMER1] = '6'; break;
    case '8' : scoreboard[TIMER1] = '7'; break;
    case '9' : scoreboard[TIMER1] = '8'; break;
    default  : printf("Invalid Value Exists in Timer 1 spot\r\n"); break;
  }
  updatedisplay();
  return timerexpired; 
}

/***** UpdateDisplay() *******/
/* Force the display to update to the contents in memory */
void updatedisplay(void) {
  display(scoreboard);
  return;
}

/***** UpdateDisplayFractionally() *******/
/* To prevent updating the display from slowing the E&S framework       */
/* call this function to only occasionally update it (10% of the time)  */
/* when including this function in a display loop.  This is recommended */
/* as otherwise the display may drift due to shift register problems    */
void updatedisplayfractionally(void) {
  static int n = 0;
  n++;
  if (n % 10 == 0) {
    display(scoreboard);
  }
  return;
}

/**********************************/
/******** PRIVATE FUNCTIONS *******/
/**********************************/

/***** Initialize() *******/
/* Initializes the outputs on the C32 */
static void initialize(void)
{
  // Set Ports T0-1 to outputs
  DDRT = DDRT | (CLOCKBITHI | COMPSCOREBITHI | TIMER1BITHI | TIMER10BITHI | USERSCOREBITHI);
  return;
}

/***** pulse_clock() *******/
/* briefly pulse the clock high */
static void pulse_clock(void)
{
  int n = 0;
  for (n=0; n<50; n++);
  PTT = PTT | CLOCKBITHI;          /* Rising edge on the clock */
  for (n=0; n<50; n++);            /* Waste some time to let the pulse go through */
  PTT = PTT & CLOCKBITLO;          /* Falling edge on the clock */
  for (n=0; n<10; n++);
  return; 
}

/***** wait_milliseconds() *******/
/* Convenient, blocking function to wait during initialization or testing sequences */
static void wait_milliseconds(int milliseconds)
{
  int time_started;
  TMRS12_Init(TMRS12_RATE_1MS);  /* ensure timer module is started and the tick is right */
  time_started = TMRS12_GetTime();
  while (TMRS12_GetTime()-time_started<=milliseconds)
  {        /* wait for the count to reach the critical value */
  }
  return;
}

/***** display() *******/
/* Uses a switch case to parse an string of 4 input characters into patterns */
/* for display on a 7-segment LED display.  It then calls the function       */
/* load_shift_register to write the sequences to the scoreboard.             */
static void display(char *input)
{
  char *output[4];
  int i;
 
  /* Repeat the matching for each input character */
  for (i=0; i<4; i++) {
   
    switch (input[i]) {
    /* Map characters to appropriate patterns of segments.  1s are on, 0s are off   */
    /* Sequences correspond to the standard A,B,C,D,E,F,G,DP labelling of 7-segment */
    /* displays.                                                                    */
      case '0' : output[i] = "11111100"; break;
      case '1' : output[i] = "01100000"; break;
      case '2' : output[i] = "11011010"; break;
      case '3' : output[i] = "11110010"; break;
      case '4' : output[i] = "01100110"; break;
      case '5' : output[i] = "10110110"; break;
      case '6' : output[i] = "10111110"; break;
      case '7' : output[i] = "11100000"; break;
      case '8' : output[i] = "11111110"; break;
      case '9' : output[i] = "11110110"; break;
      case 'A' : output[i] = "11101110"; break; 
      case 'B' : output[i] = "00111110"; break;
      case 'C' : output[i] = "10011100"; break;
      case 'D' : output[i] = "01111010"; break;
      case 'E' : output[i] = "10011110"; break;
      case 'F' : output[i] = "10001110"; break;
      case 'H' : output[i] = "01101110"; break;
      case 'I' : output[i] = "01100000"; break;
      case 'O' : output[i] = "11111100"; break;
      case 'S' : output[i] = "10110110"; break;
      case 'L' : output[i] = "00011100"; break;
      case 'P' : output[i] = "11001110"; break;
      case 'Y' : output[i] = "01110110"; break;
      case ' ' : output[i] = "00000000"; break;     
      /* Debugging cases */
      case 'a' : output[i] = "10000000"; printf("a\r\n"); break;     
      case 'b' : output[i] = "01000000"; printf("b\r\n"); break;     
      case 'c' : output[i] = "00100000"; printf("c\r\n"); break;     
      case 'd' : output[i] = "00010000"; printf("d\r\n"); break;     
      case 'e' : output[i] = "00001000"; printf("e\r\n"); break;     
      case 'f' : output[i] = "00000100"; printf("f\r\n"); break;     
      case 'g' : output[i] = "00000010"; printf("g\r\n"); break;     
      case 'p' : output[i] = "00000001"; printf("dp\r\n"); break;
      /* Default Case */    
      default : output[i] = "00000000"; break;
    }
  }
  load_shift_register(output);  /* load the sequences to the shift register */
  return;                                                                                                           
}

 

/***** load_shift_register() *******/
/* Loads the output of the display function to the shift registers */
static void load_shift_register(char *displayinfo[])
{
  int i;
 
  /* clear out what's in the register by writing a bunch of HIs to turn it all off*/
  for(i=1; i<=8; i++) {
    PTT = PTT & (COMPSCOREBITLO & TIMER1BITLO & TIMER10BITLO & USERSCOREBITLO);  
    pulse_clock();
  } 
  /* Load the new display, changing a single segment across all */
  /* four displays, then pulsing the shared clock to set it     */
  /* and move on to the next segment */
  for(i=7; i>=0; i--) {
 
    if (displayinfo[USER][i] == '1') PTT = PTT | (USERSCOREBITHI);
    if (displayinfo[USER][i] == '0') PTT = PTT & (USERSCOREBITLO);
   
    if (displayinfo[COMP][i] == '1') PTT = PTT | (COMPSCOREBITHI);
    if (displayinfo[COMP][i] == '0') PTT = PTT & (COMPSCOREBITLO);
   
    if (displayinfo[TIMER1][i] == '1') PTT = PTT | (TIMER1BITHI);
    if (displayinfo[TIMER1][i] == '0') PTT = PTT & (TIMER1BITLO);
   
    if (displayinfo[TIMER10][i] == '1') PTT = PTT | (TIMER10BITHI);
    if (displayinfo[TIMER10][i] == '0') PTT = PTT & (TIMER10BITLO);

    pulse_clock();
  }
  return;
}

/***** Cycle_test() *******/
/* Displays a looping message on the scoreboard */

static void cycle_test(void) {
  /* Display a never ending loop of "HI 218 PEOPLE" */
  int foolwhile = 5;
  while(foolwhile) {
      display("   H"); wait_milliseconds(600);
      display("  HI"); wait_milliseconds(600);
      display(" HI "); wait_milliseconds(600);
      display("HI 2"); wait_milliseconds(600);
      display("I 21"); wait_milliseconds(600);
      display(" 218"); wait_milliseconds(600); 
      display("218 "); wait_milliseconds(600);
      display("18 P"); wait_milliseconds(600); 
      display("8 PE"); wait_milliseconds(600);
      display(" PEO"); wait_milliseconds(600);
      display("PEOP"); wait_milliseconds(600);
      display("EOPL"); wait_milliseconds(600);
      display("OPLE"); wait_milliseconds(600);
      display("PLE "); wait_milliseconds(600);
      display("LE  "); wait_milliseconds(600);
      display("E   "); wait_milliseconds(600);
      display("    "); wait_milliseconds(600);       
    }
}

 

/***** SetUserScore() *******/
/* Allows the User Score to be set to any value.  Used for initalization */
static void SetUserScore(char input) {
  scoreboard[USER] = input;
  return;
}

/***** SetComputerScore() *******/
/* Allows the Computer Score to be set to any value.  Used for initalization */
static void SetComputerScore(char input) {
  scoreboard[COMP]  = input;
  return;
}