lcd_text.c

Go to the documentation of this file.
00001 
00040 /*#*
00041  *#* $Log$
00042  *#* Revision 1.4  2006/09/20 20:02:43  marco
00043  *#* Replaced ISLISTEMPTY with LIST_EMPTY
00044  *#*
00045  *#* Revision 1.3  2006/07/19 12:56:25  bernie
00046  *#* Convert to new Doxygen style.
00047  *#*
00048  *#* Revision 1.2  2006/02/24 00:27:14  bernie
00049  *#* Use new naming convention for list macros.
00050  *#*
00051  *#* Revision 1.1  2005/11/04 18:00:42  bernie
00052  *#* Import into DevLib.
00053  *#*
00054  *#* Revision 1.11  2005/06/14 14:43:43  bernie
00055  *#* Add DevLib headers.
00056  *#*
00057  *#* Revision 1.10  2005/06/06 17:41:57  batt
00058  *#* Add lcd_layerSet function.
00059  *#*
00060  *#* Revision 1.9  2005/06/01 16:40:07  batt
00061  *#* Remove debug string.
00062  *#*
00063  *#* Revision 1.8  2005/06/01 16:38:04  batt
00064  *#* Adapt to changes in mware/list.h.
00065  *#*
00066  *#* Revision 1.7  2005/06/01 10:45:22  batt
00067  *#* lcd_setAddr(): Bugfix boundary condition; Misc cleanup.
00068  *#*
00069  *#* Revision 1.6  2005/06/01 10:36:23  batt
00070  *#* Layer: Rename member variables and Doxygenize.
00071  *#*
00072  *#* Revision 1.5  2005/05/27 11:05:58  batt
00073  *#* Do not write on lcd if layer is hidden.
00074  *#*/
00075 
00076 #include "lcd_text.h"
00077 #include "lcd_hd44.h"
00078 #include <drv/timer.h> // timer_delay()
00079 #include <mware/formatwr.h> // _formatted_write()
00080 #include <mware/list.h> // LIST_EMPTY()
00081 #include <cfg/macros.h> // BV()
00082 #include <cfg/debug.h>
00083 
00084 #include <string.h> // strlen()
00085 
00086 
00088 #define LCD_LAYERS 6
00089 
00090 #if CONFIG_KERNEL
00091 
00092     static struct Semaphore lcd_semaphore;
00093     #define LOCK_LCD    sem_obtain(&lcd_semaphore)
00094     #define UNLOCK_LCD  sem_release(&lcd_semaphore)
00095 #else /* !CONFIG_KERNEL */
00096     #define LOCK_LCD    do {} while (0)
00097     #define UNLOCK_LCD  do {} while (0)
00098 #endif /* !CONFIG_KERNEL */
00099 
00100 DECLARE_LIST_TYPE(Layer);
00101 
00102 Layer *lcd_DefLayer;
00103 static Layer lcd_LayersPool[LCD_LAYERS];
00104 static LIST_TYPE(Layer) lcd_Layers;
00105 static LIST_TYPE(Layer) lcd_FreeLayers;
00106 
00112 static uint8_t lcd_CursorStatus;
00113 
00115 static lcdpos_t lcd_CursorAddr;
00116 
00117 
00118 void lcd_setAddr(Layer *layer, lcdpos_t addr)
00119 {
00120     /* Sanity check: wrap around to display limits */
00121     while (addr >= LCD_ROWS * LCD_COLS)
00122         addr -= LCD_ROWS * LCD_COLS;
00123 
00124     layer->addr = addr;
00125 }
00126 
00127 #if CONFIG_KERNEL
00128 
00129 void lcd_lock(void)
00130 {
00131     LOCK_LCD;
00132 }
00133 
00134 
00135 void lcd_unlock(void)
00136 {
00137     UNLOCK_LCD;
00138 }
00139 
00140 #endif /* CONFIG_KERNEL */
00141 
00142 
00151 static void lcd_putCharUnlocked(char c, Layer *layer)
00152 {
00153     Layer *l2;
00154     lcdpos_t addr = layer->addr;
00155 
00156     /* Store character in layer buffer */
00157     layer->buf[addr] = c;
00158 
00159     /* Move to next character */
00160     if (++layer->addr >= LCD_COLS * LCD_ROWS)
00161         layer->addr = 0;
00162 
00163     /* Do not write on LCD if layer is hidden. */
00164     if (layer->pri == LAYER_HIDDEN)
00165         return;
00166 
00167     /*
00168      * Check if this location is obscured by
00169      * other layers above us.
00170      */
00171     for (l2 = layer->pred; l2->pred; l2 = l2->pred)
00172     {
00173         if (l2->buf[addr])
00174         {
00175             /* DB(kprintf("layer %04x obs %04x at %d\n", l2, layer, addr);) */
00176             return;
00177         }
00178     }
00179 
00180     /* Write character */
00181     if (c)
00182         lcd_putc(addr, c);
00183     else
00184         /* FIXME: should look for layers beneath! */
00185         lcd_putc(addr, ' ');
00186 }
00187 
00188 
00189 void lcd_putChar(char c, Layer *layer)
00190 {
00191     LOCK_LCD;
00192     lcd_putCharUnlocked(c, layer);
00193     UNLOCK_LCD;
00194 }
00195 
00196 void lcd_layerSet(Layer *layer, char c)
00197 {
00198     int i;
00199 
00200     LOCK_LCD;
00201     lcd_setAddr(layer, 0);
00202     for (i = 0; i < LCD_COLS * LCD_ROWS; i++)
00203         lcd_putCharUnlocked(c, layer);
00204     UNLOCK_LCD;
00205 }
00206 
00207 
00208 void lcd_clear(Layer *layer)
00209 {
00210     lcd_layerSet(layer, 0);
00211 }
00212 
00213 
00214 void lcd_clearLine(Layer *layer, int y)
00215 {
00216     int i;
00217 
00218     LOCK_LCD;
00219     lcd_setAddr(layer, LCD_POS(0, y));
00220     for (i = 0; i < LCD_COLS; i++)
00221         lcd_putCharUnlocked(0, layer);
00222     UNLOCK_LCD;
00223 }
00224 
00225 
00226 void lcd_moveCursor(lcdpos_t addr)
00227 {
00228     LOCK_LCD;
00229     lcd_moveTo(addr);
00230     UNLOCK_LCD;
00231 }
00232 
00233 
00234 char lcd_setCursor(char mode)
00235 {
00236     static const char cursor_cmd[3] =
00237     {
00238         LCD_CMD_CURSOR_OFF, LCD_CMD_CURSOR_BLOCK, LCD_CMD_CURSOR_LINE
00239     };
00240     char oldmode = lcd_CursorStatus;
00241 
00242     LOCK_LCD;
00243     lcd_CursorStatus = mode;
00244     lcd_setReg(cursor_cmd[(int)mode]);
00245     if (mode)
00246         lcd_moveCursor(lcd_CursorAddr);
00247     UNLOCK_LCD;
00248 
00249     return oldmode;
00250 }
00251 
00252 
00253 int lcd_vprintf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, va_list ap)
00254 {
00255     int len;
00256 
00257     LOCK_LCD;
00258 
00259     /*
00260      * Se il cursore era acceso, spegnilo durante
00261      * l'output per evitare che salti alla posizione
00262      * in cui si scrive.
00263      */
00264     if (lcd_CursorStatus)
00265         lcd_setReg(LCD_CMD_CURSOR_OFF);
00266 
00267     /* Spostamento del cursore */
00268     lcd_setAddr(layer, addr);
00269 
00270     if (mode & LCD_CENTER)
00271     {
00272         int pad;
00273 
00274         /*
00275          * NOTE: calculating the string lenght BEFORE it gets
00276          * printf()-formatted. Real lenght may differ.
00277          */
00278         pad = (LCD_COLS - strlen(format)) / 2;
00279         while (pad--)
00280             lcd_putCharUnlocked(' ', layer);
00281     }
00282 
00283     len = _formatted_write(format, (void (*)(char, void *))lcd_putCharUnlocked, layer, ap);
00284 
00285     if (mode & (LCD_FILL | LCD_CENTER))
00286         while (layer->addr % LCD_COLS)
00287             lcd_putCharUnlocked(' ', layer);
00288 
00289     /*
00290      * Riaccendi il cursore e riportalo alla
00291      * vecchia posizione
00292      */
00293     if (lcd_CursorStatus)
00294         lcd_setCursor(lcd_CursorStatus);
00295 
00296     UNLOCK_LCD;
00297 
00298     return len;
00299 }
00300 
00301 
00302 int lcd_printf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, ...)
00303 {
00304     int len;
00305     va_list ap;
00306 
00307     va_start(ap, format);
00308     len = lcd_vprintf(layer, addr, mode, format, ap);
00309     va_end(ap);
00310 
00311     return len;
00312 }
00313 
00314 
00321 static void lcd_enqueueLayer(Layer *layer, char pri)
00322 {
00323     Layer *l2;
00324 
00325     /* Remove layer from whatever list it was in before */
00326     REMOVE(layer);
00327 
00328     layer->pri = pri;
00329 
00330     /*
00331      * Search for the first layer whose priority
00332      * is less or equal to the layer we are adding.
00333      */
00334     FOREACH_NODE(l2, &lcd_Layers)
00335         if (l2->pri <= pri)
00336             break;
00337 
00338     /* Enqueue layer */
00339     INSERT_BEFORE(layer, l2);
00340 }
00341 
00342 Layer *lcd_newLayer(char pri)
00343 {
00344     Layer *layer;
00345 
00346     LOCK_LCD;
00347 
00348     if (LIST_EMPTY(&lcd_FreeLayers))
00349     {
00350         UNLOCK_LCD;
00351         //ASSERT(false);
00352         return NULL;
00353     }
00354 
00355     layer = (Layer *)LIST_HEAD(&lcd_FreeLayers);
00356     layer->addr = 0;
00357     memset(layer->buf, 0, LCD_ROWS * LCD_COLS);
00358 
00359     lcd_enqueueLayer(layer, pri);
00360 
00361     UNLOCK_LCD;
00362     return layer;
00363 }
00364 
00370 static void lcd_refresh(void)
00371 {
00372     lcdpos_t addr;
00373     Layer *l;
00374 
00375     for (addr = 0; addr < LCD_ROWS * LCD_COLS; ++addr)
00376     {
00377         FOREACH_NODE(l, &lcd_Layers)
00378         {
00379             //kprintf("%d %x %p\n", addr, l->buf[0], l);
00380             if (l->pri == LAYER_HIDDEN)
00381                 break;
00382 
00383             if (l->buf[addr])
00384             {
00385                 /* Refresh location */
00386                 lcd_putc(addr, l->buf[addr]);
00387                 goto done;
00388             }
00389         }
00390 
00391         /* Draw background */
00392         lcd_putc(addr, ' ');
00393     done:
00394         ;
00395     }
00396 }
00397 
00403 void lcd_setLayerDepth(Layer *layer, char pri)
00404 {
00405     if (pri != layer->pri)
00406     {
00407         LOCK_LCD;
00408         lcd_enqueueLayer(layer, pri);
00409         /* Vile but simple */
00410         lcd_refresh();
00411         UNLOCK_LCD;
00412     }
00413 }
00414 
00415 void lcd_deleteLayer(Layer *layer)
00416 {
00417     LOCK_LCD;
00418 
00419 /* We use lcd_refresh() instead.  Much simpler than this mess, but slower. */
00420 #if 0
00421     Layer *l2;
00422     lcdpos_t addr;
00423 
00424     /* Repair damage on underlaying layers */
00425     for (addr = 0; addr < LCD_ROWS * LCD_COLS; ++addr)
00426     {
00427         /* If location was covered by us */
00428         if (layer->buf[addr])
00429         {
00430             /* ...and it wasn't covered by others above us... */
00431             for (l2 = layer->pred; l2->pred; l2 = l2->pred)
00432                 if (l2->buf[addr])
00433                     /* can't just break here! */
00434                     goto not_visible;
00435 
00436             /* ...scan underlaying layers to repair damage */
00437             for (l2 = layer->succ; l2->succ; l2 = l2->succ)
00438                 if (l2->buf[addr])
00439                 {
00440                     /* Refresh character */
00441                     lcd_putc(addr, l2->buf[addr]);
00442 
00443                     /* No need to search on deeper layers */
00444                     break;
00445                 }
00446 
00447             not_visible:
00448                 ;
00449         }
00450     }
00451 #endif
00452 
00453     // Remove layer from lcd_Layers list.
00454     REMOVE(layer);
00455 
00456     /* Put layer back into free list */
00457     ADDHEAD(&lcd_FreeLayers, layer);
00458 
00459     lcd_refresh();
00460 
00461     UNLOCK_LCD;
00462 }
00463 
00464 
00465 static void lcd_setDefLayer(Layer *layer)
00466 {
00467     lcd_DefLayer = layer;
00468 }
00469 
00470 #include <cfg/debug.h>
00471 void lcd_init(void)
00472 {
00473     int i;
00474 
00475     LIST_INIT(&lcd_Layers);
00476     LIST_INIT(&lcd_FreeLayers);
00477     for (i = 0; i < LCD_LAYERS; ++i)
00478         ADDHEAD(&lcd_FreeLayers, &lcd_LayersPool[i]);
00479 
00480     lcd_setDefLayer(lcd_newLayer(0));
00481 
00482     lcd_hw_init();
00483 
00484     lcd_setCursor(0);
00485 }
00486 
00487 #if CONFIG_TEST
00488 void lcd_test(void)
00489 {
00490     int i;
00491 
00492     for (i = 0; i < LCD_ROWS * LCD_COLS; ++i)
00493     {
00494         lcd_putCharUnlocked('0' + (i % 10), lcd_DefLayer);
00495         timer_delay(100);
00496     }
00497 }
00498 #endif /* CONFIG_TEST */
00499