/************************************************************************ * * * Title: lcd_test.c * * Programmer: A. Antonio Arroyo {from code by A. Barnett & J. Hartman * * Date: February 19, 2008 * * Version: 1.0 * * * * Description: * * Verifies operation of an LCD attached to the BDMICRO MAVRICII Board * ************************************************************************/ /*************************** Includes ********************************/ #include /*************************** Prototypes ********************************/ void lcd_delay(); // short delay (50000 clocks) void lcd_init(); // sets lcd in 4 bit mode, 2-line mode, with cursor on and set to blink void lcd_cmd(); // use to send commands to lcd void lcd_disp(); // use to display text on lcd void lcd_clear(); // use to clear LCD and return cursor to home position void lcd_row(int row); // use to put the LCD at the desired row /* IMPORTANT! Before using this code make sure your LCD is wired up the same way mine is, or change the code to match the wiring of your own LCD. My LCD is connected to PortC of the At-Mega128 in the following manner: PortC bit 7 : LCD data bit 7 (MSB) PortC bit 6 : LCD data bit 6 PortC bit 5 : LCD data bit 5 PortC bit 4 : LCD data bit 4 (LSB) PortC bit 3 : (not connected) PortC bit 2 : LCD enable pin (clock) PortC bit 1 : LCD R/W (read / write) signal PortC bit 0 : LCD RS (register select) pin Also remember you must connect a potentiometer (variable resistor) to the vcc, gnd, and contrast pins on the LCD. The output of the pot (middle pin) should be connected to the contrast pin. The other two can be on either pin. */ void lcd_delay() // delay for 10000 clock cycles { long int ms_count = 0; while (ms_count < 10000) { ms_count = ms_count + 1; } } void lcd_cmd( unsigned int myData ) { /* READ THIS!!! The & and | functions are the BITWISE AND and BITWISE OR functions respectively. DO NOT confuse these with the && and || functions (which are the LOGICAL AND and LOGICAL OR functions). The logical functions will only return a single 1 or 0 value, thus they do not work in this scenario since we need the 8-bit value passed to this function to be preserved as 8-bits */ unsigned int temp_data = 0; temp_data = ( myData | 0b00000100 ); // these two lines leave the upper nibble as-is, and set temp_data = ( temp_data & 0b11110100 ); // the appropriate control bits in the lower nibble PORTC = temp_data; lcd_delay(); PORTC = (temp_data & 0b11110000); // we have written upper nibble to the LCD temp_data = ( myData << 4 ); // here, we reload myData into our temp. variable and shift the bits // to the left 4 times. This puts the lower nibble into the upper 4 bits temp_data = (temp_data & 0b11110100); // temp_data now contains the original temp_data = (temp_data | 0b00000100); // lower nibble plus high clock signal PORTC = temp_data; // write the data to PortC lcd_delay(); PORTC = (temp_data & 0b11110000); // re-write the data to PortC with the clock signal low (thus creating the falling edge) lcd_delay(); } void lcd_disp(unsigned int disp) { /* This function is identical to the lcd_cmd function with only one exception. This least significant bit of PortC is forced high so the LCD interprets the values written to is as data instead of a command. */ unsigned int temp_data = 0; temp_data = ( disp & 0b11110000 ); temp_data = ( temp_data | 0b00000101 ); PORTC = temp_data; lcd_delay(); PORTC = (temp_data & 0b11110001); lcd_delay(); // upper nibble temp_data = (disp << 4 ); temp_data = ( temp_data & 0b11110000 ); temp_data = ( temp_data | 0b00000101 ); PORTC = temp_data; lcd_delay(); PORTC = (temp_data & 0b11110001); lcd_delay(); // lower nibble } void lcd_init() { lcd_cmd(0x33); // writing 0x33 followed by lcd_cmd(0x32); // 0x32 puts the LCD in 4-bit mode lcd_cmd(0x28); // writing 0x28 puts the LCD in 2-line mode lcd_cmd(0x0F); // writing 0x0F turns the display on, curson on, and puts the cursor in blink mode lcd_cmd(0x01); // writing 0x01 clears the LCD and sets the cursor to the home (top left) position //LCD is on... ready to write } void lcd_string(char *a) { /* This function writes a string to the LCD. LCDs can only print one character at a time so we need to print each letter or number in the string one at a time. This is accomplished by creating a pointer to the beginning of the string (which logically points to the first character). It is important to understand that all strings in C end with the "null" character which is interpreted by the language as a 0. So to print an entire string to the LCD we point to the beginning of the string, print the first letter, then we increment the pointer (thus making it point to the second letter), print that letter, and keep incrementing until we reach the "null" character". This can all be easily done by using a while loop that continuously prints a letter and increments the pointer as long as a 0 is not what the pointer points to. */ while (*a != 0) { lcd_disp((unsigned int) *a); // display the character that our pointer (a) is pointing to a++; // increment a } return; } void lcd_int(int value) { /* This routine will take an integer and display it in the proper order on your LCD. Thanks to Josh Hartman (IMDL Spring 2007) for writing this in lab */ int temp_val; int x = 10000; // since integers only go up to 32768, we only need to worry about // numbers containing at most a ten-thousands place while (value / x == 0) // the purpose of this loop is to find out the largest position (in decimal) { // that our integer contains. As soon as we get a non-zero value, we know x/=10; // how many positions there are int the int and x will be properly initialized to the largest } // power of 10 that will return a non-zero value when our integer is divided by x. if (value==0) lcd_disp(0x30); else while (x >= 1) // this loop is where the printing to the LCD takes place. First, we divide { // our integer by x (properly initialized by the last loop) and store it in temp_val = value / x; // a temporary variable so our original value is preserved.Next we subtract the value -= temp_val * x; // temp. variable times x from our original value. This will "pull" off the most lcd_disp(temp_val+ 0x30); // significant digit from our original integer but leave all the remaining digits alone. // After this, we add a hex 30 to our temp. variable because ASCII values for integers x /= 10; // 0 through 9 correspond to hex numbers 30 through 39. We then send this value to the } // LCD (which understands ASCII). Finally, we divide x by 10 and repeat the process // until we get a zero value (note: since our value is an integer, any decimal value return; // less than 1 will be truncated to a 0) } void lcd_clear() // this function clears the LCD and sets the cursor to the home (upper left) position { lcd_cmd(0x01); return; } void lcd_row(int row) // this function moves the cursor to the beginning of the specified row without changing { // any of the current text on the LCD. switch(row) { case 0: lcd_cmd(0x02); case 1: lcd_cmd(0xC0); } return; } int main(void) { long i, counter=0; char s1[20]="Arroyo Rocks! :) "; int temp; DDRC = 0xFF; // set portC to output (could also use DDRC = 0b11111111) DDRE = 0xFF; // set portE to output //DDRE = 0b11111111; // set portE to output DDRB = 0xFF; // set portB to output lcd_init(); // set lcd in 4 bit mode, 2-line mode, with cursor on and set to blink lcd_string("Your LCD is working."); // if your LCD is wired up correctly, you will see this text // on it when you power up your Micro-controller board. lcd_row(1); s1[20]=0; lcd_string(s1); lcd_clear(); lcd_string(s1); while(counter<32767) { counter++; lcd_row(1); lcd_int(counter); for (i = 0; i < 10; i++) { lcd_delay(); //delay to read LCD (humans reading) } PORTE = 0xAA; // set alternating pins on portE to 5v temp = PINB; // read portB, store value to temp PORTB = !(temp); // complement portB and write back } return 0; }