oswald/metawatch/mw_lcd.c

294 lines
5.7 KiB
C
Raw Normal View History

2021-02-14 18:03:13 +01:00
#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;
}
}