add lcdbuf lib

master
Ondřej Hruška 1 year ago
parent 795c7fdd0d
commit ceb18c7c80
  1. 4
      CMakeLists.txt
  2. 32
      src/lcd.c
  3. 7
      src/lcd.h
  4. 477
      src/lcd/cgram.c
  5. 25
      src/lcd/cgram.h
  6. 136
      src/lcd/cgrom.c
  7. 24
      src/lcd/cgrom.h
  8. 291
      src/lcd/lcdbuf.c
  9. 105
      src/lcd/lcdbuf.h
  10. 136
      src/lcd/utf8.c
  11. 115
      src/lcd/utf8.h
  12. 69
      src/main.c

@ -16,6 +16,10 @@ add_executable(zavlaha
src/lcd.c src/lcd.c
src/ds_rtc.c src/ds_rtc.c
src/ee.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 # Add pico_stdlib library which aggregates commonly used features

@ -27,6 +27,28 @@
// Start address of rows // Start address of rows
const uint8_t LCD_ROW_ADDR[] = {0x00, 0x40, 0x14, 0x54}; 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 // Internal prototypes
void _lcd_mode_r(); void _lcd_mode_r();
@ -293,7 +315,7 @@ void _lcd_wait_bf()
/** Send a string to LCD */ /** Send a string to LCD */
void lcd_puts(char *str_p) void lcd_puts(const char *str_p)
{ {
char c; char c;
while ((c = *str_p++)) { 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 */ /** Sedn a char to LCD */
void lcd_putc(const char c) void lcd_putc(const char c)
{ {

@ -16,13 +16,16 @@
void lcd_init(); void lcd_init();
/** Send a string to LCD */ /** 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 */ /** Sedn a char to LCD */
void lcd_putc(char c); void lcd_putc(char c);
/** Show string at X, Y */ /** 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 */ /** Show char at X, Y */
#define lcd_putc_xy(x, y, c) do { lcd_xy((x), (y)); lcd_putc((c)); } while(0) #define lcd_putc_xy(x, y, c) do { lcd_xy((x), (y)); lcd_putc((c)); } while(0)

@ -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

@ -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

@ -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;
}

@ -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

@ -8,6 +8,7 @@
#include "lcd.h" #include "lcd.h"
#include "ds_rtc.h" #include "ds_rtc.h"
#include "ee.h" #include "ee.h"
#include "lcd/lcdbuf.h"
/* /*
@ -132,6 +133,72 @@ int main()
adc_gpio_init(PIN_MOISTURE); adc_gpio_init(PIN_MOISTURE);
lcd_init(); 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!"); lcd_puts("HELLO WORLD!");
uint8_t ee_data[1] = {}; uint8_t ee_data[1] = {};
@ -182,4 +249,6 @@ int main()
} }
open_valve(cnt); open_valve(cnt);
} }
#endif
} }

Loading…
Cancel
Save