#include #include "t6a04.h" void t6a04_init(T6A04 *lcd) { memset(lcd->ram, 0, T6A04_RAMSIZE); lcd->enabled = false; lcd->incmode = T6A04_XINC; lcd->offset = 0; lcd->currow = 0; lcd->curcol = 0; lcd->just_moved = true; lcd->has8bitmode = false; } uint8_t t6a04_cmd_rd(T6A04 *lcd) { return 0; // we are always ready for a new cmd } /* * 0x00/0x01: 6/8 bit mode * 0x02/0x03: enable/disable * 0x04-0x07: incmodes * 0x20-0x34: set col * 0x40-0x7f: set Z offset * 0x80-0xbf: set row * 0xc0-0xff: set contrast */ void t6a04_cmd_wr(T6A04 *lcd, uint8_t val) { if ((val & 0xc0) == 0xc0) { // contrast, ignoring } else if (val & 0x80) { lcd->currow = val & 0x3f; lcd->just_moved = true; } else if (val & 0x40) { lcd->offset = val & 0x3f; } else if (val & 0x20) { lcd->curcol = val & 0x1f; lcd->just_moved = true; } else if (val & 0x18) { // stuff we don't emulate } else if (val & 0x04) { lcd->incmode = val & 0x03; } else if (val & 0x02) { lcd->enabled = val & 0x01; } else { lcd->has8bitmode = val; } } // Advance current position according to current incmode static void _advance(T6A04 *lcd) { uint8_t maxY = lcd->has8bitmode ? 14 : 19; switch (lcd->incmode) { case T6A04_XDEC: lcd->currow = (lcd->currow-1) & 0x3f; break; case T6A04_XINC: lcd->currow = (lcd->currow+1) & 0x3f; break; case T6A04_YDEC: if (lcd->curcol == 0) { lcd->curcol = maxY; } else { lcd->curcol--; } break; case T6A04_YINC: if (lcd->curcol < maxY) { lcd->curcol++; } else { lcd->curcol = 0; } break; } } uint8_t t6a04_data_rd(T6A04 *lcd) { uint8_t res; if (lcd->just_moved) { // After a move command, the first read op is a noop. lcd->just_moved = false; return 0; } if (lcd->has8bitmode) { int pos = lcd->currow * T6A04_ROWSIZE + lcd->curcol; res = lcd->ram[pos]; } else { // 6bit mode is a bit more complicated because the 6-bit number often // spans two bytes. We manage this by loading two bytes into a uint16_t // and then shift it right properly. // bitpos represents the leftmost bit of our 6bit number. int bitpos = lcd->curcol * 6; // offset represents the shift right we need to perform from the two // bytes following bitpos/8 so that we can have our number with a 6-bit // mask. // Example, col 3 has a bitpos of 18, which means that it loads bytes 2 // and 3. Its bits would be in bit pos 14:8, which means it has an // offset of 8. There is always an offset and its always in the 3-10 // range int offset = 10 - (bitpos % 8); // 10 is for 16bit - 6bit int pos = (lcd->currow * T6A04_ROWSIZE) + (bitpos / 8); uint16_t word = lcd->ram[pos] << 8; word |= lcd->ram[pos+1]; res = (word >> offset) & 0x3f; } _advance(lcd); return res; } void t6a04_data_wr(T6A04 *lcd, uint8_t val) { lcd->just_moved = false; if (lcd->has8bitmode) { int pos = lcd->currow * T6A04_ROWSIZE + lcd->curcol; lcd->ram[pos] = val; } else { // See comments in t6a04_data_rd(). int bitpos = lcd->curcol * 6; int offset = 10 - (bitpos % 8); int pos = (lcd->currow * T6A04_ROWSIZE) + (bitpos / 8); uint16_t word = lcd->ram[pos] << 8; word |= lcd->ram[pos+1]; // word contains our current ram value. Let's fit val in this. word &= ~(0x003f << offset); word |= val << offset; lcd->ram[pos] = word >> 8; lcd->ram[pos+1] = word & 0xff; } _advance(lcd); } bool t6a04_pixel(T6A04 *lcd, uint8_t y, uint8_t x) { x = (x + lcd->offset) & 0x3f; uint8_t val = lcd->ram[x * T6A04_ROWSIZE + (y / 8)]; return (val >> (7 - (y % 8))) & 1; }