4 * Copyright 2005 Develer S.r.l. (http://www.develer.com/)
5 * This file is part of DevLib - See README.devlib for information.
8 * \brief Generic text LCD driver (impl.).
11 * \author Bernardo Innocenti <bernie@develer.com>
12 * \author Stefano Fedrigo <aleph@develer.com>
17 *#* Revision 1.4 2006/09/20 20:02:43 marco
18 *#* Replaced ISLISTEMPTY with LIST_EMPTY
20 *#* Revision 1.3 2006/07/19 12:56:25 bernie
21 *#* Convert to new Doxygen style.
23 *#* Revision 1.2 2006/02/24 00:27:14 bernie
24 *#* Use new naming convention for list macros.
26 *#* Revision 1.1 2005/11/04 18:00:42 bernie
27 *#* Import into DevLib.
29 *#* Revision 1.11 2005/06/14 14:43:43 bernie
30 *#* Add DevLib headers.
32 *#* Revision 1.10 2005/06/06 17:41:57 batt
33 *#* Add lcd_layerSet function.
35 *#* Revision 1.9 2005/06/01 16:40:07 batt
36 *#* Remove debug string.
38 *#* Revision 1.8 2005/06/01 16:38:04 batt
39 *#* Adapt to changes in mware/list.h.
41 *#* Revision 1.7 2005/06/01 10:45:22 batt
42 *#* lcd_setAddr(): Bugfix boundary condition; Misc cleanup.
44 *#* Revision 1.6 2005/06/01 10:36:23 batt
45 *#* Layer: Rename member variables and Doxygenize.
47 *#* Revision 1.5 2005/05/27 11:05:58 batt
48 *#* Do not write on lcd if layer is hidden.
53 #include <drv/timer.h> // timer_delay()
54 #include <mware/formatwr.h> // _formatted_write()
55 #include <mware/list.h> // LIST_EMPTY()
56 #include <cfg/macros.h> // BV()
57 #include <cfg/debug.h>
59 #include <string.h> // strlen()
62 /** Maximum number of layers. */
66 /** Semaphore to arbitrate access to the display. */
67 static struct Semaphore lcd_semaphore;
68 #define LOCK_LCD sem_obtain(&lcd_semaphore)
69 #define UNLOCK_LCD sem_release(&lcd_semaphore)
70 #else /* !CONFIG_KERNEL */
71 #define LOCK_LCD do {} while (0)
72 #define UNLOCK_LCD do {} while (0)
73 #endif /* !CONFIG_KERNEL */
75 DECLARE_LIST_TYPE(Layer);
78 static Layer lcd_LayersPool[LCD_LAYERS];
79 static LIST_TYPE(Layer) lcd_Layers;
80 static LIST_TYPE(Layer) lcd_FreeLayers;
83 * Current cursor status.
85 * One of LCD_CMD_CURSOR_OFF, LCD_CMD_CURSOR_BLOCK or LCD_CMD_CURSOR_LINE.
87 static uint8_t lcd_CursorStatus;
89 /** Current cursor position, encoded as a Cursor position and status. */
90 static lcdpos_t lcd_CursorAddr;
93 void lcd_setAddr(Layer *layer, lcdpos_t addr)
95 /* Sanity check: wrap around to display limits */
96 while (addr >= LCD_ROWS * LCD_COLS)
97 addr -= LCD_ROWS * LCD_COLS;
110 void lcd_unlock(void)
115 #endif /* CONFIG_KERNEL */
119 * Write one character to the display at the current
120 * cursor prosition, then move the cursor right. The
121 * cursor is wrapped to the next line when it moves
122 * beyond the end of the current line.
124 * \note Does _NOT_ lock the display semaphore.
126 static void lcd_putCharUnlocked(char c, Layer *layer)
129 lcdpos_t addr = layer->addr;
131 /* Store character in layer buffer */
132 layer->buf[addr] = c;
134 /* Move to next character */
135 if (++layer->addr >= LCD_COLS * LCD_ROWS)
138 /* Do not write on LCD if layer is hidden. */
139 if (layer->pri == LAYER_HIDDEN)
143 * Check if this location is obscured by
144 * other layers above us.
146 for (l2 = layer->pred; l2->pred; l2 = l2->pred)
150 /* DB(kprintf("layer %04x obs %04x at %d\n", l2, layer, addr);) */
155 /* Write character */
159 /* FIXME: should look for layers beneath! */
164 void lcd_putChar(char c, Layer *layer)
167 lcd_putCharUnlocked(c, layer);
171 void lcd_layerSet(Layer *layer, char c)
176 lcd_setAddr(layer, 0);
177 for (i = 0; i < LCD_COLS * LCD_ROWS; i++)
178 lcd_putCharUnlocked(c, layer);
183 void lcd_clear(Layer *layer)
185 lcd_layerSet(layer, 0);
189 void lcd_clearLine(Layer *layer, int y)
194 lcd_setAddr(layer, LCD_POS(0, y));
195 for (i = 0; i < LCD_COLS; i++)
196 lcd_putCharUnlocked(0, layer);
201 void lcd_moveCursor(lcdpos_t addr)
209 char lcd_setCursor(char mode)
211 static const char cursor_cmd[3] =
213 LCD_CMD_CURSOR_OFF, LCD_CMD_CURSOR_BLOCK, LCD_CMD_CURSOR_LINE
215 char oldmode = lcd_CursorStatus;
218 lcd_CursorStatus = mode;
219 lcd_setReg(cursor_cmd[(int)mode]);
221 lcd_moveCursor(lcd_CursorAddr);
228 int lcd_vprintf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, va_list ap)
235 * Se il cursore era acceso, spegnilo durante
236 * l'output per evitare che salti alla posizione
239 if (lcd_CursorStatus)
240 lcd_setReg(LCD_CMD_CURSOR_OFF);
242 /* Spostamento del cursore */
243 lcd_setAddr(layer, addr);
245 if (mode & LCD_CENTER)
250 * NOTE: calculating the string lenght BEFORE it gets
251 * printf()-formatted. Real lenght may differ.
253 pad = (LCD_COLS - strlen(format)) / 2;
255 lcd_putCharUnlocked(' ', layer);
258 len = _formatted_write(format, (void (*)(char, void *))lcd_putCharUnlocked, layer, ap);
260 if (mode & (LCD_FILL | LCD_CENTER))
261 while (layer->addr % LCD_COLS)
262 lcd_putCharUnlocked(' ', layer);
265 * Riaccendi il cursore e riportalo alla
268 if (lcd_CursorStatus)
269 lcd_setCursor(lcd_CursorStatus);
277 int lcd_printf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, ...)
282 va_start(ap, format);
283 len = lcd_vprintf(layer, addr, mode, format, ap);
291 * Internal function to move a layer between two positions.
293 * \note The layer must be *already* enqueued in some list.
294 * \note The display must be already locked!
296 static void lcd_enqueueLayer(Layer *layer, char pri)
300 /* Remove layer from whatever list it was in before */
306 * Search for the first layer whose priority
307 * is less or equal to the layer we are adding.
309 FOREACH_NODE(l2, &lcd_Layers)
314 INSERT_BEFORE(layer, l2);
317 Layer *lcd_newLayer(char pri)
323 if (LIST_EMPTY(&lcd_FreeLayers))
330 layer = (Layer *)LIST_HEAD(&lcd_FreeLayers);
332 memset(layer->buf, 0, LCD_ROWS * LCD_COLS);
334 lcd_enqueueLayer(layer, pri);
341 * Redraw the display (internal).
343 * \note The display must be already locked.
345 static void lcd_refresh(void)
350 for (addr = 0; addr < LCD_ROWS * LCD_COLS; ++addr)
352 FOREACH_NODE(l, &lcd_Layers)
354 //kprintf("%d %x %p\n", addr, l->buf[0], l);
355 if (l->pri == LAYER_HIDDEN)
360 /* Refresh location */
361 lcd_putc(addr, l->buf[addr]);
366 /* Draw background */
374 * Rearrange layer depth and refresh display accordingly.
376 * \note Setting a priority of LAYER_HIDDEN makes the layer invisible.
378 void lcd_setLayerDepth(Layer *layer, char pri)
380 if (pri != layer->pri)
383 lcd_enqueueLayer(layer, pri);
384 /* Vile but simple */
390 void lcd_deleteLayer(Layer *layer)
394 /* We use lcd_refresh() instead. Much simpler than this mess, but slower. */
399 /* Repair damage on underlaying layers */
400 for (addr = 0; addr < LCD_ROWS * LCD_COLS; ++addr)
402 /* If location was covered by us */
403 if (layer->buf[addr])
405 /* ...and it wasn't covered by others above us... */
406 for (l2 = layer->pred; l2->pred; l2 = l2->pred)
408 /* can't just break here! */
411 /* ...scan underlaying layers to repair damage */
412 for (l2 = layer->succ; l2->succ; l2 = l2->succ)
415 /* Refresh character */
416 lcd_putc(addr, l2->buf[addr]);
418 /* No need to search on deeper layers */
428 // Remove layer from lcd_Layers list.
431 /* Put layer back into free list */
432 ADDHEAD(&lcd_FreeLayers, layer);
440 static void lcd_setDefLayer(Layer *layer)
442 lcd_DefLayer = layer;
445 #include <cfg/debug.h>
450 LIST_INIT(&lcd_Layers);
451 LIST_INIT(&lcd_FreeLayers);
452 for (i = 0; i < LCD_LAYERS; ++i)
453 ADDHEAD(&lcd_FreeLayers, &lcd_LayersPool[i]);
455 lcd_setDefLayer(lcd_newLayer(0));
467 for (i = 0; i < LCD_ROWS * LCD_COLS; ++i)
469 lcd_putCharUnlocked('0' + (i % 10), lcd_DefLayer);
473 #endif /* CONFIG_TEST */