#include "ks0108.h" lcdCoord ks0108_coord; uint8_t ks0108_inverted=0; ks0108_fontcallback ks0108_font_read; uint8_t ks0108_font_colour; const uint8_t* ks0108_font; void ks0108DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t colour) { 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; } ks0108DrawVertLine(x1, y1, y2-y1, colour); // // horizontal line // } else if(y1 == y2) { // x1|y1 must be the left point if(x1 > x2) { xTmp = x1; x1 = x2; x2 = xTmp; } ks0108DrawHoriLine(x1, y1, x2-x1, colour); // // schiefe 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--; ks0108DrawLine(x1+i, yAlt, x1+i, y, colour); 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--; ks0108DrawLine(yAlt, y1+i, y, y1+i, colour); if(length <= (x2-x1) && x1 < x2) yAlt = y+1; else if(length <= (x1-x2) && x1 > x2) yAlt = y-1; else yAlt = y; } } } } void ks0108DrawRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t colour) { ks0108DrawHoriLine(x, y, width, colour); // top ks0108DrawHoriLine(x, y+height, width, colour); // bottom ks0108DrawVertLine(x, y, height, colour); // left ks0108DrawVertLine(x+width, y, height, colour); // right } void ks0108DrawRoundRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t radius, uint8_t colour) { int16_t tSwitch, x1 = 0, y1 = radius; tSwitch = 3 - 2 * radius; while (x1 <= y1) { ks0108SetDot(x+radius - x1, y+radius - y1, colour); ks0108SetDot(x+radius - y1, y+radius - x1, colour); ks0108SetDot(x+width-radius + x1, y+radius - y1, colour); ks0108SetDot(x+width-radius + y1, y+radius - x1, colour); ks0108SetDot(x+width-radius + x1, y+height-radius + y1, colour); ks0108SetDot(x+width-radius + y1, y+height-radius + x1, colour); ks0108SetDot(x+radius - x1, y+height-radius + y1, colour); ks0108SetDot(x+radius - y1, y+height-radius + x1, colour); if (tSwitch < 0) { tSwitch += (4 * x1 + 6); } else { tSwitch += (4 * (x1 - y1) + 10); y1--; } x1++; } ks0108DrawHoriLine(x+radius, y, width-(2*radius), colour); // top ks0108DrawHoriLine(x+radius, y+height, width-(2*radius), colour); // bottom ks0108DrawVertLine(x, y+radius, height-(2*radius), colour); // left ks0108DrawVertLine(x+width, y+radius, height-(2*radius), colour); // right } /* * Hardware-Functions */ void ks0108FillRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t colour) { 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; ks0108_gotoxy(x, y); for(i=0; i<=width; i++) { data = ks0108_read_data(); if(colour == BLACK) { data |= mask; } else { data &= ~mask; } ks0108_write_data(data); } while(h+8 <= height) { h += 8; y += 8; ks0108_gotoxy(x, y); for(i=0; i<=width; i++) { ks0108_write_data(colour); } } if(h < height) { mask = ~(0xFF << (height-h)); ks0108_gotoxy(x, y+8); for(i=0; i<=width; i++) { data = ks0108_read_data(); if(colour == BLACK) { data |= mask; } else { data &= ~mask; } ks0108_write_data(data); } } } void ks0108InvertRect(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; ks0108_gotoxy(x, y); for(i=0; i<=width; i++) { data = ks0108_read_data(); tmpData = ~data; data = (tmpData & mask) | (data & ~mask); ks0108_write_data(data); } while(h+8 <= height) { h += 8; y += 8; ks0108_gotoxy(x, y); for(i=0; i<=width; i++) { data = ks0108_read_data(); ks0108_write_data(~data); } } if(h < height) { mask = ~(0xFF << (height-h)); ks0108_gotoxy(x, y+8); for(i=0; i<=width; i++) { data = ks0108_read_data(); tmpData = ~data; data = (tmpData & mask) | (data & ~mask); ks0108_write_data(data); } } } void ks0108SetInverted(uint8_t invert) { if(ks0108_inverted != invert) { ks0108InvertRect(0,0,127,63); ks0108_inverted = invert; } } void ks0108SetDot(uint8_t x, uint8_t y, uint8_t colour) { uint8_t data; ks0108_gotoxy(x, y-y%8); // read data from display memory data = ks0108_read_data(); if(colour == BLACK) { data |= 0x01 << (y%8); // set dot } else { data &= ~(0x01 << (y%8)); // clear dot } ks0108_write_data(data); // write data back to display } // // Font Functions // uint8_t ks0108_read_font_data(const uint8_t* ptr) { return pgm_read_byte(ptr); } void ks0108_select_font(const uint8_t* font, ks0108_fontcallback callback, uint8_t colour) { ks0108_font = font; ks0108_font_read = callback; ks0108_font_colour = colour; } int ks0108_putchar(char c) { uint8_t width = 0; uint8_t height = ks0108_font_read(ks0108_font+FONT_HEIGHT); uint8_t bytes = (height+7)/8; uint8_t firstChar = ks0108_font_read(ks0108_font+FONT_FIRST_CHAR); uint8_t charCount = ks0108_font_read(ks0108_font+FONT_CHAR_COUNT); uint16_t index = 0; uint8_t x = ks0108_coord.x, y = ks0108_coord.y; if(c < firstChar || c >= (firstChar+charCount)) { return 1; } c-= firstChar; if( ks0108_font_read(ks0108_font+FONT_LENGTH) == 0 && ks0108_font_read(ks0108_font+FONT_LENGTH+1) == 0) { // zero length is flag indicating fixed width font (array does not contain width data entries) width = ks0108_font_read(ks0108_font+FONT_FIXED_WIDTH); index = c*bytes*width+FONT_WIDTH_TABLE; } else{ // read width data, to get the index for(uint8_t i=0; i 8 && height < (i+1)*8) { data >>= (i+1)*8-height; } if(ks0108_font_colour == BLACK) { ks0108_write_data(data); } else { ks0108_write_data(~data); } } // 1px gap between chars if(ks0108_font_colour == BLACK) { ks0108_write_data(0x00); } else { ks0108_write_data(0xFF); } ks0108_gotoxy(x, ks0108_coord.y+8); } ks0108_gotoxy(x+width+1, y); return 0; } void ks0108_print_number(uint8_t n) { uint8_t buf[10]; // prints up to 10 digits uint8_t i=0; if(n==0) ks0108_putchar('0'); else{ if(n < 0){ ks0108_putchar('-'); n = -n; } while(n>0 && i <= 10){ buf[i++] = n % 10; // n % base n /= 10; // n/= base } for(; i >0; i--) ks0108_putchar((char) (buf[i-1] < 10 ? '0' + buf[i-1] : 'A' + buf[i-1] - 10)); } } void ks0108_puts(char* str) { int x = ks0108_coord.x; while(*str != 0) { if(*str == '\n') { ks0108_gotoxy(x, ks0108_coord.y+ks0108_font_read(ks0108_font+FONT_HEIGHT)); } else { ks0108_putchar(*str); } str++; } } void ks0108_puts_p(PGM_P str) { int x = ks0108_coord.x; while(pgm_read_byte(str) != 0) { if(pgm_read_byte(str) == '\n') { ks0108_gotoxy(x, ks0108_coord.y+ks0108_font_read(ks0108_font+FONT_HEIGHT)); } else { ks0108_putchar(pgm_read_byte(str)); } str++; } } uint8_t ks0108_char_width(char c) { uint8_t width = 0; uint8_t firstChar = ks0108_font_read(ks0108_font+FONT_FIRST_CHAR); uint8_t charCount = ks0108_font_read(ks0108_font+FONT_CHAR_COUNT); // read width data if(c >= firstChar && c < (firstChar+charCount)) { c -= firstChar; width = ks0108_font_read(ks0108_font+FONT_WIDTH_TABLE+c)+1; } return width; } uint16_t ks0108_string_width(char* str) { uint16_t width = 0; while(*str != 0) { width += ks0108_char_width(*str++); } return width; } uint16_t ks0108_string_width_p(PGM_P str) { uint16_t width = 0; while(pgm_read_byte(str) != 0) { width += ks0108_char_width(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 { ks0108_gotoxy( x * (ks0108_font_read(ks0108_font+FONT_FIXED_WIDTH)+1), y * (ks0108_font_read(ks0108_font+FONT_HEIGHT)+1) ) ; } void ks0108_gotoxy(uint8_t x, uint8_t y) { uint8_t chip = CHIP1, cmd; if(x > 127) x = 0; // ensure that coordinates are legal if(y > 63) y = 0; ks0108_coord.x = x; // save new coordinates ks0108_coord.y = y; ks0108_coord.page = y/8; if(x >= 64) { // select the right chip x -= 64; chip = CHIP2; } cmd = LCD_SET_ADD | x; ks0108_write_command(cmd, chip); // set x address on active chip cmd = LCD_SET_PAGE | ks0108_coord.page; // set y address on both chips ks0108_write_command(cmd, CHIP1); ks0108_write_command(cmd, CHIP2); } void ks0108_init(uint8_t invert) { ks0108_coord.x = 0; ks0108_coord.y = 0; ks0108_coord.page = 0; ks0108_inverted = invert; lcd_cmd_ddr = 0xFF; // command port is output ks0108_write_command(LCD_ON, CHIP1); // power on ks0108_write_command(LCD_ON, CHIP2); ks0108_write_command(LCD_DISP_START, CHIP1); // display start line = 0 ks0108_write_command(LCD_DISP_START, CHIP2); ks0108ClearScreen(); // display clear ks0108_gotoxy(0,0); } void ks0108Enable(void) { lcd_cmd_port |= 0x01 << EN; // EN high level width: min. 450ns.Here the machine cycle is 1/16Mhz=62.5nS.So I created 8 nops to get a approx delay of 500nS. asm volatile( "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" ::); lcd_cmd_port &= ~(0x01 << EN); for(volatile uint8_t i=0; i<8; i++); // a little delay loop (faster than reading the busy flag) } uint8_t ks0108DoReadData(uint8_t first) { uint8_t data; volatile uint8_t i; lcd_data_port = 0x00; lcd_data_ddr = 0x00; // data port is input if(ks0108_coord.x < 64) { lcd_cmd_port &= ~(0x01 << CSEL2); // deselect chip 2 lcd_cmd_port |= 0x01 << CSEL1; // select chip 1 } else if(ks0108_coord.x >= 64) { lcd_cmd_port &= ~(0x01 << CSEL1); // deselect chip 1 lcd_cmd_port |= 0x01 << CSEL2; // select chip 2 } if(ks0108_coord.x == 64 && first) { // chip2 X-address = 0 ks0108_write_command(LCD_SET_ADD, CHIP2); // wuff wuff } lcd_cmd_port |= 0x01 << D_I; // D/I = 1 lcd_cmd_port |= 0x01 << R_W; // R/W = 1 lcd_cmd_port |= 0x01 << EN; // EN high level width: min. 450ns.I created 500ns with 16Mhz crystal. asm volatile( "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" ::); //_delay_us(0.45); data = lcd_data_pin; // read Data lcd_cmd_port &= ~(0x01 << EN); for(i=0; i<8; i++); // a little delay loop (faster than reading the busy flag) lcd_data_ddr = 0xFF; ks0108_gotoxy(ks0108_coord.x, ks0108_coord.y); if(ks0108_inverted) data = ~data; return data; } uint8_t ks0108_read_data(void) { ks0108DoReadData(1); // dummy read return ks0108DoReadData(0); // "real" read } void ks0108_write_command(uint8_t cmd, uint8_t chip) { if(chip == CHIP1) { lcd_cmd_port &= ~(0x01 << CSEL2); // deselect chip 2 lcd_cmd_port |= 0x01 << CSEL1; // select chip 1 } else if(chip == CHIP2) { lcd_cmd_port &= ~(0x01 << CSEL1); // deselect chip 1 lcd_cmd_port |= 0x01 << CSEL2; // select chip 2 } lcd_cmd_port &= ~(0x01 << D_I); // D/I = 0 lcd_cmd_port &= ~(0x01 << R_W); // R/W = 0 lcd_data_ddr = 0xFF; // data port is output lcd_data_port = cmd; // write command ks0108Enable(); // enable lcd_data_port = 0x00; } void ks0108_write_data(uint8_t data) { uint8_t displayData, yOffset, cmdPort; #ifdef DEBUG volatile uint16_t i; for(i=0; i<5000; i++); #endif if(ks0108_coord.x >= 128) return; if(ks0108_coord.x < 64) { lcd_cmd_port &= ~(0x01 << CSEL2); // deselect chip 2 lcd_cmd_port |= 0x01 << CSEL1; // select chip 1 } else if(ks0108_coord.x >= 64) { lcd_cmd_port &= ~(0x01 << CSEL1); // deselect chip 1 lcd_cmd_port |= 0x01 << CSEL2; // select chip 2 } if(ks0108_coord.x == 64) // chip2 X-address = 0 ks0108_write_command(LCD_SET_ADD, CHIP2); lcd_cmd_port |= 0x01 << D_I; // D/I = 1 lcd_cmd_port &= ~(0x01 << R_W); // R/W = 0 lcd_data_ddr = 0xFF; // data port is output yOffset = ks0108_coord.y%8; if(yOffset != 0) { // first page cmdPort = lcd_cmd_port; // save command port displayData = ks0108_read_data(); lcd_cmd_port = cmdPort; // restore command port lcd_data_ddr = 0xFF; // data port is output displayData |= data << yOffset; if(ks0108_inverted) displayData = ~displayData; lcd_data_port = displayData; // write data ks0108Enable(); // enable // second page ks0108_gotoxy(ks0108_coord.x, ks0108_coord.y+8); displayData = ks0108_read_data(); lcd_cmd_port = cmdPort; // restore command port lcd_data_ddr = 0xFF; // data port is output displayData |= data >> (8-yOffset); if(ks0108_inverted) displayData = ~displayData; lcd_data_port = displayData; // write data ks0108Enable(); // enable ks0108_gotoxy(ks0108_coord.x+1, ks0108_coord.y-8); } else { if(ks0108_inverted) data = ~data; lcd_data_port = data; // write data ks0108Enable(); // enable ks0108_coord.x++; } lcd_data_port = 0x00; }