Mikrokontroler RP204 má dvě jádra (dva procesory), které běží nezávisle na sobě. U každého jádra máme k dispozici samostatný zásobník.
Pokud napíšete svůj vlasntí program, budete v něm určitě mít funkci main() a tento program poběží na procesoru 0. Procesor 1 se bude flákat.
Vyzkoušíme si velmi výpočetně náročný program, kde zapojíme do práce oba procesory. Zkusíme hrubou silou vyřešit rovnici:
která nám nakreslí na VGA obrazovku velmi hezký obrázek.
Hlavní problém programu spočívá v tom, že jsem potřeboval vypočítat rovnici s danou chybou, protože nalezení přesného řešení může být výpočetně velmi náročné. Proto jsem rozdělil souřadnice x a y do série malých kroků a rovnici jsem vypočítal s použitím těchto souřadnic. Pokud je výsledek v daném limitu chyby, nakreslíme na dané místo malý červený kruh (nebo fialový na druhém procesoru).
Dále se podíváme, jak se používá VGA knihovna a vyzkoušíme počeštěný font Spleen 8x16 bodů.

Multiprocesing
Základní schéma použití multiprocesingu je v následujícím úryvku programu. Funkce main() (běží na core 0) a funkce core1_entry() (běží na core 1).
Funkce core1_entry() hraje na procesoru 1 stejnou roli jako funkce main() na procesoru 0. Je to vstupní bod programu.
Tyto funkce v naší ukázce spolu nijak nekomunikují.
#include "pico/multicore.h" // pico multicore knihovna
// program co pobeží na procesoru 1
void core1_entry()
{
}
// main poběží na core 0 (procesor 0)
int main()
{
// tady používám core 0
// ...
// funkce core1_entry od teď poběží nezávisle na procesoru 1
multicore_launch_core1(core1_entry);
// tady používáme core 0
// ...
}
V našem programu funkce main() a core1_entry() zapisují do VGA paměti o velikosti 153600 bajtů unsigned char vga_data_array[TXCOUNT];, t.j. "kreslí obrázek".
Červená část srdce se počítá uvnitř funkce core1_entry() a fialová část se dělá uvnitř main().
Pokud někde dojde k průsečíku červené a fialové funkce, průsečík má barvu podle funkce, která zapsala do pole vga_data_array[i] na pozici i později.
Vlastní kreslení obrázku na monitor probíhá nezávisle na výpočtu a stará se o to PIO koprocesor spolu s DMA.
Program
vga_srdce.c
/*
* Parth Sarthi Sharma (pss242@cornell.edu)
* Code based on examples from Raspberry Pi Foundation.
* This code is an implementation of a custom heart function
* on the Raspberry Pi Pico to draw a pattern.
*
* Čeština Jirka Chráska 2025, jirka@lixis.cz
*
* Aby správně fungoval český font, je potřeba uložit tento soubor v kódování
* ISO-8859-2 a nikoliv v UTF-8, protože font_spleen16x8a.h je v ISO-8859-2
*/
#include <stdio.h> // standardní C knihovna
#include <math.h> // standardní matematická knihovna
#include "pico/stdlib.h" // standardní C knihovna pro Pico
#include "hardware/pio.h" // hardware PIO knihovna
#include "hardware/dma.h" // hardware DMA knihovna
#include "pico/time.h" // pico time knihovna
#include "hardware/gpio.h" // hardware GPIO knihovna
#include "pico/multicore.h" // pico multicore knihovna
#include "vga16_graphics.h" // knihovna pro VGA grafiku
#define HEIGHT 480 // výška VGA obrazovky
#define WIDTH 640 // šířka VGA obrazovky
#define PI 3.14159265358 // hodnota PI
// program co pobeží na procesoru 1
void core1_entry()
{
// chyba, krok, souřadnice x, souřadnice y, iterator, and the answer variables
double error = 0.01, step = 0.01, x, y, a, ans;
for(a = 11; a <= 20; a += 0.5){ // Pohybujeme parametrem a v rozsahu [11, 20] s krokem 0.5
for(x = -3; x < 3; x += step){ // Pohybujeme souřadnicí x v rozsahu [-3, 3) s krokem 0.01
for(y = -3; y < 3; y += step){ // Pohybujeme souřadnicí y v rozsahu [-3, 3) s krokem 0.01
// řešíme rovnici
ans = pow((sin(a * PI / 10.0) + x), 2.0) + pow((cos(a * PI / 10.0) + y), 2.0) - (0.7 * fabs(x) * y);
if((ans < 1 + error) && (ans > 1 - error)){
// Kde je řesení ans menší než požadovaná chyba, nakreslíme červený kroužek
fillCircle((50 * x) + (WIDTH / 2), (HEIGHT / 2) - (50 * y), 1, RED);
}
}
}
}
}
// program co poběží na procesoru 0
int main()
{
// chyba, krok, souřadnice x, souřadnice y, iterator, parametr a výsledek
double error = 0.01, step = 0.01, x, y, a, ans;
stdio_init_all(); // inicializace stdio
initVGA(); // inicializace VGA obrazovky a grafických funkcí
// psaní textu po obrazovce: font je 8 bodů široký a 16 bodů vysoký
setTextSize(1); // nejmenší velikost písma
setTextColor(WHITE); // barva písma
setCursor(0,0); // nastavení počátečního bodu
// rovnice
writeString("Na obrázku se přibližně řeší rovnice (chyba je 0.01). Pracují obě jádra ARM M0+.");
setCursor(0,16);
writeString("(sin(a*PI/10)+x)^2 + (cos(a*PI/10) + y)^2 = 0.7*|x|*y+1 ");
setTextColor(MAGENTA);
writeString("a = <1,10> ");
setTextColor(RED);
writeString("a = <11,20> ");
// test většího písma
setTextColor(BLUE);
setTextSize(2);
setCursor(0,32);
writeString("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
// test češtiny
setTextSize(1);
setCursor(0,480-32);
writeString("Příliš žluťoučký kůň pěl ďábelské ódy. Maličký ježeček.");
// resetujeme core1 a uvedeme funkci, která bude spuštěna: core1_entry
multicore_launch_core1(core1_entry);
// Budeme měnit parametr rovnice a v rozsahu [1, 10] s krokem 0.5
for(a = 1; a <= 10; a += 0.5){
// Budeme měnit x v rozsahu [-3, 3] s krokem 0.01
for(x = -3; x < 3; x += step){
// Budeme měnit y v rozsahu [-3, 3] s krokem 0.01ce
for(y = -3; y < 3; y += step){
// výpočet rovnice
ans = pow((sin(a * PI / 10.0) + x), 2.0) + pow((cos(a * PI / 10.0) + y), 2.0) - (0.7 * fabs(x) * y);
// Kde je řešení ans menší než chyba, nakreslíme fialový kroužek
if((ans < 1 + error) && (ans > 1 - error)){
fillCircle((50 * x) + (WIDTH / 2), (HEIGHT / 2) - (50 * y), 1, MAGENTA);
}
}
}
}
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(vga_srdce-project)
pico_sdk_init()
add_executable(vga_srdce)
pico_enable_stdio_usb(vga_srdce 1)
pico_enable_stdio_uart(vga_srdce 1)
pico_generate_pio_header(vga_srdce ${CMAKE_CURRENT_LIST_DIR}/hsync.pio)
pico_generate_pio_header(vga_srdce ${CMAKE_CURRENT_LIST_DIR}/vsync.pio)
pico_generate_pio_header(vga_srdce ${CMAKE_CURRENT_LIST_DIR}/rgb.pio)
target_sources(vga_srdce PRIVATE vga_srdce.c vga_srdce.c vga16_graphics.c glcdfont.c)
target_link_libraries(vga_srdce PRIVATE pico_stdlib hardware_pio hardware_dma hardware_adc hardware_irq pico_time pico_multicore)
pico_add_extra_outputs(vga_srdce)
VGA knihovna
VGA knihovna umí kreslit body, úsečky, obdélníky, vyplněné obdélníky, kružnice, vyplněné kruhy a písmenka. Můžeme používat jenom 16 různých barev, protože jsme omezeni velikosti operační paměti Raspberry Pi Pico, která je 260 kByte. Pro každý bod na obrazovce potřebujeme 4 bity, 1bit pro hodnotu červené barvy, 2bity pro hodnotu zelené barvy a 1 bit pro hodnotu modré barvy. Velikost potřebné paměti pro 640x480 bodů obrazovky je tedy \(640\cdot480\cdot 4 bity = 153600 bytů\). 256 různých barev bychom mohli používat na Raspberry Pi Pico 2, který má velikost operační paměti 512 kbyte. Pro jeden bod obrazovky potřebujeme 8bitů (3 bity červená, 3 bity zelená a 2 bity modrá barva). Celkem \(640\cdot480\cdot 8 bitů = 307200 bytů\).
VGA knihovnu napsal pan V. Hunter Adams z Cornell university, já jsem ji upravil tak, aby bylo možné psát češtinou fontem Spleen 16x8.
Všechny funkce můžeme přečíst v hlavičkovém souboru
vga16_graphics.h
/**
* Hunter Adams (vha3@cornell.edu)
* modifed for 16 colors by BRL4
*
*
* HARDWARE CONNECTIONS
* - GPIO 16 ---> VGA Hsync
* - GPIO 17 ---> VGA Vsync
* - GPIO 18 ---> 660 ohm resistor ---> VGA Green
* - GPIO 19 ---> 470 ohm resistor ---> VGA Green
* - GPIO 20 ---> 330 ohm resistor ---> VGA Blue
* - GPIO 21 ---> 330 ohm resistor ---> VGA Red
* - RP2040 GND ---> VGA GND
*
* RESOURCES USED
* - PIO state machines 0, 1, and 2 on PIO instance 0
* - DMA channels 0, 1, 2, and 3
* - 153.6 kBytes of RAM (for pixel color data)
*
* NOTE
* - This is a translation of the display primitives
* for the PIC32 written by Bruce Land and students
*
*/
/* Úprava pro psaní počeštěným fontem Spleen 16x8 (ISO-8859-2)
* Jirka Chráska, <jirka@lixis.cz>
*/
// Give the I/O pins that we're using some names that make sense - usable in main()
enum vga_pins {HSYNC=16, VSYNC, LO_GRN, HI_GRN, BLUE_PIN, RED_PIN} ;
// We can only produce 8 (3-bit) colors, so let's give them readable names - usable in main()
//enum colors {BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE} ;
enum colors {BLACK, DARK_GREEN, MED_GREEN, GREEN,
DARK_BLUE, BLUE, LIGHT_BLUE, CYAN,
RED, DARK_ORANGE, ORANGE, YELLOW,
MAGENTA, PINK, LIGHT_PINK, WHITE} ;
// VGA primitives - usable in main
void initVGA(void) ;
void drawPixel(short x, short y, char color) ;
void drawVLine(short x, short y, short h, char color) ;
void drawHLine(short x, short y, short w, char color) ;
void drawLine(short x0, short y0, short x1, short y1, char color) ;
void drawRect(short x, short y, short w, short h, char color);
void drawCircle(short x0, short y0, short r, char color) ;
void drawCircleHelper( short x0, short y0, short r, unsigned char cornername, char color) ;
void fillCircle(short x0, short y0, short r, char color) ;
void fillCircleHelper(short x0, short y0, short r, unsigned char cornername, short delta, char color) ;
void drawRoundRect(short x, short y, short w, short h, short r, char color) ;
void fillRoundRect(short x, short y, short w, short h, short r, char color) ;
void fillRect(short x, short y, short w, short h, char color) ;
void drawChar(short x, short y, unsigned char c, char color, char bg, unsigned char size) ;
void setCursor(short x, short y);
void setTextColor(char c);
void setTextColor2(char c, char bg);
void setTextSize(unsigned char s);
void setTextWrap(char w);
void tft_write(unsigned char c) ;
void writeString(char* str) ;
První funkcí, kterou musíme zavolat je initVGA(). Ta zajistí nastavení PIO koprocesoru a DMA.
Od této chvíle můžeme kreslit, co chceme a nemusíme se starat o to, jak to bude dlouho trvat.
Systém souřadnic je uspořádán tak, že bod [0, 0] je na obrazovce vlevo nahoře. Souřadnice x roste doprava a souřadnice y roste dolů.
Maximální hodnota x je 639 a maximální hodnota y je 479. Souřadnice se zadávají pomocí short x a short y.
Barva color může mí 16 hodnot podle výčtu enum color {}.
Grafické funkce nevracejí chybu, mají návratovou hodnotu void. Pokud například překročíme souřadnice, funkce prostě nenakreslí nic.
Seznam grafických funkcí
void drawPixel( short x, short y, char color);
void drawLine(short x0, short y0, short x1, short y1, char color);
x0 a y0 jsou souřadnice počátku; x1 a y1 jsou souřadnice konce.
Šikmá úsečka se kreslí Bresenhamovým algoritmem.
void drawHLine(short x, short y, short w, char color); // vodorovná
void drawVLine(short x, short y, short h, char color); // svislá
x a y jsou souřadnice počátečního bodu; w je šířka vodorovné úsečky a h je výška svislé úsečky.
void drawRect(short x, short y, short w, short h, char color);
x a y jsou souřadnice levého horního rohu obdélníku; w a h je šířka a výška obdélníku.
void fillRect(short x, short y, short w, short h, char color);
x a y jsou souřadnice levého horního rohu obdélníku; w a h je šířka a výška obdélníku.
Obdélník je vyplněný barvou color.
void drawRoundRect(short x, short y, short w, short h, char color);
x a y jsou souřadnice levého horního rohu obdélníku; w a h je šířka a výška obdélníku. r je poloměr zaoblení rohů.
void fillRoundRect(short x, short y, short w, short h, char color);
x a y jsou souřadnice levého horního rohu obdélníku; w a h je šířka a výška obdélníku. r je poloměr zaoblení rohů.
Obdélník je vyplněný barvou color.
void drawCircle(short x0, short y0, short r, char color);
x0 a y0 je střed kružnice a r je poloměr v jednotkách bodů. Kružnice není vyplněná barvou.
void fillCircle(short x0, short y0, short r, char color) ;
x0 a y0 je střed kružnice a r je poloměr v jednotkách bodů. Kruh je vyplněn barvou color.
Funkce drawCircleHelper() a fillCircleHelper() nejsou určeny k přímému použití, ale slouží jako pomocné funkce pro kreslení kružnice.
Psaní textu
Kreslení písmenek se dělá tak, že nejprve zavoláme funkci setCursor(), kterou nastavíme počátek psaní.
Potom funkci setTextColor(), kterou nastavíme barvu písmenek nebo setTextColor2(), kterou můžeme nastavit barvu písmenek i barvu pozadí.
Dále můžeme zavolat i funkce setTextSize() pro nastavení velikosti textu a setTextWrap() pro zalomení textu pokud je delší než šířka obrazovky.
Černé pozadí se nemusí zadávat, protože je implicitní.
Potom už můžeme psát pomocí funkce writeString().
Knihovna používá globální proměnné:
unsigned short cursor_y, cursor_x; // počátek psaní textu (levý horní roh)
unsigned short textsize; // velikost písmen, normální velikost je 1 pro písmena výšky 16 pixelů a šířky 8 pixelů.
char textcolor, textbgcolor, wrap; // barva písmen, pozadí a zalamování textu
takže jakmile zavoláme některou funkci znova, tak se jejich hodnoty změní.
Příklad:
// bílý text na černém pozadí, z bodu [0, 10]
setCursor(0, 10);
setTextColor(WHITE);
writeString("Ahoj studenti.");
// modrý text na zeleném pozadí o velikosti 2, z bodu [16,10]
setCursor(16, 10);
setTextColor2(BLUE,GREEN);
writeString("Maličký ježeček.");
Chceme-li používat české znaky, musí být náš zdrojový soubor uložen v 8 bitovém kódování ISO-8859-2. Grafická knihovna neumí UTF-8 a bylo by to zbytečně složité a velké, protože v UTF-8 kódování může znak mít velikost 1 bajt až 4 bajty. Budeme-li mít uložen zdrojový soubor v kódování UTF-8, musíme se podívat do tabulky znaků ISO-8859-2 a napsat řetězec "Maličký ježeček" takto:
char ceske_znaky[] = "Mali\xe8k\xfd je\xbee\xe8ek";