LCD displej s obvodem HD44780 a I2c převodníkem PCF8574 vyžaduje napájecí napětí 5V.

Při napájení LCD displeje z 3.3V sice displej pracuje, ale není na něm skoro nic vidět. Nedá se ani regulovat jas potenciometrem zespodu displeje. Něco málo je vidět pouze při maximálním jasu.

Podle tohoto schématu s pull up rezistory 10k na linii 3.3V z pinu 36 a napájení displeje 5 Volty všechno funguje excelentně. Odběr celého zařízení je okolo 40 mA.

Pico 1602 LCD I2C schema
Figure 1. Schema zapojení
Hotový program, RPi Pico se napájí z Lion článků Panasonic 65180 (4.2V)
lcd_1602_i2c.c
/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "pico/binary_info.h"

/* Example code to drive a 16x2 LCD panel via a I2C bridge chip (e.g. PCF8574)

   NOTE: The panel must be capable of being driven at 3.3v NOT 5v. The Pico
   GPIO (and therefor I2C) cannot be used at 5v.

   You will need to use a level shifter on the I2C lines if you want to run the
   board at 5v.

   Connections on Raspberry Pi Pico board, other boards may vary.

   GPIO 4 (pin 6)-> SDA on LCD bridge board
   GPIO 5 (pin 7)-> SCL on LCD bridge board
   3.3v (pin 36) -> VCC on LCD bridge board
   GND (pin 38)  -> GND on LCD bridge board
*/
// commands
const int LCD_CLEARDISPLAY = 0x01;
const int LCD_RETURNHOME = 0x02;
const int LCD_ENTRYMODESET = 0x04;
const int LCD_DISPLAYCONTROL = 0x08;
const int LCD_CURSORSHIFT = 0x10;
const int LCD_FUNCTIONSET = 0x20;
const int LCD_SETCGRAMADDR = 0x40;
const int LCD_SETDDRAMADDR = 0x80;

// flags for display entry mode
const int LCD_ENTRYSHIFTINCREMENT = 0x01;
const int LCD_ENTRYLEFT = 0x02;

// flags for display and cursor control
const int LCD_BLINKON = 0x01;
const int LCD_CURSORON = 0x02;
const int LCD_DISPLAYON = 0x04;

// flags for display and cursor shift
const int LCD_MOVERIGHT = 0x04;
const int LCD_DISPLAYMOVE = 0x08;

// flags for function set
const int LCD_5x10DOTS = 0x04;
const int LCD_2LINE = 0x08;
const int LCD_8BITMODE = 0x10;

// flag for backlight control
const int LCD_BACKLIGHT = 0x08;

const int LCD_ENABLE_BIT = 0x04;

// By default these LCD display drivers are on bus address 0x27
static int addr = 0x27;

// Modes for lcd_send_byte
#define LCD_CHARACTER  1
#define LCD_COMMAND    0

#define MAX_LINES      2
#define MAX_CHARS      16

/* Quick helper function for single byte transfers */
void i2c_write_byte(uint8_t val) {
#ifdef i2c_default
    i2c_write_blocking(i2c_default, addr, &val, 1, false);
#endif
}

void lcd_toggle_enable(uint8_t val) {
    // Toggle enable pin on LCD display
    // We cannot do this too quickly or things don't work
#define DELAY_US 600
    sleep_us(DELAY_US);
    i2c_write_byte(val | LCD_ENABLE_BIT);
    sleep_us(DELAY_US);
    i2c_write_byte(val & ~LCD_ENABLE_BIT);
    sleep_us(DELAY_US);
}

// The display is sent a byte as two separate nibble transfers
void lcd_send_byte(uint8_t val, int mode) {
    uint8_t high = mode | (val & 0xF0) | LCD_BACKLIGHT;
    uint8_t low = mode | ((val << 4) & 0xF0) | LCD_BACKLIGHT;

    i2c_write_byte(high);
    lcd_toggle_enable(high);
    i2c_write_byte(low);
    lcd_toggle_enable(low);
}

void lcd_clear(void) {
    lcd_send_byte(LCD_CLEARDISPLAY, LCD_COMMAND);
}

// go to location on LCD
void lcd_set_cursor(int line, int position) {
    int val = (line == 0) ? 0x80 + position : 0xC0 + position;
    lcd_send_byte(val, LCD_COMMAND);
}

static void inline lcd_char(char val) {
    lcd_send_byte(val, LCD_CHARACTER);
}

void lcd_string(const char *s) {
    while (*s) {
        lcd_char(*s++);
    }
}

void lcd_init() {
    lcd_send_byte(0x03, LCD_COMMAND);
    lcd_send_byte(0x03, LCD_COMMAND);
    lcd_send_byte(0x03, LCD_COMMAND);
    lcd_send_byte(0x02, LCD_COMMAND);

    lcd_send_byte(LCD_ENTRYMODESET | LCD_ENTRYLEFT, LCD_COMMAND);
    lcd_send_byte(LCD_FUNCTIONSET | LCD_2LINE, LCD_COMMAND);
    lcd_send_byte(LCD_DISPLAYCONTROL | LCD_DISPLAYON, LCD_COMMAND);
    lcd_clear();
}

int main() {
#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN)
    #warning i2c/lcd_1602_i2c example requires a board with I2C pins
#else
    // This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico)
    i2c_init(i2c_default, 100 * 1000); // 100 KHz
    gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
    gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
    gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
    gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
    // Make the I2C pins available to picotool
    bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C));

    lcd_init();

    static char *message[] =
            {
                    "Testovani LCD",    "displeje I2c0x27",
                    "abcdefghijklmnop", "qrstuvwxyz",
                    "ABCDEFGHIJKLMNOP", "QRSTUVWXYZ",
                    "0123456789",       ".,+-*/@&$#^{}()?",
                    "Program je psan",  "v Cecku!"
            };

    while (1) {
        for (int m = 0; m < sizeof(message) / sizeof(message[0]); m += MAX_LINES) {
            for (int line = 0; line < MAX_LINES; line++) {
                lcd_set_cursor(line, (MAX_CHARS / 2) - strlen(message[m + line]) / 2);
                lcd_string(message[m + line]);
            }
            sleep_ms(3000);
            lcd_clear();
        }
    }
#endif
}
CMakeLists.txt
add_executable(lcd_1602_i2c
        lcd_1602_i2c.c
        )

# pull in common dependencies and additional i2c hardware support
target_link_libraries(lcd_1602_i2c pico_stdlib hardware_i2c)

# create map/bin/hex file etc.
pico_add_extra_outputs(lcd_1602_i2c)

# add url via pico_set_program_url
example_auto_set_url(lcd_1602_i2c)

Problém u tohoto dipleje je v tom, že neumí zobrazovat časká písmenka. Prvních 127 znaků odpovídají ASCII znakům a na pozicích 128—​255 jsou nějaké nepoužitelné paznaky. Budeme si muset nadefinovat svoje písmenka.

charset

Definice vlastních znaků

Displej má 64 bytovou paměť (CGRAM) pro uložení 8 uživatelsky definovaných znaků. Tyto znaky odpovídají znakovým kódům 00h až 07h. Znakové kódy 08h až 0Fh také odpovídají osmi uživatelsky definovaným znakům.

Každý uživatelsky definovaný znak má 5 sloupců a 8 řádků, přičemž osmý řádek je vyhrazen pro kurzor. Mapování mezi CG RAM a obrázkem na displeji je ukázána na následujícím obrázku.

custom chars

x v CG adrese je bázová adresa pro jednotlivé znaky: znak_0 = 00h, znak_1 = 08h, znak_2 = 10h, znak_3 = 18h, znak_4 = 20h, znak_5 = 28h, znak6 = 30h a znak_7 = 38h.

Jak je vidět z obrázku, každý bajt CGRAM reprezentuje jeden řádek pixelů znaku (bity D0 až D4), kde bit D0 je nejvíce vlevo a D4 je nejvíce vpravo. Horní bity D5 až D7 se nepoužívají. První adresa v definici znaku je nahoře a poslední 8. adresa je vyhrazena pro kurzor. Jednička v bitu znamená, že pixel je na displeji "černý" a nula znamená, že pixel je "bílý". CGRAM je v jiném adresovém prostoru, než DDRAM.

Při definici našich znaků nejdříve musíme přepnout displej do režimu programování uživatelských znaků a současně zadat bázovou adresu a potom můžeme posílat jednotlivé řádky. Každý zápis definice řádku pixelů automaticky posune adresu v CGRAM o jeden bajt, takže další řádek definice můžeme zapisovat bez toho, že bychom znovu museli určovat adresu v CGRAM.

Po ukončení zápisu definice musíme přepnout displej do normálního režimu.

Definice 1. Příklad pro zápis definice znaku á na 3. adresu CG paměti
RS R/W D7----D0
0  0   01011000         Přepnutí do programovacího režimu a nastaveni CG adresy na znak #3
1  0   00000010         Zapsání horního řádku pixelů
1  0   00000100                 :
1  0   00001110                 :
1  0   00000001                 :
1  0   00001111                 :
1  0   00010001                 :
1  0   00001111         Zapsání spodního řádku pixelů
1  0   00000000         Zapsání místa pro kurzor

RS je pin, který přepíná význam dat: je-li 0, data jsou příkazem pro displej, je-li 1, data jsou skutečná data.

R/W je pin, který přepíná směr: je-li 0, data se zapisují do displeje, je-li 1, data se z displeje čtou.

Piny na displeji

display hd44780

Tabulka 1. Programovací příkazy k modulu HD44780
Příkaz RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 Popis prodleva

Clear Display

0

0

0

0

0

0

0

0

0

1

Vymaže displej a dává kurzor úplně doleva na 1. řádek

1.5 ms

Return Home

0

0

0

0

0

0

0

0

1

X

Přesun kurzoru úplně doleva

\(40 \mu s\)

Entry Mode Set

0

0

0

0

0

0

0

1

I/D

SH

Nastavení směru pohybu kurzoru a povolení pohybu displeje

\(40 \mu s\)

Diplay ON/OFF control

0

0

0

0

0

0

1

D

C

B

Zapíná indikátor (D=1), kuzor (C=1) a vybírá typ kurzoru (B=1 je blikající blok, B=0 je blikající podrtžítko)

\(40 \mu s\)

Cursor or Display shift

0

0

0

0

0

1

S/C

R/L

X

X

Nastavuje posun displeje nebo kurzoru (S/C) doprava nebo doleva (R/L)

\(40 \mu s\)

Function Set

0

0

0

0

1

DL

1

0

P

0

Nastavení komunikace interface DL=0/1 — 4bitový/8bitový a stránky generátoru znaků P

\(40 \mu s\)

Set CGRAM Address

0

0

0

1

adresa CGRAM

Nastavení adresy pro následující operace s CGRAM (a nastavení kurzoru) a výběr oblasti CGRAM

\(40 \mu s\)

Sed DDRAM Adress

0

0

1

adresa DDRAM

Nastavení adresy pro následující operace s DDRAM a výběr oblasti DDRAM

\(40 \mu s\)

Read BUSY flag and Address

0

1

BS

AC

Přečíst flag BUSY a adresu

 — 

Write Data to RAM

1

0

zapisovaná data

Zápis dat do aktivní oblasti

\(40 \mu s\)

Read Data from RAM

1

1

čtená data

Čtení dat z aktivní oblasti

\(40 \mu s\)

Poznámky: Prodleva je maximální. Prodlevu není nutné dodržet při čtení flagu BS, jakmile je BS=0, je možné hned zadávat další příkaz.
Jestliže se flag BS před posláním příkazu neprověřuje -- je nezbytné udělat prodlevu dle tabulky, aby modul pracoval korektně.
Velké písmeno X znamená, že tam může být 0 nebo 1 (nemá to na nic vliv).

Podrobný popis jednotlivých pinů je možno zjistit z z datasheetu čipu HD44780 nebo z článku na electronicsforu.com.

Uživatelsky definovaný znak můžeme poslat do displeje pomocí následujícího fragmentu Céčkového kódu:

// pro RS pin
#define LCD_CHARACTER  1    //  přepíná display do režimu zobrazovaní znaků (nebo posílání dat)
#define LCD_COMMAND    0    //  přepíná display do režimu programování

const int LCD_SETCGRAMADDR 	        = 0x40; // v režimu programování nastavujeme CGRAM adresu
const int LCD_SETDDRAMADDR 	        = 0x80; // v režimu programování nastavujeme DDRAM adresu

uint8_t location = 7;       // bázová adresa v CGRAM, zde je pozice 7 v CGRAM paměti
char map[8] =               // definice znaku ý
    {
    0b00000010,
    0b00000100,
    0b00010001,
    0b00010001,
    0b00001111,
    0b00010001,
    0b00001110,
    0b00000000
    },

    location = location << 3;                                   (1)
    lcd_send_byte(LCD_SETCGRAMADDR | location, LCD_COMMAND);    (2)
    for(int i=0; i<8; i++) {
	    lcd_send_byte(map[i],LCD_CHARACTER);                    (3)
	    sleep_us(40);
	    }
    uint8_t adr = (cursor_y == 0) ? 0x80 + cursor_x : 0xC0 + cursor_x; (4)
    lcd_send_byte(LCD_SETDDRAMADDR | adr, LCD_COMMAND);         (5)
1 location musíme posunout o 3 bity doleva
2 pošleme příkaz na programování CGRAM
3 postupně pošleme 8 řádků definice znaku
4 musíme si někde pamatovat pozici kurzoru, před programováním CGRAM a po ukončení posunout DDRAM o tuto pozici, bez toho se displej vzteká a dějí se hlouposti
5 ukončíme programování CGRAM (posunujeme o adresu adr) a vrátíme se do normálního režimu práce displeje
První program s definicemi vlastních písmenek (jsou tam chyby)
/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 * Copyright (c) 2023 Jirka Chráska <jirka@lixis.cz>
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "pico/binary_info.h"

/* Example code to drive a 16x2 LCD panel via a I2C bridge chip (e.g. PCF8574)

   NOTE: The panel must be capable of being driven at 3.3v NOT 5v. The Pico
   GPIO (and therefor I2C) cannot be used at 5v.

   You will need to use a level shifter on the I2C lines if you want to run the
   board at 5v.

   Connections on Raspberry Pi Pico board, other boards may vary.

   GPIO 4 (pin 6)-> SDA on LCD bridge board
   GPIO 5 (pin 7)-> SCL on LCD bridge board
   3.3v (pin 36) -> VCC on LCD bridge board
   GND (pin 38)  -> GND on LCD bridge board
*/
// commands
const int LCD_CLEARDISPLAY 	        = 0x01;
const int LCD_RETURNHOME 	        = 0x02;
const int LCD_ENTRYMODESET 	        = 0x04;
const int LCD_DISPLAYCONTROL 	    = 0x08;
const int LCD_CURSORSHIFT 	        = 0x10;
const int LCD_FUNCTIONSET 	        = 0x20;
const int LCD_SETCGRAMADDR 	        = 0x40;
const int LCD_SETDDRAMADDR 	        = 0x80;

// flags for display entry mode
const int LCD_ENTRYSHIFTINCREMENT   = 0x01;
const int LCD_ENTRYLEFT 	        = 0x02;

// flags for display and cursor control
const int LCD_BLINKON 		        = 0x01;
const int LCD_CURSORON 		        = 0x02;
const int LCD_DISPLAYON 	        = 0x04;

// flags for display and cursor shift
const int LCD_MOVERIGHT 	        = 0x04;
const int LCD_DISPLAYMOVE 	        = 0x08;

// flags for function set
const int LCD_5x10DOTS 		        = 0x04;
const int LCD_2LINE 		        = 0x08;
const int LCD_8BITMODE 		        = 0x10;

// flag for backlight control
const int LCD_BACKLIGHT             = 0x08;

const int LCD_ENABLE_BIT            = 0x04;

// By default these LCD display drivers are on bus address 0x27
static int addr = 0x27;

// Modes for lcd_send_byte
#define LCD_CHARACTER  1
#define LCD_COMMAND    0

#define MAX_LINES      2
#define MAX_CHARS      16

// Custom characters

typedef struct cchar {
    char znak;
    char map[8];
    char location;
} CCHAR;

#define znak_a_acute    0
#define znak_e_acute    1
#define znak_e_caron    2
#define znak_c_caron    3
#define znak_d_caron    4
#define znak_i_acute    5
#define znak_o_acute    6
#define znak_y_acute    7

static CCHAR znak1 = {
    znak_a_acute,
    {
    0b00000010,
    0b00000100,
    0b00001110,
    0b00000001,
    0b00001111,
    0b00010001,
    0b00001111,
    0b00000000
    },
    0
};

static CCHAR znak2 = {
    znak_e_acute,
    {
    0b00000010,
    0b00000100,
    0b00001110,
    0b00010001,
    0b00011111,
    0b00010000,
    0b00001110,
    0b00000000
    },
    1
};

static CCHAR znak3 = {
    znak_e_caron,
    {
    0b00001010,
    0b00000100,
    0b00001110,
    0b00010001,
    0b00011111,
    0b00010000,
    0b00001110,
    0b00000000
    },
    2
};

static CCHAR znak4 = {
    znak_c_caron,
    {
    0b00001010,
    0b00000100,
    0b00001110,
    0b00010000,
    0b00010000,
    0b00010001,
    0b00001110,
    0b00000000
    },
    3
};
static CCHAR znak5 = {
    znak_d_caron,
    {
    0b00001010,
    0b00000101,
    0b00001101,
    0b00010011,
    0b00010001,
    0b00010001,
    0b00001110,
    0b00000000
    },
    4
};

static CCHAR znak6 = {
    znak_i_acute,
    {
    0b00000010,
    0b00000100,
    0b00000000,
    0b00001100,
    0b00000100,
    0b00000100,
    0b00001110,
    0b00000000
    },
    5
};

static CCHAR znak7= {
    znak_o_acute,
    {
    0b00000010,
    0b00000100,
    0b00001110,
    0b00010001,
    0b00010001,
    0b00010001,
    0b00001110,
    0b00000000
    },
    6
};

static CCHAR znak8= {
    znak_y_acute,
    {
    0b00000010,
    0b00000100,
    0b00010001,
    0b00010001,
    0b00001111,
    0b00010001,
    0b00001110,
    0b00000000
    },
    7
};

/* Quick helper function for single byte transfers */
void i2c_write_byte(uint8_t val) {
#ifdef i2c_default
    i2c_write_blocking(i2c_default, addr, &val, 1, false);
#endif
}

void lcd_toggle_enable(uint8_t val) {
    // Toggle enable pin on LCD display
    // We cannot do this too quickly or things don't work
#define DELAY_US 600
    sleep_us(DELAY_US);
    i2c_write_byte(val | LCD_ENABLE_BIT);
    sleep_us(DELAY_US);
    i2c_write_byte(val & ~LCD_ENABLE_BIT);
    sleep_us(DELAY_US);
}

// The display is sent a byte as two separate nibble transfers
void lcd_send_byte(uint8_t val, int mode) {
    uint8_t high = mode | (val & 0xF0) | LCD_BACKLIGHT;
    uint8_t low  = mode | ((val << 4) & 0xF0) | LCD_BACKLIGHT;

    i2c_write_byte(high);
    lcd_toggle_enable(high);
    i2c_write_byte(low);
    lcd_toggle_enable(low);
}

void lcd_clear(void) {
    lcd_send_byte(LCD_CLEARDISPLAY, LCD_COMMAND);
}

// go to location on LCD
void lcd_set_cursor(int line, int position) {
    int val = (line == 0) ? 0x80 + position : 0xC0 + position;
    lcd_send_byte(val, LCD_COMMAND);
}

static void inline lcd_char(char val) {
    lcd_send_byte(val, LCD_CHARACTER);
}

void lcd_string(const char *s) {
    while (*s) {
        lcd_char(*s++);
    }
}

void lcd_init()
{
    lcd_send_byte(0x03, LCD_COMMAND);
    lcd_send_byte(0x03, LCD_COMMAND);
    lcd_send_byte(0x03, LCD_COMMAND);
    lcd_send_byte(0x02, LCD_COMMAND);

    lcd_send_byte(LCD_ENTRYMODESET | LCD_ENTRYLEFT, LCD_COMMAND);
    lcd_send_byte(LCD_FUNCTIONSET | LCD_2LINE, LCD_COMMAND);
    lcd_send_byte(LCD_DISPLAYCONTROL | LCD_DISPLAYON, LCD_COMMAND);
    lcd_clear();
}

// custom character definition
void lcd_create_custom_char(uint8_t location, const char map[] )
{
    location = location << 3;
    lcd_send_byte(LCD_SETCGRAMADDR | location,LCD_COMMAND);
    for(int i=0; i<8; i++) {
	    lcd_send_byte(map[i],LCD_CHARACTER);
	    }
    lcd_send_byte(LCD_SETDDRAMADDR, LCD_COMMAND);
}

int init_gpio()
{
// This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico)
    i2c_init(i2c_default, 100 * 1000);
    gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
    gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
    gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
    gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
// Make the I2C pins available to picotool
//    bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C));
}

int main()
{
static char d1[17];
static char d2[17];

    init_gpio();
    for(int i=0; i<17; i++) {
	    d1[i] = '\0';
	    d2[i] = '\0';
	    }
    lcd_init();
    lcd_set_cursor(0,0);
    // definice vlastních znaků
    lcd_create_custom_char((uint8_t)0, znak1.map);
    lcd_create_custom_char((uint8_t)1, znak2.map);
    lcd_create_custom_char((uint8_t)2, znak3.map);
    lcd_create_custom_char((uint8_t)3, znak4.map);
    lcd_create_custom_char((uint8_t)4, znak5.map);
    lcd_create_custom_char((uint8_t)5, znak6.map);
    lcd_create_custom_char((uint8_t)6, znak7.map);
    lcd_create_custom_char((uint8_t)7, znak8.map);
    // jedeme
    lcd_string("Program init.");
    lcd_set_cursor(1,0);
    lcd_string("Jirka Chr"); lcd_char(0x8); lcd_string("ska");  (1)
    sleep_ms(10000);    // spíme 10 sekund
    lcd_clear();

    d1[0] = 'D'; d1[1] = 'e'; d1[2] = 'c'; d1[3] = ':'; d1[4] = ' ';
    d2[0] = 'Z'; d2[1] = 'n'; d2[2] = 'a'; d2[3] = 'k'; d2[4] = ' ';

    while(1) {
	for(int j=8; j<16; j++) {
	    d1[5]=' ';
	    d1[6]=' ';
	    d1[7]=' ';
	    switch(j%10) { // jednotky
		    case 0:  d1[7] = '0'; break;
		    case 1:  d1[7] = '1'; break;
		    case 2:  d1[7] = '2'; break;
		    case 3:  d1[7] = '3'; break;
		    case 4:  d1[7] = '4'; break;
		    case 5:  d1[7] = '5'; break;
		    case 6:  d1[7] = '6'; break;
		    case 7:  d1[7] = '7'; break;
		    case 8:  d1[7] = '8'; break;
		    case 9:  d1[7] = '9'; break;
		    }
	    switch( (j%100)/10 ) { // desitky
		    case 0:  d1[6] = '0'; break;
		    case 1:  d1[6] = '1'; break;
		    case 2:  d1[6] = '2'; break;
		    case 3:  d1[6] = '3'; break;
		    case 4:  d1[6] = '4'; break;
		    case 5:  d1[6] = '5'; break;
		    case 6:  d1[6] = '6'; break;
		    case 7:  d1[6] = '7'; break;
		    case 8:  d1[6] = '8'; break;
		    case 9:  d1[6] = '9'; break;
		    }
	    switch(j/100) { // stovky
		    case 0:  d1[5] = '0'; break;
		    case 1:  d1[5] = '1'; break;
		    case 2:  d1[5] = '2'; break;
		    }

	    d1[8] = '\0';
	    d2[5] = (char) j;
	    d2[6] = '\0';
	    lcd_set_cursor(0,0);
	    lcd_string(d1);
	    lcd_set_cursor(1,0);
	    lcd_string(d2);
	    sleep_ms(1000);
	    lcd_clear();
	    }
	}

return 0;
}
1 místo dlouhého á musíme poslat na displej znak s kódem 0x8 (kód 0x0 funguje blbě).
Hotový program s českými písmenky
Česká písmenka v kódování ISO-8859-2

custom chars all

Dokončený program se všemi českými znaky a snad bez chyb

lcd_czech_chars.c
/**
 * Copyright (c) 2024 Jirka Chráska <jirka@lixis.cz> All rigths reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:

 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this
 *    list of conditions and the following disclaimer in the documentation and/or
 *    other materials provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Český překlad
 * Copyright (c) 2024 Jirka Chráska <jirka@lixis.cz>. Všechna práva vyhrazena.
 *
 * Redistribuce a použití zdrojových i binárních forem díla, v původním i upravovaném tvaru, jsou
 * povoleny za následujících podmínek:
 *
 * 1. Šířený zdrojový kód musí obsahovat výše uvedenou informaci o copyrightu, tento seznam
 *    podmínek a níže uvedené zřeknutí se odpovědnosti.
 * 2. Šířený binární tvar musí nést výše uvedenou informaci o copyrightu, tento seznam
 *    podmínek a níže uvedené zřeknutí se odpovědnosti ve své dokumentaci a/nebo dalších
 *    poskytovaných materiálech.
 * 3. Ani jméno vlastníka práv, ani jména přispěvatelů nemohou být použita při podpoře nebo
 *    právních aktech souvisejících s produkty odvozenými z tohoto softwaru bez výslovného
 *    písemného povolení.
 *
 * TENTO SOFTWARE JE POSKYTOVÁN DRŽITELEM LICENCE A JEHO PŘISPĚVATELI "JAK STOJÍ A LEŽÍ" A JAKÉKOLIV
 * VÝSLOVNÉ NEBO PŘEDPOKLÁDANÉ ZÁRUKY VČETNĚ, ALE NEJEN, PŘEDPOKLÁDANÝCH OBCHODNÍCH ZÁRUK A ZÁRUKY
 * VHODNOSTI PRO JAKÝKOLIV ÚČEL JSOU POPŘENY. DRŽITEL, ANI PŘISPĚVATELÉ NEBUDOU V ŽÁDNÉM PŘÍPADĚ
 * ODPOVĚDNI ZA JAKÉKOLIV PŘÍMÉ, NEPŘÍMÉ, NÁHODNÉ, ZVLÁŠTNÍ, PŘÍKLADNÉ NEBO VYPLÝVAJÍCÍ ŠKODY
 * (VČETNĚ, ALE NEJEN, ŠKOD VZNIKLÝCH NARUŠENÍM DODÁVEK ZBOŽÍ NEBO SLUŽEB; ZTRÁTOU POUŽITELNOSTI,
 * DAT NEBO ZISKŮ; NEBO PŘERUŠENÍM OBCHODNÍ ČINNOSTI) JAKKOLIV ZPŮSOBENÉ NA ZÁKLADĚ JAKÉKOLIV TEORIE
 * O ZODPOVĚDNOSTI, AŤ UŽ PLYNOUCÍ Z JINÉHO SMLUVNÍHO VZTAHU, URČITÉ ZODPOVĚDNOSTI NEBO PŘEČINU
 * (VČETNĚ NEDBALOSTI) NA JAKÉMKOLIV ZPŮSOBU POUŽITÍ TOHOTO SOFTWARE, I V PŘÍPADĚ, ŽE DRŽITEL PRÁV
 * BYL UPOZORNĚN NA MOŽNOST TAKOVÝCH ŠKOD.
 */

#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "pico/binary_info.h"

// commands
const int LCD_CLEARDISPLAY 	        = 0x01;
const int LCD_RETURNHOME 	        = 0x02;
const int LCD_ENTRYMODESET 	        = 0x04;
const int LCD_DISPLAYCONTROL 	    = 0x08;
const int LCD_CURSORSHIFT 	        = 0x10;
const int LCD_FUNCTIONSET 	        = 0x20;
const int LCD_SETCGRAMADDR 	        = 0x40;
const int LCD_SETDDRAMADDR 	        = 0x80;

// flags for display entry mode
const int LCD_ENTRYSHIFTINCREMENT   = 0x01;
const int LCD_ENTRYLEFT 	        = 0x02;

// flags for display and cursor control
const int LCD_BLINKON 		        = 0x01;
const int LCD_CURSORON 		        = 0x02;
const int LCD_DISPLAYON 	        = 0x04;

// flags for display and cursor shift
const int LCD_MOVERIGHT 	        = 0x04;
const int LCD_DISPLAYMOVE 	        = 0x08;

// flags for function set
const int LCD_5x10DOTS 		        = 0x04;
const int LCD_2LINE 		        = 0x08;
const int LCD_8BITMODE 		        = 0x10;

// flag for backlight control
const int LCD_BACKLIGHT             = 0x08;

const int LCD_ENABLE_BIT            = 0x04;

// By default these LCD display drivers are on bus address 0x27
static int addr = 0x27;

// Modes for lcd_send_byte
#define LCD_CHARACTER  1
#define LCD_COMMAND    0

#define MAX_LINES      2
#define MAX_CHARS      16


// czech characters ISO-8859-2
const unsigned char znak_a_acute  =  '\xE1';    // á
const unsigned char znak_A_acute  =  '\xC1';    // Á
const unsigned char znak_c_caron  =  '\xE8';    // č
const unsigned char znak_C_caron  =  '\xC8';    // Č
const unsigned char znak_d_caron  =  '\xEF';    // ď
const unsigned char znak_D_caron  =  '\xCF';    // Ď
const unsigned char znak_e_acute  =  '\xE9';    // é
const unsigned char znak_E_acute  =  '\xC9';    // É
const unsigned char znak_e_caron  =  '\xEC';    // ě
const unsigned char znak_E_caron  =  '\xCC';    // Ě
const unsigned char znak_i_acute  =  '\xED';    // í
const unsigned char znak_I_acute  =  '\xCD';    // Í
const unsigned char znak_n_caron  =  '\xF2';    // ň
const unsigned char znak_N_caron  =  '\xD2';    // Ň
const unsigned char znak_o_acute  =  '\xF3';    // ó
const unsigned char znak_O_acute  =  '\xD3';    // Ó
const unsigned char znak_r_caron  =  '\xF8';    // ř
const unsigned char znak_R_caron  =  '\xD8';    // Ř
const unsigned char znak_s_caron  =  '\xB9';    // š
const unsigned char znak_S_caron  =  '\xA9';    // Š
const unsigned char znak_t_caron  =  '\xBB';    // ť
const unsigned char znak_T_caron  =  '\xAB';    // Ť
const unsigned char znak_u_acute  =  '\xFA';    // ú
const unsigned char znak_U_acute  =  '\xDA';    // Ú
const unsigned char znak_u_ogonek =  '\xF9';    // ů
const unsigned char znak_U_ogonek =  '\xD9';    // Ů
const unsigned char znak_y_acute  =  '\xFD';    // ý
const unsigned char znak_Y_acute  =  '\xDD';    // Ý
const unsigned char znak_z_caron  =  '\xBE';    // ž
const unsigned char znak_Z_caron  =  '\xAE';    // Ž

// Custom characters
static unsigned char c_char[256];
static unsigned char cz_char[8]; // custom chars definitions
static uint8_t volna_pozice = 0;
static uint8_t cursor_x         = 0;
static uint8_t cursor_y         = 0;
static uint8_t num_columns      = 40;
static uint8_t num_lines        = 2;

typedef struct cchar {
    unsigned char znak;
    char map[8];
    char location;
} CZCHAR;

#define pismenek    30
static CZCHAR czech_chars[pismenek] = {
{
    znak_a_acute, // 1
    {
    0b00000010,
    0b00000100,
    0b00001110,
    0b00000001,
    0b00001111,
    0b00010001,
    0b00001111,
    0b00000000
    },
    0
},
{
    znak_A_acute, // 2
    {
    0b00000010,
    0b00000100,
    0b00001010,
    0b00010001,
    0b00011111,
    0b00010001,
    0b00010001,
    0b00000000
    },
    0
},
{
    znak_c_caron, // 3
    {
    0b00001010,
    0b00000100,
    0b00001110,
    0b00010001,
    0b00010000,
    0b00010001,
    0b00001110,
    0b00000000
    },
    0
},
{
    znak_C_caron, // 4
    {
    0b00001010,
    0b00001110,
    0b00010001,
    0b00010000,
    0b00010000,
    0b00010001,
    0b00001110,
    0b00000000
    },
    0
},
{
    znak_d_caron, // 5
    {
    0b00010101,
    0b00001001,
    0b00001101,
    0b00010011,
    0b00010001,
    0b00010001,
    0b00001110,
    0b00000000
    },
    0
},
{
    znak_D_caron, // 6
    {
    0b00011101,
    0b00010010,
    0b00010001,
    0b00010001,
    0b00010001,
    0b00010010,
    0b00011100,
    0b00000000
    },
    0
},
{
    znak_e_acute, // 7
    {
    0b00000010,
    0b00000100,
    0b00001110,
    0b00010001,
    0b00011111,
    0b00010000,
    0b00001110,
    0b00000000
    },
    0
},
{
    znak_E_acute, // 8
    {
    0b00000010,
    0b00000100,
    0b00011111,
    0b00010000,
    0b00011111,
    0b00010000,
    0b00011111,
    0b00000000
    },
    0
},
{
    znak_e_caron, // 9
    {
    0b00001010,
    0b00000100,
    0b00001110,
    0b00010001,
    0b00011111,
    0b00010000,
    0b00001110,
    0b00000000
    },
    0
},
{
    znak_E_caron, // 10
    {
    0b00001010,
    0b00000100,
    0b00011111,
    0b00010000,
    0b00011111,
    0b00010000,
    0b00011111,
    0b00000000
    },
    0
},
{
    znak_i_acute, // 11
    {
    0b00000010,
    0b00000100,
    0b00000000,
    0b00001100,
    0b00000100,
    0b00000100,
    0b00001110,
    0b00000000
    },
    0
},
{
    znak_I_acute, // 12
    {
    0b00000010,
    0b00000101,
    0b00000000,
    0b00001110,
    0b00000100,
    0b00000100,
    0b00001110,
    0b00000000
    },
    0
},
{
    znak_n_caron, // 13
    {
    0b00001010,
    0b00000100,
    0b00010110,
    0b00011101,
    0b00010001,
    0b00010001,
    0b00010001,
    0b00000000
    },
    0
},
{
    znak_N_caron, // 14
    {
    0b00001010,
    0b00000100,
    0b00010001,
    0b00011001,
    0b00010101,
    0b00010011,
    0b00010001,
    0b00000000
    },
    0
},
{
    znak_o_acute, // 15
    {
    0b00000010,
    0b00000100,
    0b00001110,
    0b00010001,
    0b00010001,
    0b00010001,
    0b00001110,
    0b00000000
    },
    0
},
{
    znak_O_acute, // 16
    {
    0b00000001,
    0b00001110,
    0b00010001,
    0b00010001,
    0b00010001,
    0b00010001,
    0b00001110,
    0b00000000
    },
    0
},
{
    znak_r_caron, // 17
    {
    0b00001010,
    0b00000100,
    0b00010110,
    0b00011001,
    0b00010000,
    0b00010000,
    0b00010000,
    0b00000000
    },
    0
},
{
    znak_R_caron, // 18
    {
    0b00001010,
    0b00011110,
    0b00010001,
    0b00010001,
    0b00011110,
    0b00010010,
    0b00010001,
    0b00000000
    },
    0
},
{
    znak_s_caron, // 19
    {
    0b00001010,
    0b00000100,
    0b00001111,
    0b00010000,
    0b00001110,
    0b00000001,
    0b00011110,
    0b00000000
    },
    0
},
{
    znak_S_caron, // 20
    {
    0b00001010,
    0b00001111,
    0b00010000,
    0b00010000,
    0b00001110,
    0b00000001,
    0b00011110,
    0b00000000
    },
    0
},
{
    znak_t_caron, // 21
    {
    0b00001101,
    0b00001010,
    0b00011100,
    0b00001000,
    0b00001000,
    0b00001001,
    0b00000110,
    0b00000000
    },
    0
},
{
    znak_T_caron, // 22
    {
    0b00001010,
    0b00011111,
    0b00000100,
    0b00000100,
    0b00000100,
    0b00000100,
    0b00000100,
    0b00000000
    },
    0
},
{
    znak_u_acute, // 23
    {
    0b00000010,
    0b00000100,
    0b00010001,
    0b00010001,
    0b00010001,
    0b00010011,
    0b00001101,
    0b00000000
    },
    0
},
{
    znak_U_acute, // 24
    {
    0b00000010,
    0b00010101,
    0b00010001,
    0b00010001,
    0b00010001,
    0b00010001,
    0b00001110,
    0b00000000
    },
    0
},
{
    znak_u_ogonek, // 25
    {
    0b00000100,
    0b00001010,
    0b00010101,
    0b00010001,
    0b00010001,
    0b00010011,
    0b00001101,
    0b00000000
    },
    0
},
{
    znak_U_ogonek, // 26
    {
    0b00000100,
    0b00001010,
    0b00010101,
    0b00010001,
    0b00010001,
    0b00010001,
    0b00001110,
    0b00000000
    },
    0
},
{
    znak_y_acute, // 27
    {
    0b00000010,
    0b00000100,
    0b00010001,
    0b00010001,
    0b00001111,
    0b00000001,
    0b00001110,
    0b00000000
    },
    0
},
{
    znak_Y_acute, // 28
    {
    0b00000010,
    0b00010101,
    0b00010001,
    0b00001010,
    0b00000100,
    0b00000100,
    0b00001110,
    0b00000000
    },
    0
},
{
    znak_z_caron, // 29
    {
    0b00001010,
    0b00000100,
    0b00011111,
    0b00000010,
    0b00000100,
    0b00001000,
    0b00011111,
    0b00000000
    },
    0
},
{
    znak_Z_caron, // 30
    {
    0b00001010,
    0b00010101,
    0b00000001,
    0b00000010,
    0b00000100,
    0b00001000,
    0b00011111,
    0b00000000
    },
    0
}
};

/* Quick helper function for single byte transfers */
void i2c_write_byte(uint8_t val)
{
#ifdef i2c_default
    i2c_write_blocking(i2c_default, addr, &val, 1, false);
#endif
}

/* Quick helper function for single byte transfers */
uint8_t i2c_read_byte()
{
uint8_t val = 0;
#ifdef i2c_default
    i2c_read_blocking(i2c_default, addr, &val, 1, false);
#endif
return val;
}

void lcd_toggle_enable(uint8_t val)
{
// Toggle enable pin on LCD display
// We cannot do this too quickly or things don't work
#define DELAY_US 600
    sleep_us(DELAY_US);
    i2c_write_byte(val | LCD_ENABLE_BIT);
    sleep_us(DELAY_US);
    i2c_write_byte(val & ~LCD_ENABLE_BIT);
    sleep_us(DELAY_US);
}

// The display is sent a byte as two separate nibble transfers
void lcd_send_byte(uint8_t val, int mode)
{
    uint8_t high = mode | (val & 0xF0) | LCD_BACKLIGHT;
    uint8_t low  = mode | ((val << 4) & 0xF0) | LCD_BACKLIGHT;

    i2c_write_byte(high);
    lcd_toggle_enable(high);
    i2c_write_byte(low);
    lcd_toggle_enable(low);
}

// vymaže display včetně na definovaných znaků
void lcd_clear(void)
{
    lcd_send_byte(LCD_CLEARDISPLAY, LCD_COMMAND);

    for(int i=0; i<8; i++) {
        cz_char[i] = '\x00';
    }
    volna_pozice = 0;
}

void lcd_home(void)
{
    lcd_send_byte(LCD_RETURNHOME, LCD_COMMAND);
    cursor_x = 0;
    cursor_y = 0;
}

void lcd_move_left(void) // pohyb doleva
{
    lcd_send_byte(LCD_CURSORSHIFT|LCD_DISPLAYMOVE, LCD_COMMAND);
//    printf("lcd_move_left ");
}
void lcd_move_right(void) // pohyb doprava
{
    lcd_send_byte(LCD_CURSORSHIFT|LCD_DISPLAYMOVE|LCD_MOVERIGHT, LCD_COMMAND);
//    printf("lcd_move_right ");
}

// go to location on LCD
void lcd_set_cursor(int line, int position)
{
    int val = (line == 0) ? 0x80 + position : 0xC0 + position;
    cursor_x = position;
    cursor_y = line;
    lcd_send_byte(val, LCD_COMMAND);
}

static void inline lcd_char(char val)
{
    lcd_send_byte(val, LCD_CHARACTER);
    cursor_x += 1;
    if(cursor_x >= num_columns) {
        cursor_x  = 0;
        cursor_y += 1;
    }
    if(cursor_y >= num_lines) {
        cursor_y = 0;
    }
//    printf("x=%d,y=%d ",cursor_x,cursor_y);
}

void lcd_string(const char *s)
{
    while (*s) {
        lcd_char(*s++);
    }
}

// inicializace displeje
void lcd_init()
{
    lcd_send_byte(0x03, LCD_COMMAND);
    lcd_send_byte(0x03, LCD_COMMAND);
    lcd_send_byte(0x03, LCD_COMMAND);
    lcd_send_byte(0x02, LCD_COMMAND);

    lcd_send_byte(LCD_ENTRYMODESET   | LCD_ENTRYLEFT, LCD_COMMAND);
    lcd_send_byte(LCD_FUNCTIONSET    | LCD_2LINE,     LCD_COMMAND);
    lcd_send_byte(LCD_DISPLAYCONTROL | LCD_DISPLAYON, LCD_COMMAND);
    lcd_clear();
}

// custom character definition
void lcd_create_custom_char(uint8_t location, const char map[] )
{
    location = location << 3;
//    printf("location=%x ", LCD_SETCGRAMADDR | location);
    lcd_send_byte(LCD_SETCGRAMADDR | location, LCD_COMMAND);
    for(int i=0; i<8; i++) {
	    lcd_send_byte(map[i],LCD_CHARACTER);
	    sleep_us(40);
	    }
    uint8_t adr = (cursor_y == 0) ? 0x80 + cursor_x : 0xC0 + cursor_x;
    lcd_send_byte(LCD_SETDDRAMADDR | adr, LCD_COMMAND);
}

void lcd_characters_init()
{
    for(int i=0; i<256; i++) {
        if(i <  0x20)               c_char[i] = '\x20';
        if(i >= 0x20 && i< 0x80)    c_char[i] = (unsigned char) i;
        if(i >= 0x80)               c_char[i] = '\x80';
    }

    for(int i=0; i<8; i++) {
        cz_char[i] = '\x00';
    }
    volna_pozice = 0;

    c_char[znak_a_acute]  = '\xFF';
    c_char[znak_A_acute]  = '\xFF';
    c_char[znak_c_caron]  = '\xFF';
    c_char[znak_C_caron]  = '\xFF';
    c_char[znak_d_caron]  = '\xFF';
    c_char[znak_D_caron]  = '\xFF';
    c_char[znak_e_acute]  = '\xFF';
    c_char[znak_E_acute]  = '\xFF';
    c_char[znak_e_caron]  = '\xFF';
    c_char[znak_E_caron]  = '\xFF';
    c_char[znak_i_acute]  = '\xFF';
    c_char[znak_I_acute]  = '\xFF';
    c_char[znak_n_caron]  = '\xFF';
    c_char[znak_N_caron]  = '\xFF';
    c_char[znak_o_acute]  = '\xFF';
    c_char[znak_O_acute]  = '\xFF';
    c_char[znak_r_caron]  = '\xFF';
    c_char[znak_R_caron]  = '\xFF';
    c_char[znak_s_caron]  = '\xFF';
    c_char[znak_S_caron]  = '\xFF';
    c_char[znak_t_caron]  = '\xFF';
    c_char[znak_T_caron]  = '\xFF';
    c_char[znak_u_acute]  = '\xFF';
    c_char[znak_U_acute]  = '\xFF';
    c_char[znak_u_ogonek] = '\xFF';
    c_char[znak_U_ogonek] = '\xFF';
    c_char[znak_y_acute]  = '\xFF';
    c_char[znak_Y_acute]  = '\xFF';
    c_char[znak_z_caron]  = '\xFF';
    c_char[znak_Z_caron]  = '\xFF';

}

/*void debug_char(char c){

    switch(c){
        case '\x00': lcd_string("00"); break;
        case '\x01': lcd_string("01"); break;
        case '\x02': lcd_string("02"); break;
        case '\x03': lcd_string("03"); break;
        case '\x04': lcd_string("04"); break;
        case '\x05': lcd_string("05"); break;
        case '\x06': lcd_string("06"); break;
        case '\x07': lcd_string("07"); break;
        case '\x08': lcd_string("08"); break;
        case '\x09': lcd_string("09"); break;
        case '\x0a': lcd_string("0A"); break;
        case '\x0b': lcd_string("0B"); break;
        case '\x0c': lcd_string("0C"); break;
        case '\x0d': lcd_string("0D"); break;
        case '\x0e': lcd_string("0E"); break;
        case '\x0f': lcd_string("0F"); break;
        case '\x10': lcd_string("10"); break;
        case '\x11': lcd_string("11"); break;
        case '\x12': lcd_string("12"); break;
        case '\x13': lcd_string("13"); break;
        case '\x14': lcd_string("14"); break;
        case '\x15': lcd_string("15"); break;
        case '\x16': lcd_string("16"); break;
        case '\x17': lcd_string("17"); break;
        case '\x18': lcd_string("18"); break;
        case '\x19': lcd_string("19"); break;
        case '\x1a': lcd_string("1A"); break;
        case '\x1b': lcd_string("1B"); break;
        case '\x1c': lcd_string("1C"); break;
        case '\x1d': lcd_string("1D"); break;
        case '\x1e': lcd_string("1E"); break;
        case '\x1f': lcd_string("1F"); break;
        case '\x20': lcd_string("20"); break;
        case '\x21': lcd_string("21"); break;
        case '\x22': lcd_string("22"); break;
        case '\x23': lcd_string("23"); break;
        case '\x24': lcd_string("24"); break;
        case '\x25': lcd_string("25"); break;
        case '\x26': lcd_string("26"); break;
        case '\x27': lcd_string("27"); break;
        case '\x28': lcd_string("28"); break;
        case '\x29': lcd_string("29"); break;
        case '\x2a': lcd_string("2A"); break;
        case '\x2b': lcd_string("2B"); break;
        case '\x2c': lcd_string("2C"); break;
        case '\x2d': lcd_string("2D"); break;
        case '\x2e': lcd_string("2E"); break;
        case '\x2f': lcd_string("2F"); break;
        case '\x30': lcd_string("30"); break;
        case '\x31': lcd_string("31"); break;
        case '\x32': lcd_string("32"); break;
        case '\x33': lcd_string("33"); break;
        case '\x34': lcd_string("34"); break;
        case '\x35': lcd_string("35"); break;
        case '\x36': lcd_string("36"); break;
        case '\x37': lcd_string("37"); break;
        case '\x38': lcd_string("38"); break;
        case '\x39': lcd_string("39"); break;
        case '\x3a': lcd_string("3A"); break;
        case '\x3b': lcd_string("3B"); break;
        case '\x3c': lcd_string("3C"); break;
        case '\x3d': lcd_string("3D"); break;
        case '\x3e': lcd_string("3E"); break;
        case '\x3f': lcd_string("3F"); break;
        case '\x40': lcd_string("40"); break;
        case '\x41': lcd_string("41"); break;
        case '\x42': lcd_string("42"); break;
        case '\x43': lcd_string("43"); break;
        case '\x44': lcd_string("44"); break;
        case '\x45': lcd_string("45"); break;
        case '\x46': lcd_string("46"); break;
        case '\x47': lcd_string("47"); break;
        case '\x48': lcd_string("48"); break;
        case '\x49': lcd_string("49"); break;
        case '\x4a': lcd_string("4A"); break;
        case '\x4b': lcd_string("4B"); break;
        case '\x4c': lcd_string("4C"); break;
        case '\x4d': lcd_string("4D"); break;
        case '\x4e': lcd_string("4E"); break;
        case '\x4f': lcd_string("4F"); break;
        case '\x50': lcd_string("50"); break;
        case '\x51': lcd_string("51"); break;
        case '\x52': lcd_string("52"); break;
        case '\x53': lcd_string("53"); break;
        case '\x54': lcd_string("54"); break;
        case '\x55': lcd_string("55"); break;
        case '\x56': lcd_string("56"); break;
        case '\x57': lcd_string("57"); break;
        case '\x58': lcd_string("58"); break;
        case '\x59': lcd_string("59"); break;
        case '\x5a': lcd_string("5A"); break;
        case '\x5b': lcd_string("5B"); break;
        case '\x5c': lcd_string("5C"); break;
        case '\x5d': lcd_string("5D"); break;
        case '\x5e': lcd_string("5E"); break;
        case '\x5f': lcd_string("5F"); break;
        case '\x60': lcd_string("60"); break;
        case '\x61': lcd_string("61"); break;
        case '\x62': lcd_string("62"); break;
        case '\x63': lcd_string("63"); break;
        case '\x64': lcd_string("64"); break;
        case '\x65': lcd_string("65"); break;
        case '\x66': lcd_string("66"); break;
        case '\x67': lcd_string("67"); break;
        case '\x68': lcd_string("68"); break;
        case '\x69': lcd_string("69"); break;
        case '\x6a': lcd_string("6A"); break;
        case '\x6b': lcd_string("6B"); break;
        case '\x6c': lcd_string("6C"); break;
        case '\x6d': lcd_string("6D"); break;
        case '\x6e': lcd_string("6E"); break;
        case '\x6f': lcd_string("6F"); break;
        case '\x70': lcd_string("70"); break;
        case '\x71': lcd_string("71"); break;
        case '\x72': lcd_string("72"); break;
        case '\x73': lcd_string("73"); break;
        case '\x74': lcd_string("74"); break;
        case '\x75': lcd_string("75"); break;
        case '\x76': lcd_string("76"); break;
        case '\x77': lcd_string("77"); break;
        case '\x78': lcd_string("78"); break;
        case '\x79': lcd_string("79"); break;
        case '\x7a': lcd_string("7A"); break;
        case '\x7b': lcd_string("7B"); break;
        case '\x7c': lcd_string("7C"); break;
        case '\x7d': lcd_string("7D"); break;
        case '\x7e': lcd_string("7E"); break;
        case '\x7f': lcd_string("7F"); break;
        case '\x80': lcd_string("80"); break;
        case '\x81': lcd_string("81"); break;
        case '\x82': lcd_string("82"); break;
        case '\x83': lcd_string("83"); break;
        case '\x84': lcd_string("84"); break;
        case '\x85': lcd_string("85"); break;
        case '\x86': lcd_string("86"); break;
        case '\x87': lcd_string("87"); break;
        case '\x88': lcd_string("88"); break;
        case '\x89': lcd_string("89"); break;
        case '\x8a': lcd_string("8A"); break;
        case '\x8b': lcd_string("8B"); break;
        case '\x8c': lcd_string("8C"); break;
        case '\x8d': lcd_string("8D"); break;
        case '\x8e': lcd_string("8E"); break;
        case '\x8f': lcd_string("8F"); break;
        case '\x90': lcd_string("90"); break;
        case '\x91': lcd_string("91"); break;
        case '\x92': lcd_string("92"); break;
        case '\x93': lcd_string("93"); break;
        case '\x94': lcd_string("94"); break;
        case '\x95': lcd_string("95"); break;
        case '\x96': lcd_string("96"); break;
        case '\x97': lcd_string("97"); break;
        case '\x98': lcd_string("98"); break;
        case '\x99': lcd_string("99"); break;
        case '\x9a': lcd_string("9A"); break;
        case '\x9b': lcd_string("9B"); break;
        case '\x9c': lcd_string("9C"); break;
        case '\x9d': lcd_string("9D"); break;
        case '\x9e': lcd_string("9E"); break;
        case '\x9f': lcd_string("9F"); break;
        case '\xa0': lcd_string("A0"); break;
        case '\xa1': lcd_string("A1"); break;
        case '\xa2': lcd_string("A2"); break;
        case '\xa3': lcd_string("A3"); break;
        case '\xa4': lcd_string("A4"); break;
        case '\xa5': lcd_string("A5"); break;
        case '\xa6': lcd_string("A6"); break;
        case '\xa7': lcd_string("A7"); break;
        case '\xa8': lcd_string("A8"); break;
        case '\xa9': lcd_string("A9"); break;
        case '\xaa': lcd_string("AA"); break;
        case '\xab': lcd_string("AB"); break;
        case '\xac': lcd_string("AC"); break;
        case '\xad': lcd_string("AD"); break;
        case '\xae': lcd_string("AE"); break;
        case '\xaf': lcd_string("AF"); break;
        case '\xb0': lcd_string("B0"); break;
        case '\xb1': lcd_string("B1"); break;
        case '\xb2': lcd_string("B2"); break;
        case '\xb3': lcd_string("B3"); break;
        case '\xb4': lcd_string("B4"); break;
        case '\xb5': lcd_string("B5"); break;
        case '\xb6': lcd_string("B6"); break;
        case '\xb7': lcd_string("B7"); break;
        case '\xb8': lcd_string("B8"); break;
        case '\xb9': lcd_string("B9"); break;
        case '\xba': lcd_string("BA"); break;
        case '\xbb': lcd_string("BB"); break;
        case '\xbc': lcd_string("BC"); break;
        case '\xbd': lcd_string("BD"); break;
        case '\xbe': lcd_string("BE"); break;
        case '\xbf': lcd_string("BF"); break;
        case '\xc0': lcd_string("C0"); break;
        case '\xc1': lcd_string("C1"); break;
        case '\xc2': lcd_string("C2"); break;
        case '\xc3': lcd_string("C3"); break;
        case '\xc4': lcd_string("C4"); break;
        case '\xc5': lcd_string("C5"); break;
        case '\xc6': lcd_string("C6"); break;
        case '\xc7': lcd_string("C7"); break;
        case '\xc8': lcd_string("C8"); break;
        case '\xc9': lcd_string("C9"); break;
        case '\xca': lcd_string("CA"); break;
        case '\xcb': lcd_string("CB"); break;
        case '\xcc': lcd_string("CC"); break;
        case '\xcd': lcd_string("CD"); break;
        case '\xce': lcd_string("CE"); break;
        case '\xcf': lcd_string("CF"); break;
        case '\xd0': lcd_string("D0"); break;
        case '\xd1': lcd_string("D1"); break;
        case '\xd2': lcd_string("D2"); break;
        case '\xd3': lcd_string("D3"); break;
        case '\xd4': lcd_string("D4"); break;
        case '\xd5': lcd_string("D5"); break;
        case '\xd6': lcd_string("D6"); break;
        case '\xd7': lcd_string("D7"); break;
        case '\xd8': lcd_string("D8"); break;
        case '\xd9': lcd_string("D9"); break;
        case '\xda': lcd_string("DA"); break;
        case '\xdb': lcd_string("DB"); break;
        case '\xdc': lcd_string("DC"); break;
        case '\xdd': lcd_string("DD"); break;
        case '\xde': lcd_string("DE"); break;
        case '\xdf': lcd_string("DF"); break;
        case '\xe0': lcd_string("E0"); break;
        case '\xe1': lcd_string("E1"); break;
        case '\xe2': lcd_string("E2"); break;
        case '\xe3': lcd_string("E3"); break;
        case '\xe4': lcd_string("E4"); break;
        case '\xe5': lcd_string("E5"); break;
        case '\xe6': lcd_string("E6"); break;
        case '\xe7': lcd_string("E7"); break;
        case '\xe8': lcd_string("E8"); break;
        case '\xe9': lcd_string("E9"); break;
        case '\xea': lcd_string("EA"); break;
        case '\xeb': lcd_string("EB"); break;
        case '\xec': lcd_string("EC"); break;
        case '\xed': lcd_string("ED"); break;
        case '\xee': lcd_string("EE"); break;
        case '\xef': lcd_string("EF"); break;
        case '\xf0': lcd_string("F0"); break;
        case '\xf1': lcd_string("F1"); break;
        case '\xf2': lcd_string("F2"); break;
        case '\xf3': lcd_string("F3"); break;
        case '\xf4': lcd_string("F4"); break;
        case '\xf5': lcd_string("F5"); break;
        case '\xf6': lcd_string("F6"); break;
        case '\xf7': lcd_string("F7"); break;
        case '\xf8': lcd_string("F8"); break;
        case '\xf9': lcd_string("F9"); break;
        case '\xfa': lcd_string("FA"); break;
        case '\xfb': lcd_string("FB"); break;
        case '\xfc': lcd_string("FC"); break;
        case '\xfd': lcd_string("FD"); break;
        case '\xfe': lcd_string("FE"); break;
        case '\xff': lcd_string("FF"); break;
    }
}*/
// tisk znaků na LCD displej
int lcd_czchar_define(const char *str)
{
char c;
uint8_t nadefinovano = 8;
    while(*str) {
        c = *str;
//        printf("c=%c,'0x%x' ",c,c);
        nadefinovano = 8;
        if(c > '\x7F' && c < '\xFF' && c_char[c] == '\xFF') { // musíme použít custom znak:  podíváme, zda už ho máme nadefinovaný anebo ho budeme definovat
            for(unsigned int k = 0; k<8; k++) {
                if(cz_char[k] == c) { // máme nadefinováno
                    nadefinovano = k;
                }
            }
            if(nadefinovano == 8) { // nemáme nadefinováno
                // vyhledame volnou pozici k definici
                if(volna_pozice > 7) {
                    volna_pozice = 0;
                }
                for(unsigned int j=0; j < pismenek; j++) { // vyhledame definici
                    if(czech_chars[j].znak == c) { // definice noveho znaku
                        lcd_create_custom_char(volna_pozice, czech_chars[j].map);
                        cz_char[volna_pozice] = c;
                        nadefinovano = volna_pozice;
                        volna_pozice++;
                    }
                }
            }
            // máme nadefinováno
            c = nadefinovano + 8;
        } // nemusíme použít custom znak
        lcd_char(c);
        str++;
    } // for str
}

// inicializace GPIO
int init_gpio()
{
// This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico)
    i2c_init(i2c_default, 400 * 1000);
    gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
    gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
    gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
    gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
// Make the I2C pins available to picotool
    bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C));
}

// posunování celého textu na displeji tam a zpátky, pokud se tam celý text nevejde
void twist(int chars, int pause, int repeat)
{
int i=0;
while(repeat) {
    sleep_ms(pause*2);
    for(i=0; i<chars;i++) {
        lcd_move_left();
        sleep_ms(pause);
        }
    sleep_ms(pause*2);
    for(i=0; i<chars;i++) {
        lcd_move_right();
        sleep_ms(pause);
        }
    sleep_ms(pause*2);
    repeat--;
    }
}
int main()
{
    init_gpio();
    stdio_init_all();
	lcd_characters_init();
    lcd_init();
    lcd_clear();
    while (1) {
        lcd_clear();

        lcd_home();
        lcd_set_cursor(0,0);
        lcd_czchar_define("Střední škola, Podorlické vzděl. centrum");
        lcd_home();
        lcd_set_cursor(1,0);
        lcd_czchar_define(" Dobruška  Dobruška  Dobruška  Dobruška ");
        twist(24,700,2);
        lcd_clear();
        lcd_home();
        lcd_set_cursor(0,0);
        lcd_czchar_define("Mgr. Ing. Jiří Chráska  ");
        lcd_set_cursor(1,0);
        lcd_czchar_define("Jaroměř <jirka@lixis.cz>");
        twist(8,700,4);
        }
return 0;
}
CmakeLists.txt
add_executable(lcd_czech_chars
        lcd_czech_chars.c
        )

# pull in common dependencies and additional i2c hardware support
target_link_libraries(lcd_czech_chars pico_stdlib hardware_i2c)

pico_enable_stdio_usb(lcd_czech_chars 1)
pico_enable_stdio_uart(lcd_czech_chars 0)

# create map/bin/hex file etc.
pico_add_extra_outputs(lcd_czech_chars)

Zdroje a odkazy