Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to Zig 0.11.0, Microzig 'gen 2' build, STM32 HAL #31

Draft
wants to merge 10 commits into
base: zig-0.11.x
Choose a base branch
from
8 changes: 4 additions & 4 deletions .github/workflows/ci-workflow.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build with zig master
name: Build with zig 0.11.x

on:
push:
Expand All @@ -15,6 +15,6 @@ jobs:
submodules: recursive
- uses: goto-bus-stop/setup-zig@v2
with:
version: 0.11.0-dev.538+bf316e550
- run: zig build -Drelease-small=true --verbose
- run: zig fmt --check build.zig *.zig
version: 0.11.0
- run: zig build -Drelease=true --verbose
- run: zig fmt --check .
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[![Build with Zig master](https://github.com/marnix/zig-stm32f3discovery-play/workflows/Build%20with%20zig%20master/badge.svg?branch=zig-master)](https://github.com/marnix/zig-stm32f3discovery-play/actions?query=branch%3Azig-master)
[![Build with Zig 0.11.x](https://github.com/marnix/zig-stm32f3discovery-play/workflows/Build%20with%20zig%200.11.x/badge.svg?branch=zig-0.11.x)](https://github.com/marnix/zig-stm32f3discovery-play/actions?query=branch%3Azig-0.11.x)

_This branch assumes you use Zig 0.11.0-dev.538+bf316e550 (the last post-0.10 version that supports `async`)._
_This branch assumes you use Zig 0.11.x._

# Playing around with pure-Zig STM32F3DISCOVERY

Expand All @@ -19,7 +19,7 @@ Since then, this project has been updated to build on top of
To build the ELF file just run:

```
zig build -Drelease-small=true
zig build -Drelease=true
```

## Flashing
Expand All @@ -31,7 +31,7 @@ repos, the build system will try to use the `st-flash` program.
The command to flash the board is:

```
zig build flash -Drelease-small=true
zig build flash -Drelease=true
```

After flashing the board, as you tilt it,
Expand Down
46 changes: 20 additions & 26 deletions build.zig
Original file line number Diff line number Diff line change
@@ -1,40 +1,34 @@
const Builder = @import("std").build.Builder;
const builtin = @import("builtin");
const std = @import("std");
const stm32 = @import("stm32");

const microzig = @import("libs/microzig/src/main.zig");
fn root() []const u8 {
return comptime (std.fs.path.dirname(@src().file) orelse ".");
}
const build_root = root();

pub fn build(b: *Builder) !void {
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
pub fn build(b: *std.Build) !void {
const microzig = @import("microzig").init(b, "microzig");

var elf = microzig.addEmbeddedExecutable(
b,
"zig-stm32f3discovery-play.elf",
"src/main.zig",
microzig.Backing{ .board = microzig.boards.stm32f3discovery },
microzig.BuildOptions{},
);
elf.setBuildMode(mode);
elf.inner.strip = false; // we always want debug symbols, stripping brings us no benefit on embedded
elf.install();
const firmware = microzig.addFirmware(b, .{
.name = "zig-stm32f3discovery-play",
.target = stm32.boards.stm32f3discovery,
.optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseSmall }),
.source_file = .{ .path = "src/main.zig" },
});
std.debug.assert(firmware.mz == microzig); // ...just a sanity check

const bin = elf.installRaw("zig-stm32f3discovery-play.bin", .{});
const bin_step = b.step("bin", "Generate binary file to be flashed");
bin_step.dependOn(&bin.step);
const install_step = microzig.addInstallFirmware(b, firmware, .{ .format = .bin });
b.getInstallStep().dependOn(&install_step.step);

const flash_cmd = b.addSystemCommand(&[_][]const u8{
"st-flash",
"--reset",
//"--reset",
"--connect-under-reset",
"write",
b.getInstallPath(bin.dest_dir, bin.dest_filename),
b.getInstallPath(install_step.dir, install_step.dest_rel_path),
"0x8000000",
});
flash_cmd.step.dependOn(&bin.step);
flash_cmd.step.dependOn(&install_step.step);
const flash_step = b.step("flash", "Flash and run the app on your STM32F3Discovery");
flash_step.dependOn(&flash_cmd.step);

b.default_step.dependOn(&elf.inner.step);
b.installArtifact(elf.inner);
}
14 changes: 14 additions & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.{
.name = "zig-stm32f3discovery-play",
.version = "0.1.0",
.dependencies = .{
.microzig = .{
.url = "https://github.com/ZigEmbeddedGroup/microzig/archive/fdb0d185588b847cd8efc98f6d8675fec481bfdc.tar.gz",
.hash = "12205afa78531c7ef1598ccaaaa2c5548966b58a3c0972ca8224d1f3d7b89a1b0651",
},
.stm32 = .{
.url = "https://github.com/ZigEmbeddedGroup/stmicro-stm32/archive/d7e9abf9c55bb0cc1809b48736c9256295d48763.tar.gz",
.hash = "1220cf19944dc3590501d64aa727de8a1a58cba88484a621be8fffab232df45ea648",
},
},
}
1 change: 0 additions & 1 deletion libs/microzig
Submodule microzig deleted from 28692f
108 changes: 63 additions & 45 deletions src/main.zig
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
const std = @import("std");
const microzig = @import("microzig");
const regs = microzig.chip.registers;

const abs = std.math.absCast;
const regs = microzig.chip.peripherals;
const uart = microzig.core.experimental.uart;
const spi = microzig.core.experimental.spi;

pub const TIM6Timer = struct {
pub fn init() @This() {
Expand Down Expand Up @@ -96,7 +97,7 @@ const Leds = struct {
}

pub fn update(self: *@This()) void {
for (self._leds) |n, nr| {
for (self._leds, 0..) |n, nr| {
if (n > 0) {
switch (nr) {
0 => regs.GPIOE.BSRR.modify(.{ .BS8 = 1 }),
Expand Down Expand Up @@ -132,28 +133,44 @@ const Leds = struct {

const System = struct {
leds: *Leds,
timer: *TIM6Timer,
debug_writer: microzig.Uart(1, .{}).Writer = undefined,
timer: TIM6Timer,
debug_writer: ?uart.Uart(1, .{}).Writer = null,

pub fn sleep(self: *@This(), ms: u16) void {
self.timer.delayMs(ms);
}

pub fn debug(self: *@This(), comptime format: []const u8, args: anytype) !void {
try self.debug_writer.print(format, args);
if (self.debug_writer) |w| try w.print(format, args);
}
};

pub fn main() !void {
// Enable both CP10 and CP11 FPU co-processors on Cortex-M4.
// (See PM0214 Programming Manual (Revision 10),
// section 4.6.6 "Enabling the FPU".)
regs.FPU_CPACR.CPACR.modify(.{ .CP = 0b11_11 }); // CP11 and CP10
microzig.cpu.dsb();
microzig.cpu.isb();

const timer = TIM6Timer.init();
var leds = Leds.init();
const uart1 = try microzig.Uart(1, .{}).init(.{ .baud_rate = 460800 });
const uart1 = try uart.Uart(1, .{}).init(.{ .baud_rate = 460800 });
var system = System{
.leds = &leds,
.timer = timer,
.debug_writer = uart1.writer(),
};
try system.debug("\r\nMAIN START\r\n", .{});
try system.debug("tau={}.\r\n", .{comptime std.math.tau});
try system.debug("0={}.\r\n", .{comptime std.math.sin(std.math.tau)});
var x: f64 = std.math.tau;
try system.debug("tau={}.\r\n", .{x});
x = std.math.sin(x);
try system.debug("0={}.\r\n", .{x});
// try system.debug("1={}.\r\n", .{comptime std.math.cos(std.math.tau)});
// try system.debug("0={}.\r\n", .{comptime std.math.tan(std.math.tau)});
// try system.debug("+/-oo={}.\r\n", .{comptime std.math.tan(std.math.tau / 4.0)});

try slowLed(&system);
// try heavyLed(&system);
Expand All @@ -172,70 +189,69 @@ pub fn main() !void {
/// it sends the response back on the same SPI bus MOSI line
/// that the SPI bus used to send the 'read register 0x0F' request.
///
/// So reading a non-bidi response sent by a bidi device,
/// or reading a bidi response sent by a non-bidi device,
/// will both result in garbage that is very unlikely to be exactly 0xD3.
fn probeGyroBidiMode(gyro: anytype) !u1 {
var who_am_is: [2]u8 = undefined;
var spi1_bidi_mode = regs.SPI1.CR1.read().BIDIMODE;
for ([_]u1{ 0, 1 }) |_| {
who_am_is[spi1_bidi_mode] = try gyro.readRegister(0x0F);
spi1_bidi_mode = 1 - spi1_bidi_mode;
regs.SPI1.CR1.modify(.{ .BIDIMODE = spi1_bidi_mode });
}
// TODO: check that exactly one of who_am_is is 0xD3.
return @boolToInt(who_am_is[1] == 0xD3);
/// So reading a non-bidi response from the MISO line, sent by a bidi device on the MOSI line,
/// or reading a bidi response from the MOSI line, sent by a non-bidi device on the MISO line,,
/// will both read garbage that is very unlikely to be exactly 0xD3.
fn probeGyroBidiMode(spi_bus: anytype, gyro_cs_pin: anytype) !bool {
const gyro_bidi_true = spi_bus.device(gyro_cs_pin, .{ .bidi = true });
const who_am_i_bidi_true = try gyro_bidi_true.read_register(0x0F);

const gyro_bidi_false = spi_bus.device(gyro_cs_pin, .{ .bidi = false });
const who_am_i_bidi_false = try gyro_bidi_false.read_register(0x0F);
_ = who_am_i_bidi_false; // TODO: check that exactly one of who_am_i's is 0xD3.

return (who_am_i_bidi_true == 0xD3);
}

fn slowLed(system: *System) !void {
const leds = system.leds;

const spi1 = try microzig.SpiBus(1).init(.{});
var gyro = spi1.device(microzig.chip.parsePin("PE3"), .{});

try system.debug("--- switch SPI1 to the gyro's BIDI mode:\r\n", .{});
const gyro_bidi_mode = try probeGyroBidiMode(gyro);
try system.debug("setting SPI1 BIDIMODE={d} <= gyro SIM={d} <= gyro responses\r\n", .{ gyro_bidi_mode, gyro_bidi_mode });
regs.SPI1.CR1.modify(.{ .BIDIMODE = gyro_bidi_mode });
const spi1 = try spi.SpiBus(1).init(.{});
const gyro_cs_pin = microzig.hal.parse_pin("PE3");
const gyro_bidi_mode = try probeGyroBidiMode(spi1, gyro_cs_pin);
try system.debug("using SPI1 BIDIMODE={} <= gyro SIM={} <= gyro responses\r\n", .{ gyro_bidi_mode, gyro_bidi_mode });
if (gyro_bidi_mode != true) return;
const gyro = spi1.device(microzig.hal.parse_pin("PE3"), .{ .bidi = true });

var gyro_id = try gyro.readRegister(0x0F); // WHO_AM_I
var gyro_id = try gyro.read_register(0x0F); // WHO_AM_I
try system.debug("WHO_AM_I of gyroscope is {X:2}, should be D3.\r\n", .{gyro_id});

if (gyro_id != 0xD3) return;

// HERE WE MAKE THE ARBITRARY CHOICE TO TALK TO THE GYRO DEVICE IN BIDI MODE
const use_bidi_mode = true;

try system.debug("--- set SPI1 and gyro to BIDI mode? {}\r\n", .{use_bidi_mode});
try system.debug("--- set gyro and then SPI1 to BIDI mode? {}\r\n", .{use_bidi_mode});
{
const desired_mode = @boolToInt(use_bidi_mode);
const desired_mode = @intFromBool(use_bidi_mode);

try system.debug("setting gyro SIM={d}\r\n", .{desired_mode});
try gyro.writeRegister(0x23, (0x00 & 0xFE) | desired_mode);
try gyro.write_register(0x23, (0x00 & 0xFE) | desired_mode);
// ...and now we must start to talk to the gyro in the correct mode
try system.debug("setting SPI1 BIDIMODE={d}\r\n", .{desired_mode});
regs.SPI1.CR1.modify(.{ .BIDIMODE = desired_mode });

try system.debug("BIDIMODE = {d}\r\n", .{regs.SPI1.CR1.read().BIDIMODE});
try system.debug("gyro SIM mode = {d}\r\n", .{(try gyro.readRegister(0x23)) & 0b1});
try system.debug("gyro SIM mode = {d}\r\n", .{(try gyro.read_register(0x23)) & 0b1});
}

gyro_id = try gyro.readRegister(0x0F); // WHO_AM_I
gyro_id = try gyro.read_register(0x0F); // WHO_AM_I
try system.debug("WHO_AM_I of gyroscope is {X:2}, should be D3.\r\n", .{gyro_id});

if (gyro_id != 0xD3) return;

// set CTRL_REG1 (0x20) to 100 Hz with cutoff 12.5 (.DR==0b00, .BW=0b00),
// power on (.PD==0b1),
// Z/Y/X all enabled (.Zen==0, .Yen==.Xen==1)
try gyro.writeRegister(0x20, 0b00_00_1_011);
try gyro.write_register(0x20, 0b00_00_1_011);

var current_led: ?u3 = null; // led initially off

while (true) {
// get gyroscope X / Y data:
// read OUT_* registers: 4 registers starting with OUT_X_L (0x28)
var out: [4]u8 = undefined;
try gyro.readRegisters(0x28, &out);
try gyro.read_registers(0x28, &out);
const x: i16 = @as(i16, out[1]) << 8 | out[0];
const y: i16 = @as(i16, out[3]) << 8 | out[2];
if (false) {
Expand Down Expand Up @@ -285,22 +301,22 @@ fn slowLed(system: *System) !void {
fn heavyLed(system: *System) !void {
const leds = system.leds;

const i2c1 = try microzig.I2CController(1, .{}).init(.{ .target_speed = 100_000 });
const i2c1 = try microzig.core.experimental.i2c.I2CController(1, .{}).init(.{ .target_speed = 100_000 });
// STM32F3DISCOVERY board LSM303AGR accelerometer (I2C address 0b0011001)
const xl = i2c1.device(0b0011001);

// set CTRL_REG1 (0x20) to 100 Hz (.ODR==0b0101),
// normal power mode (.LPen==1),
// Y/X both enabled (.Zen==0, .Yen==.Xen==1)
try xl.writeRegister(0x20, 0b01010011);
try xl.write_register(0x20, 0b01010011);

var current_led: ?u3 = null; // led initially off

while (true) {
// get accelerometer X / Y data:
// read OUT_* registers: 4 registers starting with OUT_X_L (0x28)
var out: [4]u8 = undefined;
try xl.readRegisters(0x28, &out);
try xl.read_registers(0x28, &out);

const x: i16 = @as(i16, out[1]) << 8 | out[0];
const y: i16 = @as(i16, out[3]) << 8 | out[2];
Expand Down Expand Up @@ -340,7 +356,8 @@ fn heavyLed(system: *System) !void {

fn randomCompass(system: *System) void {
const leds = system.leds;
var rng = std.rand.DefaultPrng.init(42).random();
var pqr = std.rand.DefaultPrng.init(42);
var rng = pqr.random();

const D = 24 + 1 * 16;

Expand Down Expand Up @@ -369,7 +386,7 @@ fn randomCompass(system: *System) void {
}

fn distance(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
return std.math.min(b -% a, a -% b);
return @min(b -% a, a -% b);
}

fn twoBumpingLeds(system: *System) !void {
Expand All @@ -380,24 +397,25 @@ fn twoBumpingLeds(system: *System) !void {
leds.add(j);
leds.add(k);

const i2c1 = try microzig.I2CController(1, .{}).init(.{ .target_speed = 100_000 });
const i2c1 = try microzig.core.experimental.i2c.I2CController(1, .{}).init(.{ .target_speed = 100_000 });
// STM32F3DISCOVERY board LSM303AGR accelerometer (I2C address 0b0011001)
const xl = i2c1.device(0b0011001);
// read device ID (0x33 == 51) from "register" WHO_AM_I_A (0x0F)
const accelerometer_device_id = xl.readRegister(0x0F);
const accelerometer_device_id = xl.read_register(0x0F);
try system.debug("I2C1 device 0b0011001 device ID: {any} == 51 == 0x33\r\n", .{accelerometer_device_id});
{
// set CTRL_REG1 (0x20) to 100 Hz (.ODR==0b0101),
// normal power mode (.LPen==1),
// Z/Y/X all enabled (.Zen==.Yen==.Xen==1)
var wt = try xl.startTransfer(.write);
var wt = try xl.start_transfer(.write);
{
defer wt.stop() catch {};
try wt.writer().writeAll(&.{ 0x20, 0b01010111 });
}
}

var rng = std.rand.DefaultPrng.init(42).random();
var pqr = std.rand.DefaultPrng.init(42);
var rng = pqr.random();
while (true) {
if (rng.boolean()) {
leds.remove(j);
Expand All @@ -419,7 +437,7 @@ fn twoBumpingLeds(system: *System) !void {
// get accelerometer X / Y / Z data:
// read OUT_* registers: 6 registers starting with OUT_X_L (0x28)
var out: [6]u8 = undefined;
try xl.readRegisters(0x28, &out);
try xl.read_registers(0x28, &out);
try system.debug("I2C1 device 0b0011001 output: {any}\r\n", .{out});

const ms = rng.uintLessThan(u16, 400);
Expand Down
Loading