Jump to content
43oh

Game of Life with G2211 and Nokia 5110 LCD


Recommended Posts

Features...

Software SPI

Random number generation using DCO and VLO

Reduced memory usage by buffering only 2 columns of next grid

 

#include "msp430g2211.h"

// --- I/O Map ---
// P1.0 DC / RES
// P1.1 TxD (not used)
// P1.2 RxD (not used)
// P1.3 Switch
// P1.4 SCE
// P1.5 SCLK
// P1.6 Backlight
// P1.7 SDIN

static const unsigned TXD = BIT1;
static const unsigned RXD = BIT2;
static const unsigned SWITCH = BIT3;
static const unsigned LCD_DC = BIT0;
static const unsigned LCD_CE = BIT4;
static const unsigned LCD_CLK = BIT5;
static const unsigned LCD_BACKLIGHT = BIT6;
static const unsigned LCD_DATA = BIT7;

#define GRID_WIDTH  28
#define GRID_HEIGHT 24      // Must be multiple of 8

static unsigned char cells[GRID_WIDTH * (GRID_HEIGHT / 8)];
static unsigned char next[2 * (GRID_HEIGHT / 8)];

typedef enum {
   lcd_command = 0,        // Array of one or more commands
   lcd_data = 1,           // Array of one or more bytes of data
   lcd_data_repeat = 2     // One byte of data repeated
} lcd_cmd_type;

// Send commands or data to LCD using software SPI
void lcd_send(const unsigned char *cmd, unsigned len, lcd_cmd_type type)
{
   register unsigned mask;
                                                           // CE & DC high upon entry and exit 
   do {                                                    // Send bits 7 to 1
       mask = 0x0080;                                      // msb first
       do {                                                //
           if(*cmd & mask) {                               // Test bit
               P1OUT |= LCD_DATA;                          // 1 bit, data high
               P1OUT &= ~(LCD_CE | LCD_CLK);               // Clock & CE low
           } else {                                        //
               P1OUT &= ~(LCD_CE | LCD_CLK | LCD_DATA);    // 0 bit, data, clock & CE low
           }                                               //
           P1OUT |= LCD_CLK;                               // Clock high
           mask >>= 1;                                     // Next bit
       } while(!(mask & 1));                               // Check if bit 0, loop if not
       if(!type) P1OUT &= ~LCD_DC;                         // DC low if command
                                                           // Send bit 0
       if(*cmd & mask) {                                   // Test bit
           P1OUT |= LCD_DATA;                              // 1 bit, data high
           P1OUT &= ~LCD_CLK;                              // Clock low
       } else {                                            //
           P1OUT &= ~(LCD_CLK | LCD_DATA);                 // 0 bit, data & clock low
       }                                                   //                                                              
       P1OUT |= LCD_CLK;                                   // Clock high
       P1OUT |= LCD_DC;                                    // DC high
       if(!(type & 2)) ++cmd;                              // Inc buf pointer if not repeat
   } while(--len);                                         // Decrement byte count, next byte if not zero...
   P1OUT |= LCD_CE;                                        // CE high
}

static unsigned get_cell(int x, int y)
{
   if(x < 0 || x >= GRID_WIDTH || y < 0 || y >= GRID_HEIGHT ) return 0;
   return (cells[(x * (GRID_HEIGHT / 8)) + (y >> 3)] >> (y & 7)) & 1;
}

static void set_cell(int x, int y, unsigned 
{
   register unsigned char *p = &cells[(x * (GRID_HEIGHT / 8)) + (y >> 3)];
   register const unsigned char m = (1 << (y & 7)); 
   if( *p |= m; else *p &= ~m;
}

static unsigned get_next(int x, int y)
{
   return (next[((x & 1) ? 0 : (GRID_HEIGHT / 8)) + (y >> 3)] >> (y & 7)) & 1;
}

static void set_next(int x, int y, unsigned 
{
   register unsigned char *p = &next[((x & 1) ? (GRID_HEIGHT / 8) : 0) + (y >> 3)];
   register const unsigned char m = (1 << (y & 7)); 
   if( *p |= m; else *p &= ~m;
}

void copy_column(int x)
{
   //int y;
   //for(y = 0; y < GRID_HEIGHT; ++y) set_cell(x - 1, y, get_next(x, y));

   memcpy(cells + ((x - 1) * (GRID_HEIGHT / 8)), next + ((x & 1) ? 0 : (GRID_HEIGHT / 8)), 3); 
}

static void update_lcd(void)
{
   static const unsigned char pm[16] = {
       0x00,   // 0000 -> 00000000
       0x03,   // 0001 -> 00000011
       0x0C,   // 0010 -> 00001100
       0x0F,   // 0011 -> 00001111
       0x30,   // 0100 -> 00110000
       0x33,   // 0101 -> 00110011
       0x3C,   // 0110 -> 00111100
       0x3F,   // 0111 -> 00111111
       0xC0,   // 1000 -> 11000000
       0xC3,   // 1001 -> 11000011
       0xCC,   // 1010 -> 11001100
       0xCF,   // 1011 -> 11001111
       0xF0,   // 1100 -> 11110000
       0xF3,   // 1101 -> 11110011
       0xFC,   // 1110 -> 11111100
       0xFF    // 1111 -> 11111111
   };
   unsigned char c[2];
   register unsigned x;

   // Note: Grid height of 24 assumed
   c[0] = 0x40; // y = 0
   for(x = 0; x < GRID_WIDTH * 3; ) {
       // Each cell is 2 pixels high
       next[0] = pm[cells[x] & 0x0F];
       next[1] = pm[cells[x] >> 4];
       ++x;
       next[2] = pm[cells[x] & 0x0F];
       next[3] = pm[cells[x] >> 4];
       ++x;
       next[4] = pm[cells[x] & 0x0F];
       next[5] = pm[cells[x] >> 4];
       x -= 2;
       // Each cell is 3 pixels wide
       c[1] = 0x80 | x++;
       lcd_send(c, sizeof(c), lcd_command);
       lcd_send(next, 6, lcd_data);
       c[1] = 0x80 | x++;
       lcd_send(c, sizeof(c), lcd_command);
       lcd_send(next, 6, lcd_data);
       c[1] = 0x80 | x++;
       lcd_send(c, sizeof(c), lcd_command);
       lcd_send(next, 6, lcd_data);
   }
}

void main(void)
{
   register int x, y, n;

   static const unsigned char init[] = {
       0x20 + 0x01,    // Function set - extended instructions enabled
       0x80 + 0x40,    // Set vop (contrast) 0 - 127
       0x04 + 0x02,    // Temperature control
       0x10 + 0x03,    // Set bias system
       0x20 + 0x02,    // Function set - chip active, vertical addressing, basic instructions
       0x08 + 0x04     // Display control - normal mode
   };
   static const unsigned char clear[] = { 0x40, 0x80, 0 };

   WDTCTL = WDTPW | WDTHOLD;           // Disable watchdog

   DCOCTL = 0;                         // Use 1 MHz DCO calibration
   BCSCTL1 = CALBC1_1MHZ;
   DCOCTL = CALDCO_1MHZ;

                                       // Setup I/O
   P1REN = RXD | SWITCH;
   P1DIR = LCD_DC | LCD_CE | LCD_CLK | LCD_BACKLIGHT | LCD_DATA;
   P1OUT = RXD | SWITCH | LCD_CE | LCD_BACKLIGHT;

   TACTL = TASSEL_2 + MC_1 + ID_0;     // setup timer, up mode, SMCLK/1
   TACCR0 = 1000 - 1;                  // 1 kHz

   BCSCTL3 = LFXT1S_2;                 // Use VLOCLK as ACLK
                                       // Watchdog: Interval timer, ACLK, /64
   WDTCTL = 0x5A00 | WDTTMSEL | WDTSSEL | WDTIS1 | WDTIS0;

   __delay_cycles(250000);             // Reset LCD
   P1OUT |= LCD_DC;

   lcd_send(init, sizeof(init), lcd_command); // Initialize LCD

   for(; {
       // Clear LCD
       lcd_send(clear, 2, lcd_command);
       lcd_send(clear + 2, 504, lcd_data_repeat);
       // Create inital grid
       for(n = 0; n < GRID_WIDTH * (GRID_HEIGHT / 8); ++n) {
           x = 8;
           do {
               while(!(IFG1 & WDTIFG)); IFG1 &= ~WDTIFG;
               cells[n] <<= 1; cells[n] ^= (TAR >> 1);
           } while(--x);
       }
       // Toggle backlight if button still down
       if((P1IN & BIT3) == 0) P1OUT ^= BIT6;
       // Play game
       for(; {
           // Update LCD
           update_lcd();
           // Generation interval
           n = 200;
           do {
               TACCTL0 &= ~CCIFG;
               while(!(TACCTL0 & CCIFG));
           } while(--n  && (P1IN & SWITCH));
           // Reset when S2 pushed
           if(!(P1IN & SWITCH)) break;
           // Play the field
           for(x = 0; x < GRID_WIDTH; ++x) {
               for(y = 0; y < GRID_HEIGHT; ++y) {
                   // Tally neighbors
                   n = get_cell(x - 1, y - 1) + get_cell(x, y - 1) + get_cell(x + 1, y - 1) +
                       get_cell(x - 1, y    ) +        /* me */      get_cell(x + 1, y    ) +
                       get_cell(x - 1, y + 1) + get_cell(x, y + 1) + get_cell(x + 1, y + 1);
                   // Live or Die
                   set_next(x, y, get_cell(x, y) ? (n == 2 || n == 3) : (n == 3));
               }
               // Update field
               if(x) copy_column(x);
           }
           copy_column(x);
       }
   }
}

 

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...