/* lcd.c Routines for sending data to a serial LCD * * Copyright (C) 1998 Mark Crosbie mcrosbie@best.com * * Modified for Matrix Orbital displays Feb 2000 * Geoff McCaughan geoff@southern.co.nz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Provides the following functions: * * void lcd_putc(char c) send the byte c to the LCD * void lcd_clear(void) clears the LCD screen * void lcd_home(void) homes the LCD cursor * void lcd_puts() print a string **UNIMPLEMENTED** * void lcd_printhex(char c) prints byte c as a 2 digit hex character * void lcd_printdec(char c) prints byte c as a decimal digit string * void lcd_printbin(char c) prints byte c as a binary string * void lcd_scroll_left(char n) scrolls LCD screen left n positions * void lcd_scroll_right(char n) scrolls LCD screen right n positions * void lcd_goto(char row, char col) goto a position * void lcd_printnum(unsigned short x, char digits) Print a number with leading zero * suppression, and selectable significant digits * Matrix Orbital only: * void lcd_command(char cmd) Send an arbitrary command to the LCD * void lcd_bargraph_vert( char column, char length) display vertical bargraph * void lcd_bargraph_horiz( char row, char column, char direction, char length) * display horizontal bargraph * * Constants used: * LCDPORT port on the PIC the LCD is connected to * LCDPIN what PIN the LCD serial input is connected to * * Libraries used: * delay_ms() from the delays.lib library. * */ /* Change these definitions if you move the LCD */ #define LCDPORT PORTA #define LCDPIN 1 /* Undefine this if using another display type */ #define MATRIX_ORBITAL #define LCDCMD 254 #ifdef MATRIX_ORBITAL #define LCDCLS 'X' // Clear screen #define LCDHOME 'H' // Home the cursor #define LCDGOTO 'G' // Goto specified position #define LCDWRAPON 'C' // Enable line wrap #define LCDWRAPOFF 'D' // Disable line wrap #define LCDSCROLLON 'Q' // Enable auto scroll #define LCDSCROLLOFF 'R' // Disable auto scroll #define LCDCSRON 'J' // Turn current cursor position on #define LCDCSROFF 'K' // Turn current cursor position off #define LCDCSRLEFT 'L' // Move cursor left #define LCDCSRRIGHT 'M' // Move cursor right #define LCDBLINKON 'S' // Turn blinking cursor on #define LCDBLINKOFF 'T' // Turn blinking cursor off #define LCDLIGHTON 'B' // Turn backlight on #define LCDLIGHTOFF 'F' // Turn backlight off #define LCDBGRAPHINITH 'h' // Initialise horizontal bar graph #define LCDBGRAPHINITVTHICK 'v' // Initialise thick vertical bar graph #define LCDBGRAPHINITVTHIN 's' // Initialise thin vertical bar graph #define LCDBGRAPHH '|' // Horizontal bar graph #define LCDBGRAPHV '=' // Vertical bar graph #else #define LCDCLS 1 #define LCDHOME 2 #define LCDLINE1BASE 128 #define LCDLINEINCR 64 #define LCDSCRLRIGHT 28 #define LCDSCRLLEFT 24 #endif /* defined in delays.c */ extern void delay_ms(char); /* Print a single byte to the LCD screen * * Inverts the bits as the serial LCD expects inverse polarity. * *** Assumes 9600 8N1 communication *** The constant LCDPIN defines which pin the LCD is on *** The constant LCDPORT defines which port the LCD in on * * Arguments: character c: 0 <= c <= 255 * Return Value: none * * Uses Dwayne Reid's TXBYTE routine. Thanks Dwayne! * Also uses code from Peter H. Anderson */ void lcd_putc(char c) { char sertime; /* This code adapted from: * TXBYTE: send 1 byte @ 9600 baud: 1 RAM, 15 (16) ROM, 1043 cycles * Copyright (C) 1995 Dwayne Reid. May be freely used so long as this * copyright notice is retained. * * Other parts taken from code: * Copyright, Peter H. Anderson, Morgan State University, June 14, '97 * * This code must invert the sense of the bits because the Serial * LCD backpack expects it this way. This includes inverting the sense * of the START and STOP bits. */ clear_bit(LCDPORT, LCDPIN); /* set to stop bit */ sertime = 255; while(sertime-- != 0); /* let stop bit be set for a while */ /* now we loop through 9 bits and set the output line. After each bit, pause for 104uS. This represents 9600 baud */ asm movlw 9 ; 8 data + 1 start asm clrc ; start bit lcdtxloop: asm skpnc ; bit time = 104.167 uSec clear_bit(LCDPORT, LCDPIN); asm skpc set_bit(LCDPORT, LCDPIN); lcddloop: asm ; 95 clk cycle delay asm goto $+1 ; 2-cycle NOP in 1 instruction! asm addlw b'00010000' ; increment upper nibble asm skpc ; delay = 6n -1 (1 less when falls thru) /* A relative jump backwards */ asm goto $-3 ; goto lcddloop loop sixteen times asm addlw -1 ; dec w, valid z, c=1 if w=0 after decrement asm rrf param00_lcd_putc,F ; carry will be set - shifted in as stop bit asm skpz /* A relative jump back */ asm goto $-0xb ; goto lcdtxloop txloop is 104 clk cycles clear_bit(LCDPORT, LCDPIN); /* send stop bit */ delay_ms(10); /* a pacing delay of 10ms between characters */ } /* * clear the LCD screen * *** Assumes 9600 8N1 communication *** The constant LCDPIN defines which pin the LCD is on *** The constant LCDPORT defines which port the LCD in on * * Arguments: none * Return Value: none */ void lcd_clear(void) { lcd_putc(LCDCMD); lcd_putc(LCDCLS); } /* * Home the LCD cursor * *** Assumes 9600 8N1 communication *** The constant LCDPIN defines which pin the LCD is on *** The constant LCDPORT defines which port the LCD in on * * Arguments: none * Return Value: none */ void lcd_home(void) { lcd_putc(LCDCMD); lcd_putc(LCDHOME); } /* Print a string to the LCD screen * *** Assumes 9600 8N1 communication *** The constant LCDPIN defines which pin the LCD is on *** The constant LCDPORT defines which port the LCD in on * * Arguments: s: address of a string of bytes in DATA memory * string must be NULL terminated * Returns: number of bytes written (number of chars printed) */ char lcd_puts(char s) { } /* Print a value in decimal to the LCD screen * *** Assumes 9600 8N1 communication *** The constant LCDPIN defines which pin the LCD is on *** The constant LCDPORT defines which port the LCD in on * * Note: output is always 3 digits: 001 not 1, 025, not 25 * Arguments: x: value to print 0 <= x <= 255 * Returns: none */ void lcd_printdec(char x) { char count; /* how many hundreds? */ count=0; while(x > 99) { x = x - 100; count++; } lcd_putc('0' + count); /* how many tens? */ count=0; while(x > 9) { x = x - 10; count++; } lcd_putc('0' + count); /* and finally units */ lcd_putc('0' + x); } /* Print a value in hex to the LCD screen * *** Assumes 9600 8N1 communication *** The constant LCDPIN defines which pin the LCD is on *** The constant LCDPORT defines which port the LCD in on * * Arguments: x: value to print 0 <= x <= 255 * Returns: none */ void lcd_printhex(char x) { char nibble; /* extract high nibble */ nibble = x & 0xf0; /* swap the high and low nibbles */ asm swapf _nibble_lcd_printhex, f; if(nibble < 10) { lcd_putc('0' + nibble); } else { lcd_putc(nibble - 10 + 'A'); } /* do the same on the low nibble */ nibble = x & 0x0f; if(nibble < 10) { lcd_putc('0' + nibble); } else { lcd_putc(nibble - 10 + 'A'); } } /* Print a value to the LCD screen as binary * *** Assumes 9600 8N1 communication *** The constant LCDPIN defines which pin the LCD is on *** The constant LCDPORT defines which port the LCD in on * * Arguments: x: value to print 0 <= x <= 255 * Returns: none */ void lcd_printbin(char x) { char bits[] = {128, 64, 32, 16, 8, 4, 2, 1}; char i = 0; while(i < 8) { if(x & bits[i]) { lcd_putc('1'); } else { lcd_putc('0'); } i++; } } #ifdef MATRIX_ORBITAL void lcd_goto(char row, char col) { // Move the cursor to the specified position // Arguments: row: row on LCD: 1 <= row <= # of rows // col: column on LCD: 1 <= col <= # of chars // Matrix Orbital: columns are indexed from the left starting at col 1. lcd_putc(LCDCMD); lcd_putc(LCDGOTO); lcd_putc(col); lcd_putc(row); } #else /* Move the cursor to the specified position * *** Assumes 9600 8N1 communication *** The constant LCDPIN defines which pin the LCD is on *** The constant LCDPORT defines which port the LCD in on * * Arguments: row: row on LCD: 0 <= row <= 1 * col: column on LCD: 0 <= col <= 16 * *** Remember: columns are indexed from the left starting at col 0. * If you print off the screen, it is not displayed, but is stored in * the LCD's RAM. Use the lcd_scroll_left and lcd_scroll_right commands * to access the data * * Returns: nothing */ void lcd_goto(char row, char col) { lcd_putc(LCDCMD); lcd_putc(LCDLINE1BASE + (LCDLINEINCR * row) + col); } #endif #ifndef MATRIX_ORBITAL /* Scroll the display N characters to the left * *** Assumes 9600 8N1 communication *** The constant LCDPIN defines which pin the LCD is on *** The constant LCDPORT defines which port the LCD in on * * Arguments: N: number of characters to scroll left. N > 0 * There is a 10ms delay between each scroll action * Returns: nothing */ void lcd_scroll_left(char n) { while(n) { lcd_putc(LCDCMD); lcd_putc(LCDSCRLLEFT); delay_ms(10); n--; } } /* Scroll the display N characters to the right * *** Assumes 9600 8N1 communication *** The constant LCDPIN defines which pin the LCD is on *** The constant LCDPORT defines which port the LCD in on * * Arguments: N: number of characters to scroll right. N > 0 * There is a 10ms delay between each scroll action * Returns: nothing */ void lcd_scroll_right(char n) { while(n) { lcd_putc(LCDCMD); lcd_putc(LCDSCRLRIGHT); delay_ms(10); n--; } } #endif #ifdef MATRIX_ORBITAL void lcd_command(char cmd) { // Send the command 'cmd' to the LCD lcd_putc(LCDCMD); lcd_putc(cmd); } void lcd_bargraph_vert( char column, char length) { // Display a vertical bar graph // NOTE! You must call lcd_command(LCDBGRAPHINITVTHICK) or // lcd_command(LCDBGRAPHINITVTHIN) before calling this for the 1st time // You cannot use custom characters at the same time as bar graphs // Arguments: column: column on LCD: 1 <= col <= # of chars // length: length of bar to display: 0 <= length <= 32 for 4 row display lcd_putc(LCDCMD); lcd_putc(LCDBGRAPHV); lcd_putc(column); lcd_putc(length); } void lcd_bargraph_horiz( char row, char column, char direction, char length) { // Display a horizontal bar graph // NOTE! You must call lcd_command(LCDBGRAPHINITH) before calling this for the // 1st time // You cannot use custom characters at the same time as bar graphs // Arguments: row: row on LCD: 1 <= row <= # of rows // column: column on LCD: 1 <= col <= # of chars // direction: 0 = L to R, 1 = R to L // length: length of bar to display: 0 <= length <= 100 for 20 column display lcd_putc(LCDCMD); lcd_putc(LCDBGRAPHH); lcd_putc(column); lcd_putc(row); lcd_putc(direction); lcd_putc(length); } #endif void lcd_printnum(unsigned short x, char digits) { // Print a short value in decimal to the LCD screen with // leading 0 supression and variable significant digits // Arguments: x: value to print 0 <= x <= 65535 // digits: # of digits to print 1 <= digits <= 5 // Note the value of x MUST be consistent with the value of digits, // e.g. if digits == 4, x must not exceed 9999 char count; // Digit to display char total = 0; // Total of digits displayed so far // We display spaces until this is non-zero short limit; while (digits > 1) { switch (digits) { case 5: limit = 10000; break; case 4: limit = 1000; break; case 3: limit = 100; break; case 2: limit = 10; break; } count=0; while(x >= limit) { x = x - limit; count++; } total = total + count; if (total) { lcd_putc('0' + count); } else { lcd_putc(' '); } digits--; } // Handle units // lcd_putc('0' + low-byte-of-x); // Compiler optimises this badly, so do it in asm asm { movlw D'48' addwf param00_lcd_printnum, W movwf param00_lcd_putc call _lcd_putc } }