lcd_32122a_avr.c

Go to the documentation of this file.
00001 
00043 #include "lcd_32122a_avr.h"
00044 
00045 #include "cfg/cfg_lcd.h"
00046 
00047 #include <cfg/macros.h> /* BV() */
00048 #include <cfg/debug.h>
00049 #include <cfg/module.h>
00050 
00051 #include <gfx/gfx.h>
00052 #include <drv/timer.h>
00053 
00054 #include <cpu/irq.h>
00055 #include <cpu/types.h>
00056 
00057 #include <avr/io.h>
00058 
00059 #include <stdbool.h>
00060 #include <inttypes.h>
00061 
00062 #warning TODO:Refactor this module. Split code to hw file.
00063 
00064 /* Configuration sanity checks */
00065 #if !defined(CONFIG_LCD_SOFTINT_REFRESH) || (CONFIG_LCD_SOFTINT_REFRESH != 0 && CONFIG_LCD_SOFTINT_REFRESH != 1)
00066     #error CONFIG_LCD_SOFTINT_REFRESH must be defined to either 0 or 1
00067 #endif
00068 #if !defined(CONFIG_LCD_SOFTINT_REFRESH) || (CONFIG_LCD_SOFTINT_REFRESH != 0 && CONFIG_LCD_SOFTINT_REFRESH != 1)
00069     #error CONFIG_LCD_SOFTINT_REFRESH must be defined to either 0 or 1
00070 #endif
00071 
00072 
00073 #if CONFIG_LCD_SOFTINT_REFRESH
00074 
00076 #   define LCD_REFRESH_INTERVAL 20  /* 20ms -> 50fps */
00077 
00078 #endif /* CONFIG_LCD_SOFTINT_REFRESH */
00079 
00081 #define LCD_PAGES 4
00082 
00084 #define LCD_PAGESIZE (LCD_WIDTH / 2)
00085 
00090 #define LCD_PF_DB0   PF4
00091 #define LCD_PF_DB1   PF5
00092 #define LCD_PF_DB2   PF6
00093 #define LCD_PF_DB3   PF7
00094 #define LCD_PD_DB4   PD4
00095 #define LCD_PD_DB5   PD5
00096 #define LCD_PD_DB6   PD6
00097 #define LCD_PD_DB7   PD7
00098 #define LCD_PB_A0    PB0
00099 #define LCD_PE_RW    PE7
00100 #define LCD_PE_E1    PE2
00101 #define LCD_PE_E2    PE6
00102 
00108 #define LCD_DATA_HI_PORT    PORTD
00109 #define LCD_DATA_HI_PIN     PIND
00110 #define LCD_DATA_HI_DDR     DDRD
00111 #define LCD_DATA_HI_SHIFT   0
00112 #define LCD_DATA_HI_MASK    0xF0
00113 
00119 #define LCD_DATA_LO_PORT    PORTF
00120 #define LCD_DATA_LO_PIN     PINF
00121 #define LCD_DATA_LO_DDR     DDRF
00122 #define LCD_DATA_LO_SHIFT   4
00123 #define LCD_DATA_LO_MASK    0xF0
00124 
00130 #define LCD_CLR_A0   (PORTB &= ~BV(LCD_PB_A0))
00131 #define LCD_SET_A0   (PORTB |=  BV(LCD_PB_A0))
00132 #define LCD_CLR_RD   (PORTE &= ~BV(LCD_PE_RW))
00133 #define LCD_SET_RD   (PORTE |=  BV(LCD_PE_RW))
00134 #define LCD_CLR_E1   (PORTE &= ~BV(LCD_PE_E1))
00135 #define LCD_SET_E1   (PORTE |=  BV(LCD_PE_E1))
00136 #define LCD_CLR_E2   (PORTE &= ~BV(LCD_PE_E2))
00137 #define LCD_SET_E2   (PORTE |=  BV(LCD_PE_E2))
00138 #define LCD_SET_E(x) (PORTE |= (x))
00139 #define LCD_CLR_E(x) (PORTE &= ~(x))
00140 
00146 #define LCDF_E1 (BV(LCD_PE_E1))
00147 #define LCDF_E2 (BV(LCD_PE_E2))
00148 
00151 #define LCD_READ ( \
00152         ((LCD_DATA_LO_PIN & LCD_DATA_LO_MASK) >> LCD_DATA_LO_SHIFT) | \
00153         ((LCD_DATA_HI_PIN & LCD_DATA_HI_MASK) >> LCD_DATA_HI_SHIFT) \
00154     )
00155 
00157 #define LCD_WRITE(d) \
00158     do { \
00159         LCD_DATA_LO_PORT = (LCD_DATA_LO_PORT & ~LCD_DATA_LO_MASK) | (((d)<<LCD_DATA_LO_SHIFT) & LCD_DATA_LO_MASK); \
00160         LCD_DATA_HI_PORT = (LCD_DATA_HI_PORT & ~LCD_DATA_HI_MASK) | (((d)<<LCD_DATA_HI_SHIFT) & LCD_DATA_HI_MASK); \
00161     } while (0)
00162 
00164 #define LCD_DB_OUT \
00165     do { \
00166         LCD_DATA_LO_DDR |= LCD_DATA_LO_MASK; \
00167         LCD_DATA_HI_DDR |= LCD_DATA_HI_MASK; \
00168     } while (0)
00169 
00171 #define LCD_DB_IN \
00172     do { \
00173         LCD_DATA_LO_DDR &= ~LCD_DATA_LO_MASK; \
00174         LCD_DATA_HI_DDR &= ~LCD_DATA_HI_MASK; \
00175     } while (0)
00176 
00178 #define LCD_DELAY_WRITE \
00179     do { \
00180         NOP; \
00181         NOP; \
00182     } while (0)
00183 
00185 #define LCD_DELAY_READ \
00186     do { \
00187         NOP; \
00188         NOP; \
00189         NOP; \
00190     } while (0)
00191 
00192 
00197 #define LCD_CMD_DISPLAY_ON  0xAF
00198 #define LCD_CMD_DISPLAY_OFF 0xAE
00199 #define LCD_CMD_STARTLINE   0xC0
00200 #define LCD_CMD_PAGEADDR    0xB8
00201 #define LCD_CMD_COLADDR     0x00
00202 #define LCD_CMD_ADC_LEFT    0xA1
00203 #define LCD_CMD_ADC_RIGHT   0xA0
00204 #define LCD_CMD_STATIC_OFF  0xA4
00205 #define LCD_CMD_STATIC_ON   0xA5
00206 #define LCD_CMD_DUTY_32     0xA9
00207 #define LCD_CMD_DUTY_16     0xA8
00208 #define LCD_CMD_RMW_ON      0xE0
00209 #define LCD_CMD_RMW_OFF     0xEE
00210 #define LCD_CMD_RESET       0xE2
00211 
00213 MOD_DEFINE(lcd)
00214 
00215 
00216 /* Status flags */
00217 #define LCDF_BUSY BV(7)
00218 
00219 #if CONFIG_LCD_WAIT
00220 
00233 #define WAIT_LCD \
00234     do { \
00235         uint8_t status; \
00236         LCD_DB_IN; \
00237         do { \
00238             LCD_SET_RD; \
00239             LCD_CLR_A0; \
00240             LCD_SET_E1; \
00241             LCD_DELAY_READ; \
00242             status = LCD_READ; \
00243             LCD_CLR_E1; \
00244             LCD_SET_A0; \
00245             LCD_CLR_RD; \
00246         } while (status & LCDF_BUSY); \
00247         LCD_DB_OUT; \
00248     } while (0)
00249 
00250 #else /* CONFIG_LCD_WAIT */
00251 
00252 #define WAIT_LCD do {} while(0)
00253 
00254 #endif /* CONFIG_LCD_WAIT */
00255 
00256 
00263 DECLARE_WALL(wall_before_raster, WALL_SIZE)
00264 static uint8_t lcd_raster[RAST_SIZE(LCD_WIDTH, LCD_HEIGHT)];
00265 DECLARE_WALL(wall_after_raster, WALL_SIZE)
00266 
00268 struct Bitmap lcd_bitmap;
00269 
00270 
00271 #if CONFIG_LCD_SOFTINT_REFRESH
00272 
00274 static Timer *lcd_refresh_timer;
00275 
00276 #endif /* CONFIG_LCD_SOFTINT_REFRESH */
00277 
00278 
00279 /*
00280 static bool lcd_check(void)
00281 {
00282     uint8_t status;
00283     uint16_t retries = 32768;
00284     PORTA = 0xFF;
00285     DDRA = 0x00;
00286     do {
00287         cbi(PORTC, PCB_LCD_RS);
00288         sbi(PORTC, PCB_LCD_RW);
00289         sbi(PORTC, PCB_LCD_E);
00290         --retries;
00291         NOP;
00292         status = PINA;
00293         cbi(PORTC, PCB_LCD_E);
00294         cbi(PORTC, PCB_LCD_RW);
00295     } while ((status & LCDF_BUSY) && retries);
00296 
00297     return (retries != 0);
00298 }
00299 */
00300 
00301 
00302 static inline void lcd_32122_cmd(uint8_t cmd, uint8_t chip)
00303 {
00304     WAIT_LCD;
00305 
00306     /*      __              __
00307      * A0   __\____________/__
00308      *
00309      * R/W  __________________
00310      *            ______
00311      * E1   _____/      \_____
00312      *
00313      * DATA --<============>--
00314      */
00315     LCD_WRITE(cmd);
00316     //LCD_DB_OUT;
00317     LCD_CLR_A0;
00318     LCD_SET_E(chip);
00319     LCD_DELAY_WRITE;
00320     LCD_CLR_E(chip);
00321     LCD_SET_A0;
00322     //LCD_DB_IN;
00323 }
00324 
00325 
00326 static inline uint8_t lcd_32122_read(uint8_t chip)
00327 {
00328     uint8_t data;
00329 
00330     WAIT_LCD;
00331 
00345     LCD_DB_IN;
00346     //LCD_SET_A0;
00347     LCD_SET_RD;
00348     LCD_SET_E(chip);
00349     LCD_DELAY_READ;
00350     data = LCD_READ;
00351     LCD_CLR_E(chip);
00352     LCD_CLR_RD;
00353     //LCD_CLR_A0;
00354     LCD_DB_OUT;
00355 
00356     return data;
00357 }
00358 
00359 static inline void lcd_32122_write(uint8_t c, uint8_t chip)
00360 {
00361     WAIT_LCD;
00362 
00376     LCD_WRITE(c);
00377     //LCD_DB_OUT;
00378     //LCD_SET_A0;
00379     LCD_SET_E(chip);
00380     LCD_DELAY_WRITE;
00381     LCD_CLR_E(chip);
00382     //LCD_CLR_A0;
00383     //LCD_DB_IN;
00384 }
00385 
00386 static void lcd_32122_clear(void)
00387 {
00388     uint8_t page, j;
00389 
00390     for (page = 0; page < LCD_PAGES; ++page)
00391     {
00392         lcd_32122_cmd(LCD_CMD_COLADDR | 0, LCDF_E1 | LCDF_E2);
00393         lcd_32122_cmd(LCD_CMD_PAGEADDR | page, LCDF_E1 | LCDF_E2);
00394         for (j = 0; j < LCD_PAGESIZE; j++)
00395             lcd_32122_write(0, LCDF_E1 | LCDF_E2);
00396     }
00397 }
00398 
00399 
00400 static void lcd_32122_writeRaster(const uint8_t *raster)
00401 {
00402     uint8_t page, rows;
00403     const uint8_t *right_raster;
00404 
00405     CHECK_WALL(wall_before_raster);
00406     CHECK_WALL(wall_after_raster);
00407 
00408     for (page = 0; page < LCD_PAGES; ++page)
00409     {
00410         lcd_32122_cmd(LCD_CMD_PAGEADDR | page, LCDF_E1 | LCDF_E2);
00411         lcd_32122_cmd(LCD_CMD_COLADDR | 0, LCDF_E1 | LCDF_E2);
00412 
00413         /* Super optimized lamer loop */
00414         right_raster = raster + LCD_PAGESIZE;
00415         rows = LCD_PAGESIZE;
00416         do
00417         {
00418             lcd_32122_write(*raster++, LCDF_E1);
00419             lcd_32122_write(*right_raster++, LCDF_E2);
00420         }
00421         while (--rows);
00422         raster = right_raster;
00423     }
00424 }
00425 
00426 #if CONFIG_LCD_SOFTINT_REFRESH
00427 
00428 static void lcd_32122_refreshSoftint(void)
00429 {
00430     lcd_blit_bitmap(&lcd_bitmap);
00431     timer_add(lcd_refresh_timer);
00432 }
00433 
00434 #endif /* CONFIG_LCD_SOFTINT_REFRESH */
00435 
00439 void lcd_32122_setPwm(int duty)
00440 {
00441     ASSERT(duty >= LCD_MIN_PWM);
00442     ASSERT(duty <= LCD_MAX_PWM);
00443 
00444     OCR3C = duty;
00445 }
00446 
00450 void lcd_32122_blitBitmap(Bitmap *bm)
00451 {
00452     MOD_CHECK(lcd);
00453     lcd_32122_writeRaster(bm->raster);
00454 }
00455 
00456 
00463 void lcd_32122_init(void)
00464 {
00465     MOD_CHECK(timer);
00466 
00467     // FIXME: interrupts are already disabled when we get here?!?
00468     cpu_flags_t flags;
00469     IRQ_SAVE_DISABLE(flags);
00470 
00471     PORTB |= BV(LCD_PB_A0);
00472     DDRB |= BV(LCD_PB_A0);
00473 
00474     PORTE &= ~(BV(LCD_PE_RW) | BV(LCD_PE_E1) | BV(LCD_PE_E2));
00475     DDRE |= BV(LCD_PE_RW) | BV(LCD_PE_E1) | BV(LCD_PE_E2);
00476 
00477 /* LCD hw reset
00478     LCD_RESET_PORT |= BV(LCD_RESET_BIT);
00479     LCD_RESET_DDR |= BV(LCD_RESET_BIT);
00480     LCD_DELAY_WRITE;
00481     LCD_DELAY_WRITE;
00482     LCD_RESET_PORT &= ~BV(LCD_RESET_BIT);
00483     LCD_DELAY_WRITE;
00484     LCD_DELAY_WRITE;
00485     LCD_RESET_PORT |= BV(LCD_RESET_BIT);
00486 */
00487     /*
00488      * Data bus is in output state most of the time:
00489      * LCD r/w functions assume it is left in output state
00490      */
00491     LCD_DB_OUT;
00492 
00493     // Wait for RST line to stabilize at Vcc.
00494     IRQ_ENABLE;
00495     timer_delay(20);
00496     IRQ_SAVE_DISABLE(flags);
00497 
00498     lcd_32122_cmd(LCD_CMD_RESET, LCDF_E1 | LCDF_E2);
00499     lcd_32122_cmd(LCD_CMD_DISPLAY_ON, LCDF_E1 | LCDF_E2);
00500     lcd_32122_cmd(LCD_CMD_STARTLINE | 0, LCDF_E1 | LCDF_E2);
00501 
00502     /* Initialize anti-corruption walls for raster */
00503     INIT_WALL(wall_before_raster);
00504     INIT_WALL(wall_after_raster);
00505 
00506     IRQ_RESTORE(flags);
00507 
00508     lcd_32122_clear();
00509     lcd_32122_setPwm(LCD_DEF_PWM);
00510 
00511     gfx_bitmapInit(&lcd_bitmap, lcd_raster, LCD_WIDTH, LCD_HEIGHT);
00512     gfx_bitmapClear(&lcd_bitmap);
00513 
00514 #if CONFIG_LCD_SOFTINT_REFRESH
00515 
00516     /* Init IRQ driven LCD refresh */
00517     lcd_refresh_timer = timer_new();
00518     ASSERT(lcd_refresh_timer != NULL);
00519     INITEVENT_INT(&lcd_refresh_timer->expire, (Hook)lcd_refresh_softint, 0);
00520     lcd_refresh_timer->delay = LCD_REFRESH_INTERVAL;
00521     timer_add(lcd_refresh_timer);
00522 
00523 #endif /* CONFIG_LCD_SOFTINT_REFRESH */
00524 
00525     MOD_INIT(lcd);
00526 }
00527