Article Sections
PIC MICRO: LCD Terminal
This is the very last C project I did on PIC 16F (maybe 2006). It's a two line terminal with keypad for a Linux based ethernet Router connected to a DOCSIS modem, all in one portable battery powered case:
I would use C for the higher end Microchip CPUs such as PIC24, PIC32, PIC33 and dspPIC. But for 10F, 12F, 16F, 18F families (the "real PICs") I'd only use JAL now.
LCD Terminal using very cheap PIC programmed in C
These are so simple that they can be made on Veroboard. The prototype was used with a Linux single Board Computer as terminals, using this LCD Terminal using very cheap PIC programmed in C
These are so simple that they can be made on Veroboard. Using this software on the host
It can use a single line 8 character up to 4 line 20 character as long as the electrical signals and command set is similar. A Graphic panel is not suitable.
The keypad can be a 3 x 4 matrix up to a 5 x 4 matrix, or a 4 x 4 keypad with four additional coloured buttons.
You need a programmer to put the compiled code into the PIC.
I used to use ic-prog with a simple home made programmer.
Now I would only use Pickit2. The PIC16F876 is suitable, though almost any 16F series part with enough suitable pins will do.
Hardware
Hardware is a 2 line Text LCD, keypad and support for 8 additional buttons. A PIC is on the back of the veroboard A 5V regulator 7805 is under the LCD to supply 5V from the battery pack used by the Linux SBC and Modem.


Top view layout (red are tracks, blue are wire links on top)
mirrored to allow easy cutting of track on bottom.

Actual Veroboard

Socket on bottom for PIC and sockets on top for LCD module and keypad. Crystal soldered on bottom.
New designs should use PIC18F2550, PIC18F4550 or similar, which includes USB
Possible Terminal Commands
Terminal commands
[table ! ASCII Code(s) | Description
Esc A |up
Esc B |down
Esc C |right
Esc D |left
Esc H |home
Esc I |line up
Esc J| Erase to end Screen
Esc K | Erase to EOL
Esc Ylc |Line and Column: actual numbers + 32 from 0,0 up to 223 x 223
Esc Z| Return
Esc =| Alternate keypad
Esc >| Normal keypad
Esc 1 | Graphics on
Esc 2 | Graphics off
Esc < |Ansi mode
Esc 0q |All Lights off
Esc 1q |LED 1 on
Esc 2q | LED 2 on
Esc 3q | LED 3 on
Ctrl m |start of line
Ctrl j |line down
Del | fwd delete
Ctrl h |rev delete
Ctrl i |Tab]
For this Project you need Hitech C
initports Import"initports.h"
// Peripheral initialization function extern void initports(void);
initports.c
#include// htc.h is part of HiTide HiTech C // Initialize user ID locations //__IDLOC(0000); /* Program device configuration word * Oscillator = EC I/O * Watchdog Timer = Off * Power Up Timer = On * Brown Out Detect = Enabled * Master Clear Enable = Disabled * Low Voltage Program = Disabled * Data EE Read Protect = Disabled * Code Protect = Off */ //__CONFIG(EC & WDTDIS & PWRTEN & BOREN & 0x3FDF & LVPDIS & DATUNPROT & UNPROTECT); // Peripheral initialization function void initports(void){ /***** Common Code **** * Portbit7:4 interrupt-on-change disabled * Peripheral interrupts not enabled * Global interrupt disabled during initialization */ INTCON = 0b00000000; /* * Weak pullup on PORT disabled */ OPTION = 0b10000000; /***** PortA Code **** * Port directions: 1=input, 0=output */ TRISA = 0b11111111; /***** PortB Code **** * Port directions: 1=input, 0=output */ TRISB = 0b00000000; ei(); // Global interrupts enabled }
Main Program
#include "initports.h" // included by C-Wiz #include//__CONFIG(WDTDIS & HS & UNPROTECT); //this is for HS (more than 4 megahertz) oscillator //__CONFIG(WDTDIS & XT & UNPROTECT); //this is for XT (4 megahertz or less) oscillator // Watchdog timer would be | 0x04 but I'm not using it __CONFIG(03f41H); // also no MCLR and no LVP // Mike Watterson maybe based on Bob Blick // Change these to suit your setup // You must use one and only one of the next four #define CURSOR_OFF_NOBLINK //if you want no cursor //#define CURSOR_ON_NOBLINK //if you want a plain cursor //#define CURSOR_OFF_BLINK //if you want the print position to blink //#define CURSOR_ON_BLINK //the cursor kitchen sink // LCD formatting does line wraps correctly. Uses memory, you don't need it unless you want it. // Note that 2X40 displays wrap correctly right out of the box and need no formatting. #define LCD_FORMAT //if you want LCD line formatting uncomment this // If you chose LCD_FORMAT uncomment one and only one of the following //#define LCD_4X20 //if you have a 4x20 display and want formatting //#define LCD_2X20 #define LCD_2X16 //#define LCD_2X8 //this also works for cheap 1X16 displays that have one chip //#define LCD_1X16 // LCD hardware initialization. Two line 5x7 font is the most common. You must choose one. #define TWOLINE_5X7 //#define ONELINE_5X7 //this gives a better contrast ratio but only on one line displays //#define ONELINE_5X10 //Many of the better 1 line displays support this. // Uncomment to Translate ascii to use the full descender versions in the character map //#define DESCENDERS //if you use 5X10 font you can translate j,y,etc but uses memory #define XTAL 4000000 // Crystal frequency (Hz). #define BRATE 2400L // Baud rate. "L" prevents truncation in calculations. #define RX_OVERSAMPLE 4 // Receive oversampling. Must be at least 4 and power of two #define LCD_RT 10 //response time for 4MHz // Super-multiplexed outputs(upper 5 bits) of PORT B get set to this at startup //#define SUPER_MX_OUT_INIT 0b00000000 //lower three bits ignored /**********YOU DON'T NEED TO CHANGE ANYTHING BEYOND THIS POINT************/ // Keeping you honest :-) #if (RX_OVERSAMPLE-1)&RX_OVERSAMPLE #error RX_OVERSAMPLE_value must be a power of 2 #endif #if (RX_OVERSAMPLE < 4) #error RX_OVERSAMPLE must be 4 or greater #endif #define RX_BITCENTER ((RX_OVERSAMPLE/2) - 1) #define byte unsigned char // LCD pins static bit LCD_RS @ ((unsigned)&PORTB*8+3); // Register select static bit LCD_EN @ ((unsigned)&PORTB*8+2); // Enable // Transmit and Receive port bits change later to static volatile bit TxData @ (unsigned)&PORTB*8+1; /* bit1 in port B */ static volatile bit RxData @ (unsigned)&PORTA*8+4; /* bit4 in port A */ // Don't change these #define TIMER_VALUE (XTAL / (4 * BRATE * RX_OVERSAMPLE)) #define TRANSMIT_NUM_BITS 13 // 1 start bit + 8 data bits + 2 stop bits + safe. #define INT_PERIOD (1000000 / (BRATE * RX_OVERSAMPLE)) //that is in microseconds #if ((TIMER_VALUE) < 90) #error baud rate or oversample too high for crystal speed #endif #if ((TIMER_VALUE) > 256) #error baud rate or oversample too low for crystal speed #endif // Line lengths for LCD formatting #define BEGIN_LINE_1 0 #define LINE_1_MINUS_1 255 #ifdef LCD_4X20 #define END_LINE_1 19 #define BEGIN_LINE_2 64 #define END_LINE_2 83 #define BEGIN_LINE_3 20 #define END_LINE_3 39 #define BEGIN_LINE_4 84 #define END_LINE_4 103 #define LINE_2_MINUS_1 63 #define LINE_3_MINUS_1 19 #define LINE_4_MINUS_1 83 #endif #ifdef LCD_2X20 #define END_LINE_1 19 #define BEGIN_LINE_2 64 #define END_LINE_2 83 #define LINE_2_MINUS_1 63 #endif #ifdef LCD_2X16 #define END_LINE_1 15 #define BEGIN_LINE_2 64 #define END_LINE_2 79 #define LINE_2_MINUS_1 63 #endif #ifdef LCD_2X8 #define END_LINE_1 7 #define BEGIN_LINE_2 64 #define END_LINE_2 71 #define LINE_2_MINUS_1 63 #endif #ifdef LCD_1X16 #define END_LINE_1 15 #endif // Delay constants used by LCD routines #define T_120_US (120 / INT_PERIOD + 1) #if (T_120_US < 2) #undef T_120_US #define T_120_US 2 #endif #define T_1000_US (1000 / INT_PERIOD + 1) #define LED_TASK_PERIOD (1000 / INT_PERIOD + 1) #define LCD_STROBE ((LCD_EN = 1),(LCD_EN = 0)) //keyboard stuff #define KB_TASK_PERIOD (80000 / INT_PERIOD + 1) //50000 usec = 20 per second #define KB_DELAY_LOAD 10 //kb checks before repeat #define KB_REPEAT_RATE 2 // about 10 per second #define KB_COL_DRV 5 #define KB_ROW_DRV 4 #define KB_NO_KEY (KB_COL_DRV * KB_ROW_DRV) //scankb returns this if no key is pressed #define MAIN_PROG "/bin/sigmeter" //Keyboard rows across, columns downSW, //the HW is c1= bottom row and //the HW is r0= left columnn // ro r1 r2 r3 // c0 red grn yel Vio // c4 1 2 3 A noKey // c3 4 5 6 B // c2 7 8 9 C // c1 * 0 # D //Starting with c0, r0, r1, r2, r3 const byte Kb_tbl[] = { 0x52,0x47,0x59,0x56, 0x2a,0x30,0x23,0x44, 0x37,0x38,0x39,0x43, 0x34,0x35,0x36,0x42, 0x31,0x32,0x33,0x41, 0x0 }; // Receiver states. enum receiver_state { RS_HAVE_NOTHING, RS_WAIT_HALF_A_BIT, RS_HAVE_STARTBIT, RS_WAIT_FOR_STOP = RS_HAVE_STARTBIT+8 }; // VARIABLES static byte sendbuffer; // Where the character to sent is stored. static byte receivebuffer; // Where the character is stored as it is received. static bit receivebufferfull; // 1 = receivebuffer is full. static bit chargeon; //1 = charge static bit chargetoggle; // 0 or 1 static volatile unsigned int ledtaskcount; //decrements every interrupt static bit ledtaskflag; //goes high when it's time to check the keyboard static byte send_bitno; static byte receivestate; // Initial state of the receiver (0). static byte skipoversamples; // Used to skip receive samples. static byte rxshift; static bit tx_next_bit; static volatile byte delaycount; //decrements every interrupt if nonzero static volatile unsigned int kbtaskcount; //decrements every interrupt static bit kbtaskflag; //goes high when it's time to check the keyboard byte kbdelaycount; //timer for keypress byte kbrepeatcount; byte kboldkey; //last keypress static bit command_next; //next received character is special lcd data static byte lcdcommand; #ifdef LCD_FORMAT byte cursorpos; #endif /* static byte super_mx_in; //super-multiplexed input PORTB 5 upper bits static byte super_mx_out; //super-multiplexed output PORTB 5 upper bits */ byte promptCount; // FUNCTION PROTOTYPES void init_stuff(void); //sets up interrupt, pins, etc void rs232_putch(byte c); //puts a byte to serial transmit buffer void rs232_puts(const byte * s); //string to serial port byte rs232_getch(void); //gets byte from serial receive buffer void chargeControl(byte c); //read battery state and ext power and charges battery void ledtasks(void); void lcd_clear(void); //clear and home the LCD void lcd_puts(const byte * s); //put ascii string to LCD void lcd_putch(byte c); //put one character to LCD void lcd_goto(byte pos); //positions LCD cursor void lcd_init(void); //sets up LCD interface void lcd_putcmd(byte c); //send one byte to LCD command register void delayMs(byte t); //delays 1 to 255 milliseconds byte scankb(void); //returns which key is depressed byte getBatState(void); //similar to keypad, reads four i/p muxed to RA5 void kbrs232(byte key); //normal "keypad to rs232 out" operation void rs232lcd(byte rec); //sends rs232 received data to LCD or translates if needed #ifdef LCD_FORMAT void inc_cursor(void); //increments the LCD cursor past line breaks void dec_cursor(void); //decrements #endif byte batteryState =0; byte leds =0; byte termState =0; byte lastState =0; byte pulseCount =0; #define MAX_PULSE_CYCLE 40 #define TS_NORMAL 0 #define TS_BAT_LOW 1 #define TS_BAT_PULSE 2 #define TS_CHARGING 3 #define TS_CHARGED 4 // THE PROGRAM void main(void) { //initports(); // Function call inserted by C-Wiz lastState = TS_NORMAL; termState = TS_NORMAL; init_stuff(); delayMs(250); delayMs(250); lcd_init(); lcd_clear(); lcd_puts("Digiweb METRO"); delayMs(250); delayMs(250); delayMs(250); lcd_clear(); while(1) { if(kbtaskflag) { kbrs232(scankb()); chargeControl(getBatState()); kbtaskflag = 0; } if (ledtaskflag) { ledtasks(); ledtaskflag = 0; } if (receivebufferfull) { rs232lcd(rs232_getch()); } } // End of "while(1)" } // End of "main()" // THE FUNCTIONS void init_stuff(void) { // PORTB = 0 | (SUPER_MX_OUT_INIT & 1); //RB0 set to lo bit of super_mx_out RB0 =0; CMCON = 0b00000111; // switch off comparators or else on digital on RA0 to RA3! TRISA = 0b00011111; // PORTA inputs. TRISB = 0b00000000; // PORTB outputs promptCount = 0; leds =0; receivestate = RS_HAVE_NOTHING; receivebufferfull = 0; skipoversamples = 1; // check each interrupt for start bit kbtaskcount = KB_TASK_PERIOD; kbtaskflag = 0; ledtaskcount = LED_TASK_PERIOD; ledtaskflag = 0; kbdelaycount = KB_DELAY_LOAD; kbrepeatcount = KB_REPEAT_RATE; kboldkey = KB_NO_KEY; command_next = 0; chargeon = 0; chargetoggle = 0; // super_mx_out = SUPER_MX_OUT_INIT; #ifdef LCD_FORMAT cursorpos = 0; #endif /* Set up the timer. */ T0CS = 0; // Set timer mode for Timer0 TMR0 = (2-TIMER_VALUE); // +2 as timer stops for 2 cycles // when writing to TMR0 T0IE = 1; // Enable the Timer0 interrupt GIE = 1; // Enable interrupts } void chargeControl(byte batteryState) { // d7 = 1 normal 0 charge full // d6 = 1 battery 0 ext power // d5 = 1 low batt 0 normal // d4 = spare if ((batteryState & 0b00100000) > 0) { // battery low if ((batteryState & 0b01000000) > 0) { // no ext power, shutdown if (termState == TS_NORMAL){ rs232_putch('l'); lastState = termState; termState = TS_BAT_LOW; leds = 0; lcd_clear(); lcd_puts("BATTERY LOW"); } }else{ //ext power do charging if (termState != TS_BAT_PULSE){ lastState = termState; termState = TS_BAT_PULSE; } } }else{ //battery normal or charged if ((batteryState & 0b10000000) > 0) { // battery normal if ((batteryState & 0b01000000) > 0) { // no ext power if (termState != TS_NORMAL){ if (termState ==TS_BAT_LOW){ lcd_clear(); rs232_putch('n'); } lastState = termState; termState = TS_NORMAL; } }else{ //ext power do charging if ((termState != TS_CHARGING) && (lastState !=TS_CHARGED)){ lastState = termState; termState = TS_CHARGING; chargeon = 1; } } }else{ //battery charged // keep turning off charger chargeon =0; if ((batteryState & 0b01000000) > 0) { // no ext power if (termState != TS_NORMAL){ lastState = TS_CHARGING; termState = TS_NORMAL; } }else{ //ext power do charging if ((termState != TS_CHARGING) && (lastState !=TS_CHARGED)){ lastState = TS_CHARGING; termState = TS_CHARGED; } } } } if ((termState == TS_CHARGED)||(termState == TS_NORMAL)){ leds |= 0b00001000; } if (termState == TS_BAT_LOW){ leds &= 0b11110111; chargeon = 0; } if (termState == TS_BAT_PULSE) { pulseCount ++; if (pulseCount > MAX_PULSE_CYCLE){ pulseCount = 0; leds &= 0b11110111; chargeon = 0; }else{ if (pulseCount < (MAX_PULSE_CYCLE / 4)) leds ^= 0b00001000; chargeon = 1; } } } /*** LEDs & Charger ***/ void ledtasks(void){ if (chargeon > 0) { leds ^= 0b00001000; } if ((chargeon > 0) || (kbtaskflag > 0)) { PORTB &= 0b00000110; //drop all keyboard columns PORTB |= leds & 0b11111000; //raise the ones that need raisin' PORTB |= 0b00000001; //latch it clock up PORTB &= 0b11111110; //drop clock } } void rs232_putch(byte c) { while(send_bitno) continue; tx_next_bit = 0; sendbuffer = c; send_bitno = TRANSMIT_NUM_BITS*RX_OVERSAMPLE; } byte rs232_getch(void) { while(!receivebufferfull) continue; receivebufferfull = 0; return receivebuffer; } /* Interrupt service routine * * Transmits and receives characters which have been * "rs232_putch"ed and "rs232_getch"ed. * * This ISR runs BRATE * RX_OVERSAMPLE times per second. * */ interrupt void isr(void) { TMR0 += -TIMER_VALUE + 2; // +2 as timer stops for 2 cycles when writing to TMR0 T0IF = 0; /*** RECEIVE ***/ if( --skipoversamples == 0) { skipoversamples++; // check next time switch(receivestate) { case RS_HAVE_NOTHING: /* Check for start bit of a received char. */ if(RxData){ skipoversamples = RX_BITCENTER; receivestate++; } break; case RS_WAIT_HALF_A_BIT: if(RxData) { // valid start bit skipoversamples = RX_OVERSAMPLE; receivestate++; } else receivestate = RS_HAVE_NOTHING; break; // case RS_HAVE_STARTBIT: and subsequent values default: rxshift = (rxshift >> 1) | ((!RxData) << 7); skipoversamples = RX_OVERSAMPLE; receivestate++; break; case RS_WAIT_FOR_STOP: receivebuffer = rxshift; receivebufferfull = 1; receivestate = RS_HAVE_NOTHING; break; } } /*** TRANSMIT ***/ /* This will be called every RX_OVERSAMPLEth time * (because the RECEIVE needs to over-sample the incoming data). */ if(send_bitno) { if((send_bitno & (RX_OVERSAMPLE-1)) == 0) { TxData = !tx_next_bit; // Send next bit. tx_next_bit = sendbuffer & 1; sendbuffer = (sendbuffer >> 1) | 0x80; } send_bitno--; //count down until out of bits to send } /*** COUNTERS ETC ***/ if(delaycount) delaycount--; if(!(--kbtaskcount)) { kbtaskcount = KB_TASK_PERIOD; kbtaskflag = 1; } if(!(--ledtaskcount)) { ledtaskcount = LED_TASK_PERIOD; ledtaskflag = 1; } } // END of interrrupt service routine // LCD ROUTINES FOLLOW // Clear and home the LCD void lcd_clear(void) { lcd_putcmd(0x01); delayMs(LCD_RT); } // write a string of chars to the LCD void lcd_puts(const byte * s) { while(*s) lcd_putch(*s++); } // write one character to the LCD void lcd_putch(byte c) { LCD_RS = 1; // write characters PORTB &= 0x0F; PORTB |= c & 0xF0; LCD_STROBE; PORTB &= 0x0F; PORTB |= c << 4; LCD_STROBE; /* //only need these next two lines if using super multiplexed outputs PORTB &= 0b00000111; //drop the 5 upper pins PORTB |= super_mx_out & 0b11111000; //now raise them according to super_mx_out */ delaycount = T_120_US; while(delaycount); } // Go to the specified position void lcd_goto(byte pos) { lcd_putcmd(0x80+pos); } // initialise the LCD - put into 4 bit mode void lcd_init(void) { LCD_RS = 0; // write control bytes delayMs(150); // power on delay. LCD spec is 15 but some don't make it PORTB &= 0x0F; PORTB |= 0x30; //repeat this initialization 3 times LCD_STROBE; delayMs(LCD_RT); LCD_STROBE; delaycount = T_120_US; while(delaycount); LCD_STROBE; delayMs(LCD_RT); PORTB &= 0x0F; PORTB |= 0x20; //set 4 bit mode LCD_STROBE; delaycount = T_120_US; while(delaycount); #ifdef TWOLINE_5X7 lcd_putcmd(0x28); // 4 bit mode, 1/16 duty, 5x7 font #endif #ifdef ONELINE_5X7 lcd_putcmd(0x20); // 4 bit mode, 1/8 duty cycle, 5x7 font #endif #ifdef ONELINE_5X10 lcd_putcmd(0x24); // 4 bit mode, 1/8 duty cycle, 5x10 font #endif lcd_putcmd(0x08); // display off #ifdef CURSOR_ON_NOBLINK lcd_putcmd(0x0E); // display on with plain cursor #endif #ifdef CURSOR_OFF_BLINK lcd_putcmd(0x0D); // display on with blink character #endif #ifdef CURSOR_OFF_NOBLINK lcd_putcmd(0x0C); // display on, no cursor #endif #ifdef CURSOR_ON_BLINK lcd_putcmd(0x0F); // display on, cursor, blink character #endif lcd_putcmd(0x06); // entry mode = increment cursor, freeze display } // Write to a command register of LCD void lcd_putcmd(byte c) { LCD_RS = 0; PORTB &= 0x0F; PORTB |= c & 0xF0; LCD_STROBE; PORTB &= 0x0F; PORTB |= c << 4; LCD_STROBE; /* //only need these next two lines if using super multiplexed outputs PORTB &= 0b00000111; //drop the 5 upper pins PORTB |= super_mx_out & 0b11111000; //now raise them according to super_mx_out */ delaycount = T_120_US; while(delaycount); } // End of LCD functions // Delay a number of milliseconds void delayMs(byte t) { do { delaycount = T_1000_US; while(delaycount); } while(--t); } //write a string of characters out the serial port void rs232_puts(const byte * s) { while(*s) rs232_putch(*s++); } // Scan the keyboard, return first key found or KB_NO_KEY if none found // if you change the number of rows or columns, change KB_NO_KEY to reflect it byte scankb(void) { byte delay; byte key = 0; // first key returns 0, no key returns KB_NO_KEY byte column = 0b00001000; //RB3 is starting column. //if you reduce columncount it will sequence through fewer columns - ending earlier, //or starting later if you choose a different start column byte columncount = KB_COL_DRV+1; // one more than the number of columns while(--columncount) { PORTB &= 0b00000111; PORTB |= column; //energize column but leave low 3 pins untouched for (delay = 10; --delay;); //this delay rejects key capacitance //test each row in sequence if(RA0) break; key++; if(RA1) break; key++; if(RA2) break; key++; if(RA3) break; key++; column <<= 1; //shift to left, energize next column } /* // Only need these next 4 lines if using super multiplexed inputs TRISB = 0b11111000; //prepare to read by making upper 5 bits input for (delay = 10; --delay;); //now a short delay before reading the pins super_mx_in = PORTB; TRISB = 0; //back to all output */ return key; } byte getBatState(void) { byte delay; byte batState = 0; // Bits are set for which of the four inputs = 1 byte column = 0b00001000; //RB4 is starting column. byte columncount = KB_COL_DRV+1; // one more than the number of columns while(--columncount) { PORTB &= 0b00000111; PORTB |= column; //energize column but leave low 3 pins untouched for (delay = 10; --delay;); //this delay rejects key capacitance //test each row in sequence if(RA5) batState |= column; column <<= 1; //shift to left, energize next column } return batState; } // Converts raw key codes to rs232 activity. Very versatile, but uses a lot of ROM // Cutting out only one of the modes saves 50 words. void kbrs232(byte key) { byte kb_event_state; enum kb_event_state { KBES_NONE, //idle keyboard KBES_KEYDOWN, //key down occurred KBES_KEYUP, //key up occurred KBES_KEYHOLD, //still holding, repeat counter not run out KBES_KEYREPEAT //repeatcount has run out }; // Figure out what type of event happened if(key == KB_NO_KEY) //no keys are down { if(kboldkey == KB_NO_KEY) kb_event_state = KBES_NONE; else kb_event_state = KBES_KEYUP; } else //yes there is a key down somewhere { if(kboldkey == KB_NO_KEY) { kb_event_state = KBES_KEYDOWN; kboldkey = key; } else { if(kbdelaycount) //still counting kb_event_state = KBES_KEYHOLD; else //count ran out kb_event_state = KBES_KEYREPEAT; } } switch(kb_event_state) { case(KBES_NONE): kbdelaycount = KB_DELAY_LOAD; kbrepeatcount = 1; break; case(KBES_KEYDOWN): rs232_putch(Kb_tbl[kboldkey]); break; case(KBES_KEYUP): kbdelaycount = KB_DELAY_LOAD; kbrepeatcount = 1; kboldkey = KB_NO_KEY; break; case(KBES_KEYHOLD): if(kbdelaycount) kbdelaycount--; //never go below zero break; case(KBES_KEYREPEAT): if(!(--kbrepeatcount)) { rs232_putch(Kb_tbl[kboldkey]); kbrepeatcount = KB_REPEAT_RATE; } break; } } // Sends rs232 received data to LCD or translates if needed // Certain commands require a second byte for data, uses command_next as flag. void rs232lcd(byte rec) { if (termState != TS_BAT_LOW) { if (command_next) { // extended commands, this is the data byte command_next = 0; switch (lcdcommand) { case(0x01): // control-A, cursor move to lcd_goto(rec); #ifdef LCD_FORMAT cursorpos = rec; #endif return; case(0x10): // control-P, to LEDS rec <<= 4; // upper four bits are LEDs leds &= 0b00001111; leds |= rec ; rs232_putch(termState+119); return; case(0x12): // control-R, direct LCD command lcd_putcmd(rec); return; case(0x14): // control-T, direct LCD data lcd_putch(rec); return; default: break; } } if(rec < 0x20) { switch(rec) { case (0x02): //control-B, cursor left lcd_putcmd(0x10); //move cursor left #ifdef LCD_FORMAT dec_cursor(); #endif break; case (0x03): //control-C lcd_clear(); //clear the LCD #ifdef LCD_FORMAT cursorpos = 0; #endif break; /* case (0x06): //control-F, cursor right lcd_putcmd(0x14); //move cursor right #ifdef LCD_FORMAT inc_cursor; #endif break; case (0x08): //control-H, backspace lcd_putcmd(0x10); //left #ifdef LCD_FORMAT dec_cursor(); #endif lcd_putch(0x20); //space #ifdef LCD_FORMAT inc_cursor(); #endif lcd_putcmd(0x10); //left #ifdef LCD_FORMAT dec_cursor(); #endif break; */ case (0x0A): //control-J, line feed case (0x0D): //control-M, carriage return lcd_goto(0); //home position #ifdef LCD_FORMAT cursorpos = 0; #endif break; default: //codes not listed can use next byte as data command_next = 1; lcdcommand = rec; break; } } else { if((rec >= 0x80) && (rec < 0xA0)) { //remapping from empty area rec &= 0x07; //to custom character area } #ifdef DESCENDERS if(rec==0x67 || rec==0x6A || rec==0x70 || rec==0x71 || rec==0x79) rec += 0x80; #endif lcd_putch(rec); #ifdef LCD_FORMAT inc_cursor(); #endif } } } // End rs232_lcd #if defined LCD_4X20 void inc_cursor(void) { cursorpos++; switch(cursorpos) { case (END_LINE_1 + 1): cursorpos = BEGIN_LINE_2; lcd_goto(cursorpos); break; case (END_LINE_2 + 1): cursorpos = BEGIN_LINE_3; lcd_goto(cursorpos); break; case (END_LINE_3 + 1): cursorpos = BEGIN_LINE_4; lcd_goto(cursorpos); break; case (END_LINE_4 + 1): cursorpos = BEGIN_LINE_1; lcd_goto(cursorpos); break; } } #elif defined LCD_2X8 || defined LCD_2X16 || defined LCD_2X20 void inc_cursor(void) { cursorpos++; switch(cursorpos) { case (END_LINE_1 + 1): cursorpos = BEGIN_LINE_2; lcd_goto(cursorpos); break; case (END_LINE_2 + 1): cursorpos = BEGIN_LINE_1; lcd_goto(cursorpos); break; } } #elif defined LCD_1X16 void inc_cursor(void) { cursorpos++; switch(cursorpos) { case (END_LINE_1 + 1): cursorpos = BEGIN_LINE_1; lcd_goto(cursorpos); break; } } #endif #if defined LCD_4X20 void dec_cursor(void) { cursorpos--; switch(cursorpos) { case (LINE_1_MINUS_1): cursorpos = END_LINE_4; lcd_goto(cursorpos); break; case (LINE_2_MINUS_1): cursorpos = END_LINE_1; lcd_goto(cursorpos); break; case (LINE_3_MINUS_1): cursorpos = END_LINE_2; lcd_goto(cursorpos); break; case LINE_4_MINUS_1: cursorpos = END_LINE_3; lcd_goto(cursorpos); break; } } #elif defined LCD_2X8 || defined LCD_2X16 || defined LCD_2X20 void dec_cursor(void) { cursorpos--; switch(cursorpos) { case (LINE_1_MINUS_1): cursorpos = END_LINE_2; lcd_goto(cursorpos); break; case (LINE_2_MINUS_1): cursorpos = END_LINE_1; lcd_goto(cursorpos); break; } } #elif defined LCD_1X16 void dec_cursor(void) { cursorpos--; switch(cursorpos) { case (LINE_1_MINUS_1): cursorpos = END_LINE_1; lcd_goto(cursorpos); break; } } #endif //END ALL
