lcd_32122a_avr.c

Go to the documentation of this file.
00001 
00042 #include "lcd_32122a_avr.h"
00043 #include <gfx/gfx.h>
00044 #include <drv/timer.h>
00045 
00046 #include <cpu/irq.h>
00047 #include <cpu/types.h>
00048 #include <hw.h>
00049 #include <cfg/macros.h> /* BV() */
00050 #include <cfg/debug.h>
00051 
00052 #include <avr/io.h>
00053 #include <stdbool.h>
00054 #include <inttypes.h>
00055 
00056 /* Configuration sanity checks */
00057 #if !defined(CONFIG_LCD_SOFTINT_REFRESH) || (CONFIG_LCD_SOFTINT_REFRESH != 0 && CONFIG_LCD_SOFTINT_REFRESH != 1)
00058     #error CONFIG_LCD_SOFTINT_REFRESH must be defined to either 0 or 1
00059 #endif
00060 #if !defined(CONFIG_LCD_SOFTINT_REFRESH) || (CONFIG_LCD_SOFTINT_REFRESH != 0 && CONFIG_LCD_SOFTINT_REFRESH != 1)
00061     #error CONFIG_LCD_SOFTINT_REFRESH must be defined to either 0 or 1
00062 #endif
00063 
00064 
00065 #if CONFIG_LCD_SOFTINT_REFRESH
00066 
00068 #   define LCD_REFRESH_INTERVAL 20  /* 20ms -> 50fps */
00069 
00070 #endif /* CONFIG_LCD_SOFTINT_REFRESH */
00071 
00073 #define LCD_PAGES 4
00074 
00076 #define LCD_PAGESIZE (LCD_WIDTH / 2)
00077 
00082 #define LCD_PF_DB0   PF4
00083 #define LCD_PF_DB1   PF5
00084 #define LCD_PF_DB2   PF6
00085 #define LCD_PF_DB3   PF7
00086 #define LCD_PD_DB4   PD4
00087 #define LCD_PD_DB5   PD5
00088 #define LCD_PD_DB6   PD6
00089 #define LCD_PD_DB7   PD7
00090 #define LCD_PB_A0    PB0
00091 #define LCD_PE_RW    PE7
00092 #define LCD_PE_E1    PE2
00093 #define LCD_PE_E2    PE6
00094 
00100 #define LCD_DATA_HI_PORT    PORTD
00101 #define LCD_DATA_HI_PIN     PIND
00102 #define LCD_DATA_HI_DDR     DDRD
00103 #define LCD_DATA_HI_SHIFT   0
00104 #define LCD_DATA_HI_MASK    0xF0
00105 
00111 #define LCD_DATA_LO_PORT    PORTF
00112 #define LCD_DATA_LO_PIN     PINF
00113 #define LCD_DATA_LO_DDR     DDRF
00114 #define LCD_DATA_LO_SHIFT   4
00115 #define LCD_DATA_LO_MASK    0xF0
00116 
00122 #define LCD_CLR_A0   (PORTB &= ~BV(LCD_PB_A0))
00123 #define LCD_SET_A0   (PORTB |=  BV(LCD_PB_A0))
00124 #define LCD_CLR_RD   (PORTE &= ~BV(LCD_PE_RW))
00125 #define LCD_SET_RD   (PORTE |=  BV(LCD_PE_RW))
00126 #define LCD_CLR_E1   (PORTE &= ~BV(LCD_PE_E1))
00127 #define LCD_SET_E1   (PORTE |=  BV(LCD_PE_E1))
00128 #define LCD_CLR_E2   (PORTE &= ~BV(LCD_PE_E2))
00129 #define LCD_SET_E2   (PORTE |=  BV(LCD_PE_E2))
00130 #define LCD_SET_E(x) (PORTE |= (x))
00131 #define LCD_CLR_E(x) (PORTE &= ~(x))
00132 
00138 #define LCDF_E1 (BV(LCD_PE_E1))
00139 #define LCDF_E2 (BV(LCD_PE_E2))
00140 
00143 #define LCD_READ ( \
00144         ((LCD_DATA_LO_PIN & LCD_DATA_LO_MASK) >> LCD_DATA_LO_SHIFT) | \
00145         ((LCD_DATA_HI_PIN & LCD_DATA_HI_MASK) >> LCD_DATA_HI_SHIFT) \
00146     )
00147 
00149 #define LCD_WRITE(d) \
00150     do { \
00151         LCD_DATA_LO_PORT = (LCD_DATA_LO_PORT & ~LCD_DATA_LO_MASK) | (((d)<<LCD_DATA_LO_SHIFT) & LCD_DATA_LO_MASK); \
00152         LCD_DATA_HI_PORT = (LCD_DATA_HI_PORT & ~LCD_DATA_HI_MASK) | (((d)<<LCD_DATA_HI_SHIFT) & LCD_DATA_HI_MASK); \
00153     } while (0)
00154 
00156 #define LCD_DB_OUT \
00157     do { \
00158         LCD_DATA_LO_DDR |= LCD_DATA_LO_MASK; \
00159         LCD_DATA_HI_DDR |= LCD_DATA_HI_MASK; \
00160     } while (0)
00161 
00163 #define LCD_DB_IN \
00164     do { \
00165         LCD_DATA_LO_DDR &= ~LCD_DATA_LO_MASK; \
00166         LCD_DATA_HI_DDR &= ~LCD_DATA_HI_MASK; \
00167     } while (0)
00168 
00170 #define LCD_DELAY_WRITE \
00171     do { \
00172         NOP; \
00173         NOP; \
00174     } while (0)
00175 
00177 #define LCD_DELAY_READ \
00178     do { \
00179         NOP; \
00180         NOP; \
00181         NOP; \
00182     } while (0)
00183 
00184 
00189 #define LCD_CMD_DISPLAY_ON  0xAF
00190 #define LCD_CMD_DISPLAY_OFF 0xAE
00191 #define LCD_CMD_STARTLINE   0xC0
00192 #define LCD_CMD_PAGEADDR    0xB8
00193 #define LCD_CMD_COLADDR     0x00
00194 #define LCD_CMD_ADC_LEFT    0xA1
00195 #define LCD_CMD_ADC_RIGHT   0xA0
00196 #define LCD_CMD_STATIC_OFF  0xA4
00197 #define LCD_CMD_STATIC_ON   0xA5
00198 #define LCD_CMD_DUTY_32     0xA9
00199 #define LCD_CMD_DUTY_16     0xA8
00200 #define LCD_CMD_RMW_ON      0xE0
00201 #define LCD_CMD_RMW_OFF     0xEE
00202 #define LCD_CMD_RESET       0xE2
00203 
00205 MOD_DEFINE(lcd)
00206 
00207 
00208 /* Status flags */
00209 #define LCDF_BUSY BV(7)
00210 
00211 #if CONFIG_LCD_WAIT
00212 
00225 #define WAIT_LCD \
00226     do { \
00227         uint8_t status; \
00228         LCD_DB_IN; \
00229         do { \
00230             LCD_SET_RD; \
00231             LCD_CLR_A0; \
00232             LCD_SET_E1; \
00233             LCD_DELAY_READ; \
00234             status = LCD_READ; \
00235             LCD_CLR_E1; \
00236             LCD_SET_A0; \
00237             LCD_CLR_RD; \
00238         } while (status & LCDF_BUSY); \
00239         LCD_DB_OUT; \
00240     } while (0)
00241 
00242 #else /* CONFIG_LCD_WAIT */
00243 
00244 #define WAIT_LCD do {} while(0)
00245 
00246 #endif /* CONFIG_LCD_WAIT */
00247 
00248 
00255 DECLARE_WALL(wall_before_raster, WALL_SIZE)
00256 static uint8_t lcd_raster[RAST_SIZE(LCD_WIDTH, LCD_HEIGHT)];
00257 DECLARE_WALL(wall_after_raster, WALL_SIZE)
00258 
00260 struct Bitmap lcd_bitmap;
00261 
00262 
00263 #if CONFIG_LCD_SOFTINT_REFRESH
00264 
00266 static Timer *lcd_refresh_timer;
00267 
00268 #endif /* CONFIG_LCD_SOFTINT_REFRESH */
00269 
00270 
00271 /*
00272 static bool lcd_check(void)
00273 {
00274     uint8_t status;
00275     uint16_t retries = 32768;
00276     PORTA = 0xFF;
00277     DDRA = 0x00;
00278     do {
00279         cbi(PORTC, PCB_LCD_RS);
00280         sbi(PORTC, PCB_LCD_RW);
00281         sbi(PORTC, PCB_LCD_E);
00282         --retries;
00283         NOP;
00284         status = PINA;
00285         cbi(PORTC, PCB_LCD_E);
00286         cbi(PORTC, PCB_LCD_RW);
00287     } while ((status & LCDF_BUSY) && retries);
00288 
00289     return (retries != 0);
00290 }
00291 */
00292 
00293 
00294 static inline void lcd_cmd(uint8_t cmd, uint8_t chip)
00295 {
00296     WAIT_LCD;
00297 
00298     /*      __              __
00299      * A0   __\____________/__
00300      *
00301      * R/W  __________________
00302      *            ______
00303      * E1   _____/      \_____
00304      *
00305      * DATA --<============>--
00306      */
00307     LCD_WRITE(cmd);
00308     //LCD_DB_OUT;
00309     LCD_CLR_A0;
00310     LCD_SET_E(chip);
00311     LCD_DELAY_WRITE;
00312     LCD_CLR_E(chip);
00313     LCD_SET_A0;
00314     //LCD_DB_IN;
00315 }
00316 
00317 
00318 static inline uint8_t lcd_read(uint8_t chip)
00319 {
00320     uint8_t data;
00321 
00322     WAIT_LCD;
00323 
00337     LCD_DB_IN;
00338     //LCD_SET_A0;
00339     LCD_SET_RD;
00340     LCD_SET_E(chip);
00341     LCD_DELAY_READ;
00342     data = LCD_READ;
00343     LCD_CLR_E(chip);
00344     LCD_CLR_RD;
00345     //LCD_CLR_A0;
00346     LCD_DB_OUT;
00347 
00348     return data;
00349 }
00350 
00351 
00352 static inline void lcd_write(uint8_t c, uint8_t chip)
00353 {
00354     WAIT_LCD;
00355 
00369     LCD_WRITE(c);
00370     //LCD_DB_OUT;
00371     //LCD_SET_A0;
00372     LCD_SET_E(chip);
00373     LCD_DELAY_WRITE;
00374     LCD_CLR_E(chip);
00375     //LCD_CLR_A0;
00376     //LCD_DB_IN;
00377 }
00378 
00379 
00383 void lcd_setPwm(int duty)
00384 {
00385     ASSERT(duty >= LCD_MIN_PWM);
00386     ASSERT(duty <= LCD_MAX_PWM);
00387 
00388     OCR3C = duty;
00389 }
00390 
00391 
00392 static void lcd_clear(void)
00393 {
00394     uint8_t page, j;
00395 
00396     for (page = 0; page < LCD_PAGES; ++page)
00397     {
00398         lcd_cmd(LCD_CMD_COLADDR | 0, LCDF_E1 | LCDF_E2);
00399         lcd_cmd(LCD_CMD_PAGEADDR | page, LCDF_E1 | LCDF_E2);
00400         for (j = 0; j < LCD_PAGESIZE; j++)
00401             lcd_write(0, LCDF_E1 | LCDF_E2);
00402     }
00403 }
00404 
00405 
00406 static void lcd_writeRaster(const uint8_t *raster)
00407 {
00408     uint8_t page, rows;
00409     const uint8_t *right_raster;
00410 
00411     CHECK_WALL(wall_before_raster);
00412     CHECK_WALL(wall_after_raster);
00413 
00414     for (page = 0; page < LCD_PAGES; ++page)
00415     {
00416         lcd_cmd(LCD_CMD_PAGEADDR | page, LCDF_E1 | LCDF_E2);
00417         lcd_cmd(LCD_CMD_COLADDR | 0, LCDF_E1 | LCDF_E2);
00418 
00419         /* Super optimized lamer loop */
00420         right_raster = raster + LCD_PAGESIZE;
00421         rows = LCD_PAGESIZE;
00422         do
00423         {
00424             lcd_write(*raster++, LCDF_E1);
00425             lcd_write(*right_raster++, LCDF_E2);
00426         }
00427         while (--rows);
00428         raster = right_raster;
00429     }
00430 }
00431 
00435 void lcd_blitBitmap(Bitmap *bm)
00436 {
00437     MOD_CHECK(lcd);
00438     lcd_writeRaster(bm->raster);
00439 }
00440 
00441 
00442 #if CONFIG_LCD_SOFTINT_REFRESH
00443 
00444 static void lcd_refreshSoftint(void)
00445 {
00446     lcd_blit_bitmap(&lcd_bitmap);
00447     timer_add(lcd_refresh_timer);
00448 }
00449 
00450 #endif /* CONFIG_LCD_SOFTINT_REFRESH */
00451 
00452 
00459 void lcd_init(void)
00460 {
00461     MOD_CHECK(timer);
00462 
00463     // FIXME: interrupts are already disabled when we get here?!?
00464     cpuflags_t flags;
00465     IRQ_SAVE_DISABLE(flags);
00466 
00467     PORTB |= BV(LCD_PB_A0);
00468     DDRB |= BV(LCD_PB_A0);
00469 
00470     PORTE &= ~(BV(LCD_PE_RW) | BV(LCD_PE_E1) | BV(LCD_PE_E2));
00471     DDRE |= BV(LCD_PE_RW) | BV(LCD_PE_E1) | BV(LCD_PE_E2);
00472 
00473 /* LCD hw reset
00474     LCD_RESET_PORT |= BV(LCD_RESET_BIT);
00475     LCD_RESET_DDR |= BV(LCD_RESET_BIT);
00476     LCD_DELAY_WRITE;
00477     LCD_DELAY_WRITE;
00478     LCD_RESET_PORT &= ~BV(LCD_RESET_BIT);
00479     LCD_DELAY_WRITE;
00480     LCD_DELAY_WRITE;
00481     LCD_RESET_PORT |= BV(LCD_RESET_BIT);
00482 */
00483     /*
00484      * Data bus is in output state most of the time:
00485      * LCD r/w functions assume it is left in output state
00486      */
00487     LCD_DB_OUT;
00488 
00489     // Wait for RST line to stabilize at Vcc.
00490     IRQ_ENABLE;
00491     timer_delay(20);
00492     IRQ_SAVE_DISABLE(flags);
00493 
00494     lcd_cmd(LCD_CMD_RESET, LCDF_E1 | LCDF_E2);
00495     lcd_cmd(LCD_CMD_DISPLAY_ON, LCDF_E1 | LCDF_E2);
00496     lcd_cmd(LCD_CMD_STARTLINE | 0, LCDF_E1 | LCDF_E2);
00497 
00498     /* Initialize anti-corruption walls for raster */
00499     INIT_WALL(wall_before_raster);
00500     INIT_WALL(wall_after_raster);
00501 
00502     IRQ_RESTORE(flags);
00503 
00504     lcd_clear();
00505     lcd_setpwm(LCD_DEF_PWM);
00506 
00507     gfx_bitmapInit(&lcd_bitmap, lcd_raster, LCD_WIDTH, LCD_HEIGHT);
00508     gfx_bitmapClear(&lcd_bitmap);
00509 
00510 #if CONFIG_LCD_SOFTINT_REFRESH
00511 
00512     /* Init IRQ driven LCD refresh */
00513     lcd_refresh_timer = timer_new();
00514     ASSERT(lcd_refresh_timer != NULL);
00515     INITEVENT_INT(&lcd_refresh_timer->expire, (Hook)lcd_refresh_softint, 0);
00516     lcd_refresh_timer->delay = LCD_REFRESH_INTERVAL;
00517     timer_add(lcd_refresh_timer);
00518 
00519 #endif /* CONFIG_LCD_SOFTINT_REFRESH */
00520 
00521     MOD_INIT(lcd);
00522 }