#include #include #include "mw_main.h" #include "mw_lcd.h" #include "mw_uart.h" // SMCLK = 16MHz -> divide by 16 to get 1 MHz SPI clock, // 1MHz maximum according to LCD spec //#define SPI_PRESCALE_L 0x10 // currently we only run @1MHz #define SPI_PRESCALE_L 0x10 #define SPI_PRESCALE_H 0x00 #define LCD_STATIC_CMD 0x00 #define LCD_WRITE_CMD 0x01 #define LCD_CLEAR_CMD 0x04 static const unsigned char LCD_CLEAR_COMMAND[] = {LCD_CLEAR_CMD, 0x00}; #define LCD_CLEAR_CMD_SIZE 0x02 static const unsigned char LCD_STATIC_COMMAND[] = {LCD_STATIC_CMD, 0x00}; #define LCD_STATIC_CMD_SIZE 0x02 /* the LCD frame buffer, 96 lines */ tLcdLine lcd_buf[96]; #define LCD_DMA /* errata - DMA variables cannot be function scope */ #ifdef LCD_DMA static unsigned char LcdDmaBusy = 0; #endif void memfill(void *target, unsigned char val, unsigned int count) { while (count--) { *(unsigned char *)(target+count) = val; } } void mw_lcd_init(void) { int i; /* basic I/O setup */ ENABLE_LCD_POWER(); CONFIG_LCD_PINS(); // DISABLE_LCD_ENABLE(); ENABLE_LCD_ENABLE(); /* Put state machine in reset while it is configured */ LCD_SPI_UCBxCTL1 |= UCSWRST; /* * 3-pin, 8-bit SPI master, Clock polarity low * Clock phase set, least sig bit first * SMCLK is the clock source * set the clock prescaler */ LCD_SPI_UCBxCTL0 |= UCMST+ UCCKPH + UCSYNC; LCD_SPI_UCBxCTL1 |= UCSSEL_2; LCD_SPI_UCBxBR0 = SPI_PRESCALE_L; LCD_SPI_UCBxBR1 = SPI_PRESCALE_H; /* remove reset */ LCD_SPI_UCBxCTL1 &= ~UCSWRST; /* pre-fill the frame-buffer */ for (i=0; i<96; i++) { lcd_buf[i].Row = i+1; memfill(lcd_buf[i].Data, 0xff, 12); // lcd_buf[i].Data[0] = i; lcd_buf[i].Dummy = 0x00; }; } static void mw_lcd_write_line(const void *pData, unsigned char Size) { #ifndef xLCD_DMA unsigned char Index; #endif LCD_CS_ASSERT(); #ifdef xLCD_DMA LcdDmaBusy = 1; /* USCIB0 TXIFG is the DMA trigger * DMACTL1 controls dma2 and [dma3] */ DMACTL1 = DMA2TSEL_19; DMA2SA = (unsigned int) pData; DMA2DA = (unsigned int) &LCD_SPI_UCBxTXBUF; DMA2SZ = (unsigned int)Size; /* * single transfer, increment source address, source byte and dest byte, * level sensitive, enable interrupt, clear interrupt flag */ DMA2CTL = DMADT_0 + DMASRCINCR_3 + DMASBDB + DMALEVEL + DMAIE; /* start the transfer */ DMA2CTL |= DMAEN; while (LcdDmaBusy) nop(); #else // debug_uart_tx("+wl1"); for ( Index = 0; Index < Size; Index++ ) { LCD_SPI_UCBxTXBUF = ((unsigned char *)pData)[Index]; // debug_uart_tx("."); while (!(LCD_SPI_UCBxIFG & UCTXIFG)) { // debug_uart_tx("+"); nop(); } } // debug_uart_tx("\n+wl2\n"); #endif /* wait for shift to complete ( ~3 us ) */ while (LCD_SPI_UCBxSTAT & 0x01) { nop(); // debug_uart_tx("."); }; /* now the chip select can be deasserted */ LCD_CS_DEASSERT(); // debug_uart_tx("\n-wl\n"); } void mw_lcd_static_mode(void) { mw_lcd_write_line(LCD_STATIC_COMMAND, LCD_STATIC_CMD_SIZE); } void mw_lcd_clear(void) { unsigned char i; mw_lcd_write_line(LCD_CLEAR_COMMAND, LCD_CLEAR_CMD_SIZE); /* pre-fill the frame-buffer */ for (i=0; i<96; i++) { memfill(lcd_buf[i].Data, 0xff, 12); }; } void mw_lcd_clear_fb(void) { unsigned char i; /* pre-fill the frame-buffer */ for (i=0; i<96; i++) { #if LCD_BLACK == 0 memfill(lcd_buf[i].Data, 0xff, 12); #else memfill(lcd_buf[i].Data, 0x00, 12); #endif }; } #pragma vector=DMA_VECTOR __interrupt void DMA_ISR (void) { switch (DMAIV) { case 0: DMA0CTL &= ~DMAEN; // disable DMA0CTL &= ~DMAIFG; // clear IRQ flag debug_uart_tx("DMA0 IRQ\n"); break; case 2: debug_uart_tx("DMA1 IRQ\n"); break; case 4: debug_uart_tx("DMA2b IRQ\n"); break; case 6: DMA2CTL &= ~DMAEN; // disable DMA2CTL &= ~DMAIFG; // clear IRQ flag #ifdef LCD_DMA LcdDmaBusy = 0; #endif //LED7_TOGGLE(); // debug_uart_tx("DMA2 IRQ\n"); break; } } /* writes the complete internal framebuffer to the LCD */ void mw_lcd_update_screen(void) { //#ifndef LCD_DMA unsigned int i,j; //#endif // debug_uart_tx("uscr1\n"); // invert the buffer if (0) { for (i=0; i<96; i++) { for ( j = 0; j < 12; j++ ) { lcd_buf[i].Data[j] = ~lcd_buf[i].Data[j]; } } } LCD_CS_ASSERT(); /* send WRITE command */ LCD_SPI_UCBxTXBUF = LCD_WRITE_CMD; while (!(LCD_SPI_UCBxIFG & UCTXIFG)) nop(); // debug_uart_tx("uscr2\n"); #ifdef LCD_DMA LcdDmaBusy = 1; /* USCIB0 TXIFG is the DMA trigger * DMACTL1 controls dma2 and [dma3] */ DMACTL1 = DMA2TSEL_19; DMA2SA = (unsigned int) lcd_buf; DMA2DA = (unsigned int) &LCD_SPI_UCBxTXBUF; DMA2SZ = 96 * sizeof(tLcdLine); /* * single transfer, increment source address, source byte and dest byte, * level sensitive, enable interrupt, clear interrupt flag */ DMA2CTL = DMADT_0 + DMASRCINCR_3 + DMASBDB + DMALEVEL + DMAIE; /* start the transfer */ DMA2CTL |= DMAEN; // debug_uart_tx("uscr3\n"); // LED7_OFF(); while (LcdDmaBusy) nop(); // debug_uart_tx("uscr4\n"); #else for (i=0; i<96; i++) { for ( j = 0; j < sizeof(tLcdLine); j++ ) { LCD_SPI_UCBxTXBUF = ((unsigned char *)(&lcd_buf[i]))[j]; while (!(LCD_SPI_UCBxIFG & UCTXIFG)) nop(); } } #endif /* send final trailer byte */ LCD_SPI_UCBxTXBUF = 0x00; while (!(LCD_SPI_UCBxIFG & UCTXIFG)) nop(); // debug_uart_tx("uscr5\n"); /* wait for shift to complete ( ~3 us ) */ while (LCD_SPI_UCBxSTAT & 0x01) nop(); // debug_uart_tx("uscr6\n"); LCD_CS_DEASSERT(); mw_lcd_static_mode(); } void mw_lcd_draw_pixel(const uint8_t x, const uint8_t y, const uint8_t color) { switch (color) { case 1: lcd_buf[y].Data[x/8] |= 1 << (x % 8); break; case 0: lcd_buf[y].Data[x/8] &= ~(1 << (x % 8)); break; case 2: lcd_buf[y].Data[x/8] ^= 1 << (x % 8); break; } }