diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index fdef521..50e93a7 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,15 +1,16 @@ set(COMPONENT_SRCS "app_main.c" "knob.c" - "gui.c" + "liquid/gui.c" "analog.c" "liquid/liquid.c" - "liquid/scene_root.c" - "liquid/scene_car.c" + "scenes/scene_root.c" + "scenes/scene_car.c" "graphics/nokia.c" "graphics/utf8.c" "graphics/font.c" + "graphics/drawing.c" ) -set(COMPONENT_ADD_INCLUDEDIRS "liquid" "graphics") +set(COMPONENT_ADD_INCLUDEDIRS "." "liquid" "graphics") register_component() diff --git a/main/graphics/display_spec.h b/main/graphics/display_spec.h new file mode 100644 index 0000000..6719dc7 --- /dev/null +++ b/main/graphics/display_spec.h @@ -0,0 +1,13 @@ +/** + * TODO file description + * + * Created on 2020/01/05. + */ + +#ifndef REFLOWER_DISPLAY_SPEC_H +#define REFLOWER_DISPLAY_SPEC_H + +#define LCD_WIDTH 84 // Note: x-coordinates go wide +#define LCD_HEIGHT 48 // Note: y-coordinates go high + +#endif //REFLOWER_DISPLAY_SPEC_H diff --git a/main/graphics/drawing.c b/main/graphics/drawing.c new file mode 100644 index 0000000..b1d92bd --- /dev/null +++ b/main/graphics/drawing.c @@ -0,0 +1,313 @@ +#include "drawing.h" +#include "display_spec.h" +#include "font.h" + +extern uint8_t LCD_displayMap[LCD_WIDTH * LCD_HEIGHT / 8]; + +// This function sets a pixel on displayMap to your preferred +// color. 1=Black, 0= white. +void LCD_setPixel(int x, int y, enum Color bw) +{ + // First, double check that the coordinate is in range. + if ((x >= 0) && (x < LCD_WIDTH) && (y >= 0) && (y < LCD_HEIGHT)) { + uint8_t shift = y % 8; + + if (bw) // If black, set the bit. + LCD_displayMap[x + (y / 8) * LCD_WIDTH] |= 1 << shift; + else // If white clear the bit. + LCD_displayMap[x + (y / 8) * LCD_WIDTH] &= ~(1 << shift); + } +} + +// setLine draws a line from x0,y0 to x1,y1 with the set color. +// This function was grabbed from the SparkFun ColorLCDShield +// library. +void LCD_setLine(int x0, int y0, int x1, int y1, enum Color bw) +{ + int dy = y1 - y0; // Difference between y0 and y1 + int dx = x1 - x0; // Difference between x0 and x1 + int stepx, stepy; + + if (dy < 0) { + dy = -dy; + stepy = -1; + } + else + stepy = 1; + + if (dx < 0) { + dx = -dx; + stepx = -1; + } + else + stepx = 1; + + dy <<= 1; // dy is now 2*dy + dx <<= 1; // dx is now 2*dx + LCD_setPixel(x0, y0, bw); // Draw the first pixel. + + if (dx > dy) { + int fraction = dy - (dx >> 1); + while (x0 != x1) { + if (fraction >= 0) { + y0 += stepy; + fraction -= dx; + } + x0 += stepx; + fraction += dy; + LCD_setPixel(x0, y0, bw); + } + } + else { + int fraction = dx - (dy >> 1); + while (y0 != y1) { + if (fraction >= 0) { + x0 += stepx; + fraction -= dy; + } + y0 += stepy; + fraction += dx; + LCD_setPixel(x0, y0, bw); + } + } +} + +// setRect will draw a rectangle from x0,y0 top-left corner to +// a x1,y1 bottom-right corner. Can be filled with the fill +// parameter, and colored with bw. +// This function was grabbed from the SparkFun ColorLCDShield +// library. +void LCD_setRect(int x0, int y0, int x1, int y1, bool fill, enum Color bw) +{ + // check if the rectangle is to be filled + if (fill == 1) { + int xDiff; + + if (x0 > x1) + xDiff = x0 - x1; //Find the difference between the x vars + else + xDiff = x1 - x0; + + while (xDiff > 0) { + LCD_setLine(x0, y0, x0, y1, bw); + + if (x0 > x1) + x0--; + else + x0++; + + xDiff--; + } + } + else { + // best way to draw an unfilled rectangle is to draw four lines + LCD_setLine(x0, y0, x1, y0, bw); + LCD_setLine(x0, y1, x1, y1, bw); + LCD_setLine(x0, y0, x0, y1, bw); + LCD_setLine(x1, y0, x1, y1, bw); + } +} + +// setCircle draws a circle centered around x0,y0 with a defined +// radius. The circle can be black or white. And have a line +// thickness ranging from 1 to the radius of the circle. +// This function was grabbed from the SparkFun ColorLCDShield +// library. +void LCD_setCircle(int x0, int y0, int radius, enum Color bw, int lineThickness) +{ + for (int r = 0; r < lineThickness; r++) { + int f = 1 - radius; + int ddF_x = 0; + int ddF_y = -2 * radius; + int x = 0; + int y = radius; + + LCD_setPixel(x0, y0 + radius, bw); + LCD_setPixel(x0, y0 - radius, bw); + LCD_setPixel(x0 + radius, y0, bw); + LCD_setPixel(x0 - radius, y0, bw); + + while (x < y) { + if (f >= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x + 1; + + LCD_setPixel(x0 + x, y0 + y, bw); + LCD_setPixel(x0 - x, y0 + y, bw); + LCD_setPixel(x0 + x, y0 - y, bw); + LCD_setPixel(x0 - x, y0 - y, bw); + LCD_setPixel(x0 + y, y0 + x, bw); + LCD_setPixel(x0 - y, y0 + x, bw); + LCD_setPixel(x0 + y, y0 - x, bw); + LCD_setPixel(x0 - y, y0 - x, bw); + } + radius--; + } +} + +// This function will draw a char (defined in the ASCII table +// near the beginning of this sketch) at a defined x and y). +// The color can be either black (1) or white (0). +void LCD_setChar(struct Utf8Char character, int x, int y, enum Color bw) +{ + LCD_setCharEx(character, x, y, bw, 1); +} + +void LCD_setCharEx(struct Utf8Char character, int x, int y, enum Color bw, uint8_t style) +{ + const struct FontGraphic *symbol = Font_GetSymbol(character); + bool bg = style & 0x80; + enum FontStyle style_real = style & 0x7F; + + uint8_t column; // temp byte to store character's column bitmap + for (int i = 0; i < 5; i++) // 5 columns (x) per character + { + column = symbol->columns[i]; + for (int j = 0; j < 8; j++) // 8 rows (y) per character + { + bool bit = column & (1 << j); + + if (style_real == FONT_NORMAL) { + if (bit) {// test bits to set pixels + LCD_setPixel(x + i, y + j, bw); + } + else if (bg) { + LCD_setPixel(x + i, y + j, !bw); + } + } + else if (style_real == FONT_DOUBLE) { + if (bit) {// test bits to set pixels + LCD_setPixel(x + i * 2, y + j * 2, bw); + LCD_setPixel(x + i * 2 + 1, y + j * 2, bw); + LCD_setPixel(x + i * 2, y + j * 2 + 1, bw); + LCD_setPixel(x + i * 2 + 1, y + j * 2 + 1, bw); + } + else if (bg) { + LCD_setPixel(x + i * 2, y + j * 2, !bw); + LCD_setPixel(x + i * 2 + 1, y + j * 2, !bw); + LCD_setPixel(x + i * 2, y + j * 2 + 1, !bw); + LCD_setPixel(x + i * 2 + 1, y + j * 2 + 1, !bw); + } + } + else if (style_real == FONT_BOLD) { + if (bit) {// test bits to set pixels + LCD_setPixel(x + i, y + j, bw); + LCD_setPixel(x + i + 1, y + j, bw); + } + else if (bg) { + LCD_setPixel(x + i, y + j, !bw); + LCD_setPixel(x + i + 1, y + j, !bw); + } + } + } + } +} + +// setStr draws a string of characters, calling setChar with +// progressive coordinates until it's done. +// This function was grabbed from the SparkFun ColorLCDShield +// library. +void LCD_setStr(const char *dString, int x, int y, enum Color bw) +{ + LCD_setStrEx(dString, x, y, bw, 1); +} + +// setStr draws a string of characters, calling setChar with +// progressive coordinates until it's done. +// This function was grabbed from the SparkFun ColorLCDShield +// library. +void LCD_setStrEx(const char *dString, int x, int y, enum Color bw, uint8_t style) +{ + struct Utf8Iterator iter; + Utf8Iterator_Init(&iter, dString); + bool bg = style & 0x80; + enum FontStyle style_real = style & 0x7F; + + struct Utf8Char uchar; + while ((uchar = Utf8Iterator_Next(&iter)).uint) { + LCD_setCharEx(uchar, x, y, bw, style); + + if (style_real == FONT_NORMAL) { + x += 5; + if (bg) { + for (int i = y; i < y + 8; i++) { + LCD_setPixel(x, i, !bw); + } + } + x++; + if (x > (LCD_WIDTH - 5)) // Enables wrap around + { + x = 0; + y += 8; + } + } + else if (style_real == FONT_DOUBLE) { + x += 10; + if (bg) { + for (int i = y; i < y + 16; i++) { + LCD_setPixel(x, i, !bw); + LCD_setPixel(x + 1, i, !bw); + } + } + x += 2; + if (x > (LCD_WIDTH - 10)) // Enables wrap around + { + x = 0; + y += 16; + } + } + else if (style_real == FONT_BOLD) { + x += 6; + if (bg) { + for (int i = y; i < y + 8; i++) { + LCD_setPixel(x, i, !bw); + LCD_setPixel(x + 1, i, !bw); + } + } + x += 1; + if (x > (LCD_WIDTH - 6)) // Enables wrap around + { + x = 0; + y += 8; + } + } + } +} + +// This function will draw an array over the screen. (For now) the +// array must be the same size as the screen, covering the entirety +// of the display. +// Also, the array must reside in FLASH and declared with PROGMEM. +void LCD_setBitmap(const char *bitArray) +{ + for (int i = 0; i < (LCD_WIDTH * LCD_HEIGHT / 8); i++) { + char c = bitArray[i]; + LCD_displayMap[i] = c; + } +} + +// This function clears the entire display either white (0) or +// black (1). +// The screen won't actually clear until you call updateDisplay()! +void LCD_clearDisplay(enum Color bw) +{ + for (int i = 0; i < (LCD_WIDTH * LCD_HEIGHT / 8); i++) { + if (bw) + LCD_displayMap[i] = 0xFF; + else + LCD_displayMap[i] = 0; + } +} + +void LCD_invertDisplayData() +{ + /* Indirect, swap bits in displayMap option: */ + for (int i = 0; i < (LCD_WIDTH * LCD_HEIGHT / 8); i++) { + LCD_displayMap[i] ^= 0xFF; + } +} diff --git a/main/graphics/drawing.h b/main/graphics/drawing.h new file mode 100644 index 0000000..6a11ea6 --- /dev/null +++ b/main/graphics/drawing.h @@ -0,0 +1,75 @@ +/** + * TODO file description + * + * Created on 2020/01/05. + */ + +#ifndef REFLOWER_DRAWING_H +#define REFLOWER_DRAWING_H + +#include +#include + +#include "utf8.h" + +enum Color { + WHITE = 0, + BLACK = 1, +}; + +// This function sets a pixel on displayMap to your preferred +// color. 1=Black, 0= white. +void LCD_setPixel(int x, int y, enum Color bw); + +// setLine draws a line from x0,y0 to x1,y1 with the set color +void LCD_setLine(int x0, int y0, int x1, int y1, enum Color bw); + +// setRect will draw a rectangle from x0,y0 top-left corner to +// a x1,y1 bottom-right corner. Can be filled with the fill +// parameter, and colored with bw. +void LCD_setRect(int x0, int y0, int x1, int y1, bool fill, enum Color bw); + +// setCircle draws a circle centered around x0,y0 with a defined +// radius. The circle can be black or white. And have a line +// thickness ranging from 1 to the radius of the circle. +void LCD_setCircle (int x0, int y0, int radius, enum Color bw, int lineThickness); + +/* + FONT FUNCTIONS + size = 1 ... normal + size = 2 ... 2x + size = 3 ... normal bold + size | 0x80 ... clear background behind characters +*/ + +enum FontStyle { + FONT_NORMAL = 1, + FONT_DOUBLE = 2, + FONT_BOLD = 3, + FONT_BG = 0x80, +}; + +// This function will draw a char (defined in the ASCII table +// near the beginning of this sketch) at a defined x and y). +// The color can be either black (1) or white (0). +void LCD_setChar(struct Utf8Char character, int x, int y, enum Color bw); + +void LCD_setCharEx(struct Utf8Char character, int x, int y, enum Color bw, uint8_t style); + +// setStr draws a string of characters, calling setChar with +// progressive coordinates until it's done. +// This function was grabbed from the SparkFun ColorLCDShield +// library. +void LCD_setStr(const char * dString, int x, int y, enum Color bw); + +void LCD_setStrEx(const char *dString, int x, int y, enum Color bw, uint8_t style); + +// This function clears the entire display either white (0) or +// black (1). +// The screen won't actually clear until you call updateDisplay()! +void LCD_clearDisplay(enum Color bw); + +/* Invert colors (hard change in data; does NOT send to display immediately) */ +void LCD_invertDisplayData(); + +#endif //REFLOWER_DRAWING_H diff --git a/main/graphics/font.c b/main/graphics/font.c index 19841eb..5e57dd6 100644 --- a/main/graphics/font.c +++ b/main/graphics/font.c @@ -4,142 +4,156 @@ #define FONT_EXTRAS_START (0x7e - 0x20 + 1) -const struct FontSymbol font[] = { - // ASCII from 0x20 to 0x7e are ordered at the beginning for quick access by index - {.symbol=" ", .graphic = {0x00, 0x00, 0x00, 0x00, 0x00}}, // 0x20 - {.symbol="!", .graphic = {0x00, 0x00, 0x5f, 0x00, 0x00}}, // 0x21 - {.symbol="\"", .graphic = {0x00, 0x07, 0x00, 0x07, 0x00}}, // 0x22 - {.symbol="#", .graphic = {0x14, 0x7f, 0x14, 0x7f, 0x14}}, // 0x23 - {.symbol="$", .graphic = {0x24, 0x2a, 0x7f, 0x2a, 0x12}}, // 0x24 - {.symbol="%", .graphic = {0x23, 0x13, 0x08, 0x64, 0x62}}, // 0x25 - {.symbol="&", .graphic = {0x36, 0x49, 0x55, 0x22, 0x50}}, // 0x26 - {.symbol="'", .graphic = {0x00, 0x05, 0x03, 0x00, 0x00}}, // 0x27 - {.symbol="(", .graphic = {0x00, 0x1c, 0x22, 0x41, 0x00}}, // 0x28 - {.symbol=")", .graphic = {0x00, 0x41, 0x22, 0x1c, 0x00}}, // 0x29 - {.symbol="*", .graphic = {0x14, 0x08, 0x3e, 0x08, 0x14}}, // 0x2a - {.symbol="+", .graphic = {0x08, 0x08, 0x3e, 0x08, 0x08}}, // 0x2b - {.symbol=",", .graphic = {0x00, 0x50, 0x30, 0x00, 0x00}}, // 0x2c - {.symbol="-", .graphic = {0x08, 0x08, 0x08, 0x08, 0x08}}, // 0x2d - {.symbol=".", .graphic = {0x00, 0x60, 0x60, 0x00, 0x00}}, // 0x2e - {.symbol="/", .graphic = {0x20, 0x10, 0x08, 0x04, 0x02}}, // 0x2f - {.symbol="0", .graphic = {0x3e, 0x51, 0x49, 0x45, 0x3e}}, // 0x30 - {.symbol="1", .graphic = {0x00, 0x42, 0x7f, 0x40, 0x00}}, // 0x31 - {.symbol="2", .graphic = {0x42, 0x61, 0x51, 0x49, 0x46}}, // 0x32 - {.symbol="3", .graphic = {0x21, 0x41, 0x45, 0x4b, 0x31}}, // 0x33 - {.symbol="4", .graphic = {0x18, 0x14, 0x12, 0x7f, 0x10}}, // 0x34 - {.symbol="5", .graphic = {0x27, 0x45, 0x45, 0x45, 0x39}}, // 0x35 - {.symbol="6", .graphic = {0x3c, 0x4a, 0x49, 0x49, 0x30}}, // 0x36 - {.symbol="7", .graphic = {0x01, 0x71, 0x09, 0x05, 0x03}}, // 0x37 - {.symbol="8", .graphic = {0x36, 0x49, 0x49, 0x49, 0x36}}, // 0x38 - {.symbol="9", .graphic = {0x06, 0x49, 0x49, 0x29, 0x1e}}, // 0x39 - {.symbol=":", .graphic = {0x00, 0x36, 0x36, 0x00, 0x00}}, // 0x3a - {.symbol=";", .graphic = {0x00, 0x56, 0x36, 0x00, 0x00}}, // 0x3b - {.symbol="<", .graphic = {0x08, 0x14, 0x22, 0x41, 0x00}}, // 0x3c - {.symbol="=", .graphic = {0x14, 0x14, 0x14, 0x14, 0x14}}, // 0x3d - {.symbol=">", .graphic = {0x00, 0x41, 0x22, 0x14, 0x08}}, // 0x3e - {.symbol="?", .graphic = {0x02, 0x01, 0x51, 0x09, 0x06}}, // 0x3f - {.symbol="@", .graphic = {0x32, 0x49, 0x79, 0x41, 0x3e}}, // 0x40 - {.symbol="A", .graphic = {0x7e, 0x11, 0x11, 0x11, 0x7e}}, // 0x41 - {.symbol="B", .graphic = {0x7f, 0x49, 0x49, 0x49, 0x36}}, // 0x42 - {.symbol="C", .graphic = {0x3e, 0x41, 0x41, 0x41, 0x22}}, // 0x43 - {.symbol="D", .graphic = {0x7f, 0x41, 0x41, 0x22, 0x1c}}, // 0x44 - {.symbol="E", .graphic = {0x7f, 0x49, 0x49, 0x49, 0x41}}, // 0x45 - {.symbol="F", .graphic = {0x7f, 0x09, 0x09, 0x09, 0x01}}, // 0x46 - {.symbol="G", .graphic = {0x3e, 0x41, 0x49, 0x49, 0x7a}}, // 0x47 - {.symbol="H", .graphic = {0x7f, 0x08, 0x08, 0x08, 0x7f}}, // 0x48 - {.symbol="I", .graphic = {0x00, 0x41, 0x7f, 0x41, 0x00}}, // 0x49 - {.symbol="J", .graphic = {0x20, 0x40, 0x41, 0x3f, 0x01}}, // 0x4a - {.symbol="K", .graphic = {0x7f, 0x08, 0x14, 0x22, 0x41}}, // 0x4b - {.symbol="L", .graphic = {0x7f, 0x40, 0x40, 0x40, 0x40}}, // 0x4c - {.symbol="M", .graphic = {0x7f, 0x02, 0x0c, 0x02, 0x7f}}, // 0x4d - {.symbol="N", .graphic = {0x7f, 0x04, 0x08, 0x10, 0x7f}}, // 0x4e - {.symbol="O", .graphic = {0x3e, 0x41, 0x41, 0x41, 0x3e}}, // 0x4f - {.symbol="P", .graphic = {0x7f, 0x09, 0x09, 0x09, 0x06}}, // 0x50 - {.symbol="Q", .graphic = {0x3e, 0x41, 0x51, 0x21, 0x5e}}, // 0x51 - {.symbol="R", .graphic = {0x7f, 0x09, 0x19, 0x29, 0x46}}, // 0x52 - {.symbol="S", .graphic = {0x46, 0x49, 0x49, 0x49, 0x31}}, // 0x53 - {.symbol="T", .graphic = {0x01, 0x01, 0x7f, 0x01, 0x01}}, // 0x54 - {.symbol="U", .graphic = {0x3f, 0x40, 0x40, 0x40, 0x3f}}, // 0x55 - {.symbol="V", .graphic = {0x1f, 0x20, 0x40, 0x20, 0x1f}}, // 0x56 - {.symbol="W", .graphic = {0x3f, 0x40, 0x38, 0x40, 0x3f}}, // 0x57 - {.symbol="X", .graphic = {0x63, 0x14, 0x08, 0x14, 0x63}}, // 0x58 - {.symbol="Y", .graphic = {0x07, 0x08, 0x70, 0x08, 0x07}}, // 0x59 - {.symbol="Z", .graphic = {0x61, 0x51, 0x49, 0x45, 0x43}}, // 0x5a - {.symbol="[", .graphic = {0x00, 0x7f, 0x41, 0x41, 0x00}}, // 0x5b - {.symbol="\\", .graphic = {0x02, 0x04, 0x08, 0x10, 0x20}}, // 0x5c - {.symbol="]", .graphic = {0x00, 0x41, 0x41, 0x7f, 0x00}}, // 0x5d - {.symbol="^", .graphic = {0x04, 0x02, 0x01, 0x02, 0x04}}, // 0x5e - {.symbol="_", .graphic = {0x40, 0x40, 0x40, 0x40, 0x40}}, // 0x5f - {.symbol="`", .graphic = {0x00, 0x01, 0x02, 0x04, 0x00}}, // 0x60 - {.symbol="a", .graphic = {0x20, 0x54, 0x54, 0x54, 0x78}}, // 0x61 - {.symbol="b", .graphic = {0x7f, 0x48, 0x44, 0x44, 0x38}}, // 0x62 - {.symbol="c", .graphic = {0x38, 0x44, 0x44, 0x44, 0x20}}, // 0x63 - {.symbol="d", .graphic = {0x38, 0x44, 0x44, 0x48, 0x7f}}, // 0x64 - {.symbol="e", .graphic = {0x38, 0x54, 0x54, 0x54, 0x18}}, // 0x65 - {.symbol="f", .graphic = {0x08, 0x7e, 0x09, 0x01, 0x02}}, // 0x66 - {.symbol="g", .graphic = {0x0c, 0x52, 0x52, 0x52, 0x3e}}, // 0x67 - {.symbol="h", .graphic = {0x7f, 0x08, 0x04, 0x04, 0x78}}, // 0x68 - {.symbol="i", .graphic = {0x00, 0x44, 0x7d, 0x40, 0x00}}, // 0x69 - {.symbol="j", .graphic = {0x20, 0x40, 0x44, 0x3d, 0x00}}, // 0x6a - {.symbol="k", .graphic = {0x7f, 0x10, 0x28, 0x44, 0x00}}, // 0x6b - {.symbol="l", .graphic = {0x00, 0x41, 0x7f, 0x40, 0x00}}, // 0x6c - {.symbol="m", .graphic = {0x7c, 0x04, 0x18, 0x04, 0x78}}, // 0x6d - {.symbol="n", .graphic = {0x7c, 0x08, 0x04, 0x04, 0x78}}, // 0x6e - {.symbol="o", .graphic = {0x38, 0x44, 0x44, 0x44, 0x38}}, // 0x6f - {.symbol="p", .graphic = {0x7c, 0x14, 0x14, 0x14, 0x08}}, // 0x70 - {.symbol="q", .graphic = {0x08, 0x14, 0x14, 0x18, 0x7c}}, // 0x71 - {.symbol="r", .graphic = {0x7c, 0x08, 0x04, 0x04, 0x08}}, // 0x72 - {.symbol="s", .graphic = {0x48, 0x54, 0x54, 0x54, 0x20}}, // 0x73 - {.symbol="t", .graphic = {0x04, 0x3f, 0x44, 0x40, 0x20}}, // 0x74 - {.symbol="u", .graphic = {0x3c, 0x40, 0x40, 0x20, 0x7c}}, // 0x75 - {.symbol="v", .graphic = {0x1c, 0x20, 0x40, 0x20, 0x1c}}, // 0x76 - {.symbol="w", .graphic = {0x3c, 0x40, 0x30, 0x40, 0x3c}}, // 0x77 - {.symbol="x", .graphic = {0x44, 0x28, 0x10, 0x28, 0x44}}, // 0x78 - {.symbol="y", .graphic = {0x0c, 0x50, 0x50, 0x50, 0x3c}}, // 0x79 - {.symbol="z", .graphic = {0x44, 0x64, 0x54, 0x4c, 0x44}}, // 0x7a - {.symbol="{", .graphic = {0x00, 0x08, 0x36, 0x41, 0x00}}, // 0x7b - {.symbol="|", .graphic = {0x00, 0x00, 0x7f, 0x00, 0x00}}, // 0x7c - {.symbol="}", .graphic = {0x00, 0x41, 0x36, 0x08, 0x00}}, // 0x7d - {.symbol="~", .graphic = {0x10, 0x08, 0x08, 0x10, 0x08}}, // 0x7e +struct Utf8Symbol { + union { + const char symbol[4]; + const uint32_t uint; + }; + struct FontGraphic graphic; +}; - // start of UTF8 glyphs. These can include custom graphics mapped to codepoints - {.symbol="�", .graphic = {0xFF, 0x81, 0x81, 0x81, 0xFF}}, // box +// ASCII symbols are stored as bare graphic to reduce ROM size +const struct FontGraphic ascii_glyphs[95] = { + {{0x00, 0x00, 0x00, 0x00, 0x00}}, // " 0x20 + {{0x00, 0x00, 0x5f, 0x00, 0x00}}, // ! 0x21 + {{0x00, 0x07, 0x00, 0x07, 0x00}}, // \" 0x22 + {{0x14, 0x7f, 0x14, 0x7f, 0x14}}, // # 0x23 + {{0x24, 0x2a, 0x7f, 0x2a, 0x12}}, // $ 0x24 + {{0x23, 0x13, 0x08, 0x64, 0x62}}, // % 0x25 + {{0x36, 0x49, 0x55, 0x22, 0x50}}, // & 0x26 + {{0x00, 0x05, 0x03, 0x00, 0x00}}, // ' 0x27 + {{0x00, 0x1c, 0x22, 0x41, 0x00}}, // ( 0x28 + {{0x00, 0x41, 0x22, 0x1c, 0x00}}, // ) 0x29 + {{0x14, 0x08, 0x3e, 0x08, 0x14}}, // * 0x2a + {{0x08, 0x08, 0x3e, 0x08, 0x08}}, // + 0x2b + {{0x00, 0x50, 0x30, 0x00, 0x00}}, // , 0x2c + {{0x08, 0x08, 0x08, 0x08, 0x08}}, // - 0x2d + {{0x00, 0x60, 0x60, 0x00, 0x00}}, // . 0x2e + {{0x20, 0x10, 0x08, 0x04, 0x02}}, // / 0x2f + {{0x3e, 0x51, 0x49, 0x45, 0x3e}}, // 0 0x30 + {{0x00, 0x42, 0x7f, 0x40, 0x00}}, // 1 0x31 + {{0x42, 0x61, 0x51, 0x49, 0x46}}, // 2 0x32 + {{0x21, 0x41, 0x45, 0x4b, 0x31}}, // 3 0x33 + {{0x18, 0x14, 0x12, 0x7f, 0x10}}, // 4 0x34 + {{0x27, 0x45, 0x45, 0x45, 0x39}}, // 5 0x35 + {{0x3c, 0x4a, 0x49, 0x49, 0x30}}, // 6 0x36 + {{0x01, 0x71, 0x09, 0x05, 0x03}}, // 7 0x37 + {{0x36, 0x49, 0x49, 0x49, 0x36}}, // 8 0x38 + {{0x06, 0x49, 0x49, 0x29, 0x1e}}, // 9 0x39 + {{0x00, 0x36, 0x36, 0x00, 0x00}}, // : 0x3a + {{0x00, 0x56, 0x36, 0x00, 0x00}}, // ; 0x3b + {{0x08, 0x14, 0x22, 0x41, 0x00}}, // < 0x3c + {{0x14, 0x14, 0x14, 0x14, 0x14}}, // = 0x3d + {{0x00, 0x41, 0x22, 0x14, 0x08}}, // > 0x3e + {{0x02, 0x01, 0x51, 0x09, 0x06}}, // ? 0x3f + {{0x32, 0x49, 0x79, 0x41, 0x3e}}, // @ 0x40 + {{0x7e, 0x11, 0x11, 0x11, 0x7e}}, // A 0x41 + {{0x7f, 0x49, 0x49, 0x49, 0x36}}, // B 0x42 + {{0x3e, 0x41, 0x41, 0x41, 0x22}}, // C 0x43 + {{0x7f, 0x41, 0x41, 0x22, 0x1c}}, // D 0x44 + {{0x7f, 0x49, 0x49, 0x49, 0x41}}, // E 0x45 + {{0x7f, 0x09, 0x09, 0x09, 0x01}}, // F 0x46 + {{0x3e, 0x41, 0x49, 0x49, 0x7a}}, // G 0x47 + {{0x7f, 0x08, 0x08, 0x08, 0x7f}}, // H 0x48 + {{0x00, 0x41, 0x7f, 0x41, 0x00}}, // I 0x49 + {{0x20, 0x40, 0x41, 0x3f, 0x01}}, // J 0x4a + {{0x7f, 0x08, 0x14, 0x22, 0x41}}, // K 0x4b + {{0x7f, 0x40, 0x40, 0x40, 0x40}}, // L 0x4c + {{0x7f, 0x02, 0x0c, 0x02, 0x7f}}, // M 0x4d + {{0x7f, 0x04, 0x08, 0x10, 0x7f}}, // N 0x4e + {{0x3e, 0x41, 0x41, 0x41, 0x3e}}, // O 0x4f + {{0x7f, 0x09, 0x09, 0x09, 0x06}}, // P 0x50 + {{0x3e, 0x41, 0x51, 0x21, 0x5e}}, // Q 0x51 + {{0x7f, 0x09, 0x19, 0x29, 0x46}}, // R 0x52 + {{0x46, 0x49, 0x49, 0x49, 0x31}}, // S 0x53 + {{0x01, 0x01, 0x7f, 0x01, 0x01}}, // T 0x54 + {{0x3f, 0x40, 0x40, 0x40, 0x3f}}, // U 0x55 + {{0x1f, 0x20, 0x40, 0x20, 0x1f}}, // V 0x56 + {{0x3f, 0x40, 0x38, 0x40, 0x3f}}, // W 0x57 + {{0x63, 0x14, 0x08, 0x14, 0x63}}, // X 0x58 + {{0x07, 0x08, 0x70, 0x08, 0x07}}, // Y 0x59 + {{0x61, 0x51, 0x49, 0x45, 0x43}}, // Z 0x5a + {{0x00, 0x7f, 0x41, 0x41, 0x00}}, // [ 0x5b + {{0x02, 0x04, 0x08, 0x10, 0x20}}, // \\ 0x5c + {{0x00, 0x41, 0x41, 0x7f, 0x00}}, // ] 0x5d + {{0x04, 0x02, 0x01, 0x02, 0x04}}, // ^ 0x5e + {{0x40, 0x40, 0x40, 0x40, 0x40}}, // _ 0x5f + {{0x00, 0x01, 0x02, 0x04, 0x00}}, // ` 0x60 + {{0x20, 0x54, 0x54, 0x54, 0x78}}, // a 0x61 + {{0x7f, 0x48, 0x44, 0x44, 0x38}}, // b 0x62 + {{0x38, 0x44, 0x44, 0x44, 0x20}}, // c 0x63 + {{0x38, 0x44, 0x44, 0x48, 0x7f}}, // d 0x64 + {{0x38, 0x54, 0x54, 0x54, 0x18}}, // e 0x65 + {{0x08, 0x7e, 0x09, 0x01, 0x02}}, // f 0x66 + {{0x0c, 0x52, 0x52, 0x52, 0x3e}}, // g 0x67 + {{0x7f, 0x08, 0x04, 0x04, 0x78}}, // h 0x68 + {{0x00, 0x44, 0x7d, 0x40, 0x00}}, // i 0x69 + {{0x20, 0x40, 0x44, 0x3d, 0x00}}, // j 0x6a + {{0x7f, 0x10, 0x28, 0x44, 0x00}}, // k 0x6b + {{0x00, 0x41, 0x7f, 0x40, 0x00}}, // l 0x6c + {{0x7c, 0x04, 0x18, 0x04, 0x78}}, // m 0x6d + {{0x7c, 0x08, 0x04, 0x04, 0x78}}, // n 0x6e + {{0x38, 0x44, 0x44, 0x44, 0x38}}, // o 0x6f + {{0x7c, 0x14, 0x14, 0x14, 0x08}}, // p 0x70 + {{0x08, 0x14, 0x14, 0x18, 0x7c}}, // q 0x71 + {{0x7c, 0x08, 0x04, 0x04, 0x08}}, // r 0x72 + {{0x48, 0x54, 0x54, 0x54, 0x20}}, // s 0x73 + {{0x04, 0x3f, 0x44, 0x40, 0x20}}, // t 0x74 + {{0x3c, 0x40, 0x40, 0x20, 0x7c}}, // u 0x75 + {{0x1c, 0x20, 0x40, 0x20, 0x1c}}, // v 0x76 + {{0x3c, 0x40, 0x30, 0x40, 0x3c}}, // w 0x77 + {{0x44, 0x28, 0x10, 0x28, 0x44}}, // x 0x78 + {{0x0c, 0x50, 0x50, 0x50, 0x3c}}, // y 0x79 + {{0x44, 0x64, 0x54, 0x4c, 0x44}}, // z 0x7a + {{0x00, 0x08, 0x36, 0x41, 0x00}}, // { 0x7b + {{0x00, 0x00, 0x7f, 0x00, 0x00}}, // | 0x7c + {{0x00, 0x41, 0x36, 0x08, 0x00}}, // } 0x7d + {{0x10, 0x08, 0x08, 0x10, 0x08}}, // ~ 0x7e +}; - {.symbol="×", .graphic = { 0x22, 0x14, 0x08, 0x14, 0x22 }}, // cross - {.symbol="↑", .graphic = { 0x08, 0x04, 0x3e, 0x04, 0x08 }}, // arrow_up - {.symbol="↓", .graphic = { 0x08, 0x10, 0x3e, 0x10, 0x08 }}, // arrow_down - {.symbol="←", .graphic = { 0x08, 0x1c, 0x2a, 0x08, 0x08 }}, // arrow_left - {.symbol="→", .graphic = { 0x08, 0x08, 0x2a, 0x1c, 0x08 }}, // arrow_right - {.symbol="⏰", .graphic = { 0x1c, 0x22, 0x2e, 0x2a, 0x1c }}, // clock - {.symbol="⌛", .graphic = { 0x63, 0x55, 0x4d, 0x55, 0x63 }}, // hourglass - {.symbol="☸", .graphic = { 0x1c, 0x22, 0x2a, 0x22, 0x1c }}, // wheel - {.symbol="⏎", .graphic = { 0x10, 0x38, 0x54, 0x10, 0x1e }}, // return - {.symbol="🌡", .graphic = { 0x60, 0x9e, 0x81, 0x9e, 0x6a }}, // thermometer - {.symbol="°", .graphic = { 0x00, 0x07, 0x05, 0x07, 0x00 }}, // degree - {.symbol="🔙", .graphic = { 0x04, 0x4e, 0x55, 0x44, 0x38 }}, // back - {.symbol="▶", .graphic = { 0x7f, 0x3e, 0x1c, 0x08, 0x00 }}, // tri_right - {.symbol="◀", .graphic = { 0x00, 0x08, 0x1c, 0x3e, 0x7f }}, // tri_left +// utf8 characters list ending with empty struct +const struct Utf8Symbol utf8_glyphs[] = { + { .symbol="�", {{0xFE, 0x82, 0x82, 0x82, 0xFE}} }, // box + { .symbol="×", {{0x22, 0x14, 0x08, 0x14, 0x22}} }, // cross + { .symbol="↑", {{0x08, 0x04, 0x3e, 0x04, 0x08}} }, // arrow_up + { .symbol="↓", {{0x08, 0x10, 0x3e, 0x10, 0x08}} }, // arrow_down + { .symbol="←", {{0x08, 0x1c, 0x2a, 0x08, 0x08}} }, // arrow_left + { .symbol="→", {{0x08, 0x08, 0x2a, 0x1c, 0x08}} }, // arrow_right + { .symbol="⏰", {{0x1c, 0x22, 0x2e, 0x2a, 0x1c}} }, // clock + { .symbol="⌛", {{0x63, 0x55, 0x4d, 0x55, 0x63}} }, // hourglass + { .symbol="☸", {{0x1c, 0x22, 0x2a, 0x22, 0x1c}} }, // wheel + { .symbol="⏎", {{0x10, 0x38, 0x54, 0x10, 0x1e}} }, // return + { .symbol="🌡", {{0x60, 0x9e, 0x81, 0x9e, 0x6a}} }, // thermometer + { .symbol="°", {{0x00, 0x07, 0x05, 0x07, 0x00}} }, // degree + { .symbol="🔙", {{0x04, 0x4e, 0x55, 0x44, 0x38}} }, // back + { .symbol="▶", {{0x7f, 0x3e, 0x1c, 0x08, 0x00}} }, // tri_right + { .symbol="◀", {{0x00, 0x08, 0x1c, 0x3e, 0x7f}} }, // tri_left // End of the list {}, }; -const struct FontSymbol *Font_GetSymbol(struct Utf8Char ch) { - if (ch.bytes[0] < ' ') { - return &font[FONT_EXTRAS_START]; // replacement character +const struct FontGraphic *Font_GetSymbol(struct Utf8Char ch) { + if (ch.bytes[0] < 32) { + // low ASCII is not supported. + goto fail; } if (ch.bytes[0] < 127) { - return &font[ch.bytes[0] - 0x20]; + // valid ASCII at hard positions. + // In this case only the first byte is significant. + return &ascii_glyphs[ch.bytes[0] - 32]; } - const struct FontSymbol *sym = &font[FONT_EXTRAS_START]; + // search UTF8 + const struct Utf8Symbol *sym = &utf8_glyphs[0]; while (sym->symbol[0]) { if (sym->uint == ch.uint) { - return sym; + return &sym->graphic; } sym++; } - return &font[FONT_EXTRAS_START]; // replacement character +fail: + return &utf8_glyphs[0].graphic; // replacement character } diff --git a/main/graphics/font.h b/main/graphics/font.h index b7440e5..1a3980c 100644 --- a/main/graphics/font.h +++ b/main/graphics/font.h @@ -1,24 +1,20 @@ /** - * TODO file description + * UTF-8 capable bitmap font * * Created on 2020/01/04. */ -#ifndef REFLOWER_FONT_H -#define REFLOWER_FONT_H +#ifndef GFX_FONT_H +#define GFX_FONT_H #include "font.h" #include "utf8.h" #include -struct FontSymbol { - union { - const char symbol[4]; - const uint32_t uint; - }; - uint8_t graphic[5]; +struct FontGraphic { + uint8_t columns[5]; }; -const struct FontSymbol *Font_GetSymbol(struct Utf8Char ch); +const struct FontGraphic *Font_GetSymbol(struct Utf8Char ch); -#endif //REFLOWER_FONT_H +#endif //GFX_FONT_H diff --git a/main/graphics/nokia.c b/main/graphics/nokia.c index 96cddb6..d031967 100644 --- a/main/graphics/nokia.c +++ b/main/graphics/nokia.c @@ -1,9 +1,6 @@ #include -#include #include #include "nokia.h" -#include "utf8.h" -#include "font.h" #include /* Pin definitions: @@ -38,7 +35,7 @@ to the PCD8544. Because the PCD8544 won't let us write individual pixels at a time, this is how we can make targeted changes to the display. */ -static uint8_t displayMap[LCD_WIDTH * LCD_HEIGHT / 8]; +uint8_t LCD_displayMap[LCD_WIDTH * LCD_HEIGHT / 8] = {}; // There are two memory banks in the LCD, data/RAM and commands. // This function sets the DC pin high or low depending, and then @@ -72,298 +69,6 @@ static void LCD_SendByte(bool data_or_command, uint8_t data) assert(ret == ESP_OK); //Should have had no issues. } -// This function sets a pixel on displayMap to your preferred -// color. 1=Black, 0= white. -void LCD_setPixel(int x, int y, bool bw) -{ - // First, double check that the coordinate is in range. - if ((x >= 0) && (x < LCD_WIDTH) && (y >= 0) && (y < LCD_HEIGHT)) { - uint8_t shift = y % 8; - - if (bw) // If black, set the bit. - displayMap[x + (y / 8) * LCD_WIDTH] |= 1 << shift; - else // If white clear the bit. - displayMap[x + (y / 8) * LCD_WIDTH] &= ~(1 << shift); - } -} - -// setLine draws a line from x0,y0 to x1,y1 with the set color. -// This function was grabbed from the SparkFun ColorLCDShield -// library. -void LCD_setLine(int x0, int y0, int x1, int y1, bool bw) -{ - int dy = y1 - y0; // Difference between y0 and y1 - int dx = x1 - x0; // Difference between x0 and x1 - int stepx, stepy; - - if (dy < 0) { - dy = -dy; - stepy = -1; - } - else - stepy = 1; - - if (dx < 0) { - dx = -dx; - stepx = -1; - } - else - stepx = 1; - - dy <<= 1; // dy is now 2*dy - dx <<= 1; // dx is now 2*dx - LCD_setPixel(x0, y0, bw); // Draw the first pixel. - - if (dx > dy) { - int fraction = dy - (dx >> 1); - while (x0 != x1) { - if (fraction >= 0) { - y0 += stepy; - fraction -= dx; - } - x0 += stepx; - fraction += dy; - LCD_setPixel(x0, y0, bw); - } - } - else { - int fraction = dx - (dy >> 1); - while (y0 != y1) { - if (fraction >= 0) { - x0 += stepx; - fraction -= dy; - } - y0 += stepy; - fraction += dx; - LCD_setPixel(x0, y0, bw); - } - } -} - -// setRect will draw a rectangle from x0,y0 top-left corner to -// a x1,y1 bottom-right corner. Can be filled with the fill -// parameter, and colored with bw. -// This function was grabbed from the SparkFun ColorLCDShield -// library. -void LCD_setRect(int x0, int y0, int x1, int y1, bool fill, bool bw) -{ - // check if the rectangle is to be filled - if (fill == 1) { - int xDiff; - - if (x0 > x1) - xDiff = x0 - x1; //Find the difference between the x vars - else - xDiff = x1 - x0; - - while (xDiff > 0) { - LCD_setLine(x0, y0, x0, y1, bw); - - if (x0 > x1) - x0--; - else - x0++; - - xDiff--; - } - } - else { - // best way to draw an unfilled rectangle is to draw four lines - LCD_setLine(x0, y0, x1, y0, bw); - LCD_setLine(x0, y1, x1, y1, bw); - LCD_setLine(x0, y0, x0, y1, bw); - LCD_setLine(x1, y0, x1, y1, bw); - } -} - -// setCircle draws a circle centered around x0,y0 with a defined -// radius. The circle can be black or white. And have a line -// thickness ranging from 1 to the radius of the circle. -// This function was grabbed from the SparkFun ColorLCDShield -// library. -void LCD_setCircle(int x0, int y0, int radius, bool bw, int lineThickness) -{ - for (int r = 0; r < lineThickness; r++) { - int f = 1 - radius; - int ddF_x = 0; - int ddF_y = -2 * radius; - int x = 0; - int y = radius; - - LCD_setPixel(x0, y0 + radius, bw); - LCD_setPixel(x0, y0 - radius, bw); - LCD_setPixel(x0 + radius, y0, bw); - LCD_setPixel(x0 - radius, y0, bw); - - while (x < y) { - if (f >= 0) { - y--; - ddF_y += 2; - f += ddF_y; - } - x++; - ddF_x += 2; - f += ddF_x + 1; - - LCD_setPixel(x0 + x, y0 + y, bw); - LCD_setPixel(x0 - x, y0 + y, bw); - LCD_setPixel(x0 + x, y0 - y, bw); - LCD_setPixel(x0 - x, y0 - y, bw); - LCD_setPixel(x0 + y, y0 + x, bw); - LCD_setPixel(x0 - y, y0 + x, bw); - LCD_setPixel(x0 + y, y0 - x, bw); - LCD_setPixel(x0 - y, y0 - x, bw); - } - radius--; - } -} - -// This function will draw a char (defined in the ASCII table -// near the beginning of this sketch) at a defined x and y). -// The color can be either black (1) or white (0). -void LCD_setChar(struct Utf8Char character, int x, int y, bool bw) -{ - LCD_setCharEx(character, x, y, bw, 1); -} - -void LCD_setCharEx(struct Utf8Char character, int x, int y, bool bw, uint8_t size) -{ - const struct FontSymbol *symbol = Font_GetSymbol(character); - bool backfill = size &0x80; - size &= 0x7F; - - uint8_t column; // temp byte to store character's column bitmap - for (int i = 0; i < 5; i++) // 5 columns (x) per character - { - column = symbol->graphic[i]; - for (int j = 0; j < 8; j++) // 8 rows (y) per character - { - bool bit = column & (0x01 << j); - - if (size == 1) { - if (bit) {// test bits to set pixels - LCD_setPixel(x + i, y + j, bw); - } else if(backfill) { - LCD_setPixel(x + i, y + j, !bw); - } - } else if (size == 2) { - if (bit) {// test bits to set pixels - LCD_setPixel(x + i * 2, y + j * 2, bw); - LCD_setPixel(x + i * 2 + 1, y + j * 2, bw); - LCD_setPixel(x + i * 2, y + j * 2 + 1, bw); - LCD_setPixel(x + i * 2 + 1, y + j * 2 + 1, bw); - } else if(backfill) { - LCD_setPixel(x + i * 2, y + j * 2, !bw); - LCD_setPixel(x + i * 2 + 1, y + j * 2, !bw); - LCD_setPixel(x + i * 2, y + j * 2 + 1, !bw); - LCD_setPixel(x + i * 2 + 1, y + j * 2 + 1, !bw); - } - } else if (size == 3) { - if (bit) {// test bits to set pixels - LCD_setPixel(x + i, y + j, bw); - LCD_setPixel(x + i + 1, y + j, bw); - } else if(backfill) { - LCD_setPixel(x + i, y + j, !bw); - LCD_setPixel(x + i + 1, y + j, !bw); - } - } - } - } -} - -// setStr draws a string of characters, calling setChar with -// progressive coordinates until it's done. -// This function was grabbed from the SparkFun ColorLCDShield -// library. -void LCD_setStr(const char *dString, int x, int y, bool bw) -{ - LCD_setStrEx(dString, x, y, bw, 1); -} - -// setStr draws a string of characters, calling setChar with -// progressive coordinates until it's done. -// This function was grabbed from the SparkFun ColorLCDShield -// library. -void LCD_setStrEx(const char *dString, int x, int y, bool bw, uint8_t size) -{ - struct Utf8Iterator iter; - Utf8Iterator_Start(&iter, dString); - bool backfill = size & 0x80; - uint8_t size_real = size & 0x7F; - - struct Utf8Char uchar; - while ((uchar = Utf8Iterator_Next(&iter)).uint) { - LCD_setCharEx(uchar, x, y, bw, size); - - if (size_real == 1) { - x += 5; - if (backfill) { - for (int i = y; i < y + 8; i++) { - LCD_setPixel(x, i, !bw); - } - } - x++; - if (x > (LCD_WIDTH - 5)) // Enables wrap around - { - x = 0; - y += 8; - } - } else if (size_real == 2) { - x += 10; - if (backfill) { - for (int i = y; i < y + 16; i++) { - LCD_setPixel(x, i, !bw); - LCD_setPixel(x + 1, i, !bw); - } - } - x+=2; - if (x > (LCD_WIDTH - 10)) // Enables wrap around - { - x = 0; - y += 16; - } - } else if (size_real == 3) { - x += 6; - if (backfill) { - for (int i = y; i < y + 8; i++) { - LCD_setPixel(x, i, !bw); - LCD_setPixel(x + 1, i, !bw); - } - } - x+=1; - if (x > (LCD_WIDTH - 6)) // Enables wrap around - { - x = 0; - y += 8; - } - } - } -} - -// This function will draw an array over the screen. (For now) the -// array must be the same size as the screen, covering the entirety -// of the display. -// Also, the array must reside in FLASH and declared with PROGMEM. -void LCD_setBitmap(const char *bitArray) -{ - for (int i = 0; i < (LCD_WIDTH * LCD_HEIGHT / 8); i++) { - char c = bitArray[i]; - displayMap[i] = c; - } -} - -// This function clears the entire display either white (0) or -// black (1). -// The screen won't actually clear until you call updateDisplay()! -void LCD_clearDisplay(bool bw) -{ - for (int i = 0; i < (LCD_WIDTH * LCD_HEIGHT / 8); i++) { - if (bw) - displayMap[i] = 0xFF; - else - displayMap[i] = 0; - } -} // Helpful function to directly command the LCD to go to a // specific x,y coordinate. @@ -382,7 +87,7 @@ void LCD_updateDisplay() { spi_device_acquire_bus(hSPI, portMAX_DELAY); gotoXY(0, 0); - LCD_SendBytes(LCD_DATA, &displayMap[0], LCD_WIDTH * (LCD_HEIGHT / 8)); + LCD_SendBytes(LCD_DATA, &LCD_displayMap[0], LCD_WIDTH * (LCD_HEIGHT / 8)); spi_device_release_bus(hSPI); } @@ -405,14 +110,6 @@ void LCD_invertDisplay(bool invert) LCD_SendByte(LCD_COMMAND, 0x0C | invert); } -void LCD_invertDisplayData() -{ - /* Indirect, swap bits in displayMap option: */ - for (int i = 0; i < (LCD_WIDTH * LCD_HEIGHT / 8); i++) { - displayMap[i] ^= 0xFF; - } -} - //This function is called (in irq context!) just before a transmission starts. It will //set the D/C line to the value indicated in the user field. void lcd_spi_pre_transfer_callback(spi_transaction_t *t) @@ -424,11 +121,8 @@ void lcd_spi_pre_transfer_callback(spi_transaction_t *t) //This sends the magical commands to the PCD8544 void LCD_setup(void) { - // clear the display array - LCD_clearDisplay(0); - gpio_config_t output = { - .pin_bit_mask = (1< #include #include "utf8.h" - -#define LCD_WIDTH 84 // Note: x-coordinates go wide -#define LCD_HEIGHT 48 // Note: y-coordinates go high -#define WHITE 0 // For drawing pixels. A 0 draws white. -#define BLACK 1 // A 1 draws black. - -// This function sets a pixel on displayMap to your preferred -// color. 1=Black, 0= white. -void LCD_setPixel(int x, int y, bool bw); - -// setLine draws a line from x0,y0 to x1,y1 with the set color -void LCD_setLine(int x0, int y0, int x1, int y1, bool bw); - -// setRect will draw a rectangle from x0,y0 top-left corner to -// a x1,y1 bottom-right corner. Can be filled with the fill -// parameter, and colored with bw. -void LCD_setRect(int x0, int y0, int x1, int y1, bool fill, bool bw); - -// setCircle draws a circle centered around x0,y0 with a defined -// radius. The circle can be black or white. And have a line -// thickness ranging from 1 to the radius of the circle. -void LCD_setCircle (int x0, int y0, int radius, bool bw, int lineThickness); - -/* - FONT FUNCTIONS - size = 1 ... normal - size = 2 ... 2x - size = 3 ... normal bold - size | 0x80 ... clear background behind characters -*/ - - -// This function will draw a char (defined in the ASCII table -// near the beginning of this sketch) at a defined x and y). -// The color can be either black (1) or white (0). -void LCD_setChar(struct Utf8Char character, int x, int y, bool bw); - -void LCD_setCharEx(struct Utf8Char character, int x, int y, bool bw, uint8_t size); - -// setStr draws a string of characters, calling setChar with -// progressive coordinates until it's done. -// This function was grabbed from the SparkFun ColorLCDShield -// library. -void LCD_setStr(const char * dString, int x, int y, bool bw); - -void LCD_setStrEx(const char *dString, int x, int y, bool bw, uint8_t size); - -// This function clears the entire display either white (0) or -// black (1). -// The screen won't actually clear until you call updateDisplay()! -void LCD_clearDisplay(bool bw); +#include "display_spec.h" // This will actually draw on the display, whatever is currently // in the displayMap array. @@ -74,10 +31,9 @@ void LCD_updateDisplay(); void LCD_setContrast(uint8_t contrast); /* Invert colors */ -void LCD_invertDisplay(bool in_data); - -/* Invert colors (hard change in data; does NOT send to display immediately) */ -void LCD_invertDisplayData(); +void LCD_invertDisplay(bool invert); //This sends the magical commands to the PCD8544 void LCD_setup(void); + +#endif // GFX_NOKIA diff --git a/main/graphics/utf8.c b/main/graphics/utf8.c index d6fbe97..f865ec2 100644 --- a/main/graphics/utf8.c +++ b/main/graphics/utf8.c @@ -1,6 +1,5 @@ #include #include "utf8.h" -#include // // Created by MightyPork on 2017/08/20. @@ -9,7 +8,7 @@ // into a screen cell. // -const struct Utf8Char EMPTY_CHAR = (struct Utf8Char) { .uint = 0 }; +const struct Utf8Char EMPTY_CHAR = (struct Utf8Char) {.uint = 0}; // Code Points First Byte Second Byte Third Byte Fourth Byte // U+0000 - U+007F 00 - 7F @@ -30,7 +29,7 @@ struct Utf8Char Utf8Parser_Handle(struct Utf8Parser *self, char c) { uint8_t *bytes = self->buffer.bytes; - uint8_t uc = (uint8_t)c; + uint8_t uc = (uint8_t) c; // collecting unicode glyphs... if (uc & 0x80) { if (self->utf_len == 0) { @@ -98,12 +97,8 @@ fail: return EMPTY_CHAR; } -void Utf8Iterator_Start(struct Utf8Iterator *self, const char *source) { - memset(self, 0, sizeof(struct Utf8Iterator)); - self->source = source; -} - -struct Utf8Char Utf8Iterator_Next(struct Utf8Iterator *self) { +struct Utf8Char Utf8Iterator_Next(struct Utf8Iterator *self) +{ char c; struct Utf8Char uchar; while ((c = *self->source++) != 0) { diff --git a/main/graphics/utf8.h b/main/graphics/utf8.h index 89b8467..83a63e6 100644 --- a/main/graphics/utf8.h +++ b/main/graphics/utf8.h @@ -1,5 +1,5 @@ /** - * TODO file description + * UTF-8 string parsing and character iteration * * Created on 2020/01/04. */ @@ -10,33 +10,47 @@ #include #include +/** + * UTF-8 encoded character. + */ struct Utf8Char { union { + /** character bytes; padded by zero bytes if shorter than 4 */ uint8_t bytes[4]; + /** u32 view of the bytes */ uint32_t uint; }; }; +/** UTF8 string parser internal state */ struct Utf8Parser { + /** UTF-8 bytes buffer */ struct Utf8Char buffer; + /** Currently collected UTF-8 character length */ uint8_t utf_len; + /** Position in the current character */ uint8_t utf_j; }; +static inline void Utf8Parser_Clear(struct Utf8Parser *self) { + self->buffer.uint = 0; + self->utf_j = 0; + self->utf_len = 0; +} + /** * Utf8 character iterator. * * Usage: * struct Utf8Iterator iter; - * Utf8Iterator_Start(&iter, myString); + * Utf8Iterator_Init(&iter, myString); * * union Utf8Char uchar; * while ((uchar = Utf8Iterator_Next(&iter)).uint) { * // do something with the char * } * - * // Free myString if needed. - * // The iterator does not need any cleanup if it lives on stack. + * // Free myString if needed, it is not mutated. */ struct Utf8Iterator { /* Characters to parse. The pointer is advanced as the iterator progresses. */ @@ -44,7 +58,10 @@ struct Utf8Iterator { struct Utf8Parser parser; }; -void Utf8Iterator_Start(struct Utf8Iterator *self, const char *source); +static inline void Utf8Iterator_Init(struct Utf8Iterator *self, const char *source) { + Utf8Parser_Clear(&self->parser); + self->source = source; +} /** * Get the next character from the iterator; Returns empty character if there are no more characters to parse. diff --git a/main/gui.c b/main/liquid/gui.c similarity index 91% rename from main/gui.c rename to main/liquid/gui.c index 0ce85be..7f76040 100644 --- a/main/gui.c +++ b/main/liquid/gui.c @@ -1,28 +1,23 @@ -#include "gui.h" -#include "graphics/nokia.h" -#include "analog.h" #include #include -#include #include +#include "gui.h" +#include "nokia.h" +#include "liquid.h" +#include "drawing.h" + static void gui_thread(void *arg); static void gui_tick(TimerHandle_t xTimer); TaskHandle_t hGuiThread = NULL; -TimerHandle_t hTicker = NULL; - +static TimerHandle_t hTicker = NULL; void gui_init() { printf("GUI init\n"); LCD_setup(); - LCD_setContrast(48); - LCD_clearDisplay(0); - LCD_setStr("Hello World", 0, 0, 1); - LCD_updateDisplay(); - int rv = xTaskCreate(gui_thread, "gui", 4096, NULL, 6, &hGuiThread); assert (rv == pdPASS); @@ -63,8 +58,6 @@ static void __attribute__((noreturn)) gui_thread(void *arg) { bool want_repaint = false; -// printf("Knob event 0x%02x ", value); - if (value & 0b10000) { // TICK want_repaint |= Liquid_handleTick(liquid); diff --git a/main/gui.h b/main/liquid/gui.h similarity index 100% rename from main/gui.h rename to main/liquid/gui.h diff --git a/main/liquid/input_event.h b/main/liquid/input_event.h new file mode 100644 index 0000000..8758c19 --- /dev/null +++ b/main/liquid/input_event.h @@ -0,0 +1,51 @@ +/** + * Input event structural enum + * + * Created on 2020/01/05. + */ + +#ifndef LIQUID_INPUT_EVENT_H +#define LIQUID_INPUT_EVENT_H + +enum InputEvent_Kind { + InputEventKind_Wheel, + InputEventKind_Button, +}; + +struct InputEvent { + enum InputEvent_Kind kind; + union { + struct { + // Wheel delta + int32_t delta; + } wheel; + struct { + // Button state + bool state; + } button; + }; +}; + +static inline struct InputEvent InputEvent_Wheel(int32_t delta) +{ + return (struct InputEvent) { + .kind = InputEventKind_Wheel, + .wheel = {.delta = delta} + }; +} + +/** + * Button event (push, release) + * + * @param state - pushed + * @return event + */ +static inline struct InputEvent InputEvent_Button(bool state) +{ + return (struct InputEvent) { + .kind = InputEventKind_Button, + .button = {.state = state}, + }; +} + +#endif //LIQUID_INPUT_EVENT_H diff --git a/main/liquid/liquid.c b/main/liquid/liquid.c index 3d1e39e..df78be1 100644 --- a/main/liquid/liquid.c +++ b/main/liquid/liquid.c @@ -1,13 +1,15 @@ -#include "liquid.h" -#include "rom/queue.h" -#include "scenes.h" #include #include #include -#include +#include + +#include "liquid.h" +#include "nokia.h" static const char *TAG = "Liquid"; +extern struct Scene *NewScene_Root(void); + struct RunningScene { struct Scene *scene; SLIST_ENTRY(RunningScene) next; diff --git a/main/liquid/liquid.h b/main/liquid/liquid.h index fc93eb4..e969619 100644 --- a/main/liquid/liquid.h +++ b/main/liquid/liquid.h @@ -4,234 +4,17 @@ * Created on 2020/01/03. */ -#ifndef REFLOWER_LIQUID_H -#define REFLOWER_LIQUID_H +#ifndef LIQUID_H +#define LIQUID_H #include #include +#include "input_event.h" +#include "scene_event.h" +#include "scene_type.h" struct Liquid; -enum InputEvent_Kind { - InputEventKind_Wheel, - InputEventKind_Button, -}; - -struct InputEvent { - enum InputEvent_Kind kind; - union { - struct { - // Wheel delta - int32_t delta; - } wheel; - struct { - // Button state - bool state; - } button; - }; -}; - -static inline struct InputEvent InputEvent_Wheel(int32_t delta) -{ - return (struct InputEvent) { - .kind = InputEventKind_Wheel, - .wheel = {.delta = delta} - }; -} - -/** - * Button event (push, release) - * - * @param state - pushed - * @return event - */ -static inline struct InputEvent InputEvent_Button(bool state) -{ - return (struct InputEvent) { - .kind = InputEventKind_Button, - .button = {.state = state}, - }; -} - -enum SceneEvent_Kind { - SceneEventKind_Close, - SceneEventKind_OpenChild, - SceneEventKind_RequestRepaint, - SceneEventKind_None, -}; - -// forward declaration -struct Scene; - -/** - * Scene event, returned from some scene methods. - * - * Use the constructor functions to create this struct. - */ -struct SceneEvent { - /** Event kind enum */ - enum SceneEvent_Kind kind; - union { - /* data for Close event kind */ - struct { - // Status code - int32_t status; - // Return data on heap - void *data; - } close; - /* Data for Open event kind */ - struct { - // Scene (initialized, with options loaded in through the constructor) - struct Scene *scene; - // Tag used by parent to identify the open child - uint32_t tag; - } open; - }; -}; - -/** - * Create empty (null object) scene event. - * - * @return event - */ -static inline struct SceneEvent SceneEvent_None(void) -{ - return (struct SceneEvent) { - .kind = SceneEventKind_None, - }; -} - -/** - * Request scene repaint - * - * @return event - */ -static inline struct SceneEvent SceneEvent_Repaint(void) -{ - return (struct SceneEvent) { - .kind = SceneEventKind_RequestRepaint - }; -} - -/** - * Request a sub-scene to be opened - * - * @param child - child scene - * @param tag - scene tag - * @return event - */ -static inline struct SceneEvent SceneEvent_OpenChild(struct Scene *child, uint32_t tag) { - return (struct SceneEvent) { - .kind = SceneEventKind_OpenChild, - .open = { .scene = child, .tag=tag }, - }; -} - -/** - * Close this scene, returning to parent. - * - * @param status - status number for the parent - * @param data - heap-allocated data for parent, can be NULL; e.g. user input as string. - * @return event - */ -static inline struct SceneEvent SceneEvent_Close(int32_t status, void *data) -{ - return (struct SceneEvent) { - .kind = SceneEventKind_Close, - .close = {.status = status, .data=data}, - }; -} - -/** - * Scene::onInput fp type - handle user input - * - * @param scene - self - * @param event - the input event - * @return follow-up scene event, can be SceneEvent_None() if not used. - */ -typedef struct SceneEvent (*Scene_onInput_t)(struct Scene *scene, struct InputEvent event); - -/** - * Scene::onChildReturn fp type - * - * @param scene - self - * @param tag - child's tag - * @param status - status code returned from the child - * @param data - data returned from the child, must be heap-allocated if not NULL. - * @return follow-up scene event, can be SceneEvent_None() if not used. - */ -typedef struct SceneEvent (*Scene_onChildReturn_t)(struct Scene *scene, uint32_t tag, int32_t status, void *data); - -/** - * Scene::onTick fp type - * - * @param scene - self - * @return follow-up scene event, can be SceneEvent_None() if not used. - */ -typedef struct SceneEvent (*Scene_onTick_t)(struct Scene *scene); - -/** - * Scene::paint fp type - * - * @param scene - self - */ -typedef void (*Scene_paint_t)(struct Scene *scene); - -/** - * Scene::free fp type. - * Release internally allocated resources. - * This is called by the GUI engine when the scene is closed. - * - * @param scene - self - */ -typedef void (*Scene_free_t)(struct Scene *scene); - -/** - * Scene instance in the framework - */ -struct Scene { - /** - * Tag given to the scene by its parent to identify its close event. - */ - uint32_t tag; - - /** - * Handle input event. - * Can return a follow-up scene event, e.g. repaint request. - * Nullable field. - */ - Scene_onInput_t onInput; - - /** - * Child scene closed, handle its return value and data, if any. - * Can return a follow-up scene event. - * In any case, the scene will be re-painted, if it stays open and on top. - * Nullable field. - */ - Scene_onChildReturn_t onChildReturn; - - /** - * Handle a periodic tick (10ms). - * Can return a follow-up scene event, e.g. repaint request. - * Nullable field. - */ - Scene_onTick_t onTick; - - /** - * Draw the scene to the LCD buffer. - * DO NOT write to the display yet, it will be done by the GUI engine. - * - * MANDATORY FIELD - */ - Scene_paint_t paint; - - /** - * Release internally allocated resources, if any. Called on close. - * Nullable field. - */ - Scene_free_t free; -}; - /** return 1 if repaint requested */ bool Liquid_handleInput(struct Liquid *container, struct InputEvent event); @@ -244,4 +27,4 @@ void Liquid_paint(struct Liquid *container); /** Initialize the GUI system with a root scene */ struct Liquid *Liquid_start(void); -#endif //REFLOWER_LIQUID_H +#endif //LIQUID_H diff --git a/main/liquid/scene_event.h b/main/liquid/scene_event.h new file mode 100644 index 0000000..15f3c06 --- /dev/null +++ b/main/liquid/scene_event.h @@ -0,0 +1,99 @@ +/** + * Scene event structural enum + * + * Created on 2020/01/05. + */ + +#ifndef LIQUID_SCENE_EVENT_H +#define LIQUID_SCENE_EVENT_H + +// forward declaration +struct Scene; + +enum SceneEvent_Kind { + SceneEventKind_Close, + SceneEventKind_OpenChild, + SceneEventKind_RequestRepaint, + SceneEventKind_None, +}; + +/** + * Scene event, returned from some scene methods. + * + * Use the constructor functions to create this struct. + */ +struct SceneEvent { + /** Event kind enum */ + enum SceneEvent_Kind kind; + union { + /* data for Close event kind */ + struct { + // Status code + int32_t status; + // Return data on heap + void *data; + } close; + /* Data for Open event kind */ + struct { + // Scene (initialized, with options loaded in through the constructor) + struct Scene *scene; + // Tag used by parent to identify the open child + uint32_t tag; + } open; + }; +}; + +/** + * Create empty (null object) scene event. + * + * @return event + */ +static inline struct SceneEvent SceneEvent_None(void) +{ + return (struct SceneEvent) { + .kind = SceneEventKind_None, + }; +} + +/** + * Request scene repaint + * + * @return event + */ +static inline struct SceneEvent SceneEvent_Repaint(void) +{ + return (struct SceneEvent) { + .kind = SceneEventKind_RequestRepaint + }; +} + +/** + * Request a sub-scene to be opened + * + * @param child - child scene + * @param tag - scene tag + * @return event + */ +static inline struct SceneEvent SceneEvent_OpenChild(struct Scene *child, uint32_t tag) { + return (struct SceneEvent) { + .kind = SceneEventKind_OpenChild, + .open = { .scene = child, .tag=tag }, + }; +} + +/** + * Close this scene, returning to parent. + * + * @param status - status number for the parent + * @param data - heap-allocated data for parent, can be NULL; e.g. user input as string. + * @return event + */ +static inline struct SceneEvent SceneEvent_Close(int32_t status, void *data) +{ + return (struct SceneEvent) { + .kind = SceneEventKind_Close, + .close = {.status = status, .data=data}, + }; +} + +#endif //LIQUID_SCENE_EVENT_H diff --git a/main/liquid/scene_type.h b/main/liquid/scene_type.h new file mode 100644 index 0000000..513acdc --- /dev/null +++ b/main/liquid/scene_type.h @@ -0,0 +1,105 @@ +/** + * Scene struct + * + * Created on 2020/01/05. + */ + +#ifndef LIQUID_SCENE_TYPE_H +#define LIQUID_SCENE_TYPE_H + +#include + +struct Scene; +struct InputEvent; + +/** + * Scene::onInput fp type - handle user input + * + * @param scene - self + * @param event - the input event + * @return follow-up scene event, can be SceneEvent_None() if not used. + */ +typedef struct SceneEvent (*Scene_onInput_t)(struct Scene *scene, struct InputEvent event); + +/** + * Scene::onChildReturn fp type + * + * @param scene - self + * @param tag - child's tag + * @param status - status code returned from the child + * @param data - data returned from the child, must be heap-allocated if not NULL. + * @return follow-up scene event, can be SceneEvent_None() if not used. + */ +typedef struct SceneEvent (*Scene_onChildReturn_t)(struct Scene *scene, uint32_t tag, int32_t status, void *data); + +/** + * Scene::onTick fp type + * + * @param scene - self + * @return follow-up scene event, can be SceneEvent_None() if not used. + */ +typedef struct SceneEvent (*Scene_onTick_t)(struct Scene *scene); + +/** + * Scene::paint fp type + * + * @param scene - self + */ +typedef void (*Scene_paint_t)(struct Scene *scene); + +/** + * Scene::free fp type. + * Release internally allocated resources. + * This is called by the GUI engine when the scene is closed. + * + * @param scene - self + */ +typedef void (*Scene_free_t)(struct Scene *scene); + +/** + * Scene instance in the framework + */ +struct Scene { + /** + * Tag given to the scene by its parent to identify its close event. + */ + uint32_t tag; + + /** + * Handle input event. + * Can return a follow-up scene event, e.g. repaint request. + * Nullable field. + */ + Scene_onInput_t onInput; + + /** + * Child scene closed, handle its return value and data, if any. + * Can return a follow-up scene event. + * In any case, the scene will be re-painted, if it stays open and on top. + * Nullable field. + */ + Scene_onChildReturn_t onChildReturn; + + /** + * Handle a periodic tick (10ms). + * Can return a follow-up scene event, e.g. repaint request. + * Nullable field. + */ + Scene_onTick_t onTick; + + /** + * Draw the scene to the LCD buffer. + * DO NOT write to the display yet, it will be done by the GUI engine. + * + * MANDATORY FIELD + */ + Scene_paint_t paint; + + /** + * Release internally allocated resources, if any. Called on close. + * Nullable field. + */ + Scene_free_t free; +}; + +#endif //LIQUID_SCENE_TYPE_H diff --git a/main/liquid/scene_car.c b/main/scenes/scene_car.c similarity index 95% rename from main/liquid/scene_car.c rename to main/scenes/scene_car.c index 644506c..fee175e 100644 --- a/main/liquid/scene_car.c +++ b/main/scenes/scene_car.c @@ -1,7 +1,8 @@ #include "scenes.h" #include "liquid.h" -#include "../graphics/nokia.h" #include +#include "graphics/nokia.h" +#include "graphics/drawing.h" struct CarScene { struct Scene base; diff --git a/main/liquid/scene_root.c b/main/scenes/scene_root.c similarity index 96% rename from main/liquid/scene_root.c rename to main/scenes/scene_root.c index a9d8e76..68e0525 100644 --- a/main/liquid/scene_root.c +++ b/main/scenes/scene_root.c @@ -1,10 +1,12 @@ -#include "scenes.h" -#include "liquid.h" -#include "../graphics/nokia.h" -#include "../analog.h" #include #include +#include "scenes.h" +#include "liquid.h" +#include "analog.h" +#include "graphics/nokia.h" +#include "graphics/drawing.h" + /** * The struct is allocated bigger than 'Scene' to accommodate private fields. * Since the base struct is located at the beginning, it can be cast and passed around as Scene. diff --git a/main/liquid/scenes.h b/main/scenes/scenes.h similarity index 100% rename from main/liquid/scenes.h rename to main/scenes/scenes.h