Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/framework.zig
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub const led = struct {
};

pub const sensor = struct {
pub const AS5600 = @import("sensor/AS5600.zig").AS5600;
pub const ICM_20948 = @import("sensor/ICM-20948.zig").ICM_20948;
pub const MLX90640 = @import("sensor/MLX90640.zig").MLX90640;
pub const MPU_6050 = @import("sensor/MPU-6050.zig").MPU_6050;
Expand Down
175 changes: 175 additions & 0 deletions drivers/sensor/AS5600.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
//!
//! Generic driver for the ams AS5600 Position Sensor.
//!
//! Datasheet:
//! * https://www.mouser.com/datasheet/2/588/AS5600_DS000365_5-00-1877365.pdf
//!
const std = @import("std");
const mdf = @import("../framework.zig");

pub const AS5600 = struct {
const Self = @This();
const address: mdf.base.I2C_Device.Address = @enumFromInt(0x36);
dev: mdf.base.I2C_Device,

const register = enum(u8) {
ZMCO = 0x00,
ZPOS = 0x01,
MPOS = 0x03,
MANG = 0x05,
CONF = 0x07,
RAW_ANGLE = 0x0C,
ANGLE = 0x0E,
STATUS = 0x0B,
AGC = 0x1A,
MAGNITUDE = 0x1B,
BURN = 0xFF,
};

pub const Configuration = packed struct(u14) {
PM: enum(u2) {
nom = 0b00,
lpm1 = 0b01,
lpm2 = 0b10,
lpm3 = 0b11,
} = .nom,
HYST: enum(u2) {
off = 0b00,
@"1lsb" = 0b01,
@"2lsbs" = 0b10,
@"3lsbs" = 0b11,
} = .off,
OUTS: enum(u2) {
full_analog = 0b00,
reduced_analog = 0b01,
digital_pwm = 0b10,
} = .full_analog,
PWMF: enum(u2) {
@"115Hz" = 0b00,
@"230Hz" = 0b01,
@"460Hz" = 0b10,
@"920Hz" = 0b11,
} = .@"115Hz",
SF: enum(u2) {
@"2x" = 0b11,
@"4x" = 0b10,
@"8x" = 0b01,
@"16x" = 0b00,
} = .@"16x",
FTH: enum(u3) {
slow = 0b000,
@"6lsbs" = 0b001,
@"7lsbs" = 0b010,
@"9lsbs" = 0b011,
@"10lsbs" = 0b111,
@"18lsbs" = 0b100,
@"21lsbs" = 0b101,
@"24lsbs" = 0b110,
} = .slow,
WD: enum(u1) { off = 0, on = 1 } = .off,
};

pub const Status = packed struct(u8) {
reserved0: u3 = 0,
// Magnet too strong
MH: u1 = 0,
// Magnet too weak
ML: u1 = 0,
// Magnet detected
MD: u1 = 0,
reserved6: u2 = 0,
};

pub fn init(dev: mdf.base.I2C_Device) Self {
return Self{ .dev = dev };
}

pub fn read1_raw(self: *const Self, reg: Self.register) !u8 {
try self.dev.write(Self.address, &[_]u8{@intFromEnum(reg)});
var buf: [1]u8 = undefined;
const size = try self.dev.read(Self.address, &buf);
if (size != 1) return error.ReadError;
return buf[0];
}

pub fn read2_raw(self: *const Self, reg: Self.register) !u16 {
try self.dev.write(Self.address, &[_]u8{@intFromEnum(reg)});
var buf: [2]u8 = undefined;
const size = try self.dev.read(Self.address, &buf);
if (size != 2) return error.ReadError;
return std.mem.readInt(u16, &buf, .big);
}

pub fn write_raw(self: *const Self, reg: Self.register, v: u16) !void {
return self.dev.write(
Self.address,
&([1]u8{@intFromEnum(reg)} ++ @as([2]u8, @bitCast(std.mem.nativeToBig(u16, v)))),
);
}

pub fn read_zero_position(self: *const Self) !u16 {
const zpos = self.read2_raw(register.ZPOS);
return zpos & 0xFFF;
}

pub fn write_zero_position(self: *const Self, position: u12) !void {
// Read-modify-write because the high 4 bits might store config
var zpos = try self.read2_raw(register.ZPOS);
zpos = (zpos & 0xF000) | position;
try self.write_raw(register.ZPOS, zpos);
}

pub fn read_max_position(self: *const Self) !u16 {
const mpos = self.read2_raw(register.MPOS);
return mpos & 0xFFF;
}

pub fn write_max_position(self: *const Self, max: u12) !void {
// Read-modify-write because the high 4 bits might store config
var mpos = try self.read2_raw(register.MPOS);
mpos = (mpos & 0xF000) | max;
try self.write_raw(register.MPOS, mpos);
}

pub fn read_max_angle(self: *const Self) !u16 {
const mang = self.read2_raw(register.MANG);
return mang & 0xFFF;
}

pub fn write_max_angle(self: *const Self, max: u12) !void {
// Read-modify-write because the high 4 bits might store config
var mang = try self.read2_raw(register.MANG);
mang = (mang & 0xF000) | max;
try self.write_raw(register.MANG, mang);
}

pub fn read_configuration(self: *const Self) !u16 {
const configuration = self.read2_raw(register.CONF);
return @bitCast(configuration);
}

pub fn write_configuration(self: *const Self, config: Configuration) !void {
return self.write_raw(Self.register.CONF, @bitCast(config));
}

pub fn read_raw_angle(self: *const Self) !f32 {
const angle = (try self.read2_raw(register.ANGLE)) & 0xFFF;
return @as(f32, @floatFromInt(angle)) * 360 / 4096;
}

pub fn read_angle(self: *const Self) !f32 {
const angle = (try self.read2_raw(register.ANGLE)) & 0xFFF;
return @as(f32, @floatFromInt(angle)) * 360 / 4096;
}

pub fn read_status(self: *const Self) !Status {
const s = try self.read1_raw(register.STATUS);
return @bitCast(s & (0b111 << 3));
}

pub fn read_magnitude(self: *const Self) !u16 {
return self.read2_raw(register.MAGNITUDE);
}

// TODO: Write burn functions. Scary.
};
1 change: 1 addition & 0 deletions examples/nordic/nrf5x/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub fn build(b: *std.Build) void {
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_i2c_bus_scan", .file = "src/i2c_bus_scan.zig" },
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_i2c_accel", .file = "src/i2c_accel.zig" },
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_i2c_hall_effect", .file = "src/i2c_hall_effect.zig" },
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_i2c_position_sensor", .file = "src/i2c_position_sensor.zig" },
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_i2c_temp", .file = "src/i2c_temp.zig" },
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_rtt_log", .file = "src/rtt_log.zig" },
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_semihosting", .file = "src/semihosting.zig" },
Expand Down
61 changes: 61 additions & 0 deletions examples/nordic/nrf5x/src/i2c_position_sensor.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const std = @import("std");
const microzig = @import("microzig");
const time = microzig.drivers.time;
const board = microzig.board;
const nrf = microzig.hal;

const gpio = nrf.gpio;
const i2c = nrf.i2c;

const I2C_Device = nrf.drivers.I2C_Device;
const i2c0 = i2c.num(0);

const uart = nrf.uart.num(0);

const AS5600 = microzig.drivers.sensor.AS5600;

const sleep_ms = nrf.time.sleep_ms;

pub const microzig_options = microzig.Options{
.log_level = .debug,
.logFn = nrf.uart.log,
};

pub fn main() !void {
board.init();

uart.apply(.{
.tx_pin = board.uart_tx,
.rx_pin = board.uart_rx,
});

nrf.uart.init_logger(uart);

// Configure i2c peripheral
try i2c0.apply(.{
.scl_pin = gpio.num(0, 9),
.sda_pin = gpio.num(0, 10),
});
defer i2c0.reset();

// Create I2C_Device
std.log.info("Creating device", .{});
var i2c_device = I2C_Device.init(i2c0, null);
// Pass i2c and clock device to driver to create sensor instance
std.log.info("Creating driver instance", .{});
var dev = AS5600.init(i2c_device.i2c_device());

while (true) {
const status = try dev.read_status();
if (status.MD != 0 and status.MH == 0 and status.ML == 0) {
const raw_angle = try dev.read_raw_angle();
std.log.info("Raw Angle: {d:0.2}°", .{raw_angle});
const angle = try dev.read_angle();
std.log.info("Angle: {d:0.2}°", .{angle});
const magnitude = try dev.read_magnitude();
std.log.info("Magnitude: {any}", .{magnitude});
}

sleep_ms(250);
}
}
Loading