oPossum 1,083 Posted November 25, 2011 Share Posted November 25, 2011 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); } } } jbremnant 1 Quote Link to post Share on other sites
bluehash 1,581 Posted November 25, 2011 Share Posted November 25, 2011 Is that the Sparkfun bob, oPossum? ...and what song is that? Quote Link to post Share on other sites
oPossum 1,083 Posted November 25, 2011 Author Share Posted November 25, 2011 BOB came from eBay. Music is "Life's been good" by Joe Walsh Joe (WB6ACU) is a Ham and wrote and performs the theme music for Ham Nation. He was also the first guest. The host of Ham Nation is Bob Heil (K9EID), who built Joe's talk box bluehash 1 Quote Link to post Share on other sites
jbremnant 17 Posted November 27, 2011 Share Posted November 27, 2011 great work, and tidy / efficient code! Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.