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