0.6.3 Changes since last version
[digitaldcpower] / lcd.c
1 /* vim: set sw=8 ts=8 si et: */
2 /****************************************************************************
3 Title        :   HD44780 LCD library
4 Authors:   
5 Based on Volker Oth's lcd library (http://members.xoom.com/volkeroth)
6 modified by Peter Fleury's (http://jump.to/fleury). Flexible pin
7 configuration by Markus Ermert. Adapted for the tuxgraphics LCD display
8 by Guido Socher.
9
10 Software:  AVR-GCC 
11 Target:    any AVR device
12 Copyright: GPL V2
13        
14 *****************************************************************************/
15 #include <avr/io.h>
16 #include <avr/pgmspace.h>
17 #define F_CPU 8000000UL  // 8 MHz
18 #include <util/delay.h>
19
20 #include "lcd_hw.h"
21 #include "lcd.h"
22
23 /* compatibilty macros for old style */
24 #ifndef cbi
25 #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
26 #endif
27
28 #ifndef sbi
29 #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
30 #endif
31
32
33 /* 
34 ** constants/macros 
35 */
36
37 #define lcd_e_high()    sbi(LCD_E_PORT, LCD_E_PIN)
38 #define lcd_e_low()     cbi(LCD_E_PORT, LCD_E_PIN)
39
40 #define lcd_cmd_mode()        cbi(LCD_RS_PORT, LCD_RS_PIN)        /* RS=0  command mode   */
41 #define lcd_data_mode()        sbi(LCD_RS_PORT, LCD_RS_PIN)        /* RS=1  data mode      */
42 #define lcd_data_port_out()        {        /* defines all data pins as output */ \
43                                         sbi(LCD_DATA_DDR_D7,LCD_DATA_PIN_D7);\
44                                         sbi(LCD_DATA_DDR_D6,LCD_DATA_PIN_D6);\
45                                          sbi(LCD_DATA_DDR_D5,LCD_DATA_PIN_D5);\
46                                          sbi(LCD_DATA_DDR_D4,LCD_DATA_PIN_D4);\
47                                 }
48
49 #if LCD_LINES==1
50 #define LCD_FUNCTION_DEFAULT    LCD_FUNCTION_4BIT_1LINE
51 #else
52 #define LCD_FUNCTION_DEFAULT    LCD_FUNCTION_4BIT_2LINES
53 #endif
54
55 /* 
56 ** function prototypes 
57 */
58 static void lcd_e_toggle(void);
59 static void lcd_out_high(uint8_t d);
60 static void lcd_out_low(uint8_t d);
61
62 /*
63 ** local functions
64 */
65
66 void lcd_delay_ms(uint8_t ms)
67 // delay for a minimum of <ms> 
68 {
69         while(ms){
70                 _delay_ms(0.96);
71                 ms--;
72         }
73 }
74
75 static void lcd_out_low(uint8_t d)
76 {        /* output low nibble */
77         if (d&0x08)  sbi(LCD_DATA_PORT_D7,LCD_DATA_PIN_D7);
78                 else cbi(LCD_DATA_PORT_D7,LCD_DATA_PIN_D7);
79         if (d&0x04)  sbi(LCD_DATA_PORT_D6,LCD_DATA_PIN_D6);
80                 else cbi(LCD_DATA_PORT_D6,LCD_DATA_PIN_D6);
81         if (d&0x02)  sbi(LCD_DATA_PORT_D5,LCD_DATA_PIN_D5);
82                 else cbi(LCD_DATA_PORT_D5,LCD_DATA_PIN_D5);
83         if (d&0x01)  sbi(LCD_DATA_PORT_D4,LCD_DATA_PIN_D4);
84                 else cbi(LCD_DATA_PORT_D4,LCD_DATA_PIN_D4); 
85 }
86 static void lcd_out_high(uint8_t d)
87 {        /* output high nibble */ 
88         if (d&0x80)  sbi(LCD_DATA_PORT_D7,LCD_DATA_PIN_D7);
89                 else cbi(LCD_DATA_PORT_D7,LCD_DATA_PIN_D7);
90         if (d&0x40)  sbi(LCD_DATA_PORT_D6,LCD_DATA_PIN_D6);
91                 else cbi(LCD_DATA_PORT_D6,LCD_DATA_PIN_D6);
92         if (d&0x20)  sbi(LCD_DATA_PORT_D5,LCD_DATA_PIN_D5);
93                 else cbi(LCD_DATA_PORT_D5,LCD_DATA_PIN_D5);
94         if (d&0x10)  sbi(LCD_DATA_PORT_D4,LCD_DATA_PIN_D4);
95                 else cbi(LCD_DATA_PORT_D4,LCD_DATA_PIN_D4); 
96 }
97
98 static void lcd_e_toggle(void)
99 /* toggle Enable Pin */
100 {
101         lcd_e_high();
102         _delay_us(5);
103         lcd_e_low();
104         _delay_us(1);
105 }
106
107
108 static void lcd_write(uint8_t data, uint8_t rs)
109 {
110         // configure data pins as output, already done at init
111         //lcd_data_port_out();
112         _delay_us(4);
113
114         /* output high nibble first */
115
116         lcd_out_high(data);
117         _delay_us(4);
118
119         if (rs)
120                 lcd_data_mode();        /* RS=1: write data            */
121         else
122                 lcd_cmd_mode();        /* RS=0: write instruction     */
123         lcd_e_toggle();
124
125         /* output low nibble */
126         lcd_out_low(data);
127         _delay_us(4);
128
129         if (rs)
130                 lcd_data_mode();        /* RS=1: write data            */
131         else
132                 lcd_cmd_mode();        /* RS=0: write instruction     */
133
134         lcd_e_toggle();
135 }
136
137
138 static unsigned char lcd_waitcmd(unsigned char cmdwait)
139 /* this function used to loop while lcd is busy and read address i
140  * counter however for this we need the RW line. This function
141  * has been changed to just delay a bit. In that case the LCD
142  * is only slightly slower but we do not need the RW pin. */
143 {
144         _delay_us(9);
145         /* the display needs much longer to process a command */
146         if (cmdwait){
147                 lcd_delay_ms(2);
148         }
149         return (0); 
150 }
151
152
153 /*
154 ** PUBLIC FUNCTIONS 
155 */
156
157 void lcd_command(uint8_t cmd)
158 /* send commando <cmd> to LCD */
159 {
160         lcd_waitcmd(0);
161         lcd_write(cmd, 0);
162         lcd_waitcmd(1);
163 }
164
165
166 void lcd_gotoxy(uint8_t x, uint8_t y)
167 /* goto position (x,y) */
168 {
169 #if LCD_LINES==1
170         lcd_command((1 << LCD_DDRAM) + LCD_START_LINE1 + x);
171 #endif
172 #if LCD_LINES==2
173         if (y == 0)
174                 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE1 + x);
175         else
176                 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE2 + x);
177 #endif
178 #if LCD_LINES==3
179         if (y == 0)
180                 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE1 + x);
181         else if (y == 1)
182                 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE2 + x);
183         else if (y == 2)
184                 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE3 + x);
185 #endif
186 #if LCD_LINES==4
187         if (y == 0)
188                 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE1 + x);
189         else if (y == 1)
190                 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE2 + x);
191         else if (y == 2)
192                 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE3 + x);
193         else                        /* y==3 */
194                 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE4 + x);
195 #endif
196
197 }                                /* lcd_gotoxy */
198
199
200
201 void lcd_putc(char c)
202 /* print character at current cursor position */
203 {
204         lcd_waitcmd(0);
205         lcd_write((unsigned char)c, 1);
206         lcd_waitcmd(0);
207 }
208
209
210 void lcd_puts(const char *s)
211 /* print string on lcd  */
212 {
213         while (*s) {
214                 lcd_putc(*s);
215                 s++;
216         }
217
218 }
219
220
221 void lcd_puts_p(const prog_char *progmem_s)
222 /* print string from program memory on lcd  */
223 {
224         register char c;
225
226         while ((c = pgm_read_byte(progmem_s++))) {
227                 lcd_putc(c);
228         }
229
230 }
231
232 void lcd_reset(void)
233 {
234         lcd_command(LCD_FUNCTION_DEFAULT);  /* function set: display lines  */
235         lcd_command(LCD_DISP_OFF);          /* display off                  */
236         lcd_clrscr();                       /* display clear                */
237         lcd_command(LCD_MODE_DEFAULT);      /* set entry mode               */
238         lcd_command(LCD_DISP_ON);           /* display/on no cursor      */
239         lcd_waitcmd(1);
240 }
241
242 void lcd_init(void)
243 /* initialize display and select type of cursor */
244 /* dispAttr: LCD_DISP_OFF, LCD_DISP_ON, LCD_DISP_ON_CURSOR, LCD_DISP_CURSOR_BLINK */
245 {
246     /*------ Initialize lcd to 4 bit i/o mode -------*/
247
248         lcd_data_port_out();        /* all data port bits as output */
249         sbi(LCD_RS_DDR, LCD_RS_PIN);        /* RS pin as output */
250         sbi(LCD_E_DDR, LCD_E_PIN);        /* E  pin as output */
251
252
253         lcd_delay_ms(12);        /* wait 12ms or more after power-on       */
254
255         /* initial write to lcd is 8bit */
256         lcd_out_high(LCD_FUNCTION_8BIT_1LINE);
257         lcd_e_toggle();
258         lcd_delay_ms(2);        /* delay, busy flag can't be checked here */
259
260         lcd_out_high(LCD_FUNCTION_8BIT_1LINE);
261         lcd_e_toggle();
262         lcd_delay_ms(2);        /* delay, busy flag can't be checked here */
263
264         lcd_out_high(LCD_FUNCTION_8BIT_1LINE);
265         lcd_e_toggle();
266         lcd_delay_ms(2);        /* delay, busy flag can't be checked here */
267
268         lcd_out_high(LCD_FUNCTION_4BIT_1LINE);        /* set IO mode to 4bit */
269         lcd_e_toggle();
270
271         /* from now the lcd only accepts 4 bit I/O, we can use lcd_command() */
272         lcd_reset();
273 }