Kromě jazyka C a MicroPythonu se dá používat při vývoji na Raspberry Pi Pico i programovací jazyk Zig, konkrétně v předpřipraveném prostředí MicroZig. Programovací jazyk Zig je koncipován autorem Andrewem Kelley jako nástupce Céčka a je určen pro multiplatformní vývoj. Není to dosud hotový jazyk, ale dá se dost dobře používat. Zig je vyvíjen neziskovou organizací Zig Software Foundation, což je velká výhoda. Další výhodou při programování v Zigu je to, že k vývoji nepotřebujeme žádná složitá IDE nebo cmake, vystačíme si s textovým editorem a kompilátorem jazyka. Rozchodím jednoduchý příklad, budeme si vypisovat informace na UART (sériovou linku).
Mimochodem, pomocí MikroZigu se dá vyvíjet nejenom na Rasperry Pi Pico, ale i na další mikro počítače, jako třeba:
-
Gigadevice gd32
-
Microchip atsam a avr
-
Nordic nrf5x nRF52840 Dongle
-
Různá zařízení na procesoru RP2040
-
NXP
-
STMicro stm32
Předběžné práce
Schéma zapojení

Vodiče jsou zapojeny takto:
-
červený (+5V) — Pico VBUS (pin 40), budeme Pico zároveň napájet kabelem
-
černý (zem) — Pico GND (třeba pin 38)
-
bílý (TX) — Pico GP0 (pin 1)
-
zelený (RX) — Pico GP1 (pin 2)

Instalace kompilátoru Zig
Instalace kompilátoru Zig je velmi jednoduchá, stáhnete si pro váš systém balík s kompilátorem, rozbalíte ho a nastavíte cestu. Já programuji na Linuxu, použil jsem verzi Zigu 0.13.0
$ cd
$ tar xf zig-linux-x86_64-0.13.0.tar.xz
$ ln -s zig-linux-x86_64-0.13.0 zig
$ echo 'export PATH=$PATH:/home/jirka/zig' >> ~/.bashrc
$ source ~/.bashrc
$ zig version
0.13.0
$
Obrovskou výhodou Zigu je to, že kompilátor nejen kompiluje, ale i sestavuje projekt. Nyní stačí naklonovat z GitHubu MicroZig a můžeme začít.
Já jsem si nainstaloval vlastní git server kam si budu ukládat výsledky své práce, protože nechci používat GitHub. Jak jsem to udělal si můžete přečíst zde.
Instalace utilitky elf2uf2
Kompilátor Zigu dělá elf soubory, ty se musí konvertovat na soubory uf2, které můžeme snadno nahrát na Raspberry Pi Pico třeba pomocí USB.
Utilita na konverzi elf → uf2 se jmenuje elf2uf2.
Je dobré si tuto utilitu nainstalovat třeba do adresáře /usr/local/bin/.
$ git clone https://github.com/rej696/elf2uf2.git
$ make build # or run your native compiler command
$ sudo cp elf2uf2 /usr/local/bin
$ elf2uf2 <input ELF file> <output UF2 file>
Instalace Micro Zigu
jirka@jirka-Precision-T3610:~/pokusy$ git clone https://github.com/ZigEmbeddedGroup/microzig/
Cloning into 'microzig'...
remote: Enumerating objects: 9504, done.
remote: Counting objects: 100% (1037/1037), done.
remote: Compressing objects: 100% (300/300), done.
remote: Total 9504 (delta 825), reused 793 (delta 719), pack-reused 8467 (from 1)
Receiving objects: 100% (9504/9504), 12.20 MiB | 6.40 MiB/s, done.
Resolving deltas: 100% (4114/4114), done.
jirka@jirka-Precision-T3610:~/pokusy$
Sestavím celý MicroZig, protože budu používat příklady pro procesor RP2040 (Pico).
$ zig build
Sestavení trvá delší dobu. Může se stát, že MicroZig nepůjde přeložit, protože na kompilátoru i na samotném projektu MicroZig se neustále pracuje. Můžete si stáhnout odsud microzig.tar.gz, který půjde přeložit kompilátorem verze 0.13.0. Mám i uložen tento projekt na svém Gitu, ale je neveřejný.
Poté skočíme do adresáře ./examples/raspberrypi/rp2040 a přeložíme si příklady autorů MicroZigu pro Raspberry Pi Pico (procesor RP2040)
jirka@jirka-Precision-T3610:~/pokusy/microzig$ date
Pá 16. srpna 2024, 22:41:05 CEST
jirka@jirka-Precision-T3610:~/pokusy/microzig$ zig version
0.13.0
jirka@jirka-Precision-T3610:~/pokusy/microzig$ zig build
jirka@jirka-Precision-T3610:~/pokusy/microzig$ cd examples/raspberrypi/rp2040
jirka@jirka-Precision-T3610:~/pokusy/microzig/examples/raspberrypi/rp2040$ zig build
jirka@jirka-Precision-T3610:~/pokusy/microzig/examples/raspberrypi/rp2040$
zig-out/firmware/jirka@jirka-Precision-T3610:~/pokusy/microzig/examples/raspberrypi/rp2040$ ls -l zig-out/firmware/*
-rwxrwxr-x 1 jirka jirka 1024008 srp 16 22:36 zig-out/firmware/pico_adc.elf
-rw-rw-r-- 1 jirka jirka 63488 srp 16 22:36 zig-out/firmware/pico_adc.uf2
-rwxrwxr-x 1 jirka jirka 944956 srp 16 22:36 zig-out/firmware/pico_blinky.elf
-rw-rw-r-- 1 jirka jirka 39936 srp 16 22:36 zig-out/firmware/pico_blinky.uf2
-rwxrwxr-x 1 jirka jirka 1013020 srp 16 22:36 zig-out/firmware/pico_flash-id.elf
-rw-rw-r-- 1 jirka jirka 46592 srp 16 22:36 zig-out/firmware/pico_flash-id.uf2
-rwxrwxr-x 1 jirka jirka 1030592 srp 16 22:36 zig-out/firmware/pico_flash-program.elf
-rw-rw-r-- 1 jirka jirka 63488 srp 16 22:36 zig-out/firmware/pico_flash-program.uf2
-rwxrwxr-x 1 jirka jirka 930240 srp 16 22:36 zig-out/firmware/pico_gpio-clk.elf
-rw-rw-r-- 1 jirka jirka 38912 srp 16 22:36 zig-out/firmware/pico_gpio-clk.uf2
-rwxrwxr-x 1 jirka jirka 1043348 srp 16 22:36 zig-out/firmware/pico_i2c-bus-scan.elf
-rw-rw-r-- 1 jirka jirka 72704 srp 16 22:36 zig-out/firmware/pico_i2c-bus-scan.uf2
-rwxrwxr-x 1 jirka jirka 999244 srp 16 22:36 zig-out/firmware/pico_multicore.elf
-rw-rw-r-- 1 jirka jirka 41984 srp 16 22:36 zig-out/firmware/pico_multicore.uf2
-rwxrwxr-x 1 jirka jirka 951856 srp 16 22:36 zig-out/firmware/pico_pwm.elf
-rw-rw-r-- 1 jirka jirka 40960 srp 16 22:36 zig-out/firmware/pico_pwm.uf2
-rwxrwxr-x 1 jirka jirka 1132996 srp 16 22:36 zig-out/firmware/pico_random.elf
-rw-rw-r-- 1 jirka jirka 205312 srp 16 22:36 zig-out/firmware/pico_random.uf2
-rwxrwxr-x 1 jirka jirka 931224 srp 16 22:36 zig-out/firmware/pico_spi-master.elf
-rw-rw-r-- 1 jirka jirka 52224 srp 16 22:36 zig-out/firmware/pico_spi-master.uf2
-rwxrwxr-x 1 jirka jirka 1074716 srp 16 22:36 zig-out/firmware/pico_squarewave.elf
-rw-rw-r-- 1 jirka jirka 55296 srp 16 22:36 zig-out/firmware/pico_squarewave.uf2
-rwxrwxr-x 1 jirka jirka 1007344 srp 16 22:36 zig-out/firmware/pico_uart.elf
-rw-rw-r-- 1 jirka jirka 52224 srp 16 22:36 zig-out/firmware/pico_uart.uf2
-rwxrwxr-x 1 jirka jirka 1291080 srp 16 22:36 zig-out/firmware/pico_usb-cdc.elf
-rw-rw-r-- 1 jirka jirka 133632 srp 16 22:36 zig-out/firmware/pico_usb-cdc.uf2
-rwxrwxr-x 1 jirka jirka 1220436 srp 16 22:36 zig-out/firmware/pico_usb-hid.elf
-rw-rw-r-- 1 jirka jirka 128512 srp 16 22:36 zig-out/firmware/pico_usb-hid.uf2
-rwxrwxr-x 1 jirka jirka 1080580 srp 16 22:36 zig-out/firmware/pico_ws2812.elf
-rw-rw-r-- 1 jirka jirka 57344 srp 16 22:36 zig-out/firmware/pico_ws2812.uf2
-rwxrwxr-x 1 jirka jirka 1087056 srp 16 22:36 zig-out/firmware/rp2040-matrix_tiles.elf
-rw-rw-r-- 1 jirka jirka 64000 srp 16 22:36 zig-out/firmware/rp2040-matrix_tiles.uf2
jirka@jirka-Precision-T3610:~/pokusy/microzig/examples/raspberrypi/rp2040$
Máme všechno hotovo a můžeme začít programovat v Zigu. Základem pro nás budou příklady z adresáře ./src/
jirka@jirka-Precision-T3610:~/pokusy/microzig/examples/raspberrypi/rp2040$ ls -l src/*
-rw-rw-r-- 1 jirka jirka 1043 srp 16 18:52 src/adc.zig (1)
-rw-rw-r-- 1 jirka jirka 524 srp 16 18:52 src/blinky_core1.zig (2)
-rw-rw-r-- 1 jirka jirka 394 srp 16 18:52 src/blinky.zig (3)
-rw-rw-r-- 1 jirka jirka 1047 srp 16 18:52 src/flash_id.zig (4)
-rw-rw-r-- 1 jirka jirka 2333 srp 16 18:52 src/flash_program.zig (5)
-rw-rw-r-- 1 jirka jirka 402 srp 16 18:52 src/gpio_clk.zig (6)
-rw-rw-r-- 1 jirka jirka 1268 srp 16 18:52 src/i2c_bus_scan.zig (7)
-rw-rw-r-- 1 jirka jirka 767 srp 16 18:52 src/pwm.zig (8)
-rw-rw-r-- 1 jirka jirka 1792 srp 16 18:52 src/random.zig (9)
-rw-rw-r-- 1 jirka jirka 1405 srp 16 18:52 src/spi_master.zig (10)
-rw-rw-r-- 1 jirka jirka 3089 srp 16 18:52 src/squarewave.zig (11)
-rw-rw-r-- 1 jirka jirka 4147 srp 16 18:52 src/tiles.zig (12)
-rw-rw-r-- 1 jirka jirka 1084 srp 16 18:52 src/uart.zig (13)
-rw-rw-r-- 1 jirka jirka 3312 srp 16 18:52 src/usb_cdc.zig (14)
-rw-rw-r-- 1 jirka jirka 3145 srp 16 18:52 src/usb_hid.zig (15)
-rw-rw-r-- 1 jirka jirka 2984 srp 16 18:52 src/ws2812.zig (16)
jirka@jirka-Precision-T3610:~/pokusy/microzig/examples/raspberrypi/rp2040$
| 1 | Příklad pro použití ADC |
| 2 | Příklad blikání vestavěnou LEDkou s pomocí druhého jádra procesoru RP2040 |
| 3 | První příklad pro nováčky, blikání vestavěnou LEDkou. |
| 4 | Nevím |
| 5 | Nevím |
| 6 | Hodiny pomocí GPIO |
| 7 | Skenování I2C sběrnice (užitečná věc) |
| 8 | Použití pulsně šířkové modulace PWM |
| 9 | Použití vestavěného generátoru náhodných čísel |
| 10 | Použití SPI sběrnice |
| 11 | Vytváření obdélníkové vlny pomocí PIO |
| 12 | Neopixel matice 5x5 LED |
| 13 | Použití rozhraní UART |
| 14 | Použití UART sériové linky (třeba přes USB) |
| 15 | USB HID zařízení |
| 16 | Příklad na použití Neopixel LED pásku — tím se budeme zabývat. |
Zig build system
Genialitou jazyka Zig je to, že sestavovací program, který nezbytně potřebujeme abychom zkompilovali a sestavili náš projekt, je napsán přímo v jazyce Zig.
Nemusíme se učit nijaké make nebo cmake a bojovat s nastavením Makefile a CMakeFile.txt.
Podíváme se na soubor build.zig, který slouží pro sestavení příkladů.
const std = @import("std");
const MicroZig = @import("microzig/build");
const rp2040 = @import("microzig/bsp/raspberrypi/rp2040");
const available_examples = [_]Example{ (1)
// RaspberryPi Boards:
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_adc", .file = "src/adc.zig" },
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_blinky", .file = "src/blinky.zig" },
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_flash-program", .file = "src/flash_program.zig" },
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_flash-id", .file = "src/flash_id.zig" },
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_gpio-clk", .file = "src/gpio_clk.zig" },
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_i2c-bus-scan", .file = "src/i2c_bus_scan.zig" },
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_pwm", .file = "src/pwm.zig" },
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_random", .file = "src/random.zig" },
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_spi-master", .file = "src/spi_master.zig" },
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_squarewave", .file = "src/squarewave.zig" },
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_uart", .file = "src/uart.zig" }, (2)
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_usb-hid", .file = "src/usb_hid.zig" },
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_usb-cdc", .file = "src/usb_cdc.zig" },
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_ws2812", .file = "src/ws2812.zig" },
.{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_multicore" , .file = "src/blinky_core1.zig" },
// WaveShare Boards:
.{ .target = rp2040.boards.waveshare.rp2040_matrix, .name = "rp2040-matrix_tiles", .file = "src/tiles.zig" },
// .{ .target = "board:waveshare/rp2040_eth", .name = "rp2040-eth" },
// .{ .target = "board:waveshare/rp2040_plus_4m", .name = "rp2040-plus-4m" },
// .{ .target = "board:waveshare/rp2040_plus_16m", .name = "rp2040-plus-16m" },
};
pub fn build(b: *std.Build) void { (3)
const mz = MicroZig.init(b, .{});
const optimize = b.standardOptimizeOption(.{});
for (available_examples) |example| {
// `add_firmware` basically works like addExecutable, but takes a
// `microzig.Target` for target instead of a `std.zig.CrossTarget`.
//
// The target will convey all necessary information on the chip,
// cpu and potentially the board as well.
const firmware = mz.add_firmware(b, .{
.name = example.name,
.target = example.target,
.optimize = optimize,
.root_source_file = b.path(example.file),
});
// `install_firmware()` is the MicroZig pendant to `Build.installArtifact()`
// and allows installing the firmware as a typical firmware file.
//
// This will also install into `$prefix/firmware` instead of `$prefix/bin`.
mz.install_firmware(b, firmware, .{});
// For debugging, we also always install the firmware as an ELF file
mz.install_firmware(b, firmware, .{ .format = .elf });
}
}
const Example = struct { (4)
target: MicroZig.Target, (5)
name: []const u8, (6)
file: []const u8, (7)
};
| 1 | Seznam předpřipravených příkladů (pole struktur Example) |
| 2 | Náš program, který bude komunikovat přes UART |
| 3 | Funkce pro sestavení programu (spustí se příkazem zig build) |
| 4 | Definice struktury Example |
| 5 | Pro jakou desku děláme program. |
| 6 | Jak se program bude jmenovat. |
| 7 | Kde se nachází zdrojový soubor s programem. |
Pokus si budeme chtít hrát, tak si do pole available_examples přidáme další program a bude to fungovat.
Příkazem zig build se nám přeloží všechny příklady.
Rozbor programu uart.zig
Program dělá dvě věci:
-
bliká vestavěnou LED (na GPIO25), 0.5 sekundy svítí a 0.5 sekundy nesvítí.
-
vytvoří standardní log připojený k UARTU a vypisuje nám logovací informace (tyto informace snadno zachytíme třeba pomocí
sudo minicom -b 115200 -c on -D /dev/ttyUSB0)
Na zdrojový kód se podíváme po kouskách.
const std = @import("std"); (1)
const microzig = @import("microzig"); (2)
| 1 | Importujeme standardní knihovnu Zigu. |
| 2 | Importujeme knihovnu MicroZig |
const rp2040 = microzig.hal; (1)
const time = rp2040.time; (2)
const gpio = rp2040.gpio; (3)
const clocks = rp2040.clocks; (4)
const led = gpio.num(25); (5)
const uart = rp2040.uart.num(0); (6)
const baud_rate = 115200; (7)
const uart_tx_pin = gpio.num(0); (8)
const uart_rx_pin = gpio.num(1); (9)
| 1 | Zkrácení microzig.hal |
| 2 | Budeme používat funkce s měřením času |
| 3 | Budeme používat GP0 |
| 4 | Budeme používat hodiny |
| 5 | GPIO25 je na Picu vestavěná LED. |
| 6 | Budeme používat UART 0 |
| 7 | Rychlost UARTU bude 115200 baudů. |
| 8 | TX pin UARTu bude GP0 |
| 9 | RX pin UARTU bude GP1 |
pub fn panic(message: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {
std.log.err("panic: {s}", .{message});
@breakpoint();
while (true) {}
}
Funkci panic() potřebujeme na ukončení programu při vzniku chyby.
pub const microzig_options = .{
.log_level = .debug,
.logFn = rp2040.uart.log,
};
pub fn main() !void { (1)
led.set_function(.sio); (2)
led.set_direction(.out); (3)
led.put(1); (4)
uart.apply(.{ (5)
.baud_rate = baud_rate,
.tx_pin = uart_tx_pin,
.rx_pin = uart_rx_pin,
.clock_config = rp2040.clock_config,
});
rp2040.uart.init_logger(uart); (6)
var i: u32 = 0;
while (true) : (i += 1) { (7)
led.put(1);
std.log.info("what {}", .{i}); (8)
time.sleep_ms(500); (9)
led.put(0); (10)
time.sleep_ms(500);
}
}
| 1 | Funkce main() nevrací nic užitečného, ale může vrátit chybu. Proto má !void. |
| 2 | Nastavíme funkci interní LED |
| 3 | Nastavíme pin led (GP25) jako výstupní |
| 4 | Pošleme na pin led vysokou úroveň (rozsvítíme LED). |
| 5 | Aplikujeme přednastavené hodnoty do UARTu. |
| 6 | Spojíme standardní logování s UARTem a nastartujeme. |
| 7 | Nekonečná smyčka |
| 8 | Zalogujeme hlášení |
| 9 | Počkáme 500 ms. |
| 10 | Zhasneme LED, pokčáme 500 ms a smyčka se opakuje. |

Časová značka v hranatých závorkách je počet sekund od spuštění Pica.