From ceb18c7c80b5031081c7a5242d9fd59fbab7a3e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 13 Jun 2023 12:19:33 +0200 Subject: [PATCH] add lcdbuf lib --- CMakeLists.txt | 4 + src/lcd.c | 32 +++- src/lcd.h | 7 +- src/lcd/cgram.c | 477 +++++++++++++++++++++++++++++++++++++++++++++++ src/lcd/cgram.h | 25 +++ src/lcd/cgrom.c | 136 ++++++++++++++ src/lcd/cgrom.h | 24 +++ src/lcd/lcdbuf.c | 291 +++++++++++++++++++++++++++++ src/lcd/lcdbuf.h | 105 +++++++++++ src/lcd/utf8.c | 136 ++++++++++++++ src/lcd/utf8.h | 115 ++++++++++++ src/main.c | 69 +++++++ 12 files changed, 1418 insertions(+), 3 deletions(-) create mode 100644 src/lcd/cgram.c create mode 100644 src/lcd/cgram.h create mode 100644 src/lcd/cgrom.c create mode 100644 src/lcd/cgrom.h create mode 100644 src/lcd/lcdbuf.c create mode 100644 src/lcd/lcdbuf.h create mode 100644 src/lcd/utf8.c create mode 100644 src/lcd/utf8.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f4cb41..9204b74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,10 @@ add_executable(zavlaha src/lcd.c src/ds_rtc.c src/ee.c + src/lcd/utf8.c + src/lcd/cgram.c + src/lcd/cgrom.c + src/lcd/lcdbuf.c ) # Add pico_stdlib library which aggregates commonly used features diff --git a/src/lcd.c b/src/lcd.c index f4d4874..276e305 100644 --- a/src/lcd.c +++ b/src/lcd.c @@ -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) { diff --git a/src/lcd.h b/src/lcd.h index c1e78e5..38659ef 100644 --- a/src/lcd.h +++ b/src/lcd.h @@ -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) diff --git a/src/lcd/cgram.c b/src/lcd/cgram.c new file mode 100644 index 0000000..9a14aea --- /dev/null +++ b/src/lcd/cgram.c @@ -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}}, // ~ +*/ diff --git a/src/lcd/cgram.h b/src/lcd/cgram.h new file mode 100644 index 0000000..7f1bc9c --- /dev/null +++ b/src/lcd/cgram.h @@ -0,0 +1,25 @@ +/** + * TODO file description + */ + +#ifndef HD44780UTF_CGRAM_H +#define HD44780UTF_CGRAM_H + +#include "utf8.h" +#include + +/** 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 diff --git a/src/lcd/cgrom.c b/src/lcd/cgrom.c new file mode 100644 index 0000000..3874cd4 --- /dev/null +++ b/src/lcd/cgrom.c @@ -0,0 +1,136 @@ +#include +#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 */ +}; diff --git a/src/lcd/cgrom.h b/src/lcd/cgrom.h new file mode 100644 index 0000000..eddac95 --- /dev/null +++ b/src/lcd/cgrom.h @@ -0,0 +1,24 @@ +/** + * A definition of the actual CGROM + */ + +#ifndef HD44780UTF_CGROM_H +#define HD44780UTF_CGROM_H + +#include +#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 diff --git a/src/lcd/lcdbuf.c b/src/lcd/lcdbuf.c new file mode 100644 index 0000000..751f691 --- /dev/null +++ b/src/lcd/lcdbuf.c @@ -0,0 +1,291 @@ +/** + * HD44780 utf8-capable display buffer + */ + +#include +#include +//#include +#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++; + } +} diff --git a/src/lcd/lcdbuf.h b/src/lcd/lcdbuf.h new file mode 100644 index 0000000..a24b689 --- /dev/null +++ b/src/lcd/lcdbuf.h @@ -0,0 +1,105 @@ +/** + * HD44780 utf8-capable display buffer + */ + +#ifndef HD44780UTF_LCDBUF_H +#define HD44780UTF_LCDBUF_H + +#include +#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 diff --git a/src/lcd/utf8.c b/src/lcd/utf8.c new file mode 100644 index 0000000..fb28f02 --- /dev/null +++ b/src/lcd/utf8.c @@ -0,0 +1,136 @@ +#include +#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; +} diff --git a/src/lcd/utf8.h b/src/lcd/utf8.h new file mode 100644 index 0000000..e1d85a4 --- /dev/null +++ b/src/lcd/utf8.h @@ -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 +#include +#include + +/** 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 diff --git a/src/main.c b/src/main.c index 6389c82..eda1cca 100644 --- a/src/main.c +++ b/src/main.c @@ -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 }