|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732 |
- /*
- ks0108.cpp - Arduino library support for ks0108 and compatable graphic LCDs
- Copyright (c)2008 Michael Margolis All right reserved
- mailto:memargolis@hotmail.com?subject=KS0108_Library
-
- The high level functions of this library are based on version 1.1 of ks0108 graphics routines
- written and copyright by Fabian Maximilian Thiele. His sitelink is dead but
- you can obtain a copy of his original work here:
- http://www.scienceprog.com/wp-content/uploads/2007/07/glcd_ks0108.zip
-
- Code changes include conversion to an Arduino C++ library, rewriting the low level routines
- to read busy status flag and support a wider range of displays, adding more flexibility
- in port addressing and improvements in I/O speed. The interface has been made more Arduino friendly
- and some convenience functions added.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
- Version: 1.0 - May 8 2008 - first release
- Version: 1.1 - Nov 7 2008 - restructured low level code to adapt to panel speed
- - moved chip and panel configuration into seperate header files
- - added fixed width system font
- Version: 2 - May 26 2009 - second release
- - added support for Mega and Sanguino, improved panel speed tolerance, added bitmap support
-
- */
-
- #include <inttypes.h>
- #include <avr/io.h>
- #include <avr/pgmspace.h>
- #include <wiring.h>
-
- #define ksSOURCE
- #include "ks0108.h"
-
-
- #define EN_DELAY() asm volatile( "ldi r24, %0 \n\t" "subi r24, 0x1 \n\t" "and r24, r24 \n\t" "brne .-6 \n\t" ::"M" (EN_DELAY_VALUE) : "r24" )
- // the (EN_DELAY_VALUE) argument for the above delay is in ks-0108_panel.h
-
- //#define GLCD_DEBUG // uncomment this if you want to slow down drawing to see how pixels are set
-
- void ks0108::ClearPage(uint8_t page, uint8_t color){
- for(uint8_t x=0; x < DISPLAY_WIDTH; x++){
- GotoXY(x, page * 8);
- this->WriteData(color);
- }
- }
-
- void ks0108::ClearScreen(uint8_t color){
- uint8_t page;
- for( page = 0; page < 8; page++){
- GotoXY(0, page * 8);
- ClearPage(page, color);
- }
- }
-
- void ks0108::DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color) {
- uint8_t length, i, y, yAlt, xTmp, yTmp;
- int16_t m;
- //
- // vertical line
- //
- if(x1 == x2) {
- // x1|y1 must be the upper point
- if(y1 > y2) {
- yTmp = y1;
- y1 = y2;
- y2 = yTmp;
- }
- this->DrawVertLine(x1, y1, y2-y1, color);
-
- //
- // horizontal line
- //
- } else if(y1 == y2) {
- // x1|y1 must be the left point
- if(x1 > x2) {
- xTmp = x1;
- x1 = x2;
- x2 = xTmp;
- }
- this->DrawHoriLine(x1, y1, x2-x1, color);
-
- //
- // angled line :)
- //
- } else {
- // angle >= 45°
- if((y2-y1) >= (x2-x1) || (y1-y2) >= (x2-x1)) {
- // x1 must be smaller than x2
- if(x1 > x2) {
- xTmp = x1;
- yTmp = y1;
- x1 = x2;
- y1 = y2;
- x2 = xTmp;
- y2 = yTmp;
- }
-
- length = x2-x1; // not really the length :)
- m = ((y2-y1)*200)/length;
- yAlt = y1;
-
- for(i=0; i<=length; i++) {
- y = ((m*i)/200)+y1;
-
- if((m*i)%200 >= 100)
- y++;
- else if((m*i)%200 <= -100)
- y--;
-
- this->DrawLine(x1+i, yAlt, x1+i, y, color);
-
- if(length <= (y2-y1) && y1 < y2)
- yAlt = y+1;
- else if(length <= (y1-y2) && y1 > y2)
- yAlt = y-1;
- else
- yAlt = y;
- }
-
- // angle < 45°
- } else {
- // y1 must be smaller than y2
- if(y1 > y2) {
- xTmp = x1;
- yTmp = y1;
- x1 = x2;
- y1 = y2;
- x2 = xTmp;
- y2 = yTmp;
- }
-
- length = y2-y1;
- m = ((x2-x1)*200)/length;
- yAlt = x1;
-
- for(i=0; i<=length; i++) {
- y = ((m*i)/200)+x1;
-
- if((m*i)%200 >= 100)
- y++;
- else if((m*i)%200 <= -100)
- y--;
-
- this->DrawLine(yAlt, y1+i, y, y1+i, color);
- if(length <= (x2-x1) && x1 < x2)
- yAlt = y+1;
- else if(length <= (x1-x2) && x1 > x2)
- yAlt = y-1;
- else
- yAlt = y;
- }
- }
- }
- }
-
- void ks0108::DrawRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) {
- DrawHoriLine(x, y, width, color); // top
- DrawHoriLine(x, y+height, width, color); // bottom
- DrawVertLine(x, y, height, color); // left
- DrawVertLine(x+width, y, height, color); // right
- }
-
- void ks0108::DrawRoundRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t radius, uint8_t color) {
- int16_t tSwitch, x1 = 0, y1 = radius;
- tSwitch = 3 - 2 * radius;
-
- while (x1 <= y1) {
- this->SetDot(x+radius - x1, y+radius - y1, color);
- this->SetDot(x+radius - y1, y+radius - x1, color);
-
- this->SetDot(x+width-radius + x1, y+radius - y1, color);
- this->SetDot(x+width-radius + y1, y+radius - x1, color);
-
- this->SetDot(x+width-radius + x1, y+height-radius + y1, color);
- this->SetDot(x+width-radius + y1, y+height-radius + x1, color);
-
- this->SetDot(x+radius - x1, y+height-radius + y1, color);
- this->SetDot(x+radius - y1, y+height-radius + x1, color);
-
- if (tSwitch < 0) {
- tSwitch += (4 * x1 + 6);
- } else {
- tSwitch += (4 * (x1 - y1) + 10);
- y1--;
- }
- x1++;
- }
-
- this->DrawHoriLine(x+radius, y, width-(2*radius), color); // top
- this->DrawHoriLine(x+radius, y+height, width-(2*radius), color); // bottom
- this->DrawVertLine(x, y+radius, height-(2*radius), color); // left
- this->DrawVertLine(x+width, y+radius, height-(2*radius), color); // right
- }
-
- /*
- * Hardware-Functions
- */
- void ks0108::FillRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) {
- uint8_t mask, pageOffset, h, i, data;
- height++;
-
- pageOffset = y%8;
- y -= pageOffset;
- mask = 0xFF;
- if(height < 8-pageOffset) {
- mask >>= (8-height);
- h = height;
- } else {
- h = 8-pageOffset;
- }
- mask <<= pageOffset;
-
- this->GotoXY(x, y);
- for(i=0; i<=width; i++) {
- data = this->ReadData();
-
- if(color == BLACK) {
- data |= mask;
- } else {
- data &= ~mask;
- }
-
- this->WriteData(data);
- }
-
- while(h+8 <= height) {
- h += 8;
- y += 8;
- this->GotoXY(x, y);
-
- for(i=0; i<=width; i++) {
- this->WriteData(color);
- }
- }
-
- if(h < height) {
- mask = ~(0xFF << (height-h));
- this->GotoXY(x, y+8);
-
- for(i=0; i<=width; i++) {
- data = this->ReadData();
-
- if(color == BLACK) {
- data |= mask;
- } else {
- data &= ~mask;
- }
-
- this->WriteData(data);
- }
- }
- }
-
- void ks0108::InvertRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
- uint8_t mask, pageOffset, h, i, data, tmpData;
- height++;
-
- pageOffset = y%8;
- y -= pageOffset;
- mask = 0xFF;
- if(height < 8-pageOffset) {
- mask >>= (8-height);
- h = height;
- } else {
- h = 8-pageOffset;
- }
- mask <<= pageOffset;
-
- this->GotoXY(x, y);
- for(i=0; i<=width; i++) {
- data = this->ReadData();
- tmpData = ~data;
- data = (tmpData & mask) | (data & ~mask);
- this->WriteData(data);
- }
-
- while(h+8 <= height) {
- h += 8;
- y += 8;
- this->GotoXY(x, y);
-
- for(i=0; i<=width; i++) {
- data = this->ReadData();
- this->WriteData(~data);
- }
- }
-
- if(h < height) {
- mask = ~(0xFF << (height-h));
- this->GotoXY(x, y+8);
-
- for(i=0; i<=width; i++) {
- data = this->ReadData();
- tmpData = ~data;
- data = (tmpData & mask) | (data & ~mask);
- this->WriteData(data);
- }
- }
- }
-
- void ks0108::SetInverted(boolean invert) { // changed type to boolean
- if(this->Inverted != invert) {
- this->InvertRect(0,0,DISPLAY_WIDTH-1,DISPLAY_HEIGHT-1);
- this->Inverted = invert;
- }
- }
-
- void ks0108::SetDot(uint8_t x, uint8_t y, uint8_t color) {
- uint8_t data;
-
- this->GotoXY(x, y-y%8); // read data from display memory
-
- data = this->ReadData();
- if(color == BLACK) {
- data |= 0x01 << (y%8); // set dot
- } else {
- data &= ~(0x01 << (y%8)); // clear dot
- }
- this->WriteData(data); // write data back to display
- }
-
- //
- // Font Functions
- //
-
- uint8_t ReadPgmData(const uint8_t* ptr) { // note this is a static function
- return pgm_read_byte(ptr);
- }
-
- void ks0108::SelectFont(const uint8_t* font,uint8_t color, FontCallback callback) {
- this->Font = font;
- this->FontRead = callback;
- this->FontColor = color;
- }
-
- void ks0108::PrintNumber(long n){
- byte buf[10]; // prints up to 10 digits
- byte i=0;
- if(n==0)
- PutChar('0');
- else{
- if(n < 0){
- PutChar('-');
- n = -n;
- }
- while(n>0 && i <= 10){
- buf[i++] = n % 10; // n % base
- n /= 10; // n/= base
- }
- for(; i >0; i--)
- this->PutChar((char) (buf[i-1] < 10 ? '0' + buf[i-1] : 'A' + buf[i-1] - 10));
- }
- }
-
- int ks0108::PutChar(char c) {
- uint8_t width = 0;
- uint8_t height = this->FontRead(this->Font+FONT_HEIGHT);
- uint8_t bytes = (height+7)/8;
-
- uint8_t firstChar = this->FontRead(this->Font+FONT_FIRST_CHAR);
- uint8_t charCount = this->FontRead(this->Font+FONT_CHAR_COUNT);
-
- uint16_t index = 0;
- uint8_t x = this->Coord.x, y = this->Coord.y;
-
- if(c < firstChar || c >= (firstChar+charCount)) {
- return 1;
- }
- c-= firstChar;
-
- if( this->FontRead(this->Font+FONT_LENGTH) == 0 && this->FontRead(this->Font+FONT_LENGTH+1) == 0) {
- // zero length is flag indicating fixed width font (array does not contain width data entries)
- width = this->FontRead(this->Font+FONT_FIXED_WIDTH);
- index = c*bytes*width+FONT_WIDTH_TABLE;
- }
- else{
- // variable width font, read width data, to get the index
- for(uint8_t i=0; i<c; i++) {
- index += this->FontRead(this->Font+FONT_WIDTH_TABLE+i);
- }
- index = index*bytes+charCount+FONT_WIDTH_TABLE;
- width = this->FontRead(this->Font+FONT_WIDTH_TABLE+c);
- }
-
- // last but not least, draw the character
- for(uint8_t i=0; i<bytes; i++) {
- uint8_t page = i*width;
- for(uint8_t j=0; j<width; j++) {
- uint8_t data = this->FontRead(this->Font+index+page+j);
-
- if(height > 8 && height < (i+1)*8) {
- data >>= (i+1)*8-height;
- }
-
- if(this->FontColor == BLACK) {
- this->WriteData(data);
- } else {
- this->WriteData(~data);
- }
- }
- // 1px gap between chars
- if(this->FontColor == BLACK) {
- this->WriteData(0x00);
- } else {
- this->WriteData(0xFF);
- }
- this->GotoXY(x, this->Coord.y+8);
- }
- this->GotoXY(x+width+1, y);
-
- return 0;
- }
-
- void ks0108::Puts(const char* str) {
- int x = this->Coord.x;
- while(*str != 0) {
- if(*str == '\n') {
- this->GotoXY(x, this->Coord.y+this->FontRead(this->Font+FONT_HEIGHT));
- } else {
- this->PutChar(*str);
- }
- str++;
- }
- }
-
- void ks0108::Puts_P(PGM_P str) {
- int x = this->Coord.x;
- while(pgm_read_byte(str) != 0) {
- if(pgm_read_byte(str) == '\n') {
- this->GotoXY(x, this->Coord.y+this->FontRead(this->Font+FONT_HEIGHT));
- } else {
- this->PutChar(pgm_read_byte(str));
- }
- str++;
- }
- }
-
- uint8_t ks0108::CharWidth(char c) {
- uint8_t width = 0;
- uint8_t firstChar = this->FontRead(this->Font+FONT_FIRST_CHAR);
- uint8_t charCount = this->FontRead(this->Font+FONT_CHAR_COUNT);
-
- // read width data
- if(c >= firstChar && c < (firstChar+charCount)) {
- c -= firstChar;
- width = this->FontRead(this->Font+FONT_WIDTH_TABLE+c)+1;
- }
-
- return width;
- }
-
- uint16_t ks0108::StringWidth(char* str) {
- uint16_t width = 0;
-
- while(*str != 0) {
- width += this->CharWidth(*str++);
- }
-
- return width;
- }
-
- uint16_t ks0108::StringWidth_P(PGM_P str) {
- uint16_t width = 0;
-
- while(pgm_read_byte(str) != 0) {
- width += this->CharWidth(pgm_read_byte(str++));
- }
-
- return width;
- }
-
- void ks0108::CursorTo( uint8_t x, uint8_t y){ // 0 based coordinates for fixed width fonts (i.e. systemFont5x7)
- GotoXY( x * (this->FontRead(this->Font+FONT_FIXED_WIDTH)+1),
- y * (this->FontRead(this->Font+FONT_HEIGHT)+1)
- ) ;
- }
-
- void ks0108::GotoXY(uint8_t x, uint8_t y) {
- uint8_t chip, cmd;
-
- if( (x > DISPLAY_WIDTH-1) || (y > DISPLAY_HEIGHT-1) ) // exit if coordinates are not legal
- return;
- this->Coord.x = x; // save new coordinates
- this->Coord.y = y;
-
- if(y/8 != this->Coord.page) {
- this->Coord.page = y/8;
- cmd = LCD_SET_PAGE | this->Coord.page; // set y address on all chips
- for(chip=0; chip < DISPLAY_WIDTH/CHIP_WIDTH; chip++){
- this->WriteCommand(cmd, chip);
- }
- }
- chip = this->Coord.x/CHIP_WIDTH;
- x = x % CHIP_WIDTH;
- cmd = LCD_SET_ADD | x;
- this->WriteCommand(cmd, chip); // set x address on active chip
- }
-
- void ks0108::Init(boolean invert) {
- pinMode(D_I,OUTPUT);
- pinMode(R_W,OUTPUT);
- pinMode(EN,OUTPUT);
- pinMode(CSEL1,OUTPUT);
- pinMode(CSEL2,OUTPUT);
-
- delay(10);
-
- fastWriteLow(D_I);
- fastWriteLow(R_W);
- fastWriteLow(EN);
-
- this->Coord.x = 0;
- this->Coord.y = 0;
- this->Coord.page = 0;
-
- this->Inverted = invert;
-
- for(uint8_t chip=0; chip < DISPLAY_WIDTH/CHIP_WIDTH; chip++){
- delay(10);
- this->WriteCommand(LCD_ON, chip); // power on
- this->WriteCommand(LCD_DISP_START, chip); // display start line = 0
- }
- delay(50);
- this->ClearScreen(invert ? BLACK : WHITE); // display clear
- this->GotoXY(0,0);
- }
-
- __inline__ void ks0108::SelectChip(uint8_t chip) {
- //static uint8_t prevchip;
- if(chipSelect[chip] & 1)
- fastWriteHigh(CSEL1);
- else
- fastWriteLow(CSEL1);
-
- if(chipSelect[chip] & 2)
- fastWriteHigh(CSEL2);
- else
- fastWriteLow(CSEL2);
- }
-
-
- void ks0108::WaitReady( uint8_t chip){
- // wait until LCD busy bit goes to zero
-
- SelectChip(chip);
- lcdDataDir(0x00);
- fastWriteLow(D_I);
- fastWriteHigh(R_W);
- fastWriteHigh(EN);
- EN_DELAY();
- while(LCD_DATA_IN_HIGH & LCD_BUSY_FLAG)
- ;
- fastWriteLow(EN);
-
-
- }
-
- __inline__ void ks0108::Enable(void) {
- EN_DELAY();
- fastWriteHigh(EN); // EN high level width min 450 ns
- EN_DELAY();
- fastWriteLow(EN);
- //EN_DELAY(); // some displays may need this delay at the end of the enable pulse
- }
-
- uint8_t ks0108::DoReadData(uint8_t first) {
- uint8_t data, chip;
-
- chip = this->Coord.x/CHIP_WIDTH;
- this->WaitReady(chip);
- if(first){
- if(this->Coord.x % CHIP_WIDTH == 0 && chip > 0){ // todo , remove this test and call GotoXY always?
- this->GotoXY(this->Coord.x, this->Coord.y);
- this->WaitReady(chip);
- }
- }
- fastWriteHigh(D_I); // D/I = 1
- fastWriteHigh(R_W); // R/W = 1
-
- fastWriteHigh(EN); // EN high level width: min. 450ns
- EN_DELAY();
-
- #ifdef LCD_DATA_NIBBLES
- data = (LCD_DATA_IN_LOW & 0x0F) | (LCD_DATA_IN_HIGH & 0xF0);
- #else
- data = LCD_DATA_IN_LOW; // low and high nibbles on same port so read all 8 bits at once
- #endif
- fastWriteLow(EN);
- if(first == 0)
- this->GotoXY(this->Coord.x, this->Coord.y);
- if(this->Inverted)
- data = ~data;
- return data;
- }
-
- inline uint8_t ks0108::ReadData(void) {
- this->DoReadData(1); // dummy read
- return this->DoReadData(0); // "real" read
- this->Coord.x++;
- }
-
- void ks0108::WriteCommand(uint8_t cmd, uint8_t chip) {
- if(this->Coord.x % CHIP_WIDTH == 0 && chip > 0){ // todo , ignore address 0???
- EN_DELAY();
- }
- this->WaitReady(chip);
- fastWriteLow(D_I); // D/I = 0
- fastWriteLow(R_W); // R/W = 0
- lcdDataDir(0xFF);
-
- EN_DELAY();
- lcdDataOut(cmd);
- this->Enable(); // enable
- EN_DELAY();
- EN_DELAY();
- lcdDataOut(0x00);
- }
-
- void ks0108::WriteData(uint8_t data) {
- uint8_t displayData, yOffset, chip;
- //showHex("wrData",data);
- //showXY("wr", this->Coord.x,this->Coord.y);
-
- #ifdef LCD_CMD_PORT
- uint8_t cmdPort;
- #endif
-
- #ifdef GLCD_DEBUG
- volatile uint16_t i;
- for(i=0; i<5000; i++);
- #endif
-
- if(this->Coord.x >= DISPLAY_WIDTH)
- return;
- chip = this->Coord.x/CHIP_WIDTH;
- this->WaitReady(chip);
-
-
- if(this->Coord.x % CHIP_WIDTH == 0 && chip > 0){ // todo , ignore address 0???
- this->GotoXY(this->Coord.x, this->Coord.y);
- }
-
- fastWriteHigh(D_I); // D/I = 1
- fastWriteLow(R_W); // R/W = 0
- lcdDataDir(0xFF); // data port is output
-
- yOffset = this->Coord.y%8;
-
- if(yOffset != 0) {
- // first page
- #ifdef LCD_CMD_PORT
- cmdPort = LCD_CMD_PORT; // save command port
- #endif
- displayData = this->ReadData();
- #ifdef LCD_CMD_PORT
- LCD_CMD_PORT = cmdPort; // restore command port
- #else
- fastWriteHigh(D_I); // D/I = 1
- fastWriteLow(R_W); // R/W = 0
- SelectChip(chip);
- #endif
- lcdDataDir(0xFF); // data port is output
-
- displayData |= data << yOffset;
- if(this->Inverted)
- displayData = ~displayData;
- lcdDataOut( displayData); // write data
- this->Enable(); // enable
-
- // second page
- this->GotoXY(this->Coord.x, this->Coord.y+8);
-
- displayData = this->ReadData();
-
- #ifdef LCD_CMD_PORT
- LCD_CMD_PORT = cmdPort; // restore command port
- #else
- fastWriteHigh(D_I); // D/I = 1
- fastWriteLow(R_W); // R/W = 0
- SelectChip(chip);
- #endif
- lcdDataDir(0xFF); // data port is output
-
- displayData |= data >> (8-yOffset);
- if(this->Inverted)
- displayData = ~displayData;
- lcdDataOut(displayData); // write data
- this->Enable(); // enable
-
- this->GotoXY(this->Coord.x+1, this->Coord.y-8);
- }else
- {
- // just this code gets executed if the write is on a single page
- if(this->Inverted)
- data = ~data;
- EN_DELAY();
- lcdDataOut(data); // write data
- this->Enable(); // enable
- this->Coord.x++;
- //showXY("WrData",this->Coord.x, this->Coord.y);
- }
- }
- void ks0108::DrawBitmap(const uint8_t * bitmap, uint8_t x, uint8_t y, uint8_t color){
- uint8_t width, height;
- uint8_t i, j;
-
- width = ReadPgmData(bitmap++);
- height = ReadPgmData(bitmap++);
- for(j = 0; j < height / 8; j++) {
- this->GotoXY(x, y + (j*8) );
- for(i = 0; i < width; i++) {
- uint8_t displayData = ReadPgmData(bitmap++);
- if(color == BLACK)
- this->WriteData(displayData);
- else
- this->WriteData(~displayData);
- }
- }
- }
-
- // class wrapper
-
- ks0108::ks0108(){
- this->Inverted=0;
- }
-
- // Make one instance for the user
- ks0108 GLCD = ks0108();
-
|