add utf8, some glyphs, and more font rendering options

This commit is contained in:
2020-01-04 23:28:18 +01:00
parent 7299521ab1
commit 12f9d2ec2c
11 changed files with 487 additions and 142 deletions
+145
View File
@@ -0,0 +1,145 @@
#include "font.h"
#include "utf8.h"
#include <stdint.h>
#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
// start of UTF8 glyphs. These can include custom graphics mapped to codepoints
{.symbol="", .graphic = {0xFF, 0x81, 0x81, 0x81, 0xFF}}, // box
{.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
// End of the list
{},
};
const struct FontSymbol *Font_GetSymbol(struct Utf8Char ch) {
if (ch.bytes[0] < ' ') {
return &font[FONT_EXTRAS_START]; // replacement character
}
if (ch.bytes[0] < 127) {
return &font[ch.bytes[0] - 0x20];
}
const struct FontSymbol *sym = &font[FONT_EXTRAS_START];
while (sym->symbol[0]) {
if (sym->uint == ch.uint) {
return sym;
}
sym++;
}
return &font[FONT_EXTRAS_START]; // replacement character
}
+24
View File
@@ -0,0 +1,24 @@
/**
* TODO file description
*
* Created on 2020/01/04.
*/
#ifndef REFLOWER_FONT_H
#define REFLOWER_FONT_H
#include "font.h"
#include "utf8.h"
#include <stdint.h>
struct FontSymbol {
union {
const char symbol[4];
const uint32_t uint;
};
uint8_t graphic[5];
};
const struct FontSymbol *Font_GetSymbol(struct Utf8Char ch);
#endif //REFLOWER_FONT_H
+486
View File
@@ -0,0 +1,486 @@
#include <driver/gpio.h>
#include <arch/cc.h>
#include <driver/spi_master.h>
#include "nokia.h"
#include "utf8.h"
#include "font.h"
#include <string.h>
/* Pin definitions:
Most of these pins can be moved to any digital or analog pin.
DN(MOSI)and SCLK should be left where they are (SPI pins). The
LED (backlight) pin should remain on a PWM-capable pin. */
static const int scePin = 17; // SCE - Chip select, pin 3 on LCD.
static const int rstPin = 16; // RST - Reset, pin 4 on LCD.
static const int dcPin = 4; // DC - Data/Command, pin 5 on LCD.
static const int sdinPin = 15; // DN(MOSI) - Serial data, pin 6 on LCD.
static const int sclkPin = 13; // SCLK - Serial clock, pin 7 on LCD.
/* PCD8544-specific defines: */
#define LCD_COMMAND 0
#define LCD_DATA 1
#define DEFAULT_CONTRAST 48
static spi_device_handle_t hSPI;
/* The displayMap variable stores a buffer representation of the
pixels on our display. There are 504 total bits in this array,
same as how many pixels there are on a 84 x 48 display.
Each byte in this array covers a 8-pixel vertical block on the
display. Each successive byte covers the next 8-pixel column over
until you reach the right-edge of the display and step down 8 rows.
To update the display, we first have to write to this array, then
call the updateDisplay() function, which sends this whole array
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];
// There are two memory banks in the LCD, data/RAM and commands.
// This function sets the DC pin high or low depending, and then
// sends the data byte
static void LCD_SendBytes(bool data_or_command, const uint8_t *data, size_t len)
{
esp_err_t ret;
spi_transaction_t t;
if (len == 0) return; //no need to send anything
memset(&t, 0, sizeof(t)); //Zero out the transaction
t.length = len * 8; //Len is in bytes, transaction length is in bits.
t.tx_buffer = data; //Data
t.user = (void *) data_or_command; //D/C
ret = spi_device_polling_transmit(hSPI, &t); //Transmit!
assert(ret == ESP_OK); //Should have had no issues.
}
// There are two memory banks in the LCD, data/RAM and commands.
// This function sets the DC pin high or low depending, and then
// sends the data byte
static void LCD_SendByte(bool data_or_command, uint8_t data)
{
esp_err_t ret;
spi_transaction_t t;
memset(&t, 0, sizeof(t)); //Zero out the transaction
t.length = 8; // transaction length is in bits.
t.tx_data[0] = data; //Data
t.flags = SPI_TRANS_USE_TXDATA;
t.user = (void *) data_or_command; //D/C
ret = spi_device_polling_transmit(hSPI, &t); //Transmit!
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.
static void gotoXY(int x, int y)
{
const uint8_t cmd[2] = {
0x80 | x,
0x40 | y,
};
LCD_SendBytes(LCD_COMMAND, cmd, 2);
}
// This will actually draw on the display, whatever is currently
// in the displayMap array.
void LCD_updateDisplay()
{
spi_device_acquire_bus(hSPI, portMAX_DELAY);
gotoXY(0, 0);
LCD_SendBytes(LCD_DATA, &displayMap[0], LCD_WIDTH * (LCD_HEIGHT / 8));
spi_device_release_bus(hSPI);
}
// Set contrast can set the LCD Vop to a value between 0 and 127.
// 40-60 is usually a pretty good range.
void LCD_setContrast(uint8_t contrast)
{
spi_device_acquire_bus(hSPI, portMAX_DELAY);
const uint8_t cmd[3] = {
0x21, //Tell LCD that extended commands follow
0x80 | contrast,//Set LCD Vop (Contrast): Try 0xB1(good @ 3.3V) or 0xBF if your display is too dark
0x20,//Set display mode
};
LCD_SendBytes(LCD_COMMAND, cmd, 3);
spi_device_release_bus(hSPI);
}
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)
{
int dc = (int) t->user;
gpio_set_level(dcPin, dc);
}
//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<<scePin) | (1<<rstPin) | (1<<dcPin) | (1<<sdinPin) | (1<<sclkPin),
.mode = GPIO_MODE_OUTPUT
};
gpio_config(&output);
esp_err_t ret;
spi_bus_config_t buscfg = {
.miso_io_num=-1,
.mosi_io_num=sdinPin,
.sclk_io_num=sclkPin,
.quadwp_io_num=-1,
.quadhd_io_num=-1,
.max_transfer_sz=LCD_WIDTH * LCD_HEIGHT, // ??? this doesnt seem to do anything in the driver
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz=4 * 1000 * 1000, // Hz
// enable signal lead/lag
.cs_ena_pretrans = 0,
.cs_ena_posttrans = 0,
.mode=0, //SPI mode 0
.spics_io_num=scePin, //CS pin
.queue_size=1, // we use the polling mode, so this does not matter
.pre_cb=lcd_spi_pre_transfer_callback, //Specify pre-transfer callback to handle D/C line
};
//Initialize the SPI bus
ret = spi_bus_initialize(HSPI_HOST, &buscfg, 1);
ESP_ERROR_CHECK(ret);
//Attach the LCD to the SPI bus
ret = spi_bus_add_device(HSPI_HOST, &devcfg, &hSPI);
ESP_ERROR_CHECK(ret);
spi_device_acquire_bus(hSPI, portMAX_DELAY);
{
//Reset the LCD to a known state
gpio_set_level(rstPin, 0);
gpio_set_level(rstPin, 1);
const uint8_t magic[6] = {
0x21, // Tell LCD extended commands follow
0x80 + DEFAULT_CONTRAST, // Set LCD Vop (Contrast)
0x04, // Set Temp coefficent
0x14, // LCD bias mode 1:48 (try 0x13)
//We must send 0x20 before modifying the display control mode
0x20, // Clear extended option
0x0C, // Set display control, normal mode.
};
LCD_SendBytes(LCD_COMMAND, &magic[0], 6);
}
spi_device_release_bus(hSPI);
// show the blank screen (display is in indefined state after boot)
LCD_updateDisplay();
}
+83
View File
@@ -0,0 +1,83 @@
/* Pin definitions:
Most of these pins can be moved to any digital or analog pin.
DN(MOSI)and SCLK should be left where they are (SPI pins). The
LED (backlight) pin should remain on a PWM-capable pin. */
// SCE - Chip select, pin 3 on LCD.
// RST - Reset, pin 4 on LCD.
// DC - Data/Command, pin 5 on LCD.
// DN(MOSI) - Serial data, pin 6 on LCD.
// SCLK - Serial clock, pin 7 on LCD.
// LED - Backlight LED, pin 8 on LCD.
/* 84x48 LCD Defines: */
#include <stdbool.h>
#include <stdint.h>
#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);
// This will actually draw on the display, whatever is currently
// in the displayMap array.
void LCD_updateDisplay();
// Set contrast can set the LCD Vop to a value between 0 and 127.
// 40-60 is usually a pretty good range.
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();
//This sends the magical commands to the PCD8544
void LCD_setup(void);
+116
View File
@@ -0,0 +1,116 @@
#include <stdint.h>
#include "utf8.h"
#include <string.h>
//
// Created by MightyPork on 2017/08/20.
//
// UTF-8 parser - collects bytes of a code point before writing them
// into a screen cell.
//
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
// U+0080 - U+07FF C2 - DF 80 - BF
// U+0800 - U+0FFF E0 *A0 - BF 80 - BF
// U+1000 - U+CFFF E1 - EC 80 - BF 80 - BF
// U+D000 - U+D7FF ED 80 - *9F 80 - BF
// U+E000 - U+FFFF EE - EF 80 - BF 80 - BF
// U+10000 - U+3FFFF F0 *90 - BF 80 - BF 80 - BF
// U+40000 - U+FFFFF F1 - F3 80 - BF 80 - BF 80 - BF
// U+100000 - U+10FFFF F4 80 - *8F 80 - BF 80 - BF
/**
* Handle a received character
*/
struct Utf8Char Utf8Parser_Handle(struct Utf8Parser *self, char c)
{
uint8_t *bytes = self->buffer.bytes;
uint8_t uc = (uint8_t)c;
// collecting unicode glyphs...
if (uc & 0x80) {
if (self->utf_len == 0) {
bytes[0] = uc;
self->utf_j = 1;
// start
if (uc == 0xC0 || uc == 0xC1 || uc > 0xF4) {
// forbidden start codes
goto fail;
}
if ((uc & 0xE0) == 0xC0) {
self->utf_len = 2;
}
else if ((uc & 0xF0) == 0xE0) {
self->utf_len = 3;
}
else if ((uc & 0xF8) == 0xF0) {
self->utf_len = 4;
}
else {
// chars over 127 that don't start unicode sequences
goto fail;
}
}
else {
if ((uc & 0xC0) != 0x80) {
bytes[self->utf_j++] = uc;
goto fail;
}
else {
bytes[self->utf_j++] = uc;
if (self->utf_j >= self->utf_len) {
// check for bad sequences - overlong or some other problem
if (bytes[0] == 0xF4 && bytes[1] > 0x8F) goto fail;
if (bytes[0] == 0xF0 && bytes[1] < 0x90) goto fail;
if (bytes[0] == 0xED && bytes[1] > 0x9F) goto fail;
if (bytes[0] == 0xE0 && bytes[1] < 0xA0) goto fail;
// trap for surrogates - those break javascript
if (bytes[0] == 0xED && bytes[1] >= 0xA0 && bytes[1] <= 0xBF) goto fail;
goto success;
}
}
}
}
else {
bytes[0] = uc;
goto success;
}
return EMPTY_CHAR;
success:;
struct Utf8Char result = self->buffer;
self->buffer.uint = 0; // erase the buffer
self->utf_len = 0;
return result;
fail:
self->buffer.uint = 0; // erase the buffer
self->utf_len = 0;
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) {
char c;
struct Utf8Char uchar;
while ((c = *self->source++) != 0) {
uchar = Utf8Parser_Handle(&self->parser, c);
if (uchar.uint) {
return uchar;
}
}
return EMPTY_CHAR;
}
+65
View File
@@ -0,0 +1,65 @@
/**
* TODO file description
*
* Created on 2020/01/04.
*/
#ifndef LIQUIDTYPE_UTF8_H
#define LIQUIDTYPE_UTF8_H
#include <stddef.h>
#include <stdint.h>
struct Utf8Char {
union {
uint8_t bytes[4];
uint32_t uint;
};
};
struct Utf8Parser {
struct Utf8Char buffer;
uint8_t utf_len;
uint8_t utf_j;
};
/**
* Utf8 character iterator.
*
* Usage:
* struct Utf8Iterator iter;
* Utf8Iterator_Start(&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.
*/
struct Utf8Iterator {
/* Characters to parse. The pointer is advanced as the iterator progresses. */
const char *source;
struct Utf8Parser parser;
};
void Utf8Iterator_Start(struct Utf8Iterator *self, const char *source);
/**
* Get the next character from the iterator; Returns empty character if there are no more characters to parse.
*
* Invalid characters are skipped.
*/
struct Utf8Char Utf8Iterator_Next(struct Utf8Iterator *self);
/**
* Parse a character.
*
* The returned struct contains NIL (uint == 0) if no character is yet available.
*
* ASCII is passed through, utf-8 is collected and returned in one piece.
*/
struct Utf8Char Utf8Parser_Handle(struct Utf8Parser *self, char c);
#endif //LIQUIDTYPE_UTF8_H