add lcdbuf lib
This commit is contained in:
@@ -27,6 +27,28 @@
|
||||
// Start address of rows
|
||||
const uint8_t LCD_ROW_ADDR[] = {0x00, 0x40, 0x14, 0x54};
|
||||
|
||||
|
||||
// these are for the lcdbuf library
|
||||
#include "lcd/lcdbuf.h"
|
||||
|
||||
/** Clear the entire screen (CGRAM can be left unchanged, but they will be written anew if needed) */
|
||||
void LcdBuffer_IO_Clear() {
|
||||
lcd_clear();
|
||||
}
|
||||
|
||||
/** Write character data at position */
|
||||
void LcdBuffer_IO_WriteAt(lcdbuf_pos_t row, lcdbuf_pos_t col, const uint8_t *buf, lcdbuf_count_t len)
|
||||
{
|
||||
lcd_xy(col, row);
|
||||
lcd_putsn(buf, len);
|
||||
}
|
||||
|
||||
/** Write CGRAM data. Data is always 8 bytes long. */
|
||||
void LcdBuffer_IO_WriteCGRAM(uint8_t position, const uint8_t* data)
|
||||
{
|
||||
lcd_glyph(position, data);
|
||||
}
|
||||
|
||||
// Internal prototypes
|
||||
void _lcd_mode_r();
|
||||
|
||||
@@ -293,7 +315,7 @@ void _lcd_wait_bf()
|
||||
|
||||
|
||||
/** Send a string to LCD */
|
||||
void lcd_puts(char *str_p)
|
||||
void lcd_puts(const char *str_p)
|
||||
{
|
||||
char c;
|
||||
while ((c = *str_p++)) {
|
||||
@@ -301,6 +323,14 @@ void lcd_puts(char *str_p)
|
||||
}
|
||||
}
|
||||
|
||||
/** Send a string to LCD, N characters long */
|
||||
void lcd_putsn(const char *str_p, size_t len)
|
||||
{
|
||||
while(len--) {
|
||||
lcd_putc(*str_p++);
|
||||
}
|
||||
}
|
||||
|
||||
/** Sedn a char to LCD */
|
||||
void lcd_putc(const char c)
|
||||
{
|
||||
|
||||
@@ -16,13 +16,16 @@
|
||||
void lcd_init();
|
||||
|
||||
/** Send a string to LCD */
|
||||
void lcd_puts(char* str_p);
|
||||
void lcd_puts(const char* str_p);
|
||||
|
||||
/** Send a string to LCD, N characters long */
|
||||
void lcd_putsn(const char *str_p, size_t len);
|
||||
|
||||
/** Sedn a char to LCD */
|
||||
void lcd_putc(char c);
|
||||
|
||||
/** Show string at X, Y */
|
||||
#define lcd_puts_xy(x, y, str_p) do { lcd_xy((x), (y)); lcd_puts((str_p)); } while(0)
|
||||
#define lcd_puts_xy(x, y, str) do { lcd_xy((x), (y)); lcd_puts((str)); } while(0)
|
||||
|
||||
/** Show char at X, Y */
|
||||
#define lcd_putc_xy(x, y, c) do { lcd_xy((x), (y)); lcd_putc((c)); } while(0)
|
||||
|
||||
+477
@@ -0,0 +1,477 @@
|
||||
/**
|
||||
* TODO file description
|
||||
*/
|
||||
|
||||
#include "cgram.h"
|
||||
|
||||
const struct LcdBuf_CGRAM_Symbol CGRAM_CZ[] = {
|
||||
{
|
||||
.symbol = "ě",
|
||||
.fallback = 'e',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b01110,
|
||||
0b10001,
|
||||
0b11111,
|
||||
0b10000,
|
||||
0b01110
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "š",
|
||||
.fallback = 's',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b01110,
|
||||
0b10000,
|
||||
0b01110,
|
||||
0b00001,
|
||||
0b11110
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "č",
|
||||
.fallback = 'c',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b01110,
|
||||
0b10000,
|
||||
0b10000,
|
||||
0b10001,
|
||||
0b01110
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "ř",
|
||||
.fallback = 'r',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b10110,
|
||||
0b11001,
|
||||
0b10000,
|
||||
0b10000,
|
||||
0b10000
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "ž",
|
||||
.fallback = 'z',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b11111,
|
||||
0b00010,
|
||||
0b00100,
|
||||
0b01000,
|
||||
0b11111
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "ý",
|
||||
.fallback = 'y',
|
||||
.data = {
|
||||
0b00010,
|
||||
0b00100,
|
||||
0b10001,
|
||||
0b10001,
|
||||
0b01111,
|
||||
0b00001,
|
||||
0b01110
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "á",
|
||||
.fallback = 'a',
|
||||
.data = {
|
||||
0b00010,
|
||||
0b00100,
|
||||
0b01110,
|
||||
0b00001,
|
||||
0b01111,
|
||||
0b10001,
|
||||
0b01111
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "í",
|
||||
.fallback = 'i',
|
||||
.data = {
|
||||
0b00110,
|
||||
0b00000,
|
||||
0b01100,
|
||||
0b00100,
|
||||
0b00100,
|
||||
0b00100,
|
||||
0b01110
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "é",
|
||||
.fallback = 'e',
|
||||
.data = {
|
||||
0b00010,
|
||||
0b00100,
|
||||
0b01110,
|
||||
0b10001,
|
||||
0b11111,
|
||||
0b10000,
|
||||
0b01110
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "ú",
|
||||
.fallback = 'u',
|
||||
.data = {
|
||||
0b00010,
|
||||
0b00100,
|
||||
0b10001,
|
||||
0b10001,
|
||||
0b10001,
|
||||
0b10011,
|
||||
0b01101
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "ů",
|
||||
.fallback = 'u',
|
||||
.data = {
|
||||
0b00100,
|
||||
0b01010,
|
||||
0b10101,
|
||||
0b10001,
|
||||
0b10001,
|
||||
0b10011,
|
||||
0b01101
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "ď",
|
||||
.fallback = 'd',
|
||||
.data = {
|
||||
0b01101,
|
||||
0b00001,
|
||||
0b01101,
|
||||
0b10011,
|
||||
0b10001,
|
||||
0b10001,
|
||||
0b01111
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "ť",
|
||||
.fallback = 't',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b01001,
|
||||
0b11100,
|
||||
0b01000,
|
||||
0b01000,
|
||||
0b01001,
|
||||
0b00110
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "ň",
|
||||
.fallback = 'n',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b10110,
|
||||
0b11001,
|
||||
0b10001,
|
||||
0b10001,
|
||||
0b10001
|
||||
},
|
||||
},
|
||||
|
||||
// UPPERCASE
|
||||
{
|
||||
.symbol = "Ě",
|
||||
.fallback = 'E',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b11111,
|
||||
0b10000,
|
||||
0b11100,
|
||||
0b10000,
|
||||
0b11111
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "Š",
|
||||
.fallback = 'S',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b01111,
|
||||
0b10000,
|
||||
0b01110,
|
||||
0b00001,
|
||||
0b11110
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "Č",
|
||||
.fallback = 'C',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b01110,
|
||||
0b10001,
|
||||
0b10000,
|
||||
0b10001,
|
||||
0b01110
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "Ř",
|
||||
.fallback = 'R',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b11110,
|
||||
0b10001,
|
||||
0b11110,
|
||||
0b10010,
|
||||
0b10001
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "Ž",
|
||||
.fallback = 'Z',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b11111,
|
||||
0b00001,
|
||||
0b01110,
|
||||
0b10000,
|
||||
0b11111
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "Ý",
|
||||
.fallback = 'Y',
|
||||
.data = {
|
||||
0b00010,
|
||||
0b00100,
|
||||
0b10001,
|
||||
0b10001,
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b00100
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "Á",
|
||||
.fallback = 'A',
|
||||
.data = {
|
||||
0b00010,
|
||||
0b00100,
|
||||
0b01110,
|
||||
0b10001,
|
||||
0b11111,
|
||||
0b10001,
|
||||
0b10001
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "Í",
|
||||
.fallback = 'I',
|
||||
.data = {
|
||||
0b00010,
|
||||
0b00100,
|
||||
0b01110,
|
||||
0b00100,
|
||||
0b00100,
|
||||
0b00100,
|
||||
0b01110
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "É",
|
||||
.fallback = 'E',
|
||||
.data = {
|
||||
0b00010,
|
||||
0b00100,
|
||||
0b11111,
|
||||
0b10000,
|
||||
0b11100,
|
||||
0b10000,
|
||||
0b11111
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "Ú",
|
||||
.fallback = 'U',
|
||||
.data = {
|
||||
0b00010,
|
||||
0b00100,
|
||||
0b10001,
|
||||
0b10001,
|
||||
0b10001,
|
||||
0b10001,
|
||||
0b01110
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "Ů",
|
||||
.fallback = 'U',
|
||||
.data = {
|
||||
0b00100,
|
||||
0b01010,
|
||||
0b10101,
|
||||
0b10001,
|
||||
0b10001,
|
||||
0b10001,
|
||||
0b01110
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "Ď",
|
||||
.fallback = 'D',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b11100,
|
||||
0b10010,
|
||||
0b10001,
|
||||
0b10010,
|
||||
0b11100
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "Ť",
|
||||
.fallback = 'T',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b11111,
|
||||
0b00100,
|
||||
0b00100,
|
||||
0b00100,
|
||||
0b00100
|
||||
},
|
||||
},
|
||||
{
|
||||
.symbol = "Ň",
|
||||
.fallback = 'N',
|
||||
.data = {
|
||||
0b01010,
|
||||
0b00100,
|
||||
0b10001,
|
||||
0b11001,
|
||||
0b10101,
|
||||
0b10011,
|
||||
0b10001
|
||||
},
|
||||
},
|
||||
|
||||
{}, /* end mark */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
// this should be more or less the default font
|
||||
|
||||
{{0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000}}, //
|
||||
{{0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00000, 0b00100}}, // !
|
||||
{{0b01010, 0b01010, 0b01010, 0b00000, 0b00000, 0b00000, 0b00000}}, // "
|
||||
{{0b01010, 0b01010, 0b11111, 0b01010, 0b11111, 0b01010, 0b01010}}, // #
|
||||
{{0b00100, 0b01111, 0b10100, 0b01110, 0b00101, 0b11110, 0b00100}}, // $
|
||||
{{0b11000, 0b11001, 0b00010, 0b00100, 0b01000, 0b10011, 0b00011}}, // %
|
||||
{{0b01100, 0b10010, 0b10100, 0b01000, 0b10101, 0b10010, 0b01101}}, // &
|
||||
{{0b01100, 0b00100, 0b01000, 0b00000, 0b00000, 0b00000, 0b00000}}, // '
|
||||
{{0b00010, 0b00100, 0b01000, 0b01000, 0b01000, 0b00100, 0b00010}}, // (
|
||||
{{0b01000, 0b00100, 0b00010, 0b00010, 0b00010, 0b00100, 0b01000}}, // )
|
||||
{{0b00000, 0b00100, 0b10101, 0b01110, 0b10101, 0b00100, 0b00000}}, // *
|
||||
{{0b00000, 0b00100, 0b00100, 0b11111, 0b00100, 0b00100, 0b00000}}, // +
|
||||
{{0b00000, 0b00000, 0b00000, 0b00000, 0b01100, 0b00100, 0b01000}}, // ,
|
||||
{{0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000}}, // -
|
||||
{{0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b01100, 0b01100}}, // .
|
||||
{{0b00000, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b00000}}, // /
|
||||
{{0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110}}, // 0
|
||||
{{0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110}}, // 1
|
||||
{{0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b01000, 0b11111}}, // 2
|
||||
{{0b11111, 0b00010, 0b00100, 0b00010, 0b00001, 0b10001, 0b01110}}, // 3
|
||||
{{0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010}}, // 4
|
||||
{{0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110}}, // 5
|
||||
{{0b00110, 0b01000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110}}, // 6
|
||||
{{0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000}}, // 7
|
||||
{{0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110}}, // 8
|
||||
{{0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00010, 0b01100}}, // 9
|
||||
{{0b00000, 0b01100, 0b01100, 0b00000, 0b01100, 0b01100, 0b00000}}, // :
|
||||
{{0b00000, 0b01100, 0b01100, 0b00000, 0b01100, 0b00100, 0b01000}}, // ;
|
||||
{{0b00010, 0b00100, 0b01000, 0b10000, 0b01000, 0b00100, 0b00010}}, // <
|
||||
{{0b00000, 0b00000, 0b11111, 0b00000, 0b11111, 0b00000, 0b00000}}, // =
|
||||
{{0b01000, 0b00100, 0b00010, 0b00001, 0b00010, 0b00100, 0b01000}}, // >
|
||||
{{0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b00000, 0b00100}}, // ?
|
||||
{{0b01110, 0b10001, 0b00001, 0b01101, 0b10101, 0b10101, 0b01110}}, // @
|
||||
{{0b01110, 0b10001, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001}}, // A
|
||||
{{0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b11110}}, // B
|
||||
{{0b01110, 0b10001, 0b10000, 0b10000, 0b10000, 0b10001, 0b01110}}, // C
|
||||
{{0b11100, 0b10010, 0b10001, 0b10001, 0b10001, 0b10010, 0b11100}}, // D
|
||||
{{0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111}}, // E
|
||||
{{0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000}}, // F
|
||||
{{0b01110, 0b10001, 0b10000, 0b10111, 0b10001, 0b10001, 0b01111}}, // G
|
||||
{{0b10001, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001}}, // H
|
||||
{{0b01110, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110}}, // I
|
||||
{{0b00111, 0b00010, 0b00010, 0b00010, 0b00010, 0b10010, 0b01100}}, // J
|
||||
{{0b10001, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010, 0b10001}}, // K
|
||||
{{0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111}}, // L
|
||||
{{0b10001, 0b11011, 0b10101, 0b10101, 0b10001, 0b10001, 0b10001}}, // M
|
||||
{{0b10001, 0b10001, 0b11001, 0b10101, 0b10011, 0b10001, 0b10001}}, // N
|
||||
{{0b01110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110}}, // O
|
||||
{{0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000}}, // P
|
||||
{{0b01110, 0b10001, 0b10001, 0b10001, 0b10101, 0b10010, 0b01101}}, // Q
|
||||
{{0b11110, 0b10001, 0b10001, 0b11110, 0b10100, 0b10010, 0b10001}}, // R
|
||||
{{0b01111, 0b10000, 0b10000, 0b01110, 0b00001, 0b00001, 0b11110}}, // S
|
||||
{{0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100}}, // T
|
||||
{{0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110}}, // U
|
||||
{{0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01010, 0b00100}}, // V
|
||||
{{0b10001, 0b10001, 0b10001, 0b10101, 0b10101, 0b10101, 0b01010}}, // W
|
||||
{{0b10001, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001, 0b10001}}, // X
|
||||
{{0b10001, 0b10001, 0b10001, 0b01010, 0b00100, 0b00100, 0b00100}}, // Y
|
||||
{{0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b11111}}, // Z
|
||||
{{0b01110, 0b01000, 0b01000, 0b01000, 0b01000, 0b01000, 0b01110}}, // [
|
||||
{{0b00000, 0b10000, 0b01000, 0b00100, 0b00010, 0b00001, 0b00000}}, // \
|
||||
{{0b01110, 0b00010, 0b00010, 0b00010, 0b00010, 0b00010, 0b01110}}, // ]
|
||||
{{0b00100, 0b01010, 0b10001, 0b00000, 0b00000, 0b00000, 0b00000}}, // ^
|
||||
{{0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111}}, // _
|
||||
{{0b01000, 0b00100, 0b00010, 0b00000, 0b00000, 0b00000, 0b00000}}, // `
|
||||
{{0b00000, 0b00000, 0b01110, 0b00001, 0b01111, 0b10001, 0b01111}}, // a
|
||||
{{0b10000, 0b10000, 0b10110, 0b11001, 0b10001, 0b10001, 0b11110}}, // b
|
||||
{{0b00000, 0b00000, 0b01110, 0b10000, 0b10000, 0b10001, 0b01110}}, // c
|
||||
{{0b00001, 0b00001, 0b01101, 0b10011, 0b10001, 0b10001, 0b01111}}, // d
|
||||
{{0b00000, 0b00000, 0b01110, 0b10001, 0b11111, 0b10000, 0b01110}}, // e
|
||||
{{0b00110, 0b01001, 0b01000, 0b11100, 0b01000, 0b01000, 0b01000}}, // f
|
||||
{{0b00000, 0b01111, 0b10001, 0b10001, 0b01111, 0b00001, 0b01110}}, // g
|
||||
{{0b10000, 0b10000, 0b10110, 0b11001, 0b10001, 0b10001, 0b10001}}, // h
|
||||
{{0b00100, 0b00000, 0b01100, 0b00100, 0b00100, 0b00100, 0b01110}}, // i
|
||||
{{0b00010, 0b00000, 0b00110, 0b00010, 0b00010, 0b10010, 0b01100}}, // j
|
||||
{{0b10000, 0b10000, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010}}, // k
|
||||
{{0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110}}, // l
|
||||
{{0b00000, 0b00000, 0b11010, 0b10101, 0b10101, 0b10001, 0b10001}}, // m
|
||||
{{0b00000, 0b00000, 0b10110, 0b11001, 0b10001, 0b10001, 0b10001}}, // n
|
||||
{{0b00000, 0b00000, 0b01110, 0b10001, 0b10001, 0b10001, 0b01110}}, // o
|
||||
{{0b00000, 0b00000, 0b11110, 0b10001, 0b11110, 0b10000, 0b10000}}, // p
|
||||
{{0b00000, 0b00000, 0b01101, 0b10011, 0b01111, 0b00001, 0b00001}}, // q
|
||||
{{0b00000, 0b00000, 0b10110, 0b11001, 0b10000, 0b10000, 0b10000}}, // r
|
||||
{{0b00000, 0b00000, 0b01110, 0b10000, 0b01110, 0b00001, 0b11110}}, // s
|
||||
{{0b01000, 0b01000, 0b11100, 0b01000, 0b01000, 0b01001, 0b00110}}, // t
|
||||
{{0b00000, 0b00000, 0b10001, 0b10001, 0b10001, 0b10011, 0b01101}}, // u
|
||||
{{0b00000, 0b00000, 0b10001, 0b10001, 0b10001, 0b01010, 0b00100}}, // v
|
||||
{{0b00000, 0b00000, 0b10001, 0b10001, 0b10101, 0b10101, 0b01010}}, // w
|
||||
{{0b00000, 0b00000, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001}}, // x
|
||||
{{0b00000, 0b00000, 0b10001, 0b10001, 0b01111, 0b00001, 0b01110}}, // y
|
||||
{{0b00000, 0b00000, 0b11111, 0b00010, 0b00100, 0b01000, 0b11111}}, // z
|
||||
{{0b00010, 0b00100, 0b00100, 0b01000, 0b00100, 0b00100, 0b00010}}, // {
|
||||
{{0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100}}, // |
|
||||
{{0b01000, 0b00100, 0b00100, 0b00010, 0b00100, 0b00100, 0b01000}}, // }
|
||||
{{0b00000, 0b00000, 0b00000, 0b01101, 0b10010, 0b00000, 0b00000}}, // ~
|
||||
*/
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* TODO file description
|
||||
*/
|
||||
|
||||
#ifndef HD44780UTF_CGRAM_H
|
||||
#define HD44780UTF_CGRAM_H
|
||||
|
||||
#include "utf8.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/** Pattern for CGRAM */
|
||||
struct LcdBuf_CGRAM_Symbol {
|
||||
/** The symbol displayed */
|
||||
struct Utf8Char symbol;
|
||||
|
||||
/** ASCII char shown if there is no space for the CGRAM pattern */
|
||||
char fallback;
|
||||
|
||||
/** Graphic data (rows) - the 8th row is typically blank and can be left out from the definition */
|
||||
uint8_t data[8];
|
||||
};
|
||||
|
||||
extern const struct LcdBuf_CGRAM_Symbol CGRAM_CZ[];
|
||||
|
||||
#endif //HD44780UTF_CGRAM_H
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
#include <stdint.h>
|
||||
#include "utf8.h"
|
||||
#include "cgrom.h"
|
||||
|
||||
const struct LcdBuf_CGROM_Entry CGROM_A00[] = {
|
||||
{.address = 32, .symbol = " "},
|
||||
{.address = 33, .symbol = "!"},
|
||||
{.address = 34, .symbol = "\""},
|
||||
{.address = 35, .symbol = "#"},
|
||||
{.address = 36, .symbol = "$"},
|
||||
{.address = 37, .symbol = "%"},
|
||||
{.address = 38, .symbol = "&"},
|
||||
{.address = 39, .symbol = "'"},
|
||||
{.address = 40, .symbol = "("},
|
||||
{.address = 41, .symbol = ")"},
|
||||
{.address = 42, .symbol = "*"},
|
||||
{.address = 43, .symbol = "+"},
|
||||
{.address = 44, .symbol = ","},
|
||||
{.address = 45, .symbol = "-"},
|
||||
{.address = 46, .symbol = "."},
|
||||
{.address = 47, .symbol = "/"},
|
||||
{.address = 48, .symbol = "0"},
|
||||
{.address = 49, .symbol = "1"},
|
||||
{.address = 50, .symbol = "2"},
|
||||
{.address = 51, .symbol = "3"},
|
||||
{.address = 52, .symbol = "4"},
|
||||
{.address = 53, .symbol = "5"},
|
||||
{.address = 54, .symbol = "6"},
|
||||
{.address = 55, .symbol = "7"},
|
||||
{.address = 56, .symbol = "8"},
|
||||
{.address = 57, .symbol = "9"},
|
||||
{.address = 58, .symbol = ":"},
|
||||
{.address = 59, .symbol = ";"},
|
||||
{.address = 60, .symbol = "<"},
|
||||
{.address = 61, .symbol = "="},
|
||||
{.address = 62, .symbol = ">"},
|
||||
{.address = 63, .symbol = "?"},
|
||||
{.address = 64, .symbol = "@"},
|
||||
{.address = 65, .symbol = "A"},
|
||||
{.address = 66, .symbol = "B"},
|
||||
{.address = 67, .symbol = "C"},
|
||||
{.address = 68, .symbol = "D"},
|
||||
{.address = 69, .symbol = "E"},
|
||||
{.address = 70, .symbol = "F"},
|
||||
{.address = 71, .symbol = "G"},
|
||||
{.address = 72, .symbol = "H"},
|
||||
{.address = 73, .symbol = "I"},
|
||||
{.address = 74, .symbol = "J"},
|
||||
{.address = 75, .symbol = "K"},
|
||||
{.address = 76, .symbol = "L"},
|
||||
{.address = 77, .symbol = "M"},
|
||||
{.address = 78, .symbol = "N"},
|
||||
{.address = 79, .symbol = "O"},
|
||||
{.address = 80, .symbol = "P"},
|
||||
{.address = 81, .symbol = "Q"},
|
||||
{.address = 82, .symbol = "R"},
|
||||
{.address = 83, .symbol = "S"},
|
||||
{.address = 84, .symbol = "T"},
|
||||
{.address = 85, .symbol = "U"},
|
||||
{.address = 86, .symbol = "V"},
|
||||
{.address = 87, .symbol = "W"},
|
||||
{.address = 88, .symbol = "X"},
|
||||
{.address = 89, .symbol = "Y"},
|
||||
{.address = 90, .symbol = "Z"},
|
||||
{.address = 91, .symbol = "["},
|
||||
{.address = 92, .symbol = "¥"}, // yen
|
||||
{.address = 93, .symbol = "]"},
|
||||
{.address = 94, .symbol = "^"},
|
||||
{.address = 95, .symbol = "_"},
|
||||
{.address = 96, .symbol = "`"},
|
||||
{.address = 97, .symbol = "a"},
|
||||
{.address = 98, .symbol = "b"},
|
||||
{.address = 99, .symbol = "c"},
|
||||
{.address = 100, .symbol = "d"},
|
||||
{.address = 101, .symbol = "e"},
|
||||
{.address = 102, .symbol = "f"},
|
||||
{.address = 103, .symbol = "g"},
|
||||
{.address = 104, .symbol = "h"},
|
||||
{.address = 105, .symbol = "i"},
|
||||
{.address = 106, .symbol = "j"},
|
||||
{.address = 107, .symbol = "k"},
|
||||
{.address = 108, .symbol = "l"},
|
||||
{.address = 109, .symbol = "m"},
|
||||
{.address = 110, .symbol = "n"},
|
||||
{.address = 111, .symbol = "o"},
|
||||
{.address = 112, .symbol = "p"},
|
||||
{.address = 113, .symbol = "q"},
|
||||
{.address = 114, .symbol = "r"},
|
||||
{.address = 115, .symbol = "s"},
|
||||
{.address = 116, .symbol = "t"},
|
||||
{.address = 117, .symbol = "u"},
|
||||
{.address = 118, .symbol = "v"},
|
||||
{.address = 119, .symbol = "w"},
|
||||
{.address = 120, .symbol = "x"},
|
||||
{.address = 121, .symbol = "y"},
|
||||
{.address = 122, .symbol = "z"},
|
||||
{.address = 123, .symbol = "{"},
|
||||
{.address = 124, .symbol = "|"},
|
||||
{.address = 125, .symbol = "}"},
|
||||
{.address = 126, .symbol = "←"},
|
||||
{.address = 127, .symbol = "→"},
|
||||
|
||||
// lots of japanese symbols - add them yourself if you need them
|
||||
{.address = 0xA2, .symbol = "「"},
|
||||
{.address = 0xA3, .symbol = "」"},
|
||||
{.address = 0xA5, .symbol = "·"},
|
||||
{.address = 0xDF, .symbol = "°"},
|
||||
|
||||
// there is also some greek and obscure diacritics
|
||||
{.address = 0xE0, .symbol = "α"},
|
||||
{.address = 0xE1, .symbol = "ä"},
|
||||
{.address = 0xE2, .symbol = "β"},
|
||||
{.address = 0xE3, .symbol = "ϵ"},
|
||||
{.address = 0xE4, .symbol = "μ"},
|
||||
{.address = 0xE5, .symbol = "σ"},
|
||||
{.address = 0xE6, .symbol = "ρ"},
|
||||
{.address = 0xE8, .symbol = "√"},
|
||||
// E9 is nice superscript minus one, but it's not in Unicode :(
|
||||
|
||||
{.address = 0xEC, .symbol = "¢"},
|
||||
{.address = 0xED, .symbol = "£"},
|
||||
{.address = 0xEE, .symbol = "ñ"},
|
||||
{.address = 0xEF, .symbol = "ö"},
|
||||
|
||||
{.address = 0xF2, .symbol = "Θ"},
|
||||
{.address = 0xF3, .symbol = "∞"},
|
||||
{.address = 0xF4, .symbol = "Ω"},
|
||||
{.address = 0xF5, .symbol = "Ü"},
|
||||
{.address = 0xF6, .symbol = "Σ"},
|
||||
{.address = 0xF7, .symbol = "π"},
|
||||
|
||||
{.address = 0xFC, .symbol = "円"},
|
||||
{.address = 0xFD, .symbol = "÷"},
|
||||
{.address = 0xFF, .symbol = "█"},
|
||||
{}, /* end mark */
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* A definition of the actual CGROM
|
||||
*/
|
||||
|
||||
#ifndef HD44780UTF_CGROM_H
|
||||
#define HD44780UTF_CGROM_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "utf8.h"
|
||||
|
||||
/** CGROM look-up table entry */
|
||||
struct LcdBuf_CGROM_Entry {
|
||||
/** Address in the CGROM */
|
||||
uint8_t address;
|
||||
|
||||
/** Corresponding symbol */
|
||||
struct Utf8Char symbol;
|
||||
};
|
||||
|
||||
|
||||
/** The standard japanese lookup table, terminated by an empty entry */
|
||||
extern const struct LcdBuf_CGROM_Entry CGROM_A00[];
|
||||
|
||||
#endif //HD44780UTF_CGROM_H
|
||||
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* HD44780 utf8-capable display buffer
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
//#include <stdio.h>
|
||||
#include "lcdbuf.h"
|
||||
|
||||
|
||||
/** Initialize the struct */
|
||||
void LcdBuffer_Init(struct LcdBuffer *self, const struct LcdBuf_CGROM_Entry *cgrom, const struct LcdBuf_CGRAM_Symbol *custom_symbols)
|
||||
{
|
||||
assert(self);
|
||||
assert(cgrom);
|
||||
assert(custom_symbols);
|
||||
|
||||
LcdBuffer_Clear(self);
|
||||
self->cgrom = cgrom;
|
||||
self->custom_symbols = custom_symbols;
|
||||
}
|
||||
|
||||
/** Clear the screen */
|
||||
void LcdBuffer_Clear(struct LcdBuffer *self)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
memset(self->cgram, 0, sizeof(self->cgram));
|
||||
memset(self->screen, ' ', sizeof(self->screen));
|
||||
memset(self->dirty_extents, 0, sizeof(self->dirty_extents));
|
||||
|
||||
// everything must be written, who knows what was left on the display before!
|
||||
self->full_repaint_required = true;
|
||||
}
|
||||
|
||||
/** Write what needs to be written to the HW, clear all dirty marks */
|
||||
void LcdBuffer_Flush(struct LcdBuffer *self)
|
||||
{
|
||||
for (int i = 0; i < LCDBUF_CGRAM_CAPACITY; i++) {
|
||||
if (self->cgram[i].refcount > 0 && self->cgram[i].dirty) {
|
||||
LcdBuffer_IO_WriteCGRAM(i, self->custom_symbols[self->cgram[i].symbol_index].data);
|
||||
self->cgram[i].dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->full_repaint_required) {
|
||||
self->full_repaint_required = false;
|
||||
// Check if we have anything on the display - if not, just clear screen
|
||||
bool any_nonspace = false;
|
||||
for (int r = 0; !any_nonspace && r < LINE_NUM; r++) {
|
||||
for (int c = 0; !any_nonspace && c < LINE_LEN; c++) {
|
||||
if (self->screen[r][c] != ' ') {
|
||||
any_nonspace = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!any_nonspace) {
|
||||
LcdBuffer_IO_Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int r = 0; r < LINE_NUM; r++) {
|
||||
LcdBuffer_IO_WriteAt(r, 0, self->screen[r], LINE_LEN);
|
||||
}
|
||||
memset(self->dirty_extents, 0, sizeof(self->dirty_extents));
|
||||
} else {
|
||||
for (int e = 0; e < LCDBUF_DIRTY_LIST_LEN; e++) {
|
||||
struct LcdBuf_DirtyExtent *ext = &self->dirty_extents[e];
|
||||
if (!ext->count) {
|
||||
continue;
|
||||
}
|
||||
LcdBuffer_IO_WriteAt(ext->row, ext->col, &self->screen[ext->row][ext->col], ext->count);
|
||||
ext->count = 0; // mark the slot as free
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Fully write everything to the display */
|
||||
void LcdBuffer_FlushAll(struct LcdBuffer *self)
|
||||
{
|
||||
for (int i = 0; i < LCDBUF_CGRAM_CAPACITY; i++) {
|
||||
if (self->cgram[i].refcount > 0) {
|
||||
LcdBuffer_IO_WriteCGRAM(i, self->custom_symbols[self->cgram[i].symbol_index].data);
|
||||
}
|
||||
self->cgram[i].dirty = false;
|
||||
}
|
||||
|
||||
for (int r = 0; r < LINE_NUM; r++) {
|
||||
LcdBuffer_IO_WriteAt(r, 0, self->screen[r], LINE_LEN);
|
||||
}
|
||||
|
||||
memset(self->dirty_extents, 0, sizeof(self->dirty_extents));
|
||||
self->full_repaint_required = false;
|
||||
}
|
||||
|
||||
//static void show_dirty_slots(const struct LcdBuffer *self) {
|
||||
// printf("\n\n");
|
||||
// for (int i = 0; i < LCDBUF_DIRTY_LIST_LEN; i++) {
|
||||
// if (self->dirty_extents[i].count == 0) {
|
||||
// continue;
|
||||
// }
|
||||
// printf("dirty_extent(%d): col %d, row %d, len %d\n", i, self->dirty_extents[i].row, self->dirty_extents[i].col, self->dirty_extents[i].count);
|
||||
// }
|
||||
// printf("\n");
|
||||
//}
|
||||
|
||||
static void mark_dirty(struct LcdBuffer *self, lcdbuf_pos_t row, lcdbuf_pos_t col)
|
||||
{
|
||||
// partial updates are not needed if everything will be written anyway
|
||||
if (self->full_repaint_required) {
|
||||
return;
|
||||
}
|
||||
|
||||
int first_empty_extent_slot = -1;
|
||||
for (int i = 0; i < LCDBUF_DIRTY_LIST_LEN; i++) {
|
||||
struct LcdBuf_DirtyExtent *ext = &self->dirty_extents[i];
|
||||
if (ext->count == 0) {
|
||||
// unused
|
||||
if (first_empty_extent_slot == -1) {
|
||||
first_empty_extent_slot = i;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (ext->row != row) {
|
||||
// not this row
|
||||
continue;
|
||||
}
|
||||
|
||||
// this is a filled extent
|
||||
|
||||
if ((ext->col <= col) && (ext->col + ext->count > col)) {
|
||||
// already in this extent
|
||||
// show_dirty_slots(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((col < ext->col) && ((ext->col - col) <= LCDBUF_DIRTY_EXTEND)) {
|
||||
ext->count += ext->col - col;
|
||||
ext->col = col;
|
||||
// show_dirty_slots(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((col >= ext->col + ext->count) && ((col - (ext->col + ext->count)) <= LCDBUF_DIRTY_EXTEND)) {
|
||||
ext->count += col - (ext->col + ext->count) + 1;
|
||||
// show_dirty_slots(self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (first_empty_extent_slot == -1) {
|
||||
// printf("Give up on dirty extents\n");
|
||||
self->full_repaint_required = true;
|
||||
} else {
|
||||
// printf("New dirty extent: #%d\n", first_empty_extent_slot);
|
||||
self->dirty_extents[first_empty_extent_slot].col = col;
|
||||
self->dirty_extents[first_empty_extent_slot].row = row;
|
||||
self->dirty_extents[first_empty_extent_slot].count = 1;
|
||||
}
|
||||
|
||||
// show_dirty_slots(self);
|
||||
}
|
||||
|
||||
/** Set one utf8 character at a position */
|
||||
void LcdBuffer_Set(struct LcdBuffer *self, lcdbuf_pos_t row, lcdbuf_pos_t col, struct Utf8Char ch)
|
||||
{
|
||||
// printf("set %d:%d\n", row, col);
|
||||
assert(self);
|
||||
assert(row < LINE_NUM);
|
||||
assert(col < LINE_LEN);
|
||||
|
||||
const uint8_t oldchar = self->screen[row][col];
|
||||
|
||||
if (oldchar >= LCDBUF_CGRAM_CAPACITY && oldchar == ch.uint) {
|
||||
// No change
|
||||
return;
|
||||
}
|
||||
|
||||
// Fast path for standard ASCII - assuming this extent of the table is always the same!
|
||||
if (ch.uint >= 32 && ch.uint < 126 && ch.uint != '\\') { // A00 has YEN in place of BACKSLASH
|
||||
// normal ASCII
|
||||
self->screen[row][col] = ch.uint;
|
||||
goto done_dirty;
|
||||
}
|
||||
|
||||
// Find if it's in CGROM
|
||||
const struct LcdBuf_CGROM_Entry *rom = self->cgrom;
|
||||
for (;;) {
|
||||
if (rom->symbol.uint == 0) {
|
||||
// End of the lookup table
|
||||
break;
|
||||
}
|
||||
|
||||
if (rom->symbol.uint == ch.uint) {
|
||||
// found it!
|
||||
self->screen[row][col] = rom->address;
|
||||
goto done_dirty;
|
||||
}
|
||||
|
||||
rom++;
|
||||
}
|
||||
|
||||
// Check if the same custom char is already used - if so, increment refcount and reuse it
|
||||
int first_empty_custom_slot = -1;
|
||||
for (uint8_t slot = 0; slot < LCDBUF_CGRAM_CAPACITY; slot++) {
|
||||
if (self->cgram[slot].refcount > 0) {
|
||||
if (self->cgram[slot].uint == ch.uint) {
|
||||
if (oldchar == slot) {
|
||||
// No change, was already the same custom
|
||||
return;
|
||||
}
|
||||
|
||||
self->cgram[slot].refcount++;
|
||||
self->screen[row][col] = slot;
|
||||
goto done_dirty;
|
||||
}
|
||||
} else if (first_empty_custom_slot == -1) {
|
||||
first_empty_custom_slot = slot;
|
||||
}
|
||||
}
|
||||
|
||||
// New custom pattern is needed
|
||||
|
||||
uint16_t index = 0;
|
||||
const struct LcdBuf_CGRAM_Symbol *pattern = self->custom_symbols;
|
||||
for (;;) {
|
||||
if (pattern->symbol.uint == 0) {
|
||||
// End of the lookup table
|
||||
break;
|
||||
}
|
||||
|
||||
if (pattern->symbol.uint == ch.uint) {
|
||||
// found it!
|
||||
|
||||
if (first_empty_custom_slot == -1) {
|
||||
// Whoops, out of slots. Show a fallback glyph
|
||||
if (pattern->fallback != 0) {
|
||||
if (oldchar != pattern->fallback) {
|
||||
self->screen[row][col] = pattern->fallback;
|
||||
goto done_dirty;
|
||||
}
|
||||
} else {
|
||||
// TODO kick out some other CGRAM entry that has fallback?
|
||||
self->screen[row][col] = LCDBUF_SUBSTITUTION_CHAR;
|
||||
goto done_dirty;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate a new slot in the CGRAM
|
||||
self->cgram[first_empty_custom_slot].refcount = 1;
|
||||
self->cgram[first_empty_custom_slot].uint = ch.uint;
|
||||
self->cgram[first_empty_custom_slot].dirty = true; // it should be flushed!
|
||||
self->cgram[first_empty_custom_slot].symbol_index = index;
|
||||
|
||||
self->screen[row][col] = first_empty_custom_slot;
|
||||
goto done_dirty;
|
||||
}
|
||||
|
||||
index++;
|
||||
pattern++;
|
||||
}
|
||||
|
||||
// Fallback, no way to show this glyph
|
||||
self->screen[row][col] = LCDBUF_SUBSTITUTION_CHAR;
|
||||
|
||||
done_dirty:
|
||||
|
||||
if (oldchar < LCDBUF_CGRAM_CAPACITY) {
|
||||
// release refcount on the CGRAM cell
|
||||
assert(self->cgram[oldchar].refcount > 0);
|
||||
self->cgram[oldchar].refcount--;
|
||||
}
|
||||
mark_dirty(self, row, col);
|
||||
}
|
||||
|
||||
/** Write a UTF8 string at a position */
|
||||
void LcdBuffer_Write(struct LcdBuffer *self, lcdbuf_pos_t row, lcdbuf_pos_t col, char *utf_string)
|
||||
{
|
||||
struct Utf8Iterator iter;
|
||||
Utf8Iterator_Init(&iter, utf_string);
|
||||
struct Utf8Char uchar;
|
||||
while ((uchar = Utf8Iterator_Next(&iter)).uint) {
|
||||
if (col >= LINE_LEN) {
|
||||
break;
|
||||
}
|
||||
LcdBuffer_Set(self, row, col, uchar);
|
||||
col++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* HD44780 utf8-capable display buffer
|
||||
*/
|
||||
|
||||
#ifndef HD44780UTF_LCDBUF_H
|
||||
#define HD44780UTF_LCDBUF_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "utf8.h"
|
||||
#include "cgram.h"
|
||||
#include "cgrom.h"
|
||||
|
||||
// --- settings ---
|
||||
|
||||
#define LINE_NUM 4
|
||||
#define LINE_LEN 20
|
||||
|
||||
/// number of remembered dirty regions before giving up and just writing full display
|
||||
#define LCDBUF_DIRTY_LIST_LEN 8
|
||||
|
||||
/// if a cell is marked dirty closer than this to an existing extent,
|
||||
/// the extent can grows in the direction rather than creating a new one
|
||||
#define LCDBUF_DIRTY_EXTEND 5
|
||||
|
||||
/// Char shown if the requested symbol is missing in lookup tables
|
||||
#define LCDBUF_SUBSTITUTION_CHAR 0xDB // this is the katakana box (ro ロ?) in the codepage A00
|
||||
|
||||
/// this must contain the full width or height
|
||||
typedef uint8_t lcdbuf_pos_t;
|
||||
|
||||
/// this must contain the total number of cells in the display
|
||||
typedef uint8_t lcdbuf_count_t;
|
||||
_Static_assert(LINE_NUM * LINE_LEN < 256, "LINE_NUM * LINE_LEN must fit in u8");
|
||||
|
||||
// ----------------
|
||||
|
||||
/// Number of custom CGRAM slots (always 8)
|
||||
#define LCDBUF_CGRAM_CAPACITY 8
|
||||
|
||||
/** Indicates a range of screen cells that were changed and must be written to HW */
|
||||
struct LcdBuf_DirtyExtent {
|
||||
lcdbuf_pos_t row;
|
||||
lcdbuf_pos_t col;
|
||||
lcdbuf_count_t count;
|
||||
};
|
||||
|
||||
/** Struct for one CGRAM slot */
|
||||
struct LcdBuf_CgramState {
|
||||
/** UTF8 uint shown in this slot */
|
||||
uint32_t uint;
|
||||
/** Array index in the custom symbols table, use for look-up when writing the font data to HW */
|
||||
uint16_t symbol_index;
|
||||
/** Number of occurrences of this symbol in the screen array */
|
||||
lcdbuf_count_t refcount;
|
||||
/** This CGRAM slot needs to be written to HW */
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
struct LcdBuffer {
|
||||
/** The raw screen buffer. Custom symbols are 0x00-0x07 */
|
||||
uint8_t screen[LINE_NUM][LINE_LEN];
|
||||
/** CGRAM state array */
|
||||
struct LcdBuf_CgramState cgram[LCDBUF_CGRAM_CAPACITY];
|
||||
/** Hardware CGROM lookup table, used to map UTF8 to existing ROM symbols */
|
||||
const struct LcdBuf_CGROM_Entry *cgrom;
|
||||
/** Defined custom display pattern of utf8 symbols */
|
||||
const struct LcdBuf_CGRAM_Symbol *custom_symbols;
|
||||
|
||||
/** Array of dirty extents - ranges in the display that need to be flushed to HW */
|
||||
struct LcdBuf_DirtyExtent dirty_extents[LCDBUF_DIRTY_LIST_LEN];
|
||||
/** If the dirty extents array was not sufficient to hold all changes, this flag is set,
|
||||
* indicating the dirty_extents array should be disregarded. */
|
||||
bool full_repaint_required;
|
||||
};
|
||||
|
||||
/** Initialize the struct */
|
||||
void LcdBuffer_Init(struct LcdBuffer *self, const struct LcdBuf_CGROM_Entry *cgrom, const struct LcdBuf_CGRAM_Symbol *custom_symbols);
|
||||
|
||||
/** Clear the screen */
|
||||
void LcdBuffer_Clear(struct LcdBuffer *self);
|
||||
|
||||
/** Write what needs to be written to the HW, clear all dirty marks */
|
||||
void LcdBuffer_Flush(struct LcdBuffer *self);
|
||||
|
||||
/** Fully write everything to the display */
|
||||
void LcdBuffer_FlushAll(struct LcdBuffer *self);
|
||||
|
||||
/** Set one utf8 character at a position */
|
||||
void LcdBuffer_Set(struct LcdBuffer *self, lcdbuf_pos_t row, lcdbuf_pos_t col, struct Utf8Char ch);
|
||||
|
||||
/** Write a UTF8 string at a position */
|
||||
void LcdBuffer_Write(struct LcdBuffer *self, lcdbuf_pos_t row, lcdbuf_pos_t col, char *utf_string);
|
||||
|
||||
/* Callbacks - need to be implemented by the application! */
|
||||
|
||||
/** Clear the entire screen (CGRAM can be left unchanged, but they will be written anew if needed) */
|
||||
extern void LcdBuffer_IO_Clear();
|
||||
|
||||
/** Write character data at position */
|
||||
extern void LcdBuffer_IO_WriteAt(lcdbuf_pos_t row, lcdbuf_pos_t col, const uint8_t *buf, lcdbuf_count_t len);
|
||||
|
||||
/** Write CGRAM data. Data is always 8 bytes long. */
|
||||
extern void LcdBuffer_IO_WriteCGRAM(uint8_t position, const uint8_t* data);
|
||||
|
||||
#endif //HD44780UTF_LCDBUF_H
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
#include <stdint.h>
|
||||
#include "utf8.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
|
||||
|
||||
void Utf8Parser_Clear(struct Utf8Parser *self)
|
||||
{
|
||||
self->buffer.uint = 0;
|
||||
self->utf_j = 0;
|
||||
self->utf_len = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_Init(struct Utf8Iterator *self, const char *source)
|
||||
{
|
||||
Utf8Parser_Clear(&self->parser);
|
||||
self->source = source;
|
||||
}
|
||||
|
||||
|
||||
struct Utf8Char Utf8Iterator_Next(struct Utf8Iterator *self)
|
||||
{
|
||||
char c;
|
||||
struct Utf8Char uchar;
|
||||
while (1) {
|
||||
c = *self->source++;
|
||||
if (!c) { break; }
|
||||
|
||||
uchar = Utf8Parser_Handle(&self->parser, c);
|
||||
if (uchar.uint) {
|
||||
return uchar;
|
||||
}
|
||||
}
|
||||
return EMPTY_CHAR;
|
||||
}
|
||||
|
||||
|
||||
size_t utf8_strlen(const char *text)
|
||||
{
|
||||
// TODO optimize
|
||||
struct Utf8Iterator iter;
|
||||
Utf8Iterator_Init(&iter, text);
|
||||
size_t num = 0;
|
||||
while ((Utf8Iterator_Next(&iter)).uint) {
|
||||
num++;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* UTF-8 string parsing and character iteration
|
||||
*
|
||||
* Created on 2020/01/04.
|
||||
*/
|
||||
|
||||
#ifndef LIQUIDTYPE_UTF8_H
|
||||
#define LIQUIDTYPE_UTF8_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/** Character containing all zeros */
|
||||
extern const struct Utf8Char EMPTY_CHAR;
|
||||
|
||||
/**
|
||||
* UTF-8 encoded character.
|
||||
*
|
||||
* It's convenient to use the uint values internally to represent this symbol.
|
||||
* Since UTF8 can't contain zero bytes, just strip the trailing zeros when composing a string.
|
||||
*/
|
||||
struct Utf8Char {
|
||||
union {
|
||||
/**
|
||||
* character bytes; padded by zero bytes if shorter than 4.
|
||||
*
|
||||
* Can be initialized by a string literal in lookup tables
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the parser internal state
|
||||
*
|
||||
* @param self
|
||||
*/
|
||||
void Utf8Parser_Clear(struct Utf8Parser *self);
|
||||
|
||||
/**
|
||||
* Initialize the parser struct before starting to parse
|
||||
*/
|
||||
static void Utf8Parser_Init(struct Utf8Parser *self) {
|
||||
Utf8Parser_Clear(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);
|
||||
|
||||
/**
|
||||
* Utf8 character iterator.
|
||||
*
|
||||
* Usage:
|
||||
* struct Utf8Iterator iter;
|
||||
* Utf8Iterator_Init(&iter, myString);
|
||||
*
|
||||
* struct Utf8Char uchar;
|
||||
* while ((uchar = Utf8Iterator_Next(&iter)).uint) {
|
||||
* // do something with the char
|
||||
* }
|
||||
*/
|
||||
struct Utf8Iterator {
|
||||
/* Characters to parse. The pointer is advanced as the iterator progresses. */
|
||||
const char *source;
|
||||
struct Utf8Parser parser;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the iterator struct
|
||||
*
|
||||
* @param self
|
||||
* @param source - string to iterate; It can be in RO memory, it's only read.
|
||||
*/
|
||||
void Utf8Iterator_Init(struct Utf8Iterator *self, const char *source);
|
||||
|
||||
/**
|
||||
* Get the next character from the iterator;
|
||||
*
|
||||
* Returns empty character if there are no more characters to parse (the .uint field is zero)
|
||||
*
|
||||
* Invalid characters are skipped.
|
||||
*/
|
||||
struct Utf8Char Utf8Iterator_Next(struct Utf8Iterator *self);
|
||||
|
||||
/**
|
||||
* Get utf8 string length, counting codepoints.
|
||||
*
|
||||
* @attention This function is rather expensive, cache the result if reused
|
||||
*
|
||||
* @param text - utf8 string, zero terminated
|
||||
* @return number of codepoints
|
||||
*/
|
||||
size_t utf8_strlen(const char *text);
|
||||
|
||||
#endif //LIQUIDTYPE_UTF8_H
|
||||
+69
@@ -8,6 +8,7 @@
|
||||
#include "lcd.h"
|
||||
#include "ds_rtc.h"
|
||||
#include "ee.h"
|
||||
#include "lcd/lcdbuf.h"
|
||||
|
||||
/*
|
||||
|
||||
@@ -132,6 +133,72 @@ int main()
|
||||
adc_gpio_init(PIN_MOISTURE);
|
||||
|
||||
lcd_init();
|
||||
|
||||
struct LcdBuffer lcd;
|
||||
LcdBuffer_Init(&lcd, CGROM_A00, CGRAM_CZ);
|
||||
|
||||
LcdBuffer_Write(&lcd, 0, 0, "ěščřŽÝÁÍ pyčo!");
|
||||
LcdBuffer_Flush(&lcd);
|
||||
|
||||
char buf[100];
|
||||
while (1) {
|
||||
struct rtc_time time;
|
||||
if (0 == rtc_get_time(&time)) {
|
||||
sprintf(buf, "čas: %02d:%02d:%02d", time.hour, time.minute, time.second);
|
||||
LcdBuffer_Write(&lcd, 2, 2, buf);
|
||||
}
|
||||
LcdBuffer_Flush(&lcd);
|
||||
|
||||
sleep_ms(1000);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
// while (1) {
|
||||
// int y;
|
||||
// lcd_clear();
|
||||
//
|
||||
// y = 0;
|
||||
// for (int j = 0; j < 80; j += 20) {
|
||||
// lcd_xy(0, y++);
|
||||
// for (int i = j; i < j + 20; i++) {
|
||||
// lcd_putc(i);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// sleep_ms(5000);
|
||||
// lcd_clear();
|
||||
//
|
||||
// y = 0;
|
||||
// for (int j = 80; j < 160; j += 20) {
|
||||
// lcd_xy(0, y++);
|
||||
// for (int i = j; i < j + 20; i++) {
|
||||
// lcd_putc(i);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// sleep_ms(5000);
|
||||
// lcd_clear();
|
||||
//
|
||||
// y = 0;
|
||||
// for (int j = 160; j < 240; j += 20) {
|
||||
// lcd_xy(0, y++);
|
||||
// for (int i = j; i < j + 20; i++) {
|
||||
// lcd_putc(i);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// sleep_ms(5000);
|
||||
// lcd_clear();
|
||||
//
|
||||
// for (int i = 240; i < 256; i++) {
|
||||
// lcd_putc(i);
|
||||
// }
|
||||
//
|
||||
// sleep_ms(5000);
|
||||
// }
|
||||
|
||||
|
||||
lcd_puts("HELLO WORLD!");
|
||||
|
||||
uint8_t ee_data[1] = {};
|
||||
@@ -182,4 +249,6 @@ int main()
|
||||
}
|
||||
open_valve(cnt);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user