aboutsummaryrefslogtreecommitdiffstats
path: root/glcd/glcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'glcd/glcd.c')
-rw-r--r--glcd/glcd.c529
1 files changed, 529 insertions, 0 deletions
diff --git a/glcd/glcd.c b/glcd/glcd.c
new file mode 100644
index 00000000..af502532
--- /dev/null
+++ b/glcd/glcd.c
@@ -0,0 +1,529 @@
+#include "glcd.h"
+#include <stdlib.h>
+#include <math.h>
+
+#define EMSG(a) const struct a *emsg = (const struct a*)msg
+
+uint16_t lcd_width, lcd_height;
+static Thread *workerThread = NULL;
+
+static WORKING_AREA(waGLCDWorkerThread, GLCD_WORKER_SIZE);
+static msg_t ThreadGLCDWorker(void *arg) {
+ (void)arg;
+ Thread *p;
+
+ chRegSetThreadName("GLCDWorker");
+
+ while(TRUE) {
+ /* Wait for msg with work to do. */
+ p = chMsgWait();
+ struct glcd_msg_base *msg = (struct glcd_msg_base*)chMsgGet(p);
+ msg->result = GLCD_PROGRESS;
+
+ /* do work here */
+ switch(msg->action) {
+ case GLCD_SET_POWERMODE: {
+ EMSG(glcd_msg_powermode);
+ lld_lcdSetPowerMode(emsg->powermode);
+ msg->result = GLCD_DONE;
+ break;
+ }
+
+ case GLCD_SET_ORIENTATION: {
+ EMSG(glcd_msg_orientation);
+ lld_lcdSetOrientation(emsg->newOrientation);
+ msg->result = GLCD_DONE;
+ break;
+ }
+
+ case GLCD_SET_WINDOW: {
+ EMSG(glcd_msg_set_window);
+ lld_lcdSetWindow(emsg->x0, emsg->y0, emsg->x1, emsg->y1);
+ msg->result = GLCD_DONE;
+ break;
+ }
+
+ case GLCD_FILL_AREA: {
+ EMSG(glcd_msg_fill_area);
+ lld_lcdFillArea(emsg->x0, emsg->y0, emsg->x1, emsg->y1, emsg->color);
+ msg->result = GLCD_DONE;
+ break;
+ }
+
+ case GLCD_WRITE_AREA: {
+ EMSG(glcd_msg_write_area);
+ lld_lcdSetWindow(emsg->x0, emsg->y0, emsg->x1, emsg->y1);
+ lld_lcdWriteStreamStart();
+ lld_lcdWriteStream(emsg->buffer, emsg->size);
+ lld_lcdWriteStreamStop();
+ msg->result = GLCD_DONE;
+ break;
+ }
+
+ case GLCD_CLEAR: {
+ EMSG(glcd_msg_clear);
+ lld_lcdClear(emsg->color);
+ msg->result = GLCD_DONE;
+ break;
+ }
+
+ case GLCD_GET_PIXEL_COLOR: {
+ /* ToDo */
+
+ msg->result = GLCD_DONE;
+ break;
+ }
+
+ case GLCD_DRAW_PIXEL: {
+ EMSG(glcd_msg_draw_pixel);
+ lld_lcdDrawPixel(emsg->x, emsg->y, emsg->color);
+ msg->result = GLCD_DONE;
+ break;
+ }
+
+ case GLCD_WRITE_STREAM_START: {
+ lld_lcdWriteStreamStart();
+ msg->result = GLCD_DONE;
+ break;
+ }
+
+ case GLCD_WRITE_STREAM_STOP: {
+ lld_lcdWriteStreamStop();
+ msg->result = GLCD_DONE;
+ break;
+ }
+
+ case GLCD_WRITE_STREAM: {
+ EMSG(glcd_msg_write_stream);
+ lld_lcdWriteStream(emsg->buffer, emsg->size);
+ msg->result = GLCD_DONE;
+ break;
+ }
+ }
+
+ /* Done, release msg again. */
+ chMsgRelease(p, 0);
+ }
+
+ return 0;
+}
+
+void lcdInit(GLCDDriver *glcdp) {
+ workerThread = chThdCreateStatic(waGLCDWorkerThread, sizeof(waGLCDWorkerThread), NORMALPRIO, ThreadGLCDWorker, NULL);
+
+ lld_lcdInit();
+ lcd_width = lcdGetWidth();
+ lcd_height = lcdGetHeight();
+
+ lcdSetPowerMode(powerOn);
+ lcdSetOrientation(portrait);
+}
+
+uint16_t lcdGetHeight(void) {
+ return lld_lcdGetHeight();
+}
+
+uint16_t lcdGetWidth(void) {
+ return lld_lcdGetWidth();
+}
+
+uint16_t lcdGetOrientation(void) {
+ return lld_lcdGetOrientation();
+}
+
+void lcdSetPowerMode(uint8_t powerMode) {
+ struct glcd_msg_powermode msg;
+
+ msg.action = GLCD_SET_POWERMODE;
+ msg.powermode = powerMode;
+
+ chMsgSend(workerThread, (msg_t)&msg);
+}
+
+void lcdSetOrientation(uint8_t newOrientation) {
+ struct glcd_msg_orientation msg;
+
+ msg.action = GLCD_SET_ORIENTATION;
+ msg.newOrientation = newOrientation;
+
+ chMsgSend(workerThread, (msg_t)&msg);
+}
+
+void lcdSetWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
+ struct glcd_msg_set_window msg;
+
+ msg.action = GLCD_SET_WINDOW;
+ msg.x0 = x0;
+ msg.y0 = y0;
+ msg.x1 = x1;
+ msg.y1 = y1;
+
+ chMsgSend(workerThread, (msg_t)&msg);
+}
+
+void lcdFillArea(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color) {
+ struct glcd_msg_fill_area msg;
+
+ msg.action = GLCD_FILL_AREA;
+ msg.x0 = x0;
+ msg.y0 = y0;
+ msg.x1 = x1;
+ msg.y1 = y1;
+ msg.color = color;
+
+ chMsgSend(workerThread, (msg_t)&msg);
+}
+
+void lcdWriteArea(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t *buffer, size_t n) {
+ struct glcd_msg_write_area msg;
+
+ msg.action = GLCD_WRITE_AREA;
+ msg.x0 = x0;
+ msg.y0 = y0;
+ msg.x1 = x1;
+ msg.y1 = y1;
+ msg.buffer = buffer;
+ msg.size = n;
+
+ chMsgSend(workerThread, (msg_t)&msg);
+}
+
+void lcdClear(uint16_t color) {
+ struct glcd_msg_clear msg;
+
+ msg.action = GLCD_CLEAR;
+ msg.color = color;
+
+ chMsgSend(workerThread, (msg_t)&msg);
+}
+
+uint16_t lcdGetPixelColor(uint16_t x, uint16_t y) {
+ struct glcd_msg_get_pixel_color msg;
+ uint16_t result;
+
+ msg.action = GLCD_GET_PIXEL_COLOR;
+ msg.x = x;
+ msg.y = y;
+ msg.color = &result;
+
+ chMsgSend(workerThread, (msg_t)&msg);
+
+ while(msg.result != GLCD_DONE);
+
+ return result;
+}
+
+void lcdDrawPixel(uint16_t x, uint16_t y, uint16_t color) {
+ struct glcd_msg_draw_pixel msg;
+
+ msg.action = GLCD_DRAW_PIXEL;
+ msg.x = x;
+ msg.y = y;
+ msg.color = color;
+
+ chMsgSend(workerThread, (msg_t)&msg);
+}
+
+static void lcdWriteStreamStart(void) {
+ struct glcd_msg_write_stream_start msg;
+
+ msg.action = GLCD_WRITE_STREAM_START;
+
+ chMsgSend(workerThread, (msg_t)&msg);
+}
+
+static void lcdWriteStreamStop(void) {
+ struct glcd_msg_write_stream_stop msg;
+
+ msg.action = GLCD_WRITE_STREAM_STOP;
+
+ chMsgSend(workerThread, (msg_t)&msg);
+}
+
+static void lcdWriteStream(uint16_t *buffer, uint16_t size) {
+ struct glcd_msg_write_stream msg;
+
+ msg.action = GLCD_WRITE_STREAM;
+ msg.buffer = buffer;
+ msg.size = size;
+
+ chMsgSend(workerThread, (msg_t)&msg);
+}
+
+void lcdDrawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color) {
+ int16_t dy, dx;
+ int16_t addx = 1, addy = 1;
+ int16_t P, diff;
+
+ int16_t i = 0;
+ dx = abs((int16_t)(x1 - x0));
+ dy = abs((int16_t)(y1 - y0));
+
+ if(x0 > x1)
+ addx = -1;
+ if(y0 > y1)
+ addy = -1;
+
+ if(dx >= dy) {
+ dy *= 2;
+ P = dy - dx;
+ diff = P - dx;
+
+ for(; i<=dx; ++i) {
+ lcdDrawPixel(x0, y0, color);
+ if(P < 0) {
+ P += dy;
+ x0 += addx;
+ } else {
+ P += diff;
+ x0 += addx;
+ y0 += addy;
+ }
+ }
+ } else {
+ dx *= 2;
+ P = dx - dy;
+ diff = P - dy;
+
+ for(; i<=dy; ++i) {
+ lcdDrawPixel(x0, y0, color);
+ if(P < 0) {
+ P += dx;
+ y0 += addy;
+ } else {
+ P += diff;
+ x0 += addx;
+ y0 += addy;
+ }
+ }
+ }
+}
+
+uint16_t lcdDrawChar(uint16_t cx, uint16_t cy, char c, font_t font, uint16_t color, uint16_t bkcolor, bool_t tpText) {
+ /* Working pointer */
+ const uint8_t* ptr;
+ uint8_t x, y;
+
+ /* Variables to store character details */
+ uint8_t charWidth;
+ uint8_t charHeight = lcdGetFontHeight(font);
+ uint8_t padAfterChar = font[FONT_TABLE_PAD_AFTER_CHAR_IDX];
+
+ /* Local var to hold offset in font table */
+ uint16_t charStartOffset;
+
+ /* Working buffer for fast non-transparent text rendering [patch by Badger] */
+ static uint16_t buf[20*16];
+
+ /* No support for nongraphic characters, so just ignore them */
+ if(c < 0x20 || c > 0x7F) {
+ return RDY_OK;
+ }
+
+ /* Read the offset of the character data in the font table from the lookup table */
+ charStartOffset = *(uint16_t*)(&font[FONT_TABLE_CHAR_LOOKUP_IDX + (c - 0x20) * 2]);
+
+ /* After we're done, position the pointer at the offset.
+ * The first byte that is immediately read will be the font width
+ * After that, actual 16-bit font data follows, first column down */
+ ptr = font + charStartOffset;
+ charWidth = *(ptr++);
+
+ /* Loop through the data and display. The font data is LSB first, down the column */
+ for(x = 0; x < charWidth; x++) {
+ /* Get the font bitmap data for the column */
+ uint16_t charData = *(uint16_t*)ptr;
+
+ for(y = 0; y < charHeight; y++) {
+ /* Draw the LSB on the screen accordingly. */
+ if(!tpText) {
+ /* Store data into working buffer (patch by Badger),
+ * Then write it all onto the LCD in one stroke */
+ buf[y*charWidth + x] = (charData & 0x01) ? color : bkcolor;
+ } else {
+ /* Just draw the needed pixels onto the LCD */
+ if (charData & 0x01)
+ lcdDrawPixel(cx+x, cy+y, color);
+ }
+
+ /* Shift the data down by one bit */
+ charData >>= 1;
+ }
+
+ /* Increment pointer by 2 bytes to the next column */
+ ptr += 2;
+ }
+
+ if(!tpText) {
+ /* [Patch by Badger] Write all in one stroke */
+ lcdWriteArea(cx, cy, cx+charWidth, cy+charHeight, buf, charWidth*charHeight);
+
+ /* Do padding after character, if needed for solid text rendering
+ * TODO: To be optimised */
+ if (padAfterChar != 0) {
+ lcdFillArea(cx+charWidth, cy+charHeight, cx+charWidth+padAfterChar, cy+charHeight, bkcolor);
+ }
+ }
+
+ /* Return the width of the character, we need it so that lcdDrawString may work
+ * We don't have a static address counter */
+ return charWidth + padAfterChar;
+}
+
+/* WARNING: No boundary checks! Unpredictable behaviour if text exceeds boundary */
+void lcdDrawString(uint16_t x, uint16_t y, const char *str, font_t font, uint16_t color, uint16_t bkcolor, bool_t tpText) {
+ uint16_t cx = x, cy = y;
+
+ while (*str) {
+ cx += lcdDrawChar(cx, cy, *str++, font, color, bkcolor, tpText);
+ }
+}
+
+uint16_t lcdMeasureChar(char c, font_t font) {
+ /* Variables to store character details */
+ uint8_t charWidth;
+ uint8_t padAfterChar = font[FONT_TABLE_PAD_AFTER_CHAR_IDX];
+
+ /* Local var to hold offset in font table */
+ uint16_t charStartOffset;
+
+ /* No support for nongraphic characters, so just ignore them */
+ if(c < 0x20 || c > 0x7F) {
+ return 0;
+ }
+
+ /* Read the offset of the character data in the font table from the lookup table */
+ charStartOffset = *(uint16_t*)(&font[FONT_TABLE_CHAR_LOOKUP_IDX + (c - 0x20) * 2]);
+
+ /* Retrurn the byte at the offset, that's our charWidth */
+ charWidth = *(font + charStartOffset);
+
+ return charWidth+padAfterChar;
+}
+
+uint16_t lcdMeasureString(const char *str, font_t font) {
+ uint16_t result = 0;
+
+ /* Measure each char width, add it, return the result */
+ while (*str)
+ result += lcdMeasureChar(*str++, font);
+
+ return result;
+}
+
+uint16_t lcdBGR2RGB(uint16_t color) {
+ uint16_t r, g, b, rgb;
+
+ b = ( color>>0 ) & 0x1f;
+ g = ( color>>5 ) & 0x3f;
+ r = ( color>>11 ) & 0x1f;
+
+ rgb = (b<<11) + (g<<5) + (r<<0);
+
+ return( rgb );
+}
+
+void lcdDrawRect(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t filled, uint16_t color) {
+ uint16_t i, TempX;
+ uint16_t j, TempY;
+
+ if (x0 > x1) {
+ TempX = x1;
+ x1 = x0;
+ x0 = TempX;
+ }
+ if (y0 > y1) {
+ TempY = y1;
+ y1 = y0;
+ y0 = TempY;
+ }
+ if(filled) {
+ for(i=x0; i<x1; i++)
+ for(j=y0; j<y1; j++)
+ lcdDrawPixel(i , j , color);
+ } else {
+ lcdDrawLine(x0, y0, x1, y0, color);
+ lcdDrawLine(x0, y1, x1, y1, color);
+ lcdDrawLine(x0, y0, x0, y1, color);
+ lcdDrawLine(x1, y0, x1, y1, color);
+ }
+}
+
+void lcdDrawRectString(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, const char* str, font_t font, uint16_t fontColor, uint16_t bkColor) {
+ uint16_t off_left, off_up;
+
+ off_left = ((x1-x0)-lcdMeasureString(str, font))/2;
+ off_up = ((y1-y0) - lcdGetFontHeight(font)) / 2;
+
+ lcdDrawRect(x0, y0, x1, y1, 1, bkColor);
+ /* Abhishek: default to solid text for this? */
+ lcdDrawString(x0+off_left, y0+off_up, str, font, fontColor, bkColor, solid);
+}
+
+void lcdDrawCircle(uint16_t x, uint16_t y, uint16_t radius, uint8_t filled, uint16_t color) {
+ int16_t a, b, P;
+ a = 0;
+ b = radius;
+ P = 1 - radius;
+
+ do {
+ if(filled) {
+ lcdDrawLine(x-a, y+b, x+a, y+b, color);
+ lcdDrawLine(x-a, y-b, x+a, y-b, color);
+ lcdDrawLine(x-b, y+a, x+b, y+a, color);
+ lcdDrawLine(x-b, y-a, x+b, y-a, color);
+ } else {
+ lcdDrawPixel(a+x, b+y, color);
+ lcdDrawPixel(b+x, a+y, color);
+ lcdDrawPixel(x-a, b+y, color);
+ lcdDrawPixel(x-b, a+y, color);
+ lcdDrawPixel(b+x, y-a, color);
+ lcdDrawPixel(a+x, y-b, color);
+ lcdDrawPixel(x-a, y-b, color);
+ lcdDrawPixel(x-b, y-a, color);
+ }
+
+ if(P < 0)
+ P += 3 + 2*a++;
+ else
+ P += 5 + 2*(a++ - b--);
+ } while(a <= b);
+}
+
+void lcdDrawEllipse(uint16_t x, uint16_t y, uint16_t a, uint16_t b, uint8_t filled, uint16_t color) {
+ int dx = 0, dy = b; /* im I. Quadranten von links oben nach rechts unten */
+ long a2 = a*a, b2 = b*b;
+ long err = b2-(2*b-1)*a2, e2; /* Fehler im 1. Schritt */
+
+ do {
+ if(filled){
+ lcdDrawLine(x-dx,y+dy,x+dx,y+dy, color);
+ lcdDrawLine(x-dx,y-dy,x+dx,y-dy, color);
+ }else{
+ lcdDrawPixel(x+dx, y+dy, color); /* I. Quadrant */
+ lcdDrawPixel(x-dx, y+dy, color); /* II. Quadrant */
+ lcdDrawPixel(x-dx, y-dy, color); /* III. Quadrant */
+ lcdDrawPixel(x+dx, y-dy, color); /* IV. Quadrant */
+ }
+
+ e2 = 2*err;
+ if(e2 < (2*dx+1)*b2) {
+ dx++;
+ err += (2*dx+1)*b2;
+ }
+ if(e2 > -(2*dy-1)*a2) {
+ dy--;
+ err -= (2*dy-1)*a2;
+ }
+ } while(dy >= 0);
+
+ while(dx++ < a) { /* fehlerhafter Abbruch bei flachen Ellipsen (b=1) */
+ lcdDrawPixel(x+dx, y, color); /* -> Spitze der Ellipse vollenden */
+ lcdDrawPixel(x-dx, y, color);
+ }
+}
+
+void lcdVerticalScroll(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, int16_t lines) {
+ lld_lcdVerticalScroll(x0,y0,x1,y1,lines);
+}
+