lcd_text.c

Go to the documentation of this file.
00001 
00040 #include "lcd_text.h"
00041 #include "lcd_hd44.h"
00042 
00043 #include <cfg/macros.h> // BV()
00044 #include <cfg/debug.h>
00045 
00046 #include <drv/timer.h> // timer_delay()
00047 
00048 #include <mware/formatwr.h> // _formatted_write()
00049 #include <struct/list.h> // LIST_EMPTY()
00050 
00051 #include <string.h> // strlen()
00052 
00053 
00055 #define LCD_LAYERS 6
00056 
00057 #if CONFIG_KERN
00058     #include <kern/sem.h>
00060     static struct Semaphore lcd_semaphore;
00061     #define LOCK_LCD    sem_obtain(&lcd_semaphore)
00062     #define UNLOCK_LCD  sem_release(&lcd_semaphore)
00063 #else /* !CONFIG_KERN */
00064     #define LOCK_LCD    do {} while (0)
00065     #define UNLOCK_LCD  do {} while (0)
00066 #endif /* !CONFIG_KERN */
00067 
00068 DECLARE_LIST_TYPE(Layer);
00069 
00070 Layer *lcd_DefLayer;
00071 static Layer lcd_LayersPool[LCD_LAYERS];
00072 static LIST_TYPE(Layer) lcd_Layers;
00073 static LIST_TYPE(Layer) lcd_FreeLayers;
00074 
00080 static uint8_t lcd_CursorStatus;
00081 
00083 static lcdpos_t lcd_CursorAddr;
00084 
00085 
00086 void lcd_setAddr(Layer *layer, lcdpos_t addr)
00087 {
00088     /* Sanity check: wrap around to display limits */
00089     while (addr >= LCD_ROWS * LCD_COLS)
00090         addr -= LCD_ROWS * LCD_COLS;
00091 
00092     layer->addr = addr;
00093 }
00094 
00095 #if CONFIG_KERN
00096 
00097 void lcd_lock(void)
00098 {
00099     LOCK_LCD;
00100 }
00101 
00102 
00103 void lcd_unlock(void)
00104 {
00105     UNLOCK_LCD;
00106 }
00107 
00108 #endif /* CONFIG_KERN */
00109 
00110 
00119 static void lcd_putCharUnlocked(char c, Layer *layer)
00120 {
00121     Layer *l2;
00122     lcdpos_t addr = layer->addr;
00123 
00124     /* Store character in layer buffer */
00125     layer->buf[addr] = c;
00126 
00127     /* Move to next character */
00128     if (++layer->addr >= LCD_COLS * LCD_ROWS)
00129         layer->addr = 0;
00130 
00131     /* Do not write on LCD if layer is hidden. */
00132     if (layer->pri == LAYER_HIDDEN)
00133         return;
00134 
00135     /*
00136      * Check if this location is obscured by
00137      * other layers above us.
00138      */
00139     for (l2 = layer->pred; l2->pred; l2 = l2->pred)
00140     {
00141         if (l2->buf[addr])
00142         {
00143             /* DB(kprintf("layer %04x obs %04x at %d\n", l2, layer, addr);) */
00144             return;
00145         }
00146     }
00147 
00148     /* Write character */
00149     if (c)
00150         lcd_putc(addr, c);
00151     else
00152         /* FIXME: should look for layers beneath! */
00153         lcd_putc(addr, ' ');
00154 }
00155 
00156 
00157 void lcd_putChar(char c, Layer *layer)
00158 {
00159     LOCK_LCD;
00160     lcd_putCharUnlocked(c, layer);
00161     UNLOCK_LCD;
00162 }
00163 
00164 void lcd_layerSet(Layer *layer, char c)
00165 {
00166     int i;
00167 
00168     LOCK_LCD;
00169     lcd_setAddr(layer, 0);
00170     for (i = 0; i < LCD_COLS * LCD_ROWS; i++)
00171         lcd_putCharUnlocked(c, layer);
00172     UNLOCK_LCD;
00173 }
00174 
00175 
00176 void lcd_clear(Layer *layer)
00177 {
00178     lcd_layerSet(layer, 0);
00179 }
00180 
00181 
00182 void lcd_clearLine(Layer *layer, int y)
00183 {
00184     int i;
00185 
00186     LOCK_LCD;
00187     lcd_setAddr(layer, LCD_POS(0, y));
00188     for (i = 0; i < LCD_COLS; i++)
00189         lcd_putCharUnlocked(0, layer);
00190     UNLOCK_LCD;
00191 }
00192 
00193 
00194 void lcd_moveCursor(lcdpos_t addr)
00195 {
00196     LOCK_LCD;
00197     lcd_moveTo(addr);
00198     UNLOCK_LCD;
00199 }
00200 
00201 
00202 char lcd_setCursor(char mode)
00203 {
00204     static const char cursor_cmd[3] =
00205     {
00206         LCD_CMD_CURSOR_OFF, LCD_CMD_CURSOR_BLOCK, LCD_CMD_CURSOR_LINE
00207     };
00208     char oldmode = lcd_CursorStatus;
00209 
00210     LOCK_LCD;
00211     lcd_CursorStatus = mode;
00212     lcd_setReg(cursor_cmd[(int)mode]);
00213     if (mode)
00214         lcd_moveCursor(lcd_CursorAddr);
00215     UNLOCK_LCD;
00216 
00217     return oldmode;
00218 }
00219 
00220 
00221 int lcd_vprintf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, va_list ap)
00222 {
00223     int len;
00224 
00225     LOCK_LCD;
00226 
00227     /*
00228      * Se il cursore era acceso, spegnilo durante
00229      * l'output per evitare che salti alla posizione
00230      * in cui si scrive.
00231      */
00232     if (lcd_CursorStatus)
00233         lcd_setReg(LCD_CMD_CURSOR_OFF);
00234 
00235     /* Spostamento del cursore */
00236     lcd_setAddr(layer, addr);
00237 
00238     if (mode & LCD_CENTER)
00239     {
00240         int pad;
00241 
00242         /*
00243          * NOTE: calculating the string lenght BEFORE it gets
00244          * printf()-formatted. Real lenght may differ.
00245          */
00246         pad = (LCD_COLS - strlen(format)) / 2;
00247         while (pad--)
00248             lcd_putCharUnlocked(' ', layer);
00249     }
00250 
00251     len = _formatted_write(format, (void (*)(char, void *))lcd_putCharUnlocked, layer, ap);
00252 
00253     if (mode & (LCD_FILL | LCD_CENTER))
00254         while (layer->addr % LCD_COLS)
00255             lcd_putCharUnlocked(' ', layer);
00256 
00257     /*
00258      * Riaccendi il cursore e riportalo alla
00259      * vecchia posizione
00260      */
00261     if (lcd_CursorStatus)
00262         lcd_setCursor(lcd_CursorStatus);
00263 
00264     UNLOCK_LCD;
00265 
00266     return len;
00267 }
00268 
00269 
00270 int lcd_printf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, ...)
00271 {
00272     int len;
00273     va_list ap;
00274 
00275     va_start(ap, format);
00276     len = lcd_vprintf(layer, addr, mode, format, ap);
00277     va_end(ap);
00278 
00279     return len;
00280 }
00281 
00282 
00289 static void lcd_enqueueLayer(Layer *layer, char pri)
00290 {
00291     Layer *l2;
00292 
00293     /* Remove layer from whatever list it was in before */
00294     REMOVE(layer);
00295 
00296     layer->pri = pri;
00297 
00298     /*
00299      * Search for the first layer whose priority
00300      * is less or equal to the layer we are adding.
00301      */
00302     FOREACH_NODE(l2, &lcd_Layers)
00303         if (l2->pri <= pri)
00304             break;
00305 
00306     /* Enqueue layer */
00307     INSERT_BEFORE(layer, l2);
00308 }
00309 
00310 Layer *lcd_newLayer(char pri)
00311 {
00312     Layer *layer;
00313 
00314     LOCK_LCD;
00315 
00316     if (LIST_EMPTY(&lcd_FreeLayers))
00317     {
00318         UNLOCK_LCD;
00319         //ASSERT(false);
00320         return NULL;
00321     }
00322 
00323     layer = (Layer *)LIST_HEAD(&lcd_FreeLayers);
00324     layer->addr = 0;
00325     memset(layer->buf, 0, LCD_ROWS * LCD_COLS);
00326 
00327     lcd_enqueueLayer(layer, pri);
00328 
00329     UNLOCK_LCD;
00330     return layer;
00331 }
00332 
00338 static void lcd_refresh(void)
00339 {
00340     lcdpos_t addr;
00341     Layer *l;
00342 
00343     for (addr = 0; addr < LCD_ROWS * LCD_COLS; ++addr)
00344     {
00345         FOREACH_NODE(l, &lcd_Layers)
00346         {
00347             //kprintf("%d %x %p\n", addr, l->buf[0], l);
00348             if (l->pri == LAYER_HIDDEN)
00349                 break;
00350 
00351             if (l->buf[addr])
00352             {
00353                 /* Refresh location */
00354                 lcd_putc(addr, l->buf[addr]);
00355                 goto done;
00356             }
00357         }
00358 
00359         /* Draw background */
00360         lcd_putc(addr, ' ');
00361     done:
00362         ;
00363     }
00364 }
00365 
00371 void lcd_setLayerDepth(Layer *layer, char pri)
00372 {
00373     if (pri != layer->pri)
00374     {
00375         LOCK_LCD;
00376         lcd_enqueueLayer(layer, pri);
00377         /* Vile but simple */
00378         lcd_refresh();
00379         UNLOCK_LCD;
00380     }
00381 }
00382 
00383 void lcd_deleteLayer(Layer *layer)
00384 {
00385     LOCK_LCD;
00386 
00387 /* We use lcd_refresh() instead.  Much simpler than this mess, but slower. */
00388 #if 0
00389     Layer *l2;
00390     lcdpos_t addr;
00391 
00392     /* Repair damage on underlaying layers */
00393     for (addr = 0; addr < LCD_ROWS * LCD_COLS; ++addr)
00394     {
00395         /* If location was covered by us */
00396         if (layer->buf[addr])
00397         {
00398             /* ...and it wasn't covered by others above us... */
00399             for (l2 = layer->pred; l2->pred; l2 = l2->pred)
00400                 if (l2->buf[addr])
00401                     /* can't just break here! */
00402                     goto not_visible;
00403 
00404             /* ...scan underlaying layers to repair damage */
00405             for (l2 = layer->succ; l2->succ; l2 = l2->succ)
00406                 if (l2->buf[addr])
00407                 {
00408                     /* Refresh character */
00409                     lcd_putc(addr, l2->buf[addr]);
00410 
00411                     /* No need to search on deeper layers */
00412                     break;
00413                 }
00414 
00415             not_visible:
00416                 ;
00417         }
00418     }
00419 #endif
00420 
00421     // Remove layer from lcd_Layers list.
00422     REMOVE(layer);
00423 
00424     /* Put layer back into free list */
00425     ADDHEAD(&lcd_FreeLayers, layer);
00426 
00427     lcd_refresh();
00428 
00429     UNLOCK_LCD;
00430 }
00431 
00432 
00433 static void lcd_setDefLayer(Layer *layer)
00434 {
00435     lcd_DefLayer = layer;
00436 }
00437 
00438 #include <cfg/debug.h>
00439 void lcd_init(void)
00440 {
00441     int i;
00442 
00443     LIST_INIT(&lcd_Layers);
00444     LIST_INIT(&lcd_FreeLayers);
00445     for (i = 0; i < LCD_LAYERS; ++i)
00446         ADDHEAD(&lcd_FreeLayers, &lcd_LayersPool[i]);
00447 
00448     lcd_setDefLayer(lcd_newLayer(0));
00449 
00450     lcd_hw_init();
00451 
00452     lcd_setCursor(0);
00453 }
00454 
00455