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 #include <drv/timer.h> // timer_delay()
43 #include <mware/formatwr.h> // _formatted_write()
44 #include <mware/list.h> // LIST_EMPTY()
45 #include <cfg/macros.h> // BV()
46 #include <cfg/debug.h>
48 #include <string.h> // strlen()
51 /** Maximum number of layers. */
55 /** Semaphore to arbitrate access to the display. */
56 static struct Semaphore lcd_semaphore;
57 #define LOCK_LCD sem_obtain(&lcd_semaphore)
58 #define UNLOCK_LCD sem_release(&lcd_semaphore)
59 #else /* !CONFIG_KERNEL */
60 #define LOCK_LCD do {} while (0)
61 #define UNLOCK_LCD do {} while (0)
62 #endif /* !CONFIG_KERNEL */
64 DECLARE_LIST_TYPE(Layer);
67 static Layer lcd_LayersPool[LCD_LAYERS];
68 static LIST_TYPE(Layer) lcd_Layers;
69 static LIST_TYPE(Layer) lcd_FreeLayers;
72 * Current cursor status.
74 * One of LCD_CMD_CURSOR_OFF, LCD_CMD_CURSOR_BLOCK or LCD_CMD_CURSOR_LINE.
76 static uint8_t lcd_CursorStatus;
78 /** Current cursor position, encoded as a Cursor position and status. */
79 static lcdpos_t lcd_CursorAddr;
82 void lcd_setAddr(Layer *layer, lcdpos_t addr)
84 /* Sanity check: wrap around to display limits */
85 while (addr >= LCD_ROWS * LCD_COLS)
86 addr -= LCD_ROWS * LCD_COLS;
104 #endif /* CONFIG_KERNEL */
108 * Write one character to the display at the current
109 * cursor prosition, then move the cursor right. The
110 * cursor is wrapped to the next line when it moves
111 * beyond the end of the current line.
113 * \note Does _NOT_ lock the display semaphore.
115 static void lcd_putCharUnlocked(char c, Layer *layer)
118 lcdpos_t addr = layer->addr;
120 /* Store character in layer buffer */
121 layer->buf[addr] = c;
123 /* Move to next character */
124 if (++layer->addr >= LCD_COLS * LCD_ROWS)
127 /* Do not write on LCD if layer is hidden. */
128 if (layer->pri == LAYER_HIDDEN)
132 * Check if this location is obscured by
133 * other layers above us.
135 for (l2 = layer->pred; l2->pred; l2 = l2->pred)
139 /* DB(kprintf("layer %04x obs %04x at %d\n", l2, layer, addr);) */
144 /* Write character */
148 /* FIXME: should look for layers beneath! */
153 void lcd_putChar(char c, Layer *layer)
156 lcd_putCharUnlocked(c, layer);
160 void lcd_layerSet(Layer *layer, char c)
165 lcd_setAddr(layer, 0);
166 for (i = 0; i < LCD_COLS * LCD_ROWS; i++)
167 lcd_putCharUnlocked(c, layer);
172 void lcd_clear(Layer *layer)
174 lcd_layerSet(layer, 0);
178 void lcd_clearLine(Layer *layer, int y)
183 lcd_setAddr(layer, LCD_POS(0, y));
184 for (i = 0; i < LCD_COLS; i++)
185 lcd_putCharUnlocked(0, layer);
190 void lcd_moveCursor(lcdpos_t addr)
198 char lcd_setCursor(char mode)
200 static const char cursor_cmd[3] =
202 LCD_CMD_CURSOR_OFF, LCD_CMD_CURSOR_BLOCK, LCD_CMD_CURSOR_LINE
204 char oldmode = lcd_CursorStatus;
207 lcd_CursorStatus = mode;
208 lcd_setReg(cursor_cmd[(int)mode]);
210 lcd_moveCursor(lcd_CursorAddr);
217 int lcd_vprintf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, va_list ap)
224 * Se il cursore era acceso, spegnilo durante
225 * l'output per evitare che salti alla posizione
228 if (lcd_CursorStatus)
229 lcd_setReg(LCD_CMD_CURSOR_OFF);
231 /* Spostamento del cursore */
232 lcd_setAddr(layer, addr);
234 if (mode & LCD_CENTER)
239 * NOTE: calculating the string lenght BEFORE it gets
240 * printf()-formatted. Real lenght may differ.
242 pad = (LCD_COLS - strlen(format)) / 2;
244 lcd_putCharUnlocked(' ', layer);
247 len = _formatted_write(format, (void (*)(char, void *))lcd_putCharUnlocked, layer, ap);
249 if (mode & (LCD_FILL | LCD_CENTER))
250 while (layer->addr % LCD_COLS)
251 lcd_putCharUnlocked(' ', layer);
254 * Riaccendi il cursore e riportalo alla
257 if (lcd_CursorStatus)
258 lcd_setCursor(lcd_CursorStatus);
266 int lcd_printf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, ...)
271 va_start(ap, format);
272 len = lcd_vprintf(layer, addr, mode, format, ap);
280 * Internal function to move a layer between two positions.
282 * \note The layer must be *already* enqueued in some list.
283 * \note The display must be already locked!
285 static void lcd_enqueueLayer(Layer *layer, char pri)
289 /* Remove layer from whatever list it was in before */
295 * Search for the first layer whose priority
296 * is less or equal to the layer we are adding.
298 FOREACH_NODE(l2, &lcd_Layers)
303 INSERT_BEFORE(layer, l2);
306 Layer *lcd_newLayer(char pri)
312 if (LIST_EMPTY(&lcd_FreeLayers))
319 layer = (Layer *)LIST_HEAD(&lcd_FreeLayers);
321 memset(layer->buf, 0, LCD_ROWS * LCD_COLS);
323 lcd_enqueueLayer(layer, pri);
330 * Redraw the display (internal).
332 * \note The display must be already locked.
334 static void lcd_refresh(void)
339 for (addr = 0; addr < LCD_ROWS * LCD_COLS; ++addr)
341 FOREACH_NODE(l, &lcd_Layers)
343 //kprintf("%d %x %p\n", addr, l->buf[0], l);
344 if (l->pri == LAYER_HIDDEN)
349 /* Refresh location */
350 lcd_putc(addr, l->buf[addr]);
355 /* Draw background */
363 * Rearrange layer depth and refresh display accordingly.
365 * \note Setting a priority of LAYER_HIDDEN makes the layer invisible.
367 void lcd_setLayerDepth(Layer *layer, char pri)
369 if (pri != layer->pri)
372 lcd_enqueueLayer(layer, pri);
373 /* Vile but simple */
379 void lcd_deleteLayer(Layer *layer)
383 /* We use lcd_refresh() instead. Much simpler than this mess, but slower. */
388 /* Repair damage on underlaying layers */
389 for (addr = 0; addr < LCD_ROWS * LCD_COLS; ++addr)
391 /* If location was covered by us */
392 if (layer->buf[addr])
394 /* ...and it wasn't covered by others above us... */
395 for (l2 = layer->pred; l2->pred; l2 = l2->pred)
397 /* can't just break here! */
400 /* ...scan underlaying layers to repair damage */
401 for (l2 = layer->succ; l2->succ; l2 = l2->succ)
404 /* Refresh character */
405 lcd_putc(addr, l2->buf[addr]);
407 /* No need to search on deeper layers */
417 // Remove layer from lcd_Layers list.
420 /* Put layer back into free list */
421 ADDHEAD(&lcd_FreeLayers, layer);
429 static void lcd_setDefLayer(Layer *layer)
431 lcd_DefLayer = layer;
434 #include <cfg/debug.h>
439 LIST_INIT(&lcd_Layers);
440 LIST_INIT(&lcd_FreeLayers);
441 for (i = 0; i < LCD_LAYERS; ++i)
442 ADDHEAD(&lcd_FreeLayers, &lcd_LayersPool[i]);
444 lcd_setDefLayer(lcd_newLayer(0));
456 for (i = 0; i < LCD_ROWS * LCD_COLS; ++i)
458 lcd_putCharUnlocked('0' + (i % 10), lcd_DefLayer);
462 #endif /* CONFIG_TEST */