Skip to content

Commit 4703128

Browse files
authored
Drivers: AS5600 position sensor (#713)
1 parent 9f6b705 commit 4703128

File tree

4 files changed

+238
-0
lines changed

4 files changed

+238
-0
lines changed

drivers/framework.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub const led = struct {
4343
};
4444

4545
pub const sensor = struct {
46+
pub const AS5600 = @import("sensor/AS5600.zig").AS5600;
4647
pub const ICM_20948 = @import("sensor/ICM-20948.zig").ICM_20948;
4748
pub const MLX90640 = @import("sensor/MLX90640.zig").MLX90640;
4849
pub const MPU_6050 = @import("sensor/MPU-6050.zig").MPU_6050;

drivers/sensor/AS5600.zig

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
//!
2+
//! Generic driver for the ams AS5600 Position Sensor.
3+
//!
4+
//! Datasheet:
5+
//! * https://www.mouser.com/datasheet/2/588/AS5600_DS000365_5-00-1877365.pdf
6+
//!
7+
const std = @import("std");
8+
const mdf = @import("../framework.zig");
9+
10+
pub const AS5600 = struct {
11+
const Self = @This();
12+
const address: mdf.base.I2C_Device.Address = @enumFromInt(0x36);
13+
dev: mdf.base.I2C_Device,
14+
15+
const register = enum(u8) {
16+
ZMCO = 0x00,
17+
ZPOS = 0x01,
18+
MPOS = 0x03,
19+
MANG = 0x05,
20+
CONF = 0x07,
21+
RAW_ANGLE = 0x0C,
22+
ANGLE = 0x0E,
23+
STATUS = 0x0B,
24+
AGC = 0x1A,
25+
MAGNITUDE = 0x1B,
26+
BURN = 0xFF,
27+
};
28+
29+
pub const Configuration = packed struct(u14) {
30+
PM: enum(u2) {
31+
nom = 0b00,
32+
lpm1 = 0b01,
33+
lpm2 = 0b10,
34+
lpm3 = 0b11,
35+
} = .nom,
36+
HYST: enum(u2) {
37+
off = 0b00,
38+
@"1lsb" = 0b01,
39+
@"2lsbs" = 0b10,
40+
@"3lsbs" = 0b11,
41+
} = .off,
42+
OUTS: enum(u2) {
43+
full_analog = 0b00,
44+
reduced_analog = 0b01,
45+
digital_pwm = 0b10,
46+
} = .full_analog,
47+
PWMF: enum(u2) {
48+
@"115Hz" = 0b00,
49+
@"230Hz" = 0b01,
50+
@"460Hz" = 0b10,
51+
@"920Hz" = 0b11,
52+
} = .@"115Hz",
53+
SF: enum(u2) {
54+
@"2x" = 0b11,
55+
@"4x" = 0b10,
56+
@"8x" = 0b01,
57+
@"16x" = 0b00,
58+
} = .@"16x",
59+
FTH: enum(u3) {
60+
slow = 0b000,
61+
@"6lsbs" = 0b001,
62+
@"7lsbs" = 0b010,
63+
@"9lsbs" = 0b011,
64+
@"10lsbs" = 0b111,
65+
@"18lsbs" = 0b100,
66+
@"21lsbs" = 0b101,
67+
@"24lsbs" = 0b110,
68+
} = .slow,
69+
WD: enum(u1) { off = 0, on = 1 } = .off,
70+
};
71+
72+
pub const Status = packed struct(u8) {
73+
reserved0: u3 = 0,
74+
// Magnet too strong
75+
MH: u1 = 0,
76+
// Magnet too weak
77+
ML: u1 = 0,
78+
// Magnet detected
79+
MD: u1 = 0,
80+
reserved6: u2 = 0,
81+
};
82+
83+
pub fn init(dev: mdf.base.I2C_Device) Self {
84+
return Self{ .dev = dev };
85+
}
86+
87+
pub fn read1_raw(self: *const Self, reg: Self.register) !u8 {
88+
try self.dev.write(Self.address, &[_]u8{@intFromEnum(reg)});
89+
var buf: [1]u8 = undefined;
90+
const size = try self.dev.read(Self.address, &buf);
91+
if (size != 1) return error.ReadError;
92+
return buf[0];
93+
}
94+
95+
pub fn read2_raw(self: *const Self, reg: Self.register) !u16 {
96+
try self.dev.write(Self.address, &[_]u8{@intFromEnum(reg)});
97+
var buf: [2]u8 = undefined;
98+
const size = try self.dev.read(Self.address, &buf);
99+
if (size != 2) return error.ReadError;
100+
return std.mem.readInt(u16, &buf, .big);
101+
}
102+
103+
pub fn write_raw(self: *const Self, reg: Self.register, v: u16) !void {
104+
return self.dev.write(
105+
Self.address,
106+
&([1]u8{@intFromEnum(reg)} ++ @as([2]u8, @bitCast(std.mem.nativeToBig(u16, v)))),
107+
);
108+
}
109+
110+
pub fn read_zero_position(self: *const Self) !u16 {
111+
const zpos = self.read2_raw(register.ZPOS);
112+
return zpos & 0xFFF;
113+
}
114+
115+
pub fn write_zero_position(self: *const Self, position: u12) !void {
116+
// Read-modify-write because the high 4 bits might store config
117+
var zpos = try self.read2_raw(register.ZPOS);
118+
zpos = (zpos & 0xF000) | position;
119+
try self.write_raw(register.ZPOS, zpos);
120+
}
121+
122+
pub fn read_max_position(self: *const Self) !u16 {
123+
const mpos = self.read2_raw(register.MPOS);
124+
return mpos & 0xFFF;
125+
}
126+
127+
pub fn write_max_position(self: *const Self, max: u12) !void {
128+
// Read-modify-write because the high 4 bits might store config
129+
var mpos = try self.read2_raw(register.MPOS);
130+
mpos = (mpos & 0xF000) | max;
131+
try self.write_raw(register.MPOS, mpos);
132+
}
133+
134+
pub fn read_max_angle(self: *const Self) !u16 {
135+
const mang = self.read2_raw(register.MANG);
136+
return mang & 0xFFF;
137+
}
138+
139+
pub fn write_max_angle(self: *const Self, max: u12) !void {
140+
// Read-modify-write because the high 4 bits might store config
141+
var mang = try self.read2_raw(register.MANG);
142+
mang = (mang & 0xF000) | max;
143+
try self.write_raw(register.MANG, mang);
144+
}
145+
146+
pub fn read_configuration(self: *const Self) !u16 {
147+
const configuration = self.read2_raw(register.CONF);
148+
return @bitCast(configuration);
149+
}
150+
151+
pub fn write_configuration(self: *const Self, config: Configuration) !void {
152+
return self.write_raw(Self.register.CONF, @bitCast(config));
153+
}
154+
155+
pub fn read_raw_angle(self: *const Self) !f32 {
156+
const angle = (try self.read2_raw(register.ANGLE)) & 0xFFF;
157+
return @as(f32, @floatFromInt(angle)) * 360 / 4096;
158+
}
159+
160+
pub fn read_angle(self: *const Self) !f32 {
161+
const angle = (try self.read2_raw(register.ANGLE)) & 0xFFF;
162+
return @as(f32, @floatFromInt(angle)) * 360 / 4096;
163+
}
164+
165+
pub fn read_status(self: *const Self) !Status {
166+
const s = try self.read1_raw(register.STATUS);
167+
return @bitCast(s & (0b111 << 3));
168+
}
169+
170+
pub fn read_magnitude(self: *const Self) !u16 {
171+
return self.read2_raw(register.MAGNITUDE);
172+
}
173+
174+
// TODO: Write burn functions. Scary.
175+
};

examples/nordic/nrf5x/build.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub fn build(b: *std.Build) void {
2828
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_i2c_bus_scan", .file = "src/i2c_bus_scan.zig" },
2929
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_i2c_accel", .file = "src/i2c_accel.zig" },
3030
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_i2c_hall_effect", .file = "src/i2c_hall_effect.zig" },
31+
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_i2c_position_sensor", .file = "src/i2c_position_sensor.zig" },
3132
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_i2c_temp", .file = "src/i2c_temp.zig" },
3233
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_rtt_log", .file = "src/rtt_log.zig" },
3334
.{ .target = nrf52840_mdk, .name = "nrf52840_mdk_semihosting", .file = "src/semihosting.zig" },
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const std = @import("std");
2+
const microzig = @import("microzig");
3+
const time = microzig.drivers.time;
4+
const board = microzig.board;
5+
const nrf = microzig.hal;
6+
7+
const gpio = nrf.gpio;
8+
const i2c = nrf.i2c;
9+
10+
const I2C_Device = nrf.drivers.I2C_Device;
11+
const i2c0 = i2c.num(0);
12+
13+
const uart = nrf.uart.num(0);
14+
15+
const AS5600 = microzig.drivers.sensor.AS5600;
16+
17+
const sleep_ms = nrf.time.sleep_ms;
18+
19+
pub const microzig_options = microzig.Options{
20+
.log_level = .debug,
21+
.logFn = nrf.uart.log,
22+
};
23+
24+
pub fn main() !void {
25+
board.init();
26+
27+
uart.apply(.{
28+
.tx_pin = board.uart_tx,
29+
.rx_pin = board.uart_rx,
30+
});
31+
32+
nrf.uart.init_logger(uart);
33+
34+
// Configure i2c peripheral
35+
try i2c0.apply(.{
36+
.scl_pin = gpio.num(0, 9),
37+
.sda_pin = gpio.num(0, 10),
38+
});
39+
defer i2c0.reset();
40+
41+
// Create I2C_Device
42+
std.log.info("Creating device", .{});
43+
var i2c_device = I2C_Device.init(i2c0, null);
44+
// Pass i2c and clock device to driver to create sensor instance
45+
std.log.info("Creating driver instance", .{});
46+
var dev = AS5600.init(i2c_device.i2c_device());
47+
48+
while (true) {
49+
const status = try dev.read_status();
50+
if (status.MD != 0 and status.MH == 0 and status.ML == 0) {
51+
const raw_angle = try dev.read_raw_angle();
52+
std.log.info("Raw Angle: {d:0.2}°", .{raw_angle});
53+
const angle = try dev.read_angle();
54+
std.log.info("Angle: {d:0.2}°", .{angle});
55+
const magnitude = try dev.read_magnitude();
56+
std.log.info("Magnitude: {any}", .{magnitude});
57+
}
58+
59+
sleep_ms(250);
60+
}
61+
}

0 commit comments

Comments
 (0)