Skip to content

by965738071/zig-periphery

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

zig-periphery

Pure Zig implementation of Linux peripheral I/O libraries — no C dependencies, no FFI, no @cImport.

A ground-up Zig rewrite of c-periphery, providing idiomatic Zig APIs for common Linux embedded I/O interfaces: GPIO, I2C, SPI, serial (UART), PWM, LED, and memory-mapped I/O.

Features

  • Pure Zig — compiled entirely with Zig, no C headers, no system libraries required
  • Cross-compilation ready — build for any aarch64-linux-gnu, x86_64-linux-gnu, etc.
  • Idiomatic Zig API — error unions instead of return codes, comptime-checked constants
  • Zig 0.17+ — uses std.os.linux raw syscalls, no @cImport
  • Zero heap allocation (optional) — all handles are stack-allocatable structs

Modules

Module Description Interface
gpio General Purpose I/O GPIO v2 chardev (/dev/gpiochip*)
i2c Inter-Integrated Circuit ioctl (/dev/i2c-*)
spi Serial Peripheral Interface ioctl (/dev/spidev*)
serial UART serial port termios + poll
pwm Pulse Width Modulation sysfs (/sys/class/pwm/)
led LED control sysfs (/sys/class/leds/)
mmio Memory-mapped I/O mmap (/dev/mem)
version Library version info comptime constants

Requirements

  • Zig ≥ 0.17.0-dev (tested with 0.17.0-dev.1426+58a94eaae)
  • Linux (targets linux-gnu)

Installation

Add as a Zig build dependency:

zig fetch --save git+https://github.com/by965738071/zig-periphery.git

Then in your build.zig:

const c_periphery = b.dependency("zig_periphery", .{
    .target = target,
    .optimize = optimize,
});

// Add the module you need
const gpio_mod = c_periphery.module("gpio");
exe.root_module.addImport("gpio", gpio_mod);

Usage

GPIO

const std = @import("std");
const gpio = @import("gpio");

pub fn main() !void {
    var g = gpio.Gpio.init();
    defer g.close() catch {};

    // Open GPIO line 23 as input on chip 0
    try g.open("/dev/gpiochip0", 23, .in);

    // Read value
    const value = try g.read();
    std.debug.print("GPIO 23: {}\n", .{value});

    // Configure as output and write
    try g.setDirection(.out_high);
    try g.write(true);
}

I2C

const i2c = @import("i2c");

pub fn main() !void {
    var handle = try i2c.I2c.init(std.heap.page_allocator);
    defer handle.deinit() catch {};

    try handle.open("/dev/i2c-1");

    // Write then read (I2C EEPROM example)
    var tx_buf = [_]u8{ 0x00, 0x00 }; // register address
    var rx_buf: [16]u8 = undefined;

    var msgs = [_]i2c.I2cMsg{
        .{ .addr = 0x50, .flags = 0, .len = 2, .buf = &tx_buf },
        .{ .addr = 0x50, .flags = i2c.I2C_M_RD, .len = 16, .buf = &rx_buf },
    };

    try handle.transfer(&msgs);
}

SPI

const spi = @import("spi");

pub fn main() !void {
    var s = spi.Spi.init();
    defer s.close() catch {};

    // Open SPI bus 0, device 0, mode 0, 1MHz
    try s.open("/dev/spidev0.0", 0, 1_000_000);

    // Transfer data
    var tx_buf = [_]u8{ 0xAA, 0xBB, 0xCC, 0xDD };
    var rx_buf: [4]u8 = undefined;
    try s.transfer(&tx_buf, &rx_buf, 4);
}

Serial (UART)

const serial = @import("serial");

pub fn main() !void {
    var s = serial.Serial.init();
    defer s.close_port() catch {};

    // Open at 115200 baud
    try s.open_port("/dev/ttyUSB0", 115200);

    // Write data
    _ = try s.write_port("Hello UART!\n");

    // Read with 100ms timeout
    var buf: [128]u8 = undefined;
    const n = try s.read_port(&buf, 100);
    std.debug.print("Received {d} bytes\n", .{n});
}

PWM

const pwm = @import("pwm");

pub fn main() !void {
    var p = pwm.Pwm.init();
    defer p.close() catch {};

    // Open PWM chip 0, channel 0
    try p.open(0, 0);

    // Configure 1kHz, 50% duty cycle
    try p.setPeriodNs(1_000_000);   // 1ms period
    try p.setDutyCycleNs(500_000);  // 500us duty
    try p.enable();
}

LED

const led = @import("led");

pub fn main() !void {
    var l = try led.Led.init(std.heap.page_allocator);
    defer l.deinit(std.heap.page_allocator);

    try l.open("mmc0");
    try l.write(true);   // Turn on
    try l.write(false);  // Turn off
}

MMIO

const mmio = @import("mmio");

pub fn main() !void {
    var m = mmio.Mmio.init();
    defer m.close() catch {};

    // Map a register at physical address 0x40000000, size 4096
    try m.open(0x40000000, 4096);

    // Read/write 32-bit register
    const val = try m.read32(0x00);
    try m.write32(0x04, 0xDEAD_BEEF);
}

Error Handling

All modules return Zig error unions. Each handle carries an errmsg_buf and c_errno for detailed error info:

const g = gpio.Gpio.init();
g.open("/dev/gpiochip0", 999, .in) catch |err| {
    std.debug.print("Error: {s}\n", .{g.errmsg()});
    std.debug.print("errno: {d}\n", .{g.c_errno});
    return err;
};

Building

# Build for aarch64-linux (cross-compile)
zig build -Dtarget=aarch64-linux-gnu --release=small

# Build for native
zig build --release=fast

# Run all tests
zig build test

Project Structure

zig-periphery/
├── build.zig          # Build configuration
├── build.zig.zon      # Package manifest
├── src/
│   ├── gpio.zig       # GPIO (chardev v2)
│   ├── i2c.zig        # I2C (ioctl)
│   ├── spi.zig        # SPI (ioctl)
│   ├── serial.zig     # Serial/UART (termios)
│   ├── pwm.zig        # PWM (sysfs)
│   ├── led.zig        # LED (sysfs)
│   ├── mmio.zig       # Memory-mapped I/O (mmap)
│   └── version.zig    # Version info
├── c-periphery/       # Reference C source (not used at build time)
└── LICENSE

How It Differs from c-periphery

c-periphery (C) zig-periphery
Language C Pure Zig
Dependencies libc, kernel headers None
Error handling Integer return codes + errno Zig error unions
Memory malloc/free Zig allocator (optional)
Build system CMake/Make Zig build system
Cross-compile Manual toolchain setup zig build -Dtarget=...
GPIO backend chardev v2 + sysfs fallback chardev v2

Testing

Tests cover pure logic (struct layouts, ioctl constants, conversions) and can run without hardware:

zig build test

Hardware-dependent tests (loopback, interactive) are not included — see c-periphery/tests/ for C reference tests.

License

MIT

Acknowledgements

  • c-periphery by Ivan Googolev — the original C implementation this project is based on
  • Zig — the programming language

About

Pure Zig Linux peripheral I/O library — GPIO, I2C, SPI, UART, PWM, LED, MMIO. Zero C dependencies.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages