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