/***************************************************************************************** ** ** EASi Line Assembly Technology Expo 2006 Readerboard Give-away C Source Code ** Written by David Beecher & Keith Nelson ** Copyright 2006 CPU Ready Consulting Inc. ** ** For schematics, instructions and full source, visit: ** http://www.cpureadyconsulting.com/ ** ** Written for the Atmel ATTiny2313 ** ** License Agreement ** You are free to copy and use our source code in your non-commercial project as long as ** you include this comment block at the top of any source file which contains our code. ** Commercial use is prohibited without prior authorization from CPU Ready Consulting Inc. ** ******************************************************************************************/ #define ENABLE_BIT_DEFINITIONS 1 #include #include #include "font.h" #define COL_CLK (1 << 4) // Column shifter bit positions #define COL_DAT (1 << 5) #define COL_LOAD (1 << 2) #define COL_RST (1 << 3) #define NUM_ROWS 7 // Number of LED rows on the readerboard #define NUM_COLS 12 // Number of LED columns on the readerboard #define FONT_ROWS 7 // Number of rows in our font #define FONT_COLS 5 // Number of columns per character in the font #define SPACE_WIDTH 1 // Width of space, in LED columns, after character #define MAX_MESSAGE_LENGTH 127 // Maximum message length #define byte unsigned char // Byte definition #define word unsigned int // Word definition #define fbyte unsigned char __flash // Flash stored Byte definition #define TRUE 1 #define FALSE 0 // Message first displayed when the board is initially programmed __flash static byte firstMessage[] = "Welcome to EASi Line ATExpo 2006\0"; // Globals Display0 and Display1 are two arrays which represent, bitwise, the rows and columns // of the readerboard display. 1 is LED on and 0 is LED off. The first two bytes represent // the first row of the display, where the bits past 12 columns are ignored. The next two bytes // represent the second row and so on. // We use two displays because we essentially buffer the drawing of the display. While we are // drawing the next shift of the message one display, we are displaying the last drawing. // This ensures a smooth, clean looking shift. __no_init static byte display0[14]; __no_init static byte display1[14]; // This global variable holds the length of the current message __no_init static byte messageLength; // This global variable holds the current column data that will be bit banged to the column shifters __no_init static word colbang; // This global variable holds the speed value. __no_init static byte speed; // This global variable counts down the number of characters to display in our message __no_init static word shutdown; // These global pointers are set to either display0 or display1 depending on whether we are // currently displaying or drawing to the global displays. __no_init static byte *display; __no_init static byte *drawdisplay; // This global variable tells us which display we currently are working with __no_init static byte curDisplay; /********************************* ** ** PROCEDURE SetPortD ** ** Purpose: ** ** This procedure bit bangs the passed in word to PORTD of the Tiny2313 ** and then clocks the column shifters. ** ** Parameters: ** ** WORD w - The 16 bit value that you want to bit bang to PORTD ** **********************************/ void SetPortD(word w) { byte i; word temp; for(i=0; i<12; i++) { temp = w >> i & 1; // temp now holds the i-th bit, either a 0 or 1 which we want to bang out to the COL_DAT line if(temp == 1) PORTD = PORTD | COL_DAT; else PORTD = PORTD & ~COL_DAT; // bits have been sent through the col_dat pin now // we need to set the clock pin and clear it PORTD = PORTD | COL_CLK; PORTD = PORTD & ~COL_CLK; } } /********************************* ** ** PROCEDURE LoadData ** ** Purpose: ** ** This procedure clears and sets the Load pin ** ** ** Parameters: ** ** None ** **********************************/ void LoadData() { PORTD = PORTD & ~COL_LOAD; PORTD = PORTD | COL_LOAD; } /********************************* ** ** PROCEDURE SetRow ** ** Purpose: ** ** This procedure sets the current row of the LED matrix ** ** Parameters: ** ** Byte row - The value of the row that you would like to select in the LED matrix ** **********************************/ void SetRow(byte row) { byte rtemp = 0; rtemp = (1 << row); PORTB = rtemp; } /********************************* ** ** PROCEDURE SetColBit ** ** Purpose: ** ** This procedure adds a particular bit to the global variable colbang, which ** essentially turns on a particular column ** ** Parameters: ** ** Byte col - This is the column that you would like to turn on ** ** **********************************/ void SetColBit(byte col) { colbang = colbang | (1 << col); } /********************************* ** ** Procedure SetColumn ** ** Purpose: ** ** This procedure sends the global colbang variable to enable all of the columns ** that have been turned on by SetColBit. Once it is complete, colbang is reset to ** zero, which means all columns are turned off. ** ** Parameters: ** ** None ** **********************************/ void SetColumn() { SetPortD(colbang); colbang = 0; } /********************************* ** ** PROCEDURE DisplaySwap ** ** Purpose: ** ** Swaps between display0 and display1 as the draw and display memory. ** For simplicity sake and to help keep track, we do have the curDisplay ** rather than just an extra temp pointer for the swap. ** Cutting this global variable out would save some compile-time memory ** ** Parameters: ** ** None ** **********************************/ void DisplaySwap() { if(curDisplay == 0) { drawdisplay = display1; display = display0; curDisplay = 1; } else { drawdisplay = display0; display = display1; curDisplay = 0; } } /********************************* ** ** PROCEDURE Clear ** ** Purpose: ** ** This clears the drawing memory to enable us to draw the new characters ** ** Parameters: ** ** None ** **********************************/ void Clear() { char i; for(i=0;i<14;i++) drawdisplay[i] = 0; } /********************************* ** ** PROCEDURE Raster ** ** Purpose: ** ** This function reads the display ram and turns on the appropriate column bits, ** bangs the the column with SetColumn and sets the row. Essentially this takes ** what is in the current display ram and actually draws it to the "screen" ** ** Parameters: ** ** None ** **********************************/ void Raster() { byte i; // Rows index byte j; // Columns index byte first; // First byte of the display data, representing cols 0-7 byte second; // Second byte of display data, representing cols 8-NUM_COLS __watchdog_reset(); SetRow(NUM_ROWS-1); // this will ensure the last row gets equivalent display time. // Removing this will give extra display time and create a shadow effect on last row. for(i=0;i= NUM_COLS || row >= NUM_ROWS) return; if(col < 8) { b = drawdisplay[row*2]; b = b | (1 << col); drawdisplay[row*2] = b; } else if(col < NUM_COLS) { b = drawdisplay[row*2+1]; b = b | (1 << (col-8)); drawdisplay[row*2+1] = b; } } /********************************* ** ** PROCEDURE Letter ** ** Purpose: ** ** This draws a particular letter to the drawing ram via the Rc() function. While it ** draws the letter it also calls Raster() to update the screen with the current display ram ** as this is a fairly cpu intensive function. ** ** Parameters: ** ** Byte c - The ASCII value of the character ram that is wished to display. ** Byte displayCol - The first column of the font character to start the display ** ** **********************************/ void Letter(byte c, byte displayCol) { fbyte *ptr = 0; // Pointer into flash for reading the font byte index, row, col, curbit, temp; // Calculate the location of the character. First subtract the 32 from the ASCII value index = c-32; // Next, setup ptr to point to the flash memory location of the font array ptr = (fbyte *)font; // Finally, calculate the actual memory location of the character ptr = ptr + (index * 5); curbit = 1; for(col=0;col 9) speed = 9; Clear(); Letter('S',11); Letter(b,5); for(i=0;i<100;i++) { b = UDR; Raster(); } // Return display display = saveDisplay; return 1; } /********************************* ** ** PROCEDURE WriteFirstMessage ** ** Purpose: ** ** This function will write the global firstMessage variable to EE Prom. Called when the ** unit is first powered up after programming. ** ** Parameters: ** ** None ** **********************************/ void WriteFirstMessage() { int i; byte b; i = 0; b = firstMessage[0]; // Set our temp variable to first char // Until we've hit the null char, keep reading and storing while(b != '\0') { StoreEE(b,i); i++; b = firstMessage[i]; } if(i < MAX_MESSAGE_LENGTH) StoreEE('\0',i); } /********************************* ** ** PROCEDURE LightAllLEDS ** ** Purpose: ** ** This turns all the LED's on for a short period of time (around 4 seconds) ** just after the unit is programmed, so that we can see all LED's were installed ** on the board correctly. ** ** Parameters: ** ** None ** **********************************/ void LightAllLEDS() { char i; for(i=0;i<14;i++) display[i] = 0xFF; for(i=0;i<255;i++) Raster(); } /********************************* ** ** FUNCTION main ** ** Purpose: ** ** Main controls the entire process, including setup of the timers, USART, shifters, first messages, ** etc. Once running, it will display the message for a given period of time, shifting the ** message across the screen and then finally going into shutdown mode. ** ** Parameters: ** ** None ** ** Return Value: ** ** N/A ** ** **********************************/ int main( void ) { word i; // Number of column shifts word col; // Used to calculate the current column to draw byte b; // Temporary variable // Setup timeout feature PORTD = PORTD & ~(1 << 6); // Turn on transistor in circuit // Set Clock Uses internal clock, 1mhz TIMSK = TIMSK | (1 << 2); TCCR0B = 1; // Set the timer divide by to full // full speed is 1 // 1/8 speed is 2 // 1/64 is 3 // 1/256 is 4 // 1/1024 is 5 // turn off interruptes __disable_interrupt(); // setup the watchdog timer __watchdog_reset(); WDTCR &= ~((1< 0) { offset=0; drawing = 1; // Keep drawing characters until we're done or until we're at the end of the message while(drawing && curLetter < messageLength) { // Figure out the column of the current letter we are on col = i-(curLetter*(FONT_COLS+SPACE_WIDTH)); // If it is still in scope, we draw it and the next 3 letters if(col < NUM_COLS+FONT_COLS+1) { DisplaySwap(); // Must clear the display buffer before we can start re-writing to it Clear(); byte count = 2; // Draw the current letter on the screen space since it is still in view Letter(ReadEE(curLetter+offset),col); // This loop will draw the next three letters and stops if // it reaches the end of the message while(count) { offset++; if(curLetter+offset >= messageLength) { // If we're past the end of the message, fake some clock cycles // to slow down the message scroll. Taking this out speeds up the // last couple of characters for(byte k=0;k (messageLength * (FONT_COLS+1)) + (3 * FONT_COLS)) { i = 0; curLetter = 0; } // If a character is waiting in the UART, we begin to start a new message if(UCSRA & (1<