better orbiting
[aversive.git] / modules / devices / ihm / lcd / lcd.c
1 /*  
2  *  Copyright Droids Corporation, Microb Technology, Eirbot (2005)
3  * 
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  *  Revision : $Id: lcd.c,v 1.5.4.3 2007-08-19 10:39:31 zer0 Exp $
19  *
20  */
21
22 /* Droids-corp, Eirbot, Microb Technology 2005 - Zer0
23  * Implementation for LCD
24  */
25
26 /** \file lcd.c
27  *  \brief Implementation for the LCD module.
28  *
29  *  \todo Test the module with other wirings, verify the delay macro, stqtic inlines for the public functions ??
30  *
31  *  \test works on mega32 with all on the same port and old normal wiring, how about more speific connections ?
32  *
33  * This module provides functions for using a lcd device
34  */
35
36
37
38 #include <stdio.h>
39
40 #include <aversive/pgmspace.h>
41 #include <aversive.h>
42 #include <aversive/wait.h>
43
44 #include <lcd.h>
45 #include <lcd_protocol.h>
46
47
48 //#define lcd_e_delay()   asm volatile("nop")  /* delay 500ns with 4Mhz */
49 #define e_delay() _delay_loop_1(1) // ok ca ?
50
51
52 /**********************************************************/
53 /*
54 ** port level functions
55 */
56
57 // DDR en sortie
58 static inline void port_set_out(void)
59 {
60   uint8_t flags;
61
62   IRQ_LOCK(flags);  // if there is some other stuff on the same port
63   DDR(LCD_PORT) |= LCD_DATA_MASK;
64   IRQ_UNLOCK(flags);
65 }
66
67 // DDR en entree
68 static inline void port_set_in(void)
69 {
70   uint8_t flags;
71
72   IRQ_LOCK(flags);
73   DDR(LCD_PORT) &= ~(LCD_DATA_MASK);
74   IRQ_UNLOCK(flags);
75 }
76
77
78 /* toggle Enable Pin */
79 static inline void e_toggle(void)
80 {
81   sbi(LCD_E_PORT, LCD_E_BIT);
82   e_delay();
83   cbi(LCD_E_PORT, LCD_E_BIT);
84 }
85
86 /**********************************************************/
87 /*
88 ** byte level functions
89 */
90 static inline void lcd_write(uint8_t data,uint8_t rs) 
91 {
92   register uint8_t port_save;
93   uint8_t flags;
94
95
96   port_set_out();
97   
98
99   cbi(LCD_RW_PORT, LCD_RW_BIT);//RW=0
100
101   if (rs) 
102     sbi(LCD_RS_PORT, LCD_RS_BIT); //RS=1 
103   else
104     cbi(LCD_RS_PORT, LCD_RS_BIT); //RS=0 
105       
106
107   // port state save
108   IRQ_LOCK(flags);
109  
110  port_save= LCD_DATA_PORT & ~(LCD_DATA_MASK);
111
112   /* output high nibble first */
113   LCD_DATA_PORT = ( ( ((data>>4) << LCD_FIRST_DATA_BIT) & LCD_DATA_MASK )
114                     |  port_save );
115
116   e_toggle();
117   
118   /* output low nibble */
119   LCD_DATA_PORT = ( ((data<<LCD_FIRST_DATA_BIT) & LCD_DATA_MASK)
120                     |  port_save );
121
122
123   IRQ_UNLOCK(flags);
124
125   e_toggle();
126 }
127
128
129
130 static uint8_t lcd_read(uint8_t rs) 
131 {
132   register uint8_t dataH, dataL;
133   
134   
135   if (rs) sbi(LCD_RS_PORT, LCD_RS_BIT);    /* RS=1: read data      */
136   else    cbi(LCD_RS_PORT, LCD_RS_BIT);    /* RS=0: read busy flag */
137   sbi(LCD_RW_PORT, LCD_RW_BIT);            /* RW=1  read mode      */
138   
139   /* configure data pins as input */
140   port_set_in();
141   
142   sbi(LCD_E_PORT, LCD_E_BIT);
143   e_delay();
144   
145   
146   dataH = PIN(LCD_PORT) >> LCD_FIRST_DATA_BIT ;/* read high nibble first */
147   
148   cbi(LCD_E_PORT, LCD_E_BIT);   
149   e_delay();                           /* Enable 500ns low       */
150   
151
152   sbi(LCD_E_PORT, LCD_E_BIT);
153   e_delay();
154   
155   dataL = PIN(LCD_PORT) >> LCD_FIRST_DATA_BIT ;            /* read low nibble        */
156
157   cbi(LCD_E_PORT, LCD_E_BIT);   
158     
159   return ( (dataH<<4) | (dataL&0x0F) );
160 }
161
162
163 // use for init, to set lcd mode to 4 bits
164 void initial_8_bit_write(uint8_t value)
165 {
166   register uint8_t port_save;
167   uint8_t flags;
168
169
170   cbi(LCD_RW_PORT, LCD_RW_BIT);
171
172   cbi(LCD_RS_PORT, LCD_RS_BIT);
173
174
175   IRQ_LOCK(flags);
176
177   // port save
178   port_save = LCD_DATA_PORT & ~(LCD_DATA_MASK);
179
180
181   LCD_DATA_PORT = ( ((value <<LCD_FIRST_DATA_BIT) & LCD_DATA_MASK)
182                     |  port_save );
183
184   IRQ_UNLOCK(flags);
185
186   e_toggle();
187   wait_ms(1);           // delay, busy flag can't be checked here 
188 }
189
190 /**********************************************************/
191 /*
192 ** commands and more
193 */
194
195 static inline unsigned char lcd_waitbusy(void)
196 /* loops while lcd is busy, reads address counter */
197 {
198   register unsigned char c;
199   
200   while ( (c=lcd_read(0)) & (1<<LCD_BUSY)) ;
201   
202   return (c);  // return address counter
203 }
204
205
206
207 void lcd_command(uint8_t cmd)
208      /* send commando <cmd> to LCD */
209 {
210   lcd_waitbusy();
211   lcd_write(cmd,0);
212 }
213
214
215
216 static inline void lcd_newline(uint8_t pos)
217      /* goto start of next line */
218 {
219   register uint8_t addressCounter;
220   
221   
222 #if LCD_LINES==1
223   addressCounter = 0;
224 #endif
225 #if LCD_LINES==2
226   if ( pos < (LCD_START_LINE2) )
227     addressCounter = LCD_START_LINE2;
228   else
229     addressCounter = LCD_START_LINE1;
230 #endif
231 #if LCD_LINES==4
232   if ( pos < LCD_START_LINE3 )
233     addressCounter = LCD_START_LINE2;
234   else if ( (pos >= LCD_START_LINE2) && (pos < LCD_START_LINE4) )
235     addressCounter = LCD_START_LINE3;
236   else if ( (pos >= LCD_START_LINE3) && (pos < LCD_START_LINE2) )
237     addressCounter = LCD_START_LINE4;
238   else 
239     addressCounter = LCD_START_LINE1;
240 #endif
241
242
243   lcd_command((1<<LCD_DDRAM)+addressCounter);
244   
245 }/* lcd_newline */
246
247
248
249
250
251
252 /**********************************************************/
253
254 /*
255 ** PUBLIC FUNCTIONS 
256 */
257
258
259
260 void lcd_gotoxy(uint8_t x, uint8_t y)
261      /* goto position (x,y) */
262 {
263 #if (LCD_LINES==1)
264   lcd_command((1<<LCD_DDRAM)+LCD_START_LINE1+x);
265 #endif
266 #if (LCD_LINES==2)
267   if ( y==0 ) 
268     lcd_command((1<<LCD_DDRAM)+LCD_START_LINE1+x);
269   else
270     lcd_command((1<<LCD_DDRAM)+LCD_START_LINE2+x);
271 #endif
272 #if LCD_LINES==4
273   if ( y==0 )
274     lcd_command((1<<LCD_DDRAM)+LCD_START_LINE1+x);
275   else if ( y==1)
276     lcd_command((1<<LCD_DDRAM)+LCD_START_LINE2+x);
277   else if ( y==2)
278     lcd_command((1<<LCD_DDRAM)+LCD_START_LINE3+x);
279   else /* y==3 */
280     lcd_command((1<<LCD_DDRAM)+LCD_START_LINE4+x);
281 #endif
282   
283 }/* lcd_gotoxy */
284
285
286
287
288 void lcd_clrscr(void)
289      /* clear lcd and set cursor to home position */
290 {
291   lcd_command(1<<LCD_CLR);
292 }
293
294
295
296 void lcd_home(void)
297      /* set cursor to home position */
298 {
299   lcd_command(1<<LCD_HOME);
300 }
301
302
303
304 void lcd_putc(char c)
305      /* print character at current cursor position */
306 {
307   register unsigned char pos;
308   
309   pos = lcd_waitbusy();   // read busy-flag and address counter
310   
311 #if LCD_DOUBLE_ADDRESSING == 1
312   if(pos==8)
313     {
314       lcd_gotoxy(0,1);
315       lcd_waitbusy();
316     }
317 #endif
318
319   if (c=='\n')        // new line
320     lcd_newline(pos);
321   else if (c== '\f') // form feed = clear screen
322     lcd_clrscr();
323         else                // normal car
324     lcd_write(c, 1);
325 }
326
327 /* for use with fdevopen */
328 int lcd_dev_putc(char c, FILE* f)
329 {
330         lcd_putc(c);
331         return c;
332 }
333
334 void lcd_init(uint8_t dispAttr)
335      /* initialize display and select type of cursor */
336      /* dispAttr: LCD_DISP_OFF, LCD_DISP_ON, LCD_DISP_ON_CURSOR, LCD_DISP_CURSOR_BLINK */
337 {
338
339
340   // DDRs
341   port_set_out();
342
343   sbi(DDR(LCD_RW_PORT), LCD_RW_BIT);
344   sbi(DDR(LCD_RS_PORT), LCD_RS_BIT);
345   sbi(DDR(LCD_E_PORT), LCD_E_BIT);
346
347
348   wait_ms(16);        /* wait 16ms or more after power-on       */
349   
350   /*------ Initialize lcd to 4 bit i/o mode -------*/ 
351   /* initial write to lcd is 8bit */
352   initial_8_bit_write(LCD_FUNCTION_8BIT_1LINE>>4);
353   initial_8_bit_write(LCD_FUNCTION_8BIT_1LINE>>4);
354   initial_8_bit_write(LCD_FUNCTION_8BIT_1LINE>>4);
355   initial_8_bit_write(LCD_FUNCTION_4BIT_1LINE>>4);
356
357
358   
359   lcd_command(LCD_FUNCTION_DEFAULT);      /* function set: display lines  */
360   lcd_command(LCD_DISP_OFF);              /* display off                  */
361   lcd_clrscr();                           /* display clear                */ 
362   lcd_command(LCD_MODE_DEFAULT);          /* set entry mode               */
363   lcd_command(dispAttr);                  /* display/cursor control       */
364
365
366
367
368 }/* lcd_init */