Ovladač a knihovna v C pro OLED displej SSD1363 na I²c. Verze 0.2.
Displej má napájení 3.3V, odběr až 180mA při bílé ploše.
Zapojení
| pin na displeji | pin na Picu |
|---|---|
GND |
libovolná GND (třeba pin 8) |
VCC |
3V3(OUT) (pin 36) |
SDA |
GPIO4 (pin 6) |
SCL |
GPIO5 (pin 7) |
RST |
GPIO6 (pin 9) |
Program interface
Knihovna nepoužívá proměnné na zásobníku, takže je kompatibilní s Protothreads. Nealokuje žádná data na heapu pomocí malloc().
Funkce pro hardware
#define OLED_WIDTH 256 // šířka displeje v pixelech
#define OLED_HEIGHT 128 // výška displeje v pixelech
#define I2C_ADDRESS 0x3c // I2C adresa displeje
#define I2C_SDA_PIN 4 // datový pin na Picu
#define I2C_SCL_PIN 5 // hodinový pin na Picu
#define RESET_PIN 6 // resetovací pin na Picu
// definice barev
typedef enum colors {
BLACK = 0x0,
GREY1 = 0x1,
GREY2 = 0x2,
GREY3 = 0x3,
GREY4 = 0x4,
GREY5 = 0x5,
GREY6 = 0x6,
GREY7 = 0x7,
GREY8 = 0x8,
GREY9 = 0x9,
GREYA = 0xa,
GREYB = 0xb,
GREYC = 0xc,
GREYD = 0xd,
GREYE = 0xe,
WHITE = 0xf
} color_t;
// použité UTF-8 fonty
extern bitmapFONT font_spleen_8x16;
extern bitmapFONT font_spleen_6x12;
// framebuffer
static uint8_t pageBuffer[OLED_WIDTH/2*OLED_HEIGHT+10];
uint8_t *pb = &pageBuffer[1];
Inicializace I2C sběrnice, musí být nastaveny pull-up rezistory na pinech SDA a SCL.
// nastavení I2C sběrnice
void setup_i2c( void )
{
i2c_init(i2c0, 3300000); // minimum 400kHz podle datasheetu, dosažené maximum 3.3 MHz
// při 1MHz je přenos framebufferu do displeje 166 ms
// při 3.3MHz je přenos framebufferu do displeje 62 ms
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA_PIN);
gpio_pull_up(I2C_SCL_PIN);
}
Inicializace displeje:
void ssd1363_init( void );
Vymazání framebuferu:
// vymazání framebufferu (nastavení na černou barvu)
void ssd1363_clear( void );
Po vymazání framebufferu je potřeba zavolat funkci ssd1363_show(), která dopraví data do displeje.
Zobrazení dat framebufferu na displej:
// přesun dat z framebufferu do displeje
void ssd1363_show( void );
Inverze displeje (prohození černé za bílou, více šedou za méně šedou):
// invertování displeje
void ssd1363_invert( void );
Nastvení displej na normální zobrazení:
// přepnutí na normální displej
void ssd1363_normal( void );
Uspání displeje (program může běžet, ale na displeji nebude nic zobrazeno).
// uspání displeje
void ssd1363_sleep( void );
Probuzení displeje z režimu spánku:
// probuzení displeje
void ssd1363_on( void );
V normálním režimu může být spotřeba displeje a Pica až 186 mA z 5V zdroje (tedy 0.95W). V režimu spánku je spotřeba displeje a Pica okolo 24mA z 5V zdroje (tedy 0,12W).
Funkce pro kreslení
Počátek souřadné soustavy displeje [0,0] je v levém horním rohu. Souřadnice x se zvětšuje doprava a souřadnice y se zvětšuje dolů.
Kreslení bodu o souřadnicích [x,y] do framebufferu, kdo kreslí později je vidět (předchozí data jsou přepsána).
// kreslení do framebufferu natvrdo, kdo kreslí později vyhrál
// parametry: x - souřadnice na displeji
// y - souřadnice na dislpeji
// color -- stupěň šedé (0x0 - bílá až 0xf černá)
int drawPixel(int x, int y, color_t color);
Funkce nedovolí zapisovat za šířku displeje (OLED_WIDTH) a za výšku displeje (OLED_HEIGHT). Mimo rozsah nekreslí nic.
Získání barevné hodnoty bodu [x,y] z framebufferu
// získání barvy bodu [x,y] z framebufferu
uint8_t getPixel(int x, int y);
Pokud jsou souřadnice x,y mimo rozsah, funkce vrací 0.
Kreslení libovolné úsečky Bresenhamovým algoritmem:
// kreslení čáry (Bresenham)
// parametry: x0, y0 - souřadnice 1. bodu úsečky
// x1, y1 - souřadnice 2. bodu úsečky
void drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, color_t color);
Kreslení vodorovné úsečky (je trochu rychlejší než u obecné úsečky):
// kreslení vodorovné čáry
// parametry: x,y - souřadnice levého bodu
// len - délka v bodech
// color - barva čáry
void drawHLine( uint16_t x, uint16_t y, uint16_t len, color_t color );
Kreslení svislé úsečky (je trochu rychlejší než u obecné úsečky):
// kreslení svislé čáry
// parametry: x,y - souřadnice horního bodu
// len - výška v bodech
// color - barva čáry
void drawVLine( uint16_t x, uint16_t y, uint16_t len, color_t color );
Kreslení obdélníku:
// kreslení prázdného obdélníku
// parametry: x,y - souřadnice levého horního rohu
// w - šířka obdélníku
// h - výška obdélníku
// color - barva obdélníka
void drawRectangle( uint16_t x, uint16_t y, uint16_t w, uint16_t h, color_t color );
Kreslení plného obdélníku:
// kreslení plného obdélníku
// parametry: x0, y0 - souřadnice levého horního rohu
// x1, y1 - souřadnice pravého dolního rohu
// color - barva obdélníka včetně výplně
void drawSquare( int x0, int y0, int x1, int y1, color_t color );
Kreslení kružnice:
// kreslení kružnice
// parametry: xm, ym - souřadnice středu kružnice
// r - poloměr kružnice
// color - barva kružnice (0x0 až 0xf)
void drawCircle(int xm, int ym, int r, color_t color);
Kreslení kruhu:
// kreslení plného kruhu
// parametry: xm, ym - souřadnice středu kruhu
// r - poloměr kruhu
// color - barva (0x0 až 0xf)
void drawFilledCircle(int xm, int ym, int r, color_t color);
Kreslení UTF-8 znaků a řetězců
Použitý font musí být vložen do CMakeLists.txt a do zdrojového kódu pomocí extení proměnné, např. pro font Spleen 8x16:
add_executable(ssd1363_test
ssd1363_test.c
utf8.c
font_spleen_8x16.c
)
extern bitmapFONT font_spleen_8x16;
Kreslení UTF-8 znaku:
// kreslení UTF-8 znaku
// parametry: x, y - souřadníce levého horního rohu písmenka
// Font - ukazatel na bitmapový font
// Index - UTF-8 codepoint
// fcolor - barva písma
// bcolor - barva pozadí
// vrací šířku písmena
uint16_t drawChar(uint32_t x, uint32_t y, const bitmapFONT* Font, uint32_t Index, color_t fcolor, color_t bcolor );
Kreslení UTF-8 řetězce znaků:
// kreslení UTF-8 řetězce
// parametry: x,y - levý horní roh řetězce
// *Font - ukazatel na bitmapový font
// *pstring - ukazatel naa řetězec (UTF-8)
// fcolor - barva písma (0x0 - 0xf)
// bcolor - barva pozadí (0x0 - 0xf)
// vrací šířku řetězce
uint16_t drawString(uint32_t x, uint32_t y, const bitmapFONT * Font, const char * pString, color_t fcolor, color_t bcolor );
Zdrojové kódy
Knihovna i test knihovny v jednom.
Pokud potřebujete jenom knihovnu, odstraňte všechno pod // testování displeje.
ssd1363_test.c
/* ssd1363_test.c
* (c) Jirka Chráska 2026, <jirka@lixis.cz>
* BSD 3 clause licence
* Grafická knihovna a ovladač pro displej SSD1363 256x128 bodů, 16 stupňů šedé
* verze 0.2
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "font.h"
#include "utf8.h"
#define OLED_WIDTH 256 // šířka displeje v pixelech
#define OLED_HEIGHT 128 // výška displeje v pixelech
#define I2C_ADDRESS 0x3c // I2C adresa displeje
#define I2C_SDA_PIN 4 // datový pin na Picu
#define I2C_SCL_PIN 5 // hodinový pin na Picu
#define RESET_PIN 6 // resetovací pin na Picu
// definice barev
typedef enum colors {
BLACK = 0x0,
GREY1 = 0x1,
GREY2 = 0x2,
GREY3 = 0x3,
GREY4 = 0x4,
GREY5 = 0x5,
GREY6 = 0x6,
GREY7 = 0x7,
GREY8 = 0x8,
GREY9 = 0x9,
GREYA = 0xa,
GREYB = 0xb,
GREYC = 0xc,
GREYD = 0xd,
GREYE = 0xe,
WHITE = 0xf
} color_t;
// příkazy pro displej ssd1363
#define SET_COL_ADDR 0x15
#define SET_ROW_ADDR 0x75
#define CMD_RAM_WRITE 0x5c
#define CMD_RAM_READ 0x5d
#define CMD_RAM_COL_INC 0xa0
#define CMD_RAM_ROW_INC 0xa0
// použité UTF-8 fonty
extern bitmapFONT font_spleen_5x8;
extern bitmapFONT font_spleen_6x12;
extern bitmapFONT font_spleen_8x16;
extern bitmapFONT font_spleen_12x24;
extern bitmapFONT font_spleen_16x32;
extern bitmapFONT font_spleen_32x64;
extern bitmapFONT font_9x15;
extern bitmapFONT font_10x20;
extern bitmapFONT font_tahoma_8;
extern bitmapFONT font_tahoma_10;
extern bitmapFONT font_tahoma_12;
extern bitmapFONT font_tahoma_16;
// framebuffer
static uint8_t pageBuffer[OLED_WIDTH/2*OLED_HEIGHT+10];
uint8_t *pb = &pageBuffer[1];
void ssd1363_show(void);
void ssd1363_clear(void);
// nastavení I2C sběrnice
void setup_i2c( void )
{
i2c_init(i2c0, 3000000); // minimum 400kHz podle datasheetu, dosažené maximum 3.3 MHz
// při 1MHz je přenos framebufferu do displeje 166 ms
// při 3.3MHz je přenos framebufferu do displeje 62 ms
// pokud displej špatně vykresluje nebo se objevují chyby, snižte frekvenci
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA_PIN);
gpio_pull_up(I2C_SCL_PIN);
}
// zápis příkazu po I2c sběrnici na displej bez parametru
int wc( uint8_t val )
{
static uint8_t c[2];
c[0] = 0x0;
c[1] = val;
return i2c_write_blocking(i2c0, I2C_ADDRESS, c, 2, false);
}
// zápis příkazu s jedním parametrem
int wca( uint8_t val, uint8_t arg )
{
static uint8_t c[2];
c[0] = 0x0;
c[1] = val;
i2c_write_blocking(i2c0, I2C_ADDRESS, c, 2, true);
c[0] = 0b01000000;
c[1] = arg;
return i2c_write_blocking(i2c0, I2C_ADDRESS, c, 2, false);
}
// zápis příkazu se dvěma parametry
int wcaa( uint8_t val, uint8_t arg1, uint8_t arg2 )
{
static uint8_t c[2];
c[0] = 0x00;
c[1] = val;
i2c_write_blocking(i2c0, I2C_ADDRESS, c, 2, true);
c[0] = 0x40;
c[1] = arg1;
i2c_write_blocking(i2c0, I2C_ADDRESS, c, 2, true);
c[0] = 0x40;
c[1] = arg2;
return i2c_write_blocking(i2c0, I2C_ADDRESS, c, 2, false);
}
// zápis dat na displej
// parametry: *buf - ukazatel na framebuffer
// len - délka přenášených dat
int wd( uint8_t *buf, size_t len )
{
static uint8_t c[2] = {0x00,CMD_RAM_WRITE};
static int ret = 0;
ret = i2c_write_blocking(i2c0, I2C_ADDRESS, c, 2, false);
ret += i2c_write_blocking(i2c0, I2C_ADDRESS, buf, len, false);
return ret;
}
// nastavení displeje: funguje
void ssd1363_init(void)
{
wca(0xfa, 0x12); // command unlock
wc(0xae); // display off
wca(0xb3,0x30); // clock/oscilator freqency
wca(0xca,0x7f); // mux ratio
wca(0xa2,0x20); // display offset
wca(0xa1,0); // set display start line
wcaa(0xa0, 0x32, 0x00); // remap
wcaa(0xb4, 0x32, 0x0c); // display enhancement A
wca(0xc1, 0xfa); // kontrast
wca(0xba, 0x03); // VP
wc(0xb9); // normální gamma
wca(0xad, 0x90); // interní zdoj svícení
wca(0xb1, 0x74); // fáze
wca(0xba, 0x1); // pre charge voltage config
wca(0xbb, 0x0c); // pre charge voltage level
wca(0xb6, 0xc0); // second precharge
wca(0xbe, 0x04); // VCOMH
wc(0xa6); // normal display
wc(0xa9); // exit partial
sleep_ms(10);
// vymazání displeje (černý)
memset(pageBuffer,0b00000000,sizeof(pageBuffer));
ssd1363_show();
wc(0xaf); // display ON
}
// invertování displeje
void ssd1363_invert( void )
{
wc(0xa7);
}
// přepnutí na normální displej
void ssd1363_normal( void )
{
wc(0xa6);
}
// uspání displeje
void ssd1363_sleep( void )
{
wc(0xae);
}
// probuzení displeje
void ssd1363_on( void )
{
wc(0xaf);
}
// přesun dat z framebufferu do displeje
void ssd1363_show(void)
{
wcaa(SET_COL_ADDR, 0x08, 0x47); // column start address 0 end 79
wcaa(SET_ROW_ADDR, 0x00, 0x7f); // row start address 0, end address 127
pageBuffer[0] = 0x40; // před daty musí být bajt 0x40
wd(&pageBuffer[0],OLED_WIDTH/2*OLED_HEIGHT+1);
}
// zatím nefunguje
void ssd1363_showpartial(void)
{
wcaa(0x15, 0x08, 0x47); // column start address 0 end 79
wcaa(0x75, 0x00, 0x7f); // row start address 0, end address 127
pageBuffer[0] = 0x40;
wd(&pageBuffer[0],OLED_WIDTH/2*OLED_HEIGHT+1);
}
// vymazání framebufferu (nastavení na černou barvu)
void ssd1363_clear(void)
{
memset(&pageBuffer[1],0,OLED_WIDTH/2*OLED_HEIGHT);
}
// kreslení do framebufferu: světlejší barva má přednost (nepoužito)
int drawPixelN(int x, int y, color_t color)
{
if( x < 0 || x > OLED_WIDTH - 1 ) return -1;
if( y < 0 || y > OLED_HEIGHT - 1 ) return -1;
switch( x%4 ) {
case 0:
*(pb+(OLED_WIDTH*y+x)/2+1) |= color;
return 0;
case 1:
*(pb+(OLED_WIDTH*y+x)/2+1) |= color<<4;
return 1;
case 2:
*(pb+(OLED_WIDTH*y+x)/2-1) |= color;
return 2;
case 3:
*(pb+(OLED_WIDTH*y+x)/2-1) |= color<<4;
return 3;
}
}
// kreslení do framebufferu natvrdo, kdo kreslí později vyhrál
// parametry: x - souřadnice na displeji
// y - souřadnice na dislpeji
// color -- stupěň šedé (0x0 - bílá až 0xf černá)
int drawPixel(int x, int y, color_t color)
{
if( x < 0 || x > OLED_WIDTH - 1 ) return -1;
if( y < 0 || y > OLED_HEIGHT - 1 ) return -1;
switch( x%4 ) {
case 0:
*(pb+(OLED_WIDTH*y+x)/2+1) = (*(pb+(OLED_WIDTH*y+x)/2+1) & 0xf0) | color;
return 0;
case 1:
*(pb+(OLED_WIDTH*y+x)/2+1) = (*(pb+(OLED_WIDTH*y+x)/2+1) & 0x0f) | color<<4;
return 1;
case 2:
*(pb+(OLED_WIDTH*y+x)/2-1) = (*(pb+(OLED_WIDTH*y+x)/2-1) & 0xf0) | color;
return 2;
case 3:
*(pb+(OLED_WIDTH*y+x)/2-1) = (*(pb+(OLED_WIDTH*y+x)/2-1) & 0x0f) | color<<4;
return 3;
}
}
// získání barvy bodu [x,y] z framebufferu
uint8_t getPixel(int x, int y)
{
static uint8_t color = 0;
if( x < 0 || x > OLED_WIDTH - 1 ) return 0;
if( y < 0 || y > OLED_HEIGHT - 1 ) return 0;
switch( x%4 ) {
case 0:
color = (*(pb+((OLED_WIDTH*y+x)/2+1)) & 0x0f);
break;
case 1:
color = (*(pb+((OLED_WIDTH*y+x)/2+1)) & 0xf0)>>4;
break;
case 2:
color = (*(pb+((OLED_WIDTH*y+x)/2-1)) & 0x0f);
break;
case 3:
color = (*(pb+((OLED_WIDTH*y+x)/2-1)) & 0xf0)>>4;
break;
}
return color;
}
// kreslení bodu [x,y] s ohledem na předchozí barvu bodu
// dají se s tím dělat zajímavé efekty
// void drawPixelInv(int x, int y, color_t ncolor)
// {
// static uint8_t color;
//
// color = getPixel(x,y);
// color = (ncolor ^ color) & 0x0f;
// drawPixel(x,y,color);
// }
void drawPixelInv(int x, int y, color_t ncolor)
{
drawPixel(x,y,(ncolor ^ getPixel(x,y)) & 0x0f);
}
// kreslení čáry (Bresenham)
// parametry: x0, y0 - souřadnice 1. bodu úsečky
// x1, y1 - souřadnice 2. bodu úsečky
// color - barva úsečky (4bity: 0x0 - černá 0xf - bílá)
void drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, color_t color)
{
static int dx, sx;
static int dy, sy;
static int err, e2; /* error value e_xy */
dx = abs(x1-x0);
sx = x0<x1 ? 1 : -1;
dy = -abs(y1-y0);
sy = y0<y1 ? 1 : -1;
err = dx+dy;
for(;;){ /* loop */
drawPixel(x0, y0, color);
if (x0==x1 && y0==y1) break;
e2 = 2*err;
if (e2 >= dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */
if (e2 <= dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */
}
}
// kreslení čáry s antialiasingem
// parametry: x0, y0 - souřadnice 1. bodu úsečky
// x1, y1 - souřadnice 2. bodu úsečky
// color - barva úsečky (4bity: 0x0 - černá 0xf - bílá)
void drawLineAA(int32_t x0, int32_t y0, int32_t x1, int32_t y1, color_t color, int blend)
{
static int dx, sx;
static int dy, sy;
static int x2, err, e2; /* error value e_xy */
static int ed;
dx = abs( x1-x0 );
sx = x0<x1 ? 1 : -1;
dy = abs( y1-y0 );
sy = y0<y1 ? 1 : -1;
err = dx-dy;
ed = (dx+dy) == 0 ? 1 : sqrt((float)dx*dx + (float)dy*dy);
for( ; ; ) {
drawPixel(x0, y0, ((blend*getPixel(x0,y0+sy)+(16-blend)*color)/16) ); // color*abs(err-dx+dy)/ed
e2 = err; x2 = x0;
if( 2*e2 >= -dx ) { // krok x
if( x0==x1 ) break;
if( e2+dy < ed ) drawPixel(x0, y0+sy, ((blend*getPixel(x0,y0+sy)+(16-blend)*color)/16) ); // color*(e2+dy)/ed
err -= dy; x0 += sx;
}
if( 2*e2 <= dy ) { // krok y
if( y0 == y1 ) break;
if( dx-e2 < ed ) drawPixel(x2+sx, y0, ((blend*getPixel(x2+sx, y0)+(16-blend)*color)/16) ); // color*(dx-e2)/ed
err += dx; y0 +=sy;
}
}
}
// kreslení vodorovné čáry
// parametry: x,y - souřadnice levého bodu
// len - délka v bodech
// color - barva čáry
void drawHLine( uint16_t x, uint16_t y, uint16_t len, color_t color )
{
for(int i=x ; i<=x+len; i++) {
int ret = drawPixel(i,y,color);
}
}
// kreslení svislé čáry
// parametry: x,y - souřadnice horního bodu
// len - výška v bodech
// color - barva čáry
void drawVLine( uint16_t x, uint16_t y, uint16_t len, color_t color )
{
for(int i=y ; i<=y+len; i++) {
int ret = drawPixel(x,i,color);
}
}
// kreslení kružnice
// parametry: xm, ym - souřadnice středu kružnice
// r - poloměr kružnice
// color - barva kružnice (0x0 až 0xf)
void drawCircle(int xm, int ym, int r, color_t color)
{
static int x, y, err;
x = -r;
y = 0;
err = 2-2*r; /* II. Quadrant */
do {
drawPixel(xm-x, ym+y, color); /* I. Quadrant */
drawPixel(xm-y, ym-x, color); /* II. Quadrant */
drawPixel(xm+x, ym-y, color); /* III. Quadrant */
drawPixel(xm+y, ym+x, color); /* IV. Quadrant */
r = err;
if (r <= y) err += ++y*2+1; /* e_xy+e_y < 0 */
if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */
} while (x < 0);
}
// kreslení plného kruhu
// parametry: xm, ym - souřadnice středu kruhu
// r - poloměr kruhu
// color - barva (0x0 až 0xf)
void drawFilledCircle(int xm, int ym, int r, color_t color)
{
static int x, y, err;
x = -r;
y = 0;
err = 2-2*r; /* II. Quadrant */
do {
drawLine(xm-x,ym+y,xm+x,ym+y, color);
drawLine(xm-x,ym-y,xm+x,ym-y, color);
r = err;
if (r <= y) err += ++y*2+1; /* e_xy+e_y < 0 */
if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */
} while (x < 0);
}
// kreslení plného obdélníku
// parametry: x0, y0 - souřadnice levého horního rohu
// x1, y1 - souřadnice pravého dolního rohu
// color - barva obdélníka včetně výplně
void drawSquare( int x0, int y0, int x1, int y1, color_t color )
{
static int x = 0;
static int y = 0;
if( x0 > x1 ) {
x = x1;
x1 = x0;
x0 = x;
}
if( y0 > y1 ) {
y = y1;
y1 = y0;
y0 = y;
}
for(x=x0 ; x <= x1; x++) {
for(y=y0; y <= y1; y++) {
drawPixel(x,y,color);
}
}
}
// kreslení prázdného obdélníku
// parametry: x,y - souřadnice levého horního rohu
// w - šířka obdélníku
// h - výška obdélníku
// color - barva obdélníka
void drawRectangle( uint16_t x, uint16_t y, uint16_t w, uint16_t h, color_t color )
{
drawHLine(x , y , w, color);
drawHLine(x , y+h, w, color);
drawVLine(x , y , h, color);
drawVLine(x+w, y , h, color);
}
// kreslení UTF-8 znaku
// parametry: x, y - souřadníce levého horního rohu písmenka
// Font - ukazatel na bitmapový font
// Index - UTF-8 codepoint
// fcolor - barva písma
// bcolor - barva pozadí
// vrací šířku písmena
uint16_t drawChar(uint32_t x, uint32_t y, const bitmapFONT* Font,
uint32_t Index, color_t fcolor, color_t bcolor )
{
static uint8_t row, column, forecolor;
static uint8_t col, fcol, zb;
static uint8_t chwidth;
static uint16_t chsize;
static uint32_t bindex;
static uint8_t chw;
static int j = 0;
if( x > OLED_WIDTH || y > OLED_HEIGHT ) {
return 0;
}
col = Font->Width/8; // sloupce v bytech
fcol = col;
zb = Font->Width%8;
if( zb != 0) {
col ++;
}
if( Index > Font->Chars ) Index = 0;
chwidth = Font->Widths[Index]; // šířka znaku
chsize = Font->Height * col;
bindex = Index*chsize;
for (row = 0; row < Font->Height; row++ ) {
// lezeme po bytech (po osmičkách)
chw = chwidth;
for (column = 0; column < col; column++, chw-- ) {
for( j = 0; j<8; j++ ) {
if( chw == 0 ) break;
forecolor = ((uint8_t) Font->Bitmap[bindex+row*col+column]) & (0x80 >> j);
if( forecolor ) {
drawPixel(x + column*8 + j, y + row, fcolor);
} else {
drawPixel(x + column*8 + j, y + row, bcolor);
}
}
}
}
return chwidth;
}
// nalezení indexu pole znaků metodou půlení intervalu
int32_t getUTF8Index(const bitmapFONT *font, uint32_t codepoint)
{
static int32_t l = 0;
static int32_t r = 0;
static int32_t m = 0;
for(l=0, r= font->Chars; l<=r; ) {
m = (l+r)/2;
if(font->Index[m] == codepoint ) return m;
if(codepoint < font->Index[m] ) { // jdeme vlevo
r = m - 1;
} else { // jdeme vpravo
l = m + 1;
}
}
return -1;
}
// kreslení UTF-8 řetězce
// parametry: x,y - levý horní roh řetězce
// *Font - ukazatel na bitmapový font
// *pstring - ukazatel naa řetězec (UTF-8)
// fcolor - barva písma (0x0 - 0xf)
// bcolor - barva pozadí (0x0 - 0xf)
// vrací šířku řetězce
uint16_t drawString(uint32_t x, uint32_t y, const bitmapFONT * Font,
const char * pString, color_t fcolor, color_t bcolor )
{
static uint32_t Xpoint;
static uint32_t Ypoint;
static uint16_t index;
static uint16_t str_width = 0;
static uint16_t char_width = 0;
static uint32_t codepoint;
static int i = 0;
Xpoint = x;
Ypoint = y;
str_width = 0;
char_width = 0;
if (x > OLED_WIDTH || y > OLED_HEIGHT) {
return 0;
}
utf8_string ustr = make_utf8_string(pString);
utf8_char_iter iter = make_utf8_char_iter(ustr);
utf8_char c;
while ((c = next_utf8_char(&iter)).byte_len > 0 ) {
codepoint = unicode_code_point(c);
index = 0;
if( (index = getUTF8Index(Font, codepoint)) == -1) continue; // nemáme znak ve fontu
// test zda se to vejde na displej
if((Xpoint + Font->Width ) > OLED_WIDTH ) {
// zalomíme na další řádek
Xpoint = x;
Ypoint += Font->Height;
}
if ((Ypoint + Font->Height ) > OLED_HEIGHT ) {
// už se to nikam nevejde
return str_width;
// Xpoint = x;
// Ypoint = y;
}
char_width = drawChar(Xpoint, Ypoint, Font, index, fcolor, bcolor);
Xpoint += char_width;
str_width += char_width;
}
return str_width;
}
// konec knihovny -------------------------------------------------------------------------
// testování displeje
#define FONT_SPLEEN_5x8_TEST 0 // ve fontu chybí znaky
#define FONT_SPLEEN_6x12_TEST 0
#define FONT_SPLEEN_8x16_TEST 0
#define FONT_SPLEEN_12x24_TEST 0
#define FONT_SPLEEN_16x32_TEST 1
#define FONT_SPLEEN_32x64_TEST 1
#define FONT_9x15_TEST 1
#define FONT_10x20_TEST 1
#define FONT_TAHOMA_8_TEST 1 // font je chybný
#define FONT_TAHOMA_10_TEST 1
#define FONT_TAHOMA_12_TEST 1
#define FONT_TAHOMA_16_TEST 1
#define HODINY_TEST 0
#define TEXT_TEST 0
#define SLEEP_TEST 0
#define LOMENA_TEST 0
#define SINUS_TEST 0
#define TROJUHELNIKY_TEST 0
#define KRUZNICE_TEST 0
#define KRUHY_TEST 0
#define SQUARE_TEST 0
#define RECTANGLE_TEST 0
#if HODINY_TEST
#define PI 3.1415926
#define CX 127 // střed hodin
#define CY 63
#define R 63 // poloměr hodin
#define HR 40 // délka hodinové ručičky
#define MR 50 // délka minutové ručičky
#define SR 53 // délka sekundové ručičky
// hodiny s ručičkami
// h - hodiny (0-12)
// m - minuty (0-59)
// s - sekundy (0-59)
void analogClock( uint8_t h, uint8_t m, uint8_t s )
{
static float ang = 0.0;
static int x0 = 0, y0 = 0, x1 = 0, y1 = 0, i = 0;
static char buf[20];
// ciferník
drawCircle(CX, CY, R, GREY9);
drawCircle(CX, CY, R-1, GREY8);
// minuty stupnice
for( i=0; i<60; i++ ) {
ang = i * 12 * PI/360 - PI/2;
x0 = CX + R * cos(ang);
y0 = CY + R * sin(ang);
x1 = CX + (R-4) * cos(ang);
y1 = CY + (R-4) * sin(ang);
drawLine(x0, y0, x1, y1, GREY7);
}
// hodiny stupnice
for( i=0; i<12; i++ ) {
ang = i * 60 * PI/360 - PI/2;
x0 = CX + R * cos(ang);
y0 = CY + R * sin(ang);
x1 = CX + (R-6) * cos(ang);
y1 = CY + (R-6) * sin(ang);
drawLine(x0, y0, x1, y1, GREYA);
}
// hodinová ručička
ang = ((h % 12)*60 + m)*PI/360 - PI/2;
x0 = CX + HR * cos(ang);
y0 = CY + HR * sin(ang);
x1 = CX;
y1 = CY;
drawLine(x0, y0, x1, y1, GREYA);
drawLine(x0-1, y0-1, x1-1, y1-1, GREYA);
drawLine(x0+1, y0+1, x1+1, y1+1, GREYA);
// minutová ručička
ang = (m*60+s)*PI/1800 - PI/2;
x0 = CX + MR * cos(ang);
y0 = CY + MR * sin(ang);
drawLineAA( x0, y0, x1, y1, GREYD, GREY2);
// sekundová ručička
ang = (s*PI)/30.0 - PI/2;
x0 = CX + SR * cos(ang);
y0 = CY + SR * sin(ang);
drawLineAA(x0, y0, x1, y1, WHITE, GREY4);
// střed
drawFilledCircle(CX,CY,3,GREY9);
// čas v číslicích
sprintf(buf,"%2d:%02d:%02d",h,m,s);
drawString(CX+64, 111, &font_spleen_8x16, buf, WHITE, BLACK);
ssd1363_show();
}
// ----------------------------------------------------------------------------------
#endif
#define RANDOM_MIN 0
#define RANDOM_MAX 255
int main ( void )
{
int ret = 0;
char buf[128];
uint64_t t, tk, tp;
stdio_init_all();
memset(buf,0,sizeof(buf));
// resetování displeje
gpio_set_function(RESET_PIN, GPIO_FUNC_SIO);
gpio_set_dir(RESET_PIN, GPIO_OUT);
gpio_put(RESET_PIN, 0);
sleep_ms(50);
gpio_put(RESET_PIN, 1);
sleep_ms(100);
// nastavení parametrů I2C
setup_i2c();
// display_init
ssd1363_init();
printf("Display init a smazání obrazovky.\n");
// testování správnosti ovladače
for(int j=0; ; j++) {
int x, y, r, h, w, i, k, color;
// -----------------------------------------------------------
// test fontu font_spleen_5x8 je mizerný
#if FONT_SPLEEN_5x8_TEST
for( k=0; k<font_spleen_5x8.Chars; k++) {
ssd1363_clear();
drawChar(1,1,&font_spleen_5x8,k,GREYE,GREY4);
sprintf(buf,"font_spleen_5x8", k);
drawString(20,1,&font_spleen_5x8,buf,GREYE,GREY4);
sprintf(buf,"Character index: %d", k);
drawString(0,20,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"UTF-8 codepoint: %04lx", font_spleen_5x8.Index[k]);
drawString(0,40,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"Počet znaků: %d", font_spleen_5x8.Chars);
drawString(0,60,&font_spleen_8x16,buf,WHITE,BLACK);
if( k < font_spleen_5x8.Chars) {
for(i=0; i<42; i++) {
drawChar(0+i*6,80,&font_spleen_5x8,k+i,GREYE,GREY4);
}
}
ssd1363_show();
sleep_ms(500);
}
#endif
// -----------------------------------------------------------
// test fontu font_spleen_6x12
#if FONT_SPLEEN_6x12_TEST
for( k=0; k<font_spleen_6x12.Chars; k++) {
ssd1363_clear();
drawChar(1,1,&font_spleen_6x12,k,GREYE,GREY4);
sprintf(buf,"font_spleen_6x12", k);
drawString(20,1,&font_spleen_6x12,buf,GREYE,GREY4);
sprintf(buf,"Character index: %d", k);
drawString(0,20,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"UTF-8 codepoint: %04lx", font_spleen_6x12.Index[k]);
drawString(0,40,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"Počet znaků: %d", font_spleen_6x12.Chars);
drawString(0,60,&font_spleen_8x16,buf,WHITE,BLACK);
if( k < font_spleen_6x12.Chars) {
for(i=0; i<42; i++) {
drawChar(0+i*6,80,&font_spleen_6x12,k+i,GREYE,GREY4);
}
}
ssd1363_show();
sleep_ms(500);
}
#endif
// -----------------------------------------------------------
// test fontu font_spleen_8x16
#if FONT_SPLEEN_8x16_TEST
for( k=0; k<font_spleen_8x16.Chars; k++) {
ssd1363_clear();
drawChar(1,1,&font_spleen_8x16,k,GREYE,GREY4);
sprintf(buf,"font_spleen_8x16", k);
drawString(20,1,&font_spleen_8x16,buf,GREYE,GREY4);
sprintf(buf,"Character index: %d", k);
drawString(0,20,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"UTF-8 codepoint: %04lx", font_spleen_8x16.Index[k]);
drawString(0,40,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"Počet znaků: %d", font_spleen_8x16.Chars);
drawString(0,60,&font_spleen_8x16,buf,WHITE,BLACK);
if( k < font_spleen_8x16.Chars) {
for(i=0; i<32; i++) {
drawChar(0+i*8,80,&font_spleen_8x16,k+i,GREYE,GREY4);
}
}
ssd1363_show();
sleep_ms(500);
}
#endif
// -----------------------------------------------------------
// test fontu font_spleen_12x24
#if FONT_SPLEEN_12x24_TEST
for( k=0; k<font_spleen_12x24.Chars; k++) {
ssd1363_clear();
drawChar(0,0,&font_spleen_12x24,k,GREYE,GREY4);
sprintf(buf,"font_spleen_12x24", k);
drawString(30,0,&font_spleen_12x24,buf,GREYE,GREY4);
sprintf(buf,"Character index: %d", k);
drawString(0,26,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"UTF-8 codepoint: %04lx", font_spleen_12x24.Index[k]);
drawString(0,42,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"Počet znaků: %d", font_spleen_12x24.Chars);
drawString(0,60,&font_spleen_8x16,buf,WHITE,BLACK);
if( k < font_spleen_12x24.Chars) {
for(i=0; i<20; i++) {
drawChar(0+i*12,80,&font_spleen_12x24,k+i,GREYE,GREY4);
}
}
ssd1363_show();
sleep_ms(500);
}
#endif
// -----------------------------------------------------------
// test fontu font_spleen_16x32
#if FONT_SPLEEN_16x32_TEST
for( k=0; k<font_spleen_16x32.Chars; k++) {
ssd1363_clear();
drawChar(0,0,&font_spleen_16x32,k,GREYE,GREY4);
sprintf(buf,"spleen_16x32", k);
drawString(20,0,&font_spleen_16x32,buf,GREYE,GREY4);
sprintf(buf,"Character index: %d", k);
drawString(0,33,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"UTF-8 codepoint: %04lx", font_spleen_16x32.Index[k]);
drawString(0,49,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"Počet znaků: %d", font_spleen_16x32.Chars);
drawString(0,66,&font_spleen_8x16,buf,WHITE,BLACK);
if( k < font_spleen_16x32.Chars) {
for(i=0; i<16; i++) {
drawChar(0+i*16,86,&font_spleen_16x32,k+i,GREYE,GREY4);
}
}
ssd1363_show();
sleep_ms(500);
}
#endif
// -----------------------------------------------------------
// test fontu font_spleen_32x64
#if FONT_SPLEEN_32x64_TEST
for( k=0; k<font_spleen_32x64.Chars; k++) {
ssd1363_clear();
drawChar(1,1,&font_spleen_32x64,k,GREYE,GREY4);
sprintf(buf,"font_spleen_32x64", k);
drawString(40,1,&font_spleen_6x12,buf,GREYE,GREY4);
sprintf(buf,"Character index: %d", k);
drawString(0,72,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"UTF-8 codepoint: %04lx", font_spleen_32x64.Index[k]);
drawString(0,88,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"Počet znaků: %d", font_spleen_32x64.Chars);
drawString(0,104,&font_spleen_8x16,buf,WHITE,BLACK);
ssd1363_show();
sleep_ms(500);
}
#endif
// -----------------------------------------------------------
// test fontu font_10x20
#if FONT_10x20_TEST
for( k=0; k<font_10x20.Chars; k++) {
ssd1363_clear();
drawChar(1,1,&font_10x20,k,GREYE,GREY4);
sprintf(buf,"font_10x20", k);
drawString(40,1,&font_10x20,buf,GREYE,GREY4);
sprintf(buf,"Character index: %d", k);
drawString(0,26,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"UTF-8 codepoint: %04lx", font_10x20.Index[k]);
drawString(0,42,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"Počet znaků: %d", font_10x20.Chars);
drawString(0,60,&font_spleen_8x16,buf,WHITE,BLACK);
if( k < font_10x20.Chars) {
for(i=0; i<25; i++) {
drawChar(0+i*10,80,&font_10x20,k+i,GREYE,GREY4);
}
}
ssd1363_show();
sleep_ms(500);
}
#endif
// test fontu font_9x15
#if FONT_9x15_TEST
for( k=0; k<font_9x15.Chars; k++) {
ssd1363_clear();
drawChar(1,1,&font_9x15,k,GREYE,GREY4);
sprintf(buf,"font_9x15", k);
drawString(40,1,&font_9x15,buf,GREYE,GREY4);
sprintf(buf,"Character index: %d", k);
drawString(0,26,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"UTF-8 codepoint: %04lx", font_9x15.Index[k]);
drawString(0,42,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"Počet znaků: %d", font_9x15.Chars);
drawString(0,60,&font_spleen_8x16,buf,WHITE,BLACK);
if( k < font_9x15.Chars) {
for(i=0; i<28; i++) {
drawChar(0+i*9,80,&font_9x15,k+i,GREYE,GREY4);
}
}
ssd1363_show();
sleep_ms(500);
}
#endif
// -----------------------------------------------------------
#if FONT_TAHOMA_8_TEST
for( k=0; k<font_tahoma_8.Chars; k++) {
ssd1363_clear();
drawChar(1,1,&font_tahoma_8,k,GREYE,GREY4);
sprintf(buf,"font_tahoma_8", k);
drawString(40,1,&font_tahoma_8,buf,GREYE,GREY4);
sprintf(buf,"Character index: %d", k);
drawString(0,26,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"UTF-8 codepoint: %04lx", font_tahoma_8.Index[k]);
drawString(0,42,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"Počet znaků: %d", font_tahoma_8.Chars);
drawString(0,60,&font_spleen_8x16,buf,WHITE,BLACK);
if( k < font_tahoma_8.Chars) {
for(i=0; i<28; i++) {
drawChar(0+i*11,80,&font_tahoma_8,k+i,GREYE,GREY4);
}
}
ssd1363_show();
sleep_ms(500);
}
#endif
// -----------------------------------------------------------
#if FONT_TAHOMA_10_TEST
for( k=0; k<font_tahoma_10.Chars; k++) {
ssd1363_clear();
drawChar(1,1,&font_tahoma_10,k,GREYE,GREY4);
sprintf(buf,"font_tahoma_10", k);
drawString(40,1,&font_tahoma_10,buf,GREYE,GREY4);
sprintf(buf,"Character index: %d", k);
drawString(0,26,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"UTF-8 codepoint: %04lx", font_tahoma_10.Index[k]);
drawString(0,42,&font_spleen_8x16,buf,WHITE,BLACK);
sprintf(buf,"Počet znaků: %d", font_tahoma_10.Chars);
drawString(0,60,&font_spleen_8x16,buf,WHITE,BLACK);
if( k < font_tahoma_10.Chars) {
for(i=0; i<28; i++) {
drawChar(0+i*14,80,&font_tahoma_10,k+i,GREYE,GREY4);
}
}
ssd1363_show();
sleep_ms(500);
}
#endif
// -----------------------------------------------------------
// hodiny
#if HODINY_TEST
for( k=0; k<10; k++) {
for( i=0; i<60; i++ ) {
t = time_us_64() + 100000;
ssd1363_clear();
analogClock(8,20+k,i);
sleep_until(t);
}
}
#endif
// -----------------------------------------------------------
// testování drawPixel a getPixel
// ssd1363_clear();
// for(k = 0, color=WHITE; k<16; k++, color-- ) {
// drawPixel(k,0,color);
// printf("Kreslil jsem x=%d, y=%d, c=%x, dostal jsem d=%x\n", k, 0, color, getPixel(k, 0));
// }
// for(k = 0, color=WHITE; k<16; k++, color-- ) {
// drawPixel(k,k+1,color);
// printf("Kreslil jsem x=%d, y=%d, c=%x, dostal jsem d=%x\n", k, k+1, color, getPixel(k, k+1));
// }
// ssd1363_clear();
// -----------------------------------------------------------
#if AA_TEST
// test antialiased line
for(i=0; i<16; i++ ) {
for(k=0; k<128; k++) {
t = time_us_64() + 100000;
ssd1363_clear();
drawLineAA( 0, 0, 255, k, WHITE,i);
drawLine(0,40, 255, k+40, WHITE);
sprintf(buf,"drawLineAA(0,0,255,%d,WHITE,%d);",k,i);
drawString(0,111,&font_spleen_6x12,buf,WHITE,BLACK);
ssd1363_show();
sleep_until(t);
}
}
#endif
// for(i=0; i<10; i++) {
// printf("x=%d ",i);
// for( k=2;k<127; k++) {
// printf("%x,",getPixel(k,i));
// }
// printf("\n");
// }
// -----------------------------------------------------------
#if TEXT_TEST
// text
ssd1363_clear();
t = time_us_64();
drawString(0,0,&font_spleen_8x16,"Test displeje SSD1363, s rozměry "
"256x128 pixelů, 16 stupňů šedi.",WHITE,BLACK);
drawSquare(0,38,255,90,GREYD);
drawString(0,40,&font_spleen_6x12,"Grafická knihovna kompletně z nuly. "
"(c) Jirka Chráska 2026, <jirka@lixis.cz> "
"BSD 3 licence pro studenty Střední školy "
"Podorlické vzdělávací centrum Dobruška.",
GREY1,GREYD);
drawString(0,96,&font_spleen_8x16,"Raspberry Pi Pico je skvělý "
"mikropočítač. I²C je pomalá.", WHITE,BLACK);
tk = time_us_64() - t;
t = time_us_64();
ssd1363_show();
tp = time_us_64() - t;
sleep_ms(8000);
#endif
#if SLEEP_TEST
ssd1363_clear();
sprintf(buf,"Kreslení %llu μs", tk);
drawString(1,0,&font_spleen_8x16, buf, WHITE, BLACK);
sprintf(buf,"Přenos %llu μs", tp);
drawString(1,17,&font_spleen_8x16, buf, WHITE, BLACK);
sprintf(buf,"Frekvence I²C sběrnice 3.3 MHz");
drawString(1,33,&font_spleen_8x16, buf, WHITE, BLACK);
// test uspání displeje
if( j%2 == 1 ) { // budeme spát
sprintf(buf,"Za chvilku se displej uspí.");
drawString(1,49,&font_spleen_8x16, buf, WHITE, BLACK);
sprintf(buf,"Spát bude celý cyklus.");
drawString(1,65,&font_spleen_8x16, buf, WHITE, BLACK);
}
ssd1363_show();
sleep_ms(4000);
ssd1363_clear();
if( j%2 == 1 ) {
ssd1363_sleep();
} else {
ssd1363_on();
}
#endif
#if LOMENA_TEST
//------------------------------------------------------------
printf("Lomená čára.\n");
drawString(140,0,&font_spleen_8x16,"Lomená čára",GREYD,GREY2);
bool xplus = true;
bool yplus = true;
// x bude náhodné v rozsahu RANDOM_MIN až RANDOM_MAX
x = rand() % (RANDOM_MAX + 1 - RANDOM_MIN) + RANDOM_MIN;
sprintf(buf,"Náhoda: %d",x);
drawString(0,0,&font_spleen_8x16, buf, GREYD, GREY2);
ssd1363_show();
sleep_ms(1500);
ssd1363_clear();
ssd1363_show();
h = x; w = 21;
color = GREY3;
t = time_us_64();
for( i=0, y=21; i<8001 ; i++ ) {
drawPixelInv(x,y,WHITE);
drawPixelInv(h,w,color);
h = x; w = y;
sprintf(buf,"drawPixelI(%3d, %3d, 0x%x);i=%4d %5.2f ms",
x,y,color,i, (float)tp*0.001);
drawString(0,0,&font_spleen_6x12, buf, GREYD, GREY2);
// posíláme kresbu na displej po 8. pixelu
// poslátní dat na displej trvá 62 ms
if( x%8==0 ) {
ssd1363_show();
tp = time_us_64() - t;
t = time_us_64();
}
i%1000==0 ? color++: color;
if( x<=0 ) xplus = true;
if( x>=OLED_WIDTH-1) xplus = false;
if( y<=20 ) yplus = true;
if( y>=OLED_HEIGHT-1) yplus=false;
xplus?++x:--x;
yplus?++y:--y;
}
ssd1363_show();
sleep_ms(5000);
#endif
#if SINUS_TEST
// -----------------------------------------------------------
// sinusovka
printf("Sinusovka a kosinusovka.\n");
ssd1363_clear();
ssd1363_show();
drawString(10,110,&font_spleen_8x16,"y=sin(x)", GREYE,BLACK);
drawString(100,10,&font_spleen_8x16,"y=cos(x)", GREY8,BLACK);
// popisky os
drawString(3,2,&font_spleen_6x12,"y",GREY8,BLACK);
drawString(248,63,&font_spleen_6x12,"x",GREY8,BLACK);
// osy
drawLine(0,0,0,127,GREY8);
drawLine(0,63,255,63,GREY8);
// sinusovka a kosinusovka
for( x=0; x<256; x++) {
y = (-(sin(x*3.1415926/100) * 64)+64);
r = drawPixel(x,y,WHITE);
r = drawPixel(x,y-1,WHITE);
y = (-(cos(x*3.1415926/100) * 64)+64);
r = drawPixel(x,y,GREY5);
r = drawPixel(x,y-1,GREY5);
if(x==50) {
drawLine(x,62,x,66,GREY8);
drawString(x+1,65,&font_spleen_8x16,"π/2",GREY8,BLACK);
}
if(x==100) {
drawLine(x,62,x,66,GREY8);
drawString(x+10,65,&font_spleen_8x16,"π",GREY8,BLACK);
}
if(x==150) {
drawLine(x,62,x,66,GREY8);
drawString(x+1,65,&font_spleen_8x16,"3π/2",GREY8,BLACK);
}
if(x==200) {
drawLine(x,62,x,66,GREY8);
drawString(x+1,65,&font_spleen_8x16,"2π",GREY8,BLACK);
}
// občerstvujeme displej po 4 nakreslených bodech
x%4==0?ssd1363_show():sleep_us(1);
}
sleep_ms(5000);
// přepneme displej do invezního zobrazení
ssd1363_invert();
sleep_ms(5000);
// přepneme ho zpátky do normálního zobrazení
ssd1363_normal();
#endif
#if TROJUHELNIKY_TEST
// -----------------------------------------------------------
// trojúhelníky
printf("Trojúhelníky.\n");
ssd1363_clear();
for(x=0, y=0; y<32; y++) {
drawLine(x,0,255,y,GREY4);
}
for(x=0, y=32; y<64; y++) {
drawLine(x,0,255,y,GREY8);
}
for(x=0, y=64; y<96; y++) {
drawLine(x,0,255,y,GREYA);
}
for(x=0, y=96; y<128; y++) {
drawLine(x,0,255,y,GREYD);
}
for(x=255, y=127; y>=0; y--) {
drawLine(x,255,0,y,WHITE);
}
drawString(160,0,&font_spleen_8x16,"Trojúhelníky",WHITE,BLACK);
ssd1363_show();
sleep_ms(5000);
#endif
#if KRUZNICE_TEST
// -----------------------------------------------------------
// test kružnic
printf("Kružnice\n");
ssd1363_clear();
drawString(180,0,&font_spleen_8x16,"Kružnice",GREYA,BLACK);
// kružnice zvětšuje svůj poloměr
for(int r=10; r<64; r++) {
drawCircle(127,64,r,GREYA);
sprintf(buf,"drawCircle(%d, %d, %d, GREYA); ",127,64,r);
drawString(0,115,&font_spleen_6x12,buf, WHITE, BLACK);
ssd1363_show();
}
// kružnice zmenšuje svůj poloměr
for(int r=63; r>10; r--) {
drawCircle(127,64,r,BLACK);
sprintf(buf,"drawCircle(%d, %d, %d, BLACK); ",127,64,r);
drawString(0,115,&font_spleen_6x12,buf, WHITE, BLACK);
ssd1363_show();
}
sleep_ms(1000);
#endif
#if KRUHY_TEST
// -----------------------------------------------------------
// plné kruhy
ssd1363_clear();
drawFilledCircle(127,64,128,GREY4);
drawString(180,0,&font_spleen_8x16,"Kruhy",GREYA,BLACK);
sprintf(buf,"drawFilledCircle(%d, %d, %d, GREY4); ",127,64,128);
drawString(6,115,&font_spleen_6x12,buf, WHITE, GREYA);
ssd1363_show();
sleep_ms(1000);
drawFilledCircle(127,64,64,GREY7);
sprintf(buf,"drawFilledCircle(%d, %d, %d, GREY7); ",127,64,64);
drawString(6,115,&font_spleen_6x12,buf, WHITE, GREYA);
ssd1363_show();
sleep_ms(1000);
drawFilledCircle(127,64,32,GREYA);
sprintf(buf,"drawFilledCircle(%d, %d, %d, GREYA); ",127,64,32);
drawString(6,115,&font_spleen_6x12,buf, WHITE, GREYA);
ssd1363_show();
sleep_ms(1000);
drawFilledCircle(127,64,16,WHITE);
sprintf(buf,"drawFilledCircle(%d, %d, %d, WHITE); ",127,64,16);
drawString(6,115,&font_spleen_6x12,buf, WHITE, GREYA);
ssd1363_show();
sleep_ms(1000);
drawFilledCircle(127,64,8,BLACK);
sprintf(buf,"drawFilledCircle(%d, %d, %d, BLACK); ",127,64,8);
drawString(6,115,&font_spleen_6x12,buf, WHITE, GREYA);
ssd1363_show();
sleep_ms(5000);
#endif
#if GRAFIKA_TEST
// -----------------------------------------------------------
// složitá grafika
printf("Grafika\n");
ssd1363_clear();
// obdélník kolem displeje
drawRectangle(0,0,255,127,WHITE);
sprintf(buf,"drawRectangle(%d, %d, %d, %d, WHITE);",0,0,255,127);
drawString(5,115,&font_spleen_6x12,buf, WHITE, BLACK);
ssd1363_show();
sleep_ms(1500);
// šedý čtverec
drawSquare(78,1,178,100,GREY8);
sprintf(buf,"drawSquare(%d, %d, %d, %d, GREY8);",78,1,178,100);
drawString(2,115,&font_spleen_6x12,buf, WHITE, BLACK);
ssd1363_show();
sleep_ms(1500);
drawSquare(15,10,56,51,GREY4);
sprintf(buf,"drawSquare(%d, %d, %d, %d, GREY4);",15,10,56,51);
drawString(2,115,&font_spleen_6x12,buf, WHITE, BLACK);
ssd1363_show();
sleep_ms(1500);
for( int x=200; x<240; x++) {
for( int y=10; y<51; y++) {
drawPixel(x,y,GREY7);
}
}
drawSquare(200,10,240,51,GREY7);
sprintf(buf,"drawSquare(%d, %d, %d, %d, GREY7);",200,10,240,51);
drawString(2,115,&font_spleen_6x12,buf, WHITE, BLACK);
ssd1363_show();
sleep_ms(1500);
drawString(190,20,&font_spleen_8x16,"Grafika",BLACK,WHITE);
sprintf(buf,"drawString(190,20,&font,\"Gra..\",0x0,0xf);");
drawString(2,115,&font_spleen_6x12,buf, WHITE, BLACK);
ssd1363_show();
sleep_ms(2500);
drawString(10,20,&font_spleen_8x16,"Grafika",WHITE,BLACK);
sprintf(buf,"drawString(10,20,&font,\"Graf..\",0xf,0x0);");
ret = drawString(2,115,&font_spleen_6x12,buf, WHITE, BLACK);
ssd1363_show();
sleep_ms(2500);
drawSquare(1,115,ret+1,126,BLACK);
for( int x=0, y=60; x<128; x++ ) {
drawLine(0,127,x,y,GREY4);
drawLine(255,127,255-x,y,WHITE);
sprintf(buf,"drawLine(%d, %d, %d, %d, %x);",0,127,x,y,GREY4);
drawString(40,103,&font_spleen_6x12,buf, WHITE, BLACK);
sprintf(buf,"drawLine(%d, %d, %d, %d, %x);",255,127,255-x,y,WHITE);
drawString(36,115,&font_spleen_6x12,buf, WHITE, BLACK);
drawLine(0,0,0,127,WHITE);
ssd1363_show();
}
ssd1363_show();
sleep_ms(5000);
#endif
#if SQUARE_TEST
// -----------------------------------------------------------
// plné obdélníky
ssd1363_clear();
ssd1363_show();
drawString(180,1,&font_spleen_8x16,"Obdélníky",BLACK,WHITE);
drawSquare(0, 0, 20, 20, GREY5);
drawSquare(80, 80, 40, 40, GREY9);
drawSquare(100, 20, 250, 60, GREYE);
drawSquare(0, 100, 16, 127, GREY4);
drawSquare(16, 90, 40, 127, GREY5);
drawSquare(40, 112, 60, 127, GREY6);
drawSquare(60, 70, 80, 127, GREY7);
drawSquare(80, 60, 120, 127, GREYA);
// panelák s okénky
drawSquare(120, 90, 142, 127, GREY8);
drawSquare(122, 92, 124, 94, BLACK);
drawSquare(126, 92, 128, 94, BLACK);
drawSquare(130, 92, 132, 94, BLACK);
drawSquare(134, 92, 136, 94, BLACK);
drawSquare(138, 92, 140, 94, BLACK);
drawSquare(122, 96, 124, 98, BLACK);
drawSquare(126, 96, 128, 98, BLACK);
drawSquare(130, 96, 132, 98, BLACK);
drawSquare(134, 96, 136, 98, BLACK);
drawSquare(138, 96, 140, 98, BLACK);
drawSquare(122, 100, 124, 102, BLACK);
drawSquare(126, 100, 128, 102, BLACK);
drawSquare(130, 100, 132, 102, BLACK);
drawSquare(134, 100, 136, 102, BLACK);
drawSquare(138, 100, 140, 102, BLACK);
drawSquare(122, 104, 124, 106, BLACK);
drawSquare(126, 104, 128, 106, BLACK);
drawSquare(130, 104, 132, 106, BLACK);
drawSquare(134, 104, 136, 106, BLACK);
drawSquare(138, 104, 140, 106, BLACK);
drawSquare(122, 108, 124, 110, BLACK);
drawSquare(126, 108, 128, 110, BLACK);
drawSquare(130, 108, 132, 110, BLACK);
drawSquare(134, 108, 136, 110, BLACK);
drawSquare(138, 108, 140, 110, BLACK);
drawSquare(122, 110, 124, 112, BLACK);
drawSquare(126, 110, 128, 112, BLACK);
drawSquare(130, 110, 132, 112, BLACK);
drawSquare(134, 110, 136, 112, BLACK);
drawSquare(138, 110, 140, 112, BLACK);
// konec paneláku
drawSquare(142, 100, 180, 127, GREYD);
ssd1363_show();
sleep_ms(5000);
#endif
#if RECTANGLE_TEST
// -----------------------------------------------------------
// prázdné obdélníky
ssd1363_clear();
ssd1363_show();
t = time_us_64();
drawString(108,17,&font_spleen_8x16,"Obdélníky prázdné",BLACK,GREYA);
drawRectangle(100, 10, 151, 60,WHITE);
drawRectangle(101, 11, 149, 58,WHITE);
drawRectangle(102, 12, 147, 56,GREYD);
drawRectangle(103, 13, 145, 54,GREYD);
drawRectangle(104, 14, 143, 52,GREYB);
drawRectangle(105, 15, 141, 50,GREYB);
drawRectangle( 10, 10, 20, 100, GREYC);
color_t c = WHITE;
for(x=50, y=90, h=32, w=60; h>4; x--, y--, w -= 2, h -= 2 ) {
drawRectangle(x,y,w,h, c--);
}
tk = time_us_64() - t;
t = time_us_64();
ssd1363_show();
tp = time_us_64() - t;
sprintf(buf,"Kreslení %llu μs", tk);
drawString(114,96,&font_spleen_8x16, buf, WHITE, BLACK);
ssd1363_show();
sleep_ms(2000);
sprintf(buf,"Přenos %llu μs ", tp);
drawString(114,111,&font_spleen_8x16, buf, WHITE, BLACK);
ssd1363_show();
sleep_ms(8000);
#endif
printf("Konec smyčky.\n");
}
}
// eof ---------------------------------------------------------------------------
Nepoužité fonty jsou v CMakeLists.txt zakomentovány.
CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
set(PICO_BOARD pico)
project(sh1363_test C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(ssd1363_test
ssd1363_test.c
utf8.c
font_9x15.c
font_10x20.c
font_spleen_5x8.c
font_spleen_6x12.c
font_spleen_8x16.c
font_spleen_12x24.c
font_spleen_16x32.c
font_spleen_32x64.c
font_tahoma_8.c
font_tahoma_10.c
font_tahoma_16.c
)
target_link_libraries(ssd1363_test pico_stdlib hardware_i2c)
# create map/bin/hex file etc.
pico_enable_stdio_usb(ssd1363_test 1)
pico_enable_stdio_uart(ssd1363_test 0)
pico_add_extra_outputs(ssd1363_test)
utf8.h
/**
* @file utf8.h
* @brief simple library for working with UTF-8 encoded strings
*
* @code
* #include "utf8.h"
* #include <stdio.h>
*
* int main() {
* const char* str = "Hello, こんにちは, Здравствуйте";
* utf8_string ustr = make_utf8_string(str);
* utf8_string_slice slice = make_utf8_string_slice(ustr, 2, 11);
* utf8_char_iter iter = make_utf8_char_iter(ustr);
*
* printf("string: %s\n", ustr.str);
* printf("slice: %.*s\n", (int)slice.byte_len, slice.str);
*
* utf8_char ch;
* while ((ch = next_utf8_char(&iter)).byte_len > 0) {
* printf("character: %.*s\t", (int)ch.byte_len, ch.str);
* printf("unicode code point: U+%04X\n", unicode_code_point(ch));
* }
*
* return 0;
* }
* @endcode
*/
#ifndef ZAHASH_UTF8_H
#define ZAHASH_UTF8_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/**
* @brief Represents the validity of a UTF-8 encoded string.
*
* @details The `utf8_validity` struct indicates whether a given UTF-8 encoded string is valid or not,
* along with the position up to which it is valid.
*
* - Invalid case: "hello\xC0\xC0" => { .valid = false, .valid_upto = 5 }
* - Valid case: "hello world" => { .valid = true, .valid_upto = 11 }
*/
typedef struct {
bool valid; ///< Flag indicating the validity of the UTF-8 string.
size_t valid_upto; ///< The position up to which the string is valid.
} utf8_validity;
/**
* @brief Represents a non-owning UTF-8 encoded string. (just a wrapper type).
*
* @details The `utf8_string` struct holds a pointer to a UTF-8 encoded string along with its byte length,
*/
typedef struct {
const char* str; ///< Pointer to the UTF-8 encoded string.
size_t byte_len; ///< Byte length of the UTF-8 string ('\0' not counted).
} utf8_string;
/**
* @brief Represents a UTF-8 encoded string that fully owns its data.
*
* @details The `owned_utf8_string` struct holds a pointer to a UTF-8 encoded string that is dynamically allocated
* and therefore is owned by the struct, which means the caller is responsible for freeing the memory when
* it is no longer needed using the `free_owned_utf8_string` function.
*/
typedef struct {
char* str; ///< Pointer to the UTF-8 encoded string (owned). This memory is dynamically allocated.
size_t byte_len; ///< Byte length of the UTF-8 string ('\0' not counted).
} owned_utf8_string;
/**
* @brief Represents an iterator for traversing UTF-8 characters in a string.
*
* @details The `utf8_char_iter` struct serves as an iterator for traversing UTF-8 characters
* within a UTF-8 encoded string.
*/
typedef struct {
const char* str; ///< Pointer to the current position of the iterator.
} utf8_char_iter;
/**
* @brief Represents a UTF-8 character.
*
* @details The `utf8_char` struct encapsulates a UTF-8 character, including its pointer and byte length.
* The byte length represents the number of bytes occupied by the UTF-8 character.
*/
typedef struct {
const char* str; ///< Pointer to the UTF-8 character.
uint8_t byte_len; ///< Byte length of the UTF-8 character.
} utf8_char;
/**
* @brief Validates whether a given string is UTF-8 compliant in O(n) time.
*
* @param str The input string to validate.
* @return The validity of the UTF-8 string along with the position up to which it is valid.
*/
utf8_validity validate_utf8(const char* str);
/**
* @brief Wraps a C-style string in a UTF-8 string structure after verifying its UTF-8 compliance.
*
* @param str The input C-style string to wrap.
* @return A UTF-8 string structure containing the wrapped string if valid; otherwise, a structure with NULL string pointer.
*
* @code
* // Example usage:
* const char *str = "definitely utf8 string こんにちは नमस्ते Здравствуйте";
* utf8_string ustr = make_utf8_string(str);
* assert( ustr.str != NULL );
*
* const char *s = "non-utf8 sequence \xC0\xC0";
* utf8_string ustr = make_utf8_string(str);
* assert( ustr.str == NULL );
* @endcode
*/
utf8_string make_utf8_string(const char* str);
/**
* @brief Converts a C-style string to a UTF-8 string, replacing invalid sequences with U+FFFD REPLACEMENT CHARACTER (�).
*
* @details It takes a C-style string as input and converts it to a UTF-8 encoded string.
* Any invalid UTF-8 sequences in the input string are replaced with the U+FFFD REPLACEMENT CHARACTER (�) to ensure
* that the resulting string is valid UTF-8. The resulting string is dynamically allocated and the caller
* is responsible for freeing the memory when no longer needed using `free_owned_utf8_string`.
*
* @param str The input C-style string to convert. The string can contain invalid UTF-8 sequences.
* @return An `owned_utf8_string` structure containing the resulting UTF-8 string. If memory allocation fails, the structure
* will contain a `NULL` pointer and a `byte_len` of 0.
*
* @code
* // Example usage:
* const char* str = "hello\xC0\xC0 world!";
* owned_utf8_string owned_ustr = make_utf8_string_lossy(str);
* @endcode
*/
owned_utf8_string make_utf8_string_lossy(const char* str);
/**
* @brief Creates the non-owning UTF-8 encoded string `utf8_string` from an `owned_utf8_string`.
*
* @details The resulting `utf8_string` will point to the same underlying string without taking ownership.
* The caller must ensure the original `owned_utf8_string` remains valid as long as the reference is used.
*
* @param owned_str The owned UTF-8 string from which to create a non-owning reference.
* @return utf8_string A non-owning UTF-8 string reference (`utf8_string`) pointing to the same data.
*
* @note This function does not free or transfer ownership of the `owned_utf8_string`.
* The caller is responsible for managing the lifetime of the owned string.
*/
utf8_string as_utf8_string(const owned_utf8_string* owned_str);
/**
* @brief Frees the memory allocated for an `owned_utf8_string`.
*
* @details The `free_owned_utf8_string` function deallocates the memory used by an `owned_utf8_string`
* and sets the `str` pointer to `NULL` and `byte_len` to 0.
*
* @param owned_str A pointer to the `owned_utf8_string` structure to be freed.
*
* @code
* // Example usage:
* owned_utf8_string owned_ustr = make_utf8_string_lossy("hello\xC0\xC0 world!");
* free_owned_utf8_string(&owned_ustr);
* @endcode
*/
void free_owned_utf8_string(owned_utf8_string* owned_str);
/**
* @brief Creates a UTF-8 string slice from a specified range of bytes in the original string.
*
* @param ustr The original UTF-8 string.
* @param byte_index The starting byte index of the slice.
* @param byte_len The byte length of the slice.
* @return A UTF-8 string representing the specified byte range [offset, offset + byte_len) if valid (range between UTF-8 char boundaries);
* otherwise { .str = NULL, .byte_len = 0 }
*
* @note if `byte_index` >= strlen(ustr.str) then returns terminating '\0' of ustr.str { .str = '\0', .byte_len = 0 }
* @note if `byte_index` + `byte_len` >= strlen(ustr.str) then only chars till terminating '\0' are considered.
*/
utf8_string slice_utf8_string(utf8_string ustr, size_t byte_index, size_t byte_len);
/**
* @brief Creates an iterator for traversing UTF-8 characters within a string. (see next_utf8_char( .. ) for traversal)
*
* @param ustr The UTF-8 string to iterate over.
* @return An iterator structure initialized to the start of the string.
*/
utf8_char_iter make_utf8_char_iter(utf8_string ustr);
/**
* @brief Retrieves the next UTF-8 character from the iterator.
*
* @param iter Pointer to the UTF-8 character iterator.
* @return The next UTF-8 character from the iterator.
* @note If the iterator reaches the end, it keeps returning terminating '\0' of iter.str { .str = '\0', .byte_len = 0 }
*/
utf8_char next_utf8_char(utf8_char_iter* iter);
/**
* @brief Retrieves the UTF-8 character at the specified character index within a UTF-8 string in O(n) time.
*
* @details The `nth_utf8_char` function returns the UTF-8 character located at the specified character index
* within the given UTF-8 string. The character index is zero-based, indicating the position of
* the character in the string. If the index is out of bounds or invalid, the function returns
* { .str = NULL, .byte_len = 0 }
*
* @param ustr The UTF-8 string from which to retrieve the character.
* @param char_index The zero-based index of the character to retrieve.
* @return The UTF-8 character at the specified index within the string.
*
* @code
* // Example usage:
* utf8_string str = make_utf8_string("Hello Здравствуйте こんにちは");
* utf8_char char_at_index = nth_utf8_char(str, 7); // д
* @endcode
*/
utf8_char nth_utf8_char(utf8_string ustr, size_t char_index);
/**
* @brief Counts the number of UTF-8 characters in the given utf8_string.
*
* @param ustr The UTF-8 string whose characters are to be counted.
* @return The total number of characters in the UTF-8 string.
*/
size_t utf8_char_count(utf8_string ustr);
/**
* @brief Checks if a given byte is the start of a UTF-8 character. ('\0' is also a valid character boundary)
*
* @param str Pointer to the byte to check.
* @return `true` if the byte is the start of a UTF-8 character; otherwise, `false`.
*/
bool is_utf8_char_boundary(const char* str);
/**
* @brief Converts a UTF-8 character to its corresponding Unicode code point (which is the same as a UTF-32 value).
*
* @param uchar The UTF-8 character to convert.
* @return The Unicode code point.
*/
uint32_t unicode_code_point(utf8_char uchar);
#endif
utf8.c
#include "utf8.h"
#include <stdlib.h>
#include <string.h>
typedef struct {
bool valid;
size_t next_offset;
} utf8_char_validity;
utf8_char_validity validate_utf8_char(const char* str, size_t offset) {
// Single-byte UTF-8 characters have the form 0xxxxxxx
if (((uint8_t)str[offset] & 0b10000000) == 0b00000000)
return (utf8_char_validity) { .valid = true, .next_offset = offset + 1 };
// Two-byte UTF-8 characters have the form 110xxxxx 10xxxxxx
if (((uint8_t)str[offset + 0] & 0b11100000) == 0b11000000 &&
((uint8_t)str[offset + 1] & 0b11000000) == 0b10000000) {
// Check for overlong encoding
// 0(xxxxxxx)
// 0(1111111)
// 110(xxxxx) 10(xxxxxx)
// 110(00001) 10(111111)
// 110(00010) 10(000000)
if (((uint8_t)str[offset] & 0b00011111) < 0b00000010)
return (utf8_char_validity) { .valid = false, .next_offset = offset };
return (utf8_char_validity) { .valid = true, .next_offset = offset + 2 };
}
// Three-byte UTF-8 characters have the form 1110xxxx 10xxxxxx 10xxxxxx
if (((uint8_t)str[offset + 0] & 0b11110000) == 0b11100000 &&
((uint8_t)str[offset + 1] & 0b11000000) == 0b10000000 &&
((uint8_t)str[offset + 2] & 0b11000000) == 0b10000000) {
// Check for overlong encoding
// 110(xxxxx) 10(xxxxxx)
// 110(11111) 10(111111)
// 1110(xxxx) 10(xxxxxx) 10(xxxxxx)
// 1110(0000) 10(011111) 10(111111)
// 1110(0000) 10(100000) 10(000000)
if (((uint8_t)str[offset + 0] & 0b00001111) == 0b00000000 &&
((uint8_t)str[offset + 1] & 0b00111111) < 0b00100000)
return (utf8_char_validity) { .valid = false, .next_offset = offset };
// Reject UTF-16 surrogates
// U+D800 to U+DFFF
// 1110(1101) 10(100000) 10(000000) ED A0 80 to 1110(1101) 10(111111) 10(111111) ED BF BF
if ((uint8_t)str[offset + 0] == 0b11101101 &&
(uint8_t)str[offset + 1] >= 0b10100000 &&
(uint8_t)str[offset + 1] <= 0b10111111)
return (utf8_char_validity) { .valid = false, .next_offset = offset };
return (utf8_char_validity) { .valid = true, .next_offset = offset + 3 };
}
// Four-byte UTF-8 characters have the form 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
if (((uint8_t)str[offset + 0] & 0b11111000) == 0b11110000 &&
((uint8_t)str[offset + 1] & 0b11000000) == 0b10000000 &&
((uint8_t)str[offset + 2] & 0b11000000) == 0b10000000 &&
((uint8_t)str[offset + 3] & 0b11000000) == 0b10000000) {
// Check for overlong encoding
// 1110(xxxx) 10(xxxxxx) 10(xxxxxx)
// 1110(1111) 10(111111) 10(111111)
// 11110(xxx) 10(xxxxxx) 10(xxxxxx) 10(xxxxxx)
// 11110(000) 10(001111) 10(111111) 10(111111)
// 11110(000) 10(010000) 10(000000) 10(000000)
if (((uint8_t)str[offset + 0] & 0b00000111) == 0b00000000 &&
((uint8_t)str[offset + 1] & 0b00111111) < 0b00010000)
return (utf8_char_validity) { .valid = false, .next_offset = offset };
return (utf8_char_validity) { .valid = true, .next_offset = offset + 4 };
}
return (utf8_char_validity) { .valid = false, .next_offset = offset };
}
utf8_validity validate_utf8(const char* str) {
if (str == NULL) return (utf8_validity) { .valid = false, .valid_upto = 0 };
size_t offset = 0;
utf8_char_validity char_validity;
while (str[offset] != '\0') {
char_validity = validate_utf8_char(str, offset);
if (char_validity.valid) offset = char_validity.next_offset;
else return (utf8_validity) { .valid = false, .valid_upto = offset };
}
return (utf8_validity) { .valid = true, .valid_upto = offset };
}
utf8_string make_utf8_string(const char* str) {
utf8_validity validity = validate_utf8(str);
if (validity.valid) return (utf8_string) { .str = str, .byte_len = validity.valid_upto };
return (utf8_string) { .str = NULL, .byte_len = 0 };
}
owned_utf8_string make_utf8_string_lossy(const char* str) {
if (str == NULL) return (owned_utf8_string) { .str = NULL, .byte_len = 0 };
size_t len = strlen(str);
// Worst case scenario: every byte is invalid and is replaced with 3 bytes for U+FFFD
size_t worst_case_size = len * 3 + 1;
// Allocate buffer for the lossy UTF-8 string
char* buffer = (char*)malloc(worst_case_size);
if (!buffer) return (owned_utf8_string) { .str = NULL, .byte_len = 0 }; // failed allocation
size_t buffer_offset = 0;
size_t offset = 0;
utf8_char_validity char_validity;
while (offset < len) {
char_validity = validate_utf8_char(str, offset);
if (char_validity.valid) {
// Copy valid UTF-8 character sequence to the buffer
size_t char_len = char_validity.next_offset - offset;
memcpy(buffer + buffer_offset, str + offset, char_len);
buffer_offset += char_len;
offset = char_validity.next_offset;
} else {
// Insert the UTF-8 bytes for U+FFFD (�)
// FFFD = 1111111111111101
// = (1111) (111111) (111101)
// = 1110(1111) 10(111111) 10(111101)
// = EF BF BD
buffer[buffer_offset++] = 0xEF;
buffer[buffer_offset++] = 0xBF;
buffer[buffer_offset++] = 0xBD;
offset++;
}
}
buffer[buffer_offset] = '\0';
return (owned_utf8_string) { .str = buffer, .byte_len = buffer_offset };
}
utf8_string as_utf8_string(const owned_utf8_string* owned_str) {
return (utf8_string) { .str = owned_str->str, .byte_len = owned_str->byte_len };
}
void free_owned_utf8_string(owned_utf8_string* owned_str) {
if (owned_str->str) {
free(owned_str->str);
owned_str->str = NULL;
owned_str->byte_len = 0;
}
}
utf8_char_iter make_utf8_char_iter(utf8_string ustr) {
return (utf8_char_iter) { .str = ustr.str };
}
bool is_utf8_char_boundary(const char* str) {
return (uint8_t)*str <= 0b01111111 || (uint8_t)*str >= 0b11000000;
}
utf8_string slice_utf8_string(utf8_string ustr, size_t start_byte_index, size_t byte_len) {
if (start_byte_index > ustr.byte_len) start_byte_index = ustr.byte_len;
size_t excl_end_byte_index = start_byte_index + byte_len;
if (excl_end_byte_index > ustr.byte_len) excl_end_byte_index = ustr.byte_len;
if (is_utf8_char_boundary(ustr.str + start_byte_index) && is_utf8_char_boundary(ustr.str + excl_end_byte_index))
return (utf8_string) { .str = ustr.str + start_byte_index, .byte_len = excl_end_byte_index - start_byte_index };
return (utf8_string) { .str = NULL, .byte_len = 0 };
}
utf8_char next_utf8_char(utf8_char_iter* iter) {
if (*iter->str == '\0') return (utf8_char) { .str = iter->str, .byte_len = 0 };
// iter->str is at the current char's starting byte (char boundary).
const char* curr_boundary = iter->str;
iter->str++;
uint8_t byte_len = 1;
// find the next char's starting byte (next char boundary) and set the iter->str to that.
while (!is_utf8_char_boundary(iter->str)) {
iter->str++;
byte_len++;
}
return (utf8_char) { .str = curr_boundary, .byte_len = byte_len };
}
utf8_char nth_utf8_char(utf8_string ustr, size_t char_index) {
utf8_char_iter iter = make_utf8_char_iter(ustr);
utf8_char ch;
while ((ch = next_utf8_char(&iter)).byte_len != 0 && char_index-- != 0) {}
if (ch.byte_len == 0) return (utf8_char) { .str = NULL, .byte_len = 0 };
return ch;
}
size_t utf8_char_count(utf8_string ustr) {
utf8_char_iter iter = make_utf8_char_iter(ustr);
size_t count = 0;
while (next_utf8_char(&iter).byte_len > 0) count++;
return count;
}
uint32_t unicode_code_point(utf8_char uchar) {
switch (uchar.byte_len) {
case 1: return uchar.str[0] & 0b01111111;
case 2: return
(uchar.str[0] & 0b00011111) << 6 |
(uchar.str[1] & 0b00111111);
case 3: return
(uchar.str[0] & 0b00001111) << 12 |
(uchar.str[1] & 0b00111111) << 6 |
(uchar.str[2] & 0b00111111);
case 4: return
(uchar.str[0] & 0b00000111) << 18 |
(uchar.str[1] & 0b00111111) << 12 |
(uchar.str[2] & 0b00111111) << 6 |
(uchar.str[3] & 0b00111111);
}
return 0; // unreachable
}
font.h
/* font.h
* pro utf8 bitmapové fonty
*/
#ifndef __FONT_H
#define __FONT_H
#include "pico/stdlib.h"
/// bitmap font structure
typedef struct bitmap_font {
unsigned char Width; ///< max. character width
unsigned char Height; ///< character height
unsigned int Chars; ///< number of characters in font
const unsigned char *Widths; ///< width of each character
const uint32_t *Index; ///< encoding to character index
const unsigned char *Bitmap; ///< bitmap of all characters
} bitmapFONT;
#define ________ 0x00
#define _______X 0x01
#define ______X_ 0x02
#define ______XX 0x03
#define _____X__ 0x04
#define _____X_X 0x05
#define _____XX_ 0x06
#define _____XXX 0x07
#define ____X___ 0x08
#define ____X__X 0x09
#define ____X_X_ 0x0A
#define ____X_XX 0x0B
#define ____XX__ 0x0C
#define ____XX_X 0x0D
#define ____XXX_ 0x0E
#define ____XXXX 0x0F
#define ___X____ 0x10
#define ___X___X 0x11
#define ___X__X_ 0x12
#define ___X__XX 0x13
#define ___X_X__ 0x14
#define ___X_X_X 0x15
#define ___X_XX_ 0x16
#define ___X_XXX 0x17
#define ___XX___ 0x18
#define ___XX__X 0x19
#define ___XX_X_ 0x1A
#define ___XX_XX 0x1B
#define ___XXX__ 0x1C
#define ___XXX_X 0x1D
#define ___XXXX_ 0x1E
#define ___XXXXX 0x1F
#define __X_____ 0x20
#define __X____X 0x21
#define __X___X_ 0x22
#define __X___XX 0x23
#define __X__X__ 0x24
#define __X__X_X 0x25
#define __X__XX_ 0x26
#define __X__XXX 0x27
#define __X_X___ 0x28
#define __X_X__X 0x29
#define __X_X_X_ 0x2A
#define __X_X_XX 0x2B
#define __X_XX__ 0x2C
#define __X_XX_X 0x2D
#define __X_XXX_ 0x2E
#define __X_XXXX 0x2F
#define __XX____ 0x30
#define __XX___X 0x31
#define __XX__X_ 0x32
#define __XX__XX 0x33
#define __XX_X__ 0x34
#define __XX_X_X 0x35
#define __XX_XX_ 0x36
#define __XX_XXX 0x37
#define __XXX___ 0x38
#define __XXX__X 0x39
#define __XXX_X_ 0x3A
#define __XXX_XX 0x3B
#define __XXXX__ 0x3C
#define __XXXX_X 0x3D
#define __XXXXX_ 0x3E
#define __XXXXXX 0x3F
#define _X______ 0x40
#define _X_____X 0x41
#define _X____X_ 0x42
#define _X____XX 0x43
#define _X___X__ 0x44
#define _X___X_X 0x45
#define _X___XX_ 0x46
#define _X___XXX 0x47
#define _X__X___ 0x48
#define _X__X__X 0x49
#define _X__X_X_ 0x4A
#define _X__X_XX 0x4B
#define _X__XX__ 0x4C
#define _X__XX_X 0x4D
#define _X__XXX_ 0x4E
#define _X__XXXX 0x4F
#define _X_X____ 0x50
#define _X_X___X 0x51
#define _X_X__X_ 0x52
#define _X_X__XX 0x53
#define _X_X_X__ 0x54
#define _X_X_X_X 0x55
#define _X_X_XX_ 0x56
#define _X_X_XXX 0x57
#define _X_XX___ 0x58
#define _X_XX__X 0x59
#define _X_XX_X_ 0x5A
#define _X_XX_XX 0x5B
#define _X_XXX__ 0x5C
#define _X_XXX_X 0x5D
#define _X_XXXX_ 0x5E
#define _X_XXXXX 0x5F
#define _XX_____ 0x60
#define _XX____X 0x61
#define _XX___X_ 0x62
#define _XX___XX 0x63
#define _XX__X__ 0x64
#define _XX__X_X 0x65
#define _XX__XX_ 0x66
#define _XX__XXX 0x67
#define _XX_X___ 0x68
#define _XX_X__X 0x69
#define _XX_X_X_ 0x6A
#define _XX_X_XX 0x6B
#define _XX_XX__ 0x6C
#define _XX_XX_X 0x6D
#define _XX_XXX_ 0x6E
#define _XX_XXXX 0x6F
#define _XXX____ 0x70
#define _XXX___X 0x71
#define _XXX__X_ 0x72
#define _XXX__XX 0x73
#define _XXX_X__ 0x74
#define _XXX_X_X 0x75
#define _XXX_XX_ 0x76
#define _XXX_XXX 0x77
#define _XXXX___ 0x78
#define _XXXX__X 0x79
#define _XXXX_X_ 0x7A
#define _XXXX_XX 0x7B
#define _XXXXX__ 0x7C
#define _XXXXX_X 0x7D
#define _XXXXXX_ 0x7E
#define _XXXXXXX 0x7F
#define X_______ 0x80
#define X______X 0x81
#define X_____X_ 0x82
#define X_____XX 0x83
#define X____X__ 0x84
#define X____X_X 0x85
#define X____XX_ 0x86
#define X____XXX 0x87
#define X___X___ 0x88
#define X___X__X 0x89
#define X___X_X_ 0x8A
#define X___X_XX 0x8B
#define X___XX__ 0x8C
#define X___XX_X 0x8D
#define X___XXX_ 0x8E
#define X___XXXX 0x8F
#define X__X____ 0x90
#define X__X___X 0x91
#define X__X__X_ 0x92
#define X__X__XX 0x93
#define X__X_X__ 0x94
#define X__X_X_X 0x95
#define X__X_XX_ 0x96
#define X__X_XXX 0x97
#define X__XX___ 0x98
#define X__XX__X 0x99
#define X__XX_X_ 0x9A
#define X__XX_XX 0x9B
#define X__XXX__ 0x9C
#define X__XXX_X 0x9D
#define X__XXXX_ 0x9E
#define X__XXXXX 0x9F
#define X_X_____ 0xA0
#define X_X____X 0xA1
#define X_X___X_ 0xA2
#define X_X___XX 0xA3
#define X_X__X__ 0xA4
#define X_X__X_X 0xA5
#define X_X__XX_ 0xA6
#define X_X__XXX 0xA7
#define X_X_X___ 0xA8
#define X_X_X__X 0xA9
#define X_X_X_X_ 0xAA
#define X_X_X_XX 0xAB
#define X_X_XX__ 0xAC
#define X_X_XX_X 0xAD
#define X_X_XXX_ 0xAE
#define X_X_XXXX 0xAF
#define X_XX____ 0xB0
#define X_XX___X 0xB1
#define X_XX__X_ 0xB2
#define X_XX__XX 0xB3
#define X_XX_X__ 0xB4
#define X_XX_X_X 0xB5
#define X_XX_XX_ 0xB6
#define X_XX_XXX 0xB7
#define X_XXX___ 0xB8
#define X_XXX__X 0xB9
#define X_XXX_X_ 0xBA
#define X_XXX_XX 0xBB
#define X_XXXX__ 0xBC
#define X_XXXX_X 0xBD
#define X_XXXXX_ 0xBE
#define X_XXXXXX 0xBF
#define XX______ 0xC0
#define XX_____X 0xC1
#define XX____X_ 0xC2
#define XX____XX 0xC3
#define XX___X__ 0xC4
#define XX___X_X 0xC5
#define XX___XX_ 0xC6
#define XX___XXX 0xC7
#define XX__X___ 0xC8
#define XX__X__X 0xC9
#define XX__X_X_ 0xCA
#define XX__X_XX 0xCB
#define XX__XX__ 0xCC
#define XX__XX_X 0xCD
#define XX__XXX_ 0xCE
#define XX__XXXX 0xCF
#define XX_X____ 0xD0
#define XX_X___X 0xD1
#define XX_X__X_ 0xD2
#define XX_X__XX 0xD3
#define XX_X_X__ 0xD4
#define XX_X_X_X 0xD5
#define XX_X_XX_ 0xD6
#define XX_X_XXX 0xD7
#define XX_XX___ 0xD8
#define XX_XX__X 0xD9
#define XX_XX_X_ 0xDA
#define XX_XX_XX 0xDB
#define XX_XXX__ 0xDC
#define XX_XXX_X 0xDD
#define XX_XXXX_ 0xDE
#define XX_XXXXX 0xDF
#define XXX_____ 0xE0
#define XXX____X 0xE1
#define XXX___X_ 0xE2
#define XXX___XX 0xE3
#define XXX__X__ 0xE4
#define XXX__X_X 0xE5
#define XXX__XX_ 0xE6
#define XXX__XXX 0xE7
#define XXX_X___ 0xE8
#define XXX_X__X 0xE9
#define XXX_X_X_ 0xEA
#define XXX_X_XX 0xEB
#define XXX_XX__ 0xEC
#define XXX_XX_X 0xED
#define XXX_XXX_ 0xEE
#define XXX_XXXX 0xEF
#define XXXX____ 0xF0
#define XXXX___X 0xF1
#define XXXX__X_ 0xF2
#define XXXX__XX 0xF3
#define XXXX_X__ 0xF4
#define XXXX_X_X 0xF5
#define XXXX_XX_ 0xF6
#define XXXX_XXX 0xF7
#define XXXXX___ 0xF8
#define XXXXX__X 0xF9
#define XXXXX_X_ 0xFA
#define XXXXX_XX 0xFB
#define XXXXXX__ 0xFC
#define XXXXXX_X 0xFD
#define XXXXXXX_ 0xFE
#define XXXXXXXX 0xFF
#endif
Ostatní fonty a všechny zdrojové soubory jsou v archivu: ssd1363-static-v0.2.tar.gz
Zdroje a odkazy
Zde to jde koupit: 2.7" 256x128 OLED displej, I²C, bílý — Laskakit