mirror of
https://github.com/arduino/Arduino.git
synced 2024-12-10 21:24:12 +01:00
382 lines
11 KiB
C
Executable File
382 lines
11 KiB
C
Executable File
/*! \file ks0108.c \brief Graphic LCD driver for HD61202/KS0108 displays. */
|
|
//*****************************************************************************
|
|
//
|
|
// File Name : 'ks0108.c'
|
|
// Title : Graphic LCD driver for HD61202/KS0108 displays
|
|
// Author : Pascal Stang - Copyright (C) 2001-2003
|
|
// Date : 10/19/2002
|
|
// Revised : 5/5/2003
|
|
// Version : 0.5
|
|
// Target MCU : Atmel AVR
|
|
// Editor Tabs : 4
|
|
//
|
|
// NOTE: This code is currently below version 1.0, and therefore is considered
|
|
// to be lacking in some functionality or documentation, or may not be fully
|
|
// tested. Nonetheless, you can expect most functions to work.
|
|
//
|
|
// This code is distributed under the GNU Public License
|
|
// which can be found at http://www.gnu.org/licenses/gpl.txt
|
|
//
|
|
//*****************************************************************************
|
|
|
|
#ifndef WIN32
|
|
// AVR specific includes
|
|
#include <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
#endif
|
|
|
|
#include "global.h"
|
|
#include "ks0108.h"
|
|
|
|
// global variables
|
|
GrLcdStateType GrLcdState;
|
|
|
|
/*************************************************************/
|
|
/********************** LOCAL FUNCTIONS **********************/
|
|
/*************************************************************/
|
|
|
|
void glcdInitHW(void)
|
|
{
|
|
// initialize I/O ports
|
|
// if I/O interface is in use
|
|
#ifdef GLCD_PORT_INTERFACE
|
|
|
|
//TODO: make setup of chip select lines contingent on how
|
|
// many controllers are actually in the display
|
|
|
|
// initialize LCD control lines levels
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_RS);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_RW);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_E);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_CS0);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_CS1);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_CS2);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_CS3);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_RESET);
|
|
// initialize LCD control port to output
|
|
sbi(GLCD_CTRL_DDR, GLCD_CTRL_RS);
|
|
sbi(GLCD_CTRL_DDR, GLCD_CTRL_RW);
|
|
sbi(GLCD_CTRL_DDR, GLCD_CTRL_E);
|
|
sbi(GLCD_CTRL_DDR, GLCD_CTRL_CS0);
|
|
sbi(GLCD_CTRL_DDR, GLCD_CTRL_CS1);
|
|
sbi(GLCD_CTRL_DDR, GLCD_CTRL_CS2);
|
|
sbi(GLCD_CTRL_DDR, GLCD_CTRL_CS3);
|
|
sbi(GLCD_CTRL_DDR, GLCD_CTRL_RESET);
|
|
// initialize LCD data
|
|
outb(GLCD_DATA_PORT, 0x00);
|
|
// initialize LCD data port to output
|
|
outb(GLCD_DATA_DDR, 0xFF);
|
|
#endif
|
|
}
|
|
|
|
void glcdControllerSelect(u08 controller)
|
|
{
|
|
#ifdef GLCD_PORT_INTERFACE
|
|
//TODO: make control of chip select lines contingent on how
|
|
// many controllers are actually in the display
|
|
|
|
// unselect all controllers
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_CS0);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_CS1);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_CS2);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_CS3);
|
|
|
|
// select requested controller
|
|
switch(controller)
|
|
{
|
|
case 0: sbi(GLCD_CTRL_PORT, GLCD_CTRL_CS0); break;
|
|
case 1: sbi(GLCD_CTRL_PORT, GLCD_CTRL_CS1); break;
|
|
case 2: sbi(GLCD_CTRL_PORT, GLCD_CTRL_CS2); break;
|
|
case 3: sbi(GLCD_CTRL_PORT, GLCD_CTRL_CS3); break;
|
|
default: break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void glcdBusyWait(u08 controller)
|
|
{
|
|
#ifdef GLCD_PORT_INTERFACE
|
|
cli();
|
|
// wait until LCD busy bit goes to zero
|
|
// select the controller chip
|
|
glcdControllerSelect(controller);
|
|
// do a read from control register
|
|
outb(GLCD_DATA_PORT, 0xFF);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_RS);
|
|
outb(GLCD_DATA_DDR, 0x00);
|
|
sbi(GLCD_CTRL_PORT, GLCD_CTRL_RW);
|
|
sbi(GLCD_CTRL_PORT, GLCD_CTRL_E);
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
while(inb(GLCD_DATA_PIN) & GLCD_STATUS_BUSY)
|
|
{
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_E);
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
sbi(GLCD_CTRL_PORT, GLCD_CTRL_E);
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
}
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_E);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_RW);
|
|
outb(GLCD_DATA_DDR, 0xFF);
|
|
sei();
|
|
#else
|
|
// sbi(MCUCR, SRW); // enable RAM waitstate
|
|
// wait until LCD busy bit goes to zero
|
|
while(*(volatile unsigned char *)
|
|
(GLCD_CONTROLLER0_CTRL_ADDR + GLCD_CONTROLLER_ADDR_OFFSET*controller) & GLCD_STATUS_BUSY);
|
|
// cbi(MCUCR, SRW); // disable RAM waitstate
|
|
#endif
|
|
}
|
|
|
|
void glcdControlWrite(u08 controller, u08 data)
|
|
{
|
|
#ifdef GLCD_PORT_INTERFACE
|
|
cli();
|
|
glcdBusyWait(controller); // wait until LCD not busy
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_RS);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_RW);
|
|
sbi(GLCD_CTRL_PORT, GLCD_CTRL_E);
|
|
outb(GLCD_DATA_DDR, 0xFF);
|
|
outb(GLCD_DATA_PORT, data);
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_E);
|
|
sei();
|
|
#else
|
|
//sbi(MCUCR, SRW); // enable RAM waitstate
|
|
glcdBusyWait(controller); // wait until LCD not busy
|
|
*(volatile unsigned char *) (GLCD_CONTROLLER0_CTRL_ADDR + GLCD_CONTROLLER_ADDR_OFFSET*controller) = data;
|
|
//cbi(MCUCR, SRW); // disable RAM waitstate
|
|
#endif
|
|
}
|
|
|
|
u08 glcdControlRead(u08 controller)
|
|
{
|
|
register u08 data;
|
|
#ifdef GLCD_PORT_INTERFACE
|
|
cli();
|
|
glcdBusyWait(controller); // wait until LCD not busy
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_RS);
|
|
outb(GLCD_DATA_DDR, 0x00);
|
|
sbi(GLCD_CTRL_PORT, GLCD_CTRL_RW);
|
|
sbi(GLCD_CTRL_PORT, GLCD_CTRL_E);
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
data = inb(GLCD_DATA_PIN);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_E);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_RW);
|
|
outb(GLCD_DATA_DDR, 0xFF);
|
|
sei();
|
|
#else
|
|
//sbi(MCUCR, SRW); // enable RAM waitstate
|
|
glcdBusyWait(controller); // wait until LCD not busy
|
|
data = *(volatile unsigned char *) (GLCD_CONTROLLER0_CTRL_ADDR + GLCD_CONTROLLER_ADDR_OFFSET*controller);
|
|
//cbi(MCUCR, SRW); // disable RAM waitstate
|
|
#endif
|
|
return data;
|
|
}
|
|
|
|
void glcdDataWrite(u08 data)
|
|
{
|
|
register u08 controller = (GrLcdState.lcdXAddr/GLCD_CONTROLLER_XPIXELS);
|
|
#ifdef GLCD_PORT_INTERFACE
|
|
cli();
|
|
glcdBusyWait(controller); // wait until LCD not busy
|
|
sbi(GLCD_CTRL_PORT, GLCD_CTRL_RS);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_RW);
|
|
sbi(GLCD_CTRL_PORT, GLCD_CTRL_E);
|
|
outb(GLCD_DATA_DDR, 0xFF);
|
|
outb(GLCD_DATA_PORT, data);
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_E);
|
|
sei();
|
|
#else
|
|
//sbi(MCUCR, SRW); // enable RAM waitstate
|
|
glcdBusyWait(controller); // wait until LCD not busy
|
|
*(volatile unsigned char *) (GLCD_CONTROLLER0_CTRL_ADDR + GLCD_CONTROLLER_ADDR_OFFSET*controller) = data;
|
|
//cbi(MCUCR, SRW); // disable RAM waitstate
|
|
#endif
|
|
// increment our local address counter
|
|
GrLcdState.ctrlr[controller].xAddr++;
|
|
GrLcdState.lcdXAddr++;
|
|
if(GrLcdState.lcdXAddr >= GLCD_XPIXELS)
|
|
{
|
|
GrLcdState.lcdYAddr++;
|
|
glcdSetYAddress(GrLcdState.lcdYAddr);
|
|
glcdSetXAddress(0);
|
|
}
|
|
}
|
|
|
|
u08 glcdDataRead(void)
|
|
{
|
|
register u08 data;
|
|
register u08 controller = (GrLcdState.lcdXAddr/GLCD_CONTROLLER_XPIXELS);
|
|
#ifdef GLCD_PORT_INTERFACE
|
|
cli();
|
|
glcdBusyWait(controller); // wait until LCD not busy
|
|
sbi(GLCD_CTRL_PORT, GLCD_CTRL_RS);
|
|
outb(GLCD_DATA_DDR, 0x00);
|
|
sbi(GLCD_CTRL_PORT, GLCD_CTRL_RW);
|
|
sbi(GLCD_CTRL_PORT, GLCD_CTRL_E);
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
asm volatile ("nop"); asm volatile ("nop");
|
|
data = inb(GLCD_DATA_PIN);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_E);
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_RW);
|
|
sei();
|
|
#else
|
|
//sbi(MCUCR, SRW); // enable RAM waitstate
|
|
glcdBusyWait(controller); // wait until LCD not busy
|
|
data = *(volatile unsigned char *) (GLCD_CONTROLLER0_CTRL_ADDR + GLCD_CONTROLLER_ADDR_OFFSET*controller);
|
|
//cbi(MCUCR, SRW); // disable RAM waitstate
|
|
#endif
|
|
// increment our local address counter
|
|
GrLcdState.ctrlr[controller].xAddr++;
|
|
GrLcdState.lcdXAddr++;
|
|
if(GrLcdState.lcdXAddr >= GLCD_XPIXELS)
|
|
{
|
|
GrLcdState.lcdYAddr++;
|
|
glcdSetYAddress(GrLcdState.lcdYAddr);
|
|
glcdSetXAddress(0);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
void glcdReset(u08 resetState)
|
|
{
|
|
// reset lcd if argument is true
|
|
// run lcd if argument is false
|
|
#ifdef GLCD_PORT_INTERFACE
|
|
if(resetState)
|
|
cbi(GLCD_CTRL_PORT, GLCD_CTRL_RESET);
|
|
else
|
|
sbi(GLCD_CTRL_PORT, GLCD_CTRL_RESET);
|
|
#endif
|
|
}
|
|
|
|
void glcdSetXAddress(u08 xAddr)
|
|
{
|
|
u08 i;
|
|
// record address change locally
|
|
GrLcdState.lcdXAddr = xAddr;
|
|
|
|
// clear y (col) address on all controllers
|
|
for(i=0; i<GLCD_NUM_CONTROLLERS; i++)
|
|
{
|
|
glcdControlWrite(i, GLCD_SET_Y_ADDR | 0x00);
|
|
GrLcdState.ctrlr[i].xAddr = 0;
|
|
}
|
|
|
|
// set y (col) address on destination controller
|
|
glcdControlWrite((GrLcdState.lcdXAddr/GLCD_CONTROLLER_XPIXELS),
|
|
GLCD_SET_Y_ADDR | (GrLcdState.lcdXAddr & 0x3F));
|
|
}
|
|
|
|
void glcdSetYAddress(u08 yAddr)
|
|
{
|
|
u08 i;
|
|
// record address change locally
|
|
GrLcdState.lcdYAddr = yAddr;
|
|
// set page address for all controllers
|
|
for(i=0; i<GLCD_NUM_CONTROLLERS; i++)
|
|
{
|
|
glcdControlWrite(i, GLCD_SET_PAGE | yAddr);
|
|
}
|
|
}
|
|
|
|
/*************************************************************/
|
|
/********************* PUBLIC FUNCTIONS **********************/
|
|
/*************************************************************/
|
|
|
|
void glcdInit()
|
|
{
|
|
u08 i;
|
|
// initialize hardware
|
|
glcdInitHW();
|
|
// bring lcd out of reset
|
|
glcdReset(FALSE);
|
|
// Turn on LCD
|
|
for(i=0; i<GLCD_NUM_CONTROLLERS; i++)
|
|
{
|
|
glcdControlWrite(i, GLCD_ON_CTRL | GLCD_ON_DISPLAY);
|
|
}
|
|
// clear lcd
|
|
glcdClearScreen();
|
|
// initialize positions
|
|
glcdHome();
|
|
}
|
|
|
|
void glcdHome(void)
|
|
{
|
|
u08 i;
|
|
// initialize addresses/positions
|
|
glcdStartLine(0);
|
|
glcdSetAddress(0,0);
|
|
// initialize local data structures
|
|
for(i=0; i<GLCD_NUM_CONTROLLERS; i++)
|
|
{
|
|
GrLcdState.ctrlr[i].xAddr = 0;
|
|
GrLcdState.ctrlr[i].yAddr = 0;
|
|
}
|
|
}
|
|
|
|
void glcdClearScreen(void)
|
|
{
|
|
u08 pageAddr;
|
|
u08 xAddr;
|
|
|
|
// clear LCD
|
|
// loop through all pages
|
|
for(pageAddr=0; pageAddr<(GLCD_YPIXELS>>3); pageAddr++)
|
|
{
|
|
// set page address
|
|
glcdSetAddress(0, pageAddr);
|
|
// clear all lines of this page of display memory
|
|
for(xAddr=0; xAddr<GLCD_XPIXELS; xAddr++)
|
|
{
|
|
glcdDataWrite(0x00);
|
|
}
|
|
}
|
|
}
|
|
|
|
void glcdStartLine(u08 start)
|
|
{
|
|
glcdControlWrite(0, GLCD_START_LINE | start);
|
|
glcdControlWrite(1, GLCD_START_LINE | start);
|
|
}
|
|
|
|
void glcdSetAddress(u08 x, u08 yLine)
|
|
{
|
|
// set addresses
|
|
glcdSetYAddress(yLine);
|
|
glcdSetXAddress(x);
|
|
}
|
|
|
|
void glcdGotoChar(u08 line, u08 col)
|
|
{
|
|
glcdSetAddress(col*6, line);
|
|
}
|
|
|
|
void glcdDelay(u16 p) // 1-8us ...2-13us ...5-31us
|
|
{ // 10-60us ...50-290us
|
|
unsigned int i; // 100-580us ...500-2,9ms
|
|
unsigned char j; // 1000-5,8ms ...5000-29ms
|
|
// 10000-56ms ...30000-170ms
|
|
// 50000-295ms...60000-345ms
|
|
// for (i = 0; i < p; i++) for (j = 0; j < 10; j++) asm volatile ("nop");
|
|
for (i = 0; i < p; i++) for (j = 0; j < 10; j++);
|
|
}
|
|
|
|
|
|
// Higher level functionality has been moved to the API-layer glcd.c/glcd.h
|