4 * This file is part of BeRTOS.
6 * Bertos is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * As a special exception, you may use this file as part of a free software
21 * library without restriction. Specifically, if other files instantiate
22 * templates or use macros or inline functions from this file, or you compile
23 * this file and link it with other files to produce an executable, this
24 * file does not by itself cause the resulting executable to be covered by
25 * the GNU General Public License. This exception does not however
26 * invalidate any other reasons why the executable file might be covered by
27 * the GNU General Public License.
29 * Copyright 2005 Develer S.r.l. (http://www.develer.com/)
33 * \brief Generic text LCD driver (impl.).
36 * \author Bernardo Innocenti <bernie@develer.com>
37 * \author Stefano Fedrigo <aleph@develer.com>
42 *#* Revision 1.4 2006/09/20 20:02:43 marco
43 *#* Replaced ISLISTEMPTY with LIST_EMPTY
45 *#* Revision 1.3 2006/07/19 12:56:25 bernie
46 *#* Convert to new Doxygen style.
48 *#* Revision 1.2 2006/02/24 00:27:14 bernie
49 *#* Use new naming convention for list macros.
51 *#* Revision 1.1 2005/11/04 18:00:42 bernie
52 *#* Import into DevLib.
54 *#* Revision 1.11 2005/06/14 14:43:43 bernie
55 *#* Add DevLib headers.
57 *#* Revision 1.10 2005/06/06 17:41:57 batt
58 *#* Add lcd_layerSet function.
60 *#* Revision 1.9 2005/06/01 16:40:07 batt
61 *#* Remove debug string.
63 *#* Revision 1.8 2005/06/01 16:38:04 batt
64 *#* Adapt to changes in mware/list.h.
66 *#* Revision 1.7 2005/06/01 10:45:22 batt
67 *#* lcd_setAddr(): Bugfix boundary condition; Misc cleanup.
69 *#* Revision 1.6 2005/06/01 10:36:23 batt
70 *#* Layer: Rename member variables and Doxygenize.
72 *#* Revision 1.5 2005/05/27 11:05:58 batt
73 *#* Do not write on lcd if layer is hidden.
78 #include <drv/timer.h> // timer_delay()
79 #include <mware/formatwr.h> // _formatted_write()
80 #include <mware/list.h> // LIST_EMPTY()
81 #include <cfg/macros.h> // BV()
82 #include <cfg/debug.h>
84 #include <string.h> // strlen()
87 /** Maximum number of layers. */
91 /** Semaphore to arbitrate access to the display. */
92 static struct Semaphore lcd_semaphore;
93 #define LOCK_LCD sem_obtain(&lcd_semaphore)
94 #define UNLOCK_LCD sem_release(&lcd_semaphore)
95 #else /* !CONFIG_KERNEL */
96 #define LOCK_LCD do {} while (0)
97 #define UNLOCK_LCD do {} while (0)
98 #endif /* !CONFIG_KERNEL */
100 DECLARE_LIST_TYPE(Layer);
103 static Layer lcd_LayersPool[LCD_LAYERS];
104 static LIST_TYPE(Layer) lcd_Layers;
105 static LIST_TYPE(Layer) lcd_FreeLayers;
108 * Current cursor status.
110 * One of LCD_CMD_CURSOR_OFF, LCD_CMD_CURSOR_BLOCK or LCD_CMD_CURSOR_LINE.
112 static uint8_t lcd_CursorStatus;
114 /** Current cursor position, encoded as a Cursor position and status. */
115 static lcdpos_t lcd_CursorAddr;
118 void lcd_setAddr(Layer *layer, lcdpos_t addr)
120 /* Sanity check: wrap around to display limits */
121 while (addr >= LCD_ROWS * LCD_COLS)
122 addr -= LCD_ROWS * LCD_COLS;
135 void lcd_unlock(void)
140 #endif /* CONFIG_KERNEL */
144 * Write one character to the display at the current
145 * cursor prosition, then move the cursor right. The
146 * cursor is wrapped to the next line when it moves
147 * beyond the end of the current line.
149 * \note Does _NOT_ lock the display semaphore.
151 static void lcd_putCharUnlocked(char c, Layer *layer)
154 lcdpos_t addr = layer->addr;
156 /* Store character in layer buffer */
157 layer->buf[addr] = c;
159 /* Move to next character */
160 if (++layer->addr >= LCD_COLS * LCD_ROWS)
163 /* Do not write on LCD if layer is hidden. */
164 if (layer->pri == LAYER_HIDDEN)
168 * Check if this location is obscured by
169 * other layers above us.
171 for (l2 = layer->pred; l2->pred; l2 = l2->pred)
175 /* DB(kprintf("layer %04x obs %04x at %d\n", l2, layer, addr);) */
180 /* Write character */
184 /* FIXME: should look for layers beneath! */
189 void lcd_putChar(char c, Layer *layer)
192 lcd_putCharUnlocked(c, layer);
196 void lcd_layerSet(Layer *layer, char c)
201 lcd_setAddr(layer, 0);
202 for (i = 0; i < LCD_COLS * LCD_ROWS; i++)
203 lcd_putCharUnlocked(c, layer);
208 void lcd_clear(Layer *layer)
210 lcd_layerSet(layer, 0);
214 void lcd_clearLine(Layer *layer, int y)
219 lcd_setAddr(layer, LCD_POS(0, y));
220 for (i = 0; i < LCD_COLS; i++)
221 lcd_putCharUnlocked(0, layer);
226 void lcd_moveCursor(lcdpos_t addr)
234 char lcd_setCursor(char mode)
236 static const char cursor_cmd[3] =
238 LCD_CMD_CURSOR_OFF, LCD_CMD_CURSOR_BLOCK, LCD_CMD_CURSOR_LINE
240 char oldmode = lcd_CursorStatus;
243 lcd_CursorStatus = mode;
244 lcd_setReg(cursor_cmd[(int)mode]);
246 lcd_moveCursor(lcd_CursorAddr);
253 int lcd_vprintf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, va_list ap)
260 * Se il cursore era acceso, spegnilo durante
261 * l'output per evitare che salti alla posizione
264 if (lcd_CursorStatus)
265 lcd_setReg(LCD_CMD_CURSOR_OFF);
267 /* Spostamento del cursore */
268 lcd_setAddr(layer, addr);
270 if (mode & LCD_CENTER)
275 * NOTE: calculating the string lenght BEFORE it gets
276 * printf()-formatted. Real lenght may differ.
278 pad = (LCD_COLS - strlen(format)) / 2;
280 lcd_putCharUnlocked(' ', layer);
283 len = _formatted_write(format, (void (*)(char, void *))lcd_putCharUnlocked, layer, ap);
285 if (mode & (LCD_FILL | LCD_CENTER))
286 while (layer->addr % LCD_COLS)
287 lcd_putCharUnlocked(' ', layer);
290 * Riaccendi il cursore e riportalo alla
293 if (lcd_CursorStatus)
294 lcd_setCursor(lcd_CursorStatus);
302 int lcd_printf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, ...)
307 va_start(ap, format);
308 len = lcd_vprintf(layer, addr, mode, format, ap);
316 * Internal function to move a layer between two positions.
318 * \note The layer must be *already* enqueued in some list.
319 * \note The display must be already locked!
321 static void lcd_enqueueLayer(Layer *layer, char pri)
325 /* Remove layer from whatever list it was in before */
331 * Search for the first layer whose priority
332 * is less or equal to the layer we are adding.
334 FOREACH_NODE(l2, &lcd_Layers)
339 INSERT_BEFORE(layer, l2);
342 Layer *lcd_newLayer(char pri)
348 if (LIST_EMPTY(&lcd_FreeLayers))
355 layer = (Layer *)LIST_HEAD(&lcd_FreeLayers);
357 memset(layer->buf, 0, LCD_ROWS * LCD_COLS);
359 lcd_enqueueLayer(layer, pri);
366 * Redraw the display (internal).
368 * \note The display must be already locked.
370 static void lcd_refresh(void)
375 for (addr = 0; addr < LCD_ROWS * LCD_COLS; ++addr)
377 FOREACH_NODE(l, &lcd_Layers)
379 //kprintf("%d %x %p\n", addr, l->buf[0], l);
380 if (l->pri == LAYER_HIDDEN)
385 /* Refresh location */
386 lcd_putc(addr, l->buf[addr]);
391 /* Draw background */
399 * Rearrange layer depth and refresh display accordingly.
401 * \note Setting a priority of LAYER_HIDDEN makes the layer invisible.
403 void lcd_setLayerDepth(Layer *layer, char pri)
405 if (pri != layer->pri)
408 lcd_enqueueLayer(layer, pri);
409 /* Vile but simple */
415 void lcd_deleteLayer(Layer *layer)
419 /* We use lcd_refresh() instead. Much simpler than this mess, but slower. */
424 /* Repair damage on underlaying layers */
425 for (addr = 0; addr < LCD_ROWS * LCD_COLS; ++addr)
427 /* If location was covered by us */
428 if (layer->buf[addr])
430 /* ...and it wasn't covered by others above us... */
431 for (l2 = layer->pred; l2->pred; l2 = l2->pred)
433 /* can't just break here! */
436 /* ...scan underlaying layers to repair damage */
437 for (l2 = layer->succ; l2->succ; l2 = l2->succ)
440 /* Refresh character */
441 lcd_putc(addr, l2->buf[addr]);
443 /* No need to search on deeper layers */
453 // Remove layer from lcd_Layers list.
456 /* Put layer back into free list */
457 ADDHEAD(&lcd_FreeLayers, layer);
465 static void lcd_setDefLayer(Layer *layer)
467 lcd_DefLayer = layer;
470 #include <cfg/debug.h>
475 LIST_INIT(&lcd_Layers);
476 LIST_INIT(&lcd_FreeLayers);
477 for (i = 0; i < LCD_LAYERS; ++i)
478 ADDHEAD(&lcd_FreeLayers, &lcd_LayersPool[i]);
480 lcd_setDefLayer(lcd_newLayer(0));
492 for (i = 0; i < LCD_ROWS * LCD_COLS; ++i)
494 lcd_putCharUnlocked('0' + (i % 10), lcd_DefLayer);
498 #endif /* CONFIG_TEST */