293 lines
5.7 KiB
C
293 lines
5.7 KiB
C
#include <msp430.h>
|
|
#include <stdint.h>
|
|
|
|
#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;
|
|
}
|
|
}
|
|
|