Skip to content

Commit 37136e3

Browse files
authored
Overhaul i2c_device interfaces, implement, update some drivers (#645)
1 parent 204d2a6 commit 37136e3

28 files changed

Lines changed: 992 additions & 518 deletions

File tree

drivers/base/I2C_Device.zig

Lines changed: 34 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub const Address = enum(u7) {
4444
/// Reserved addresses are ones that match `0b0000XXX` or `0b1111XXX`.
4545
///
4646
/// See more here: https://www.i2c-bus.org/addressing/
47-
pub fn is_reserved(addr: Address) Address.Error!void {
47+
pub fn check_reserved(addr: Address) Address.Error!void {
4848
const value: u7 = @intFromEnum(addr);
4949

5050
switch (value) {
@@ -77,58 +77,52 @@ ptr: *anyopaque,
7777
/// Virtual table for the datagram device functions.
7878
vtable: *const VTable,
7979

80-
pub fn set_address(dev: I2C_Device, addr: Address, allow_reserved: Allow_Reserved) InterfaceError!void {
81-
const set_address_fn = dev.vtable.set_address_fn orelse return InterfaceError.Unsupported;
82-
return set_address_fn(dev.ptr, addr, allow_reserved);
83-
}
84-
8580
/// Writes a single `datagram` to the device.
86-
pub fn write(dev: I2C_Device, datagram: []const u8) InterfaceError!void {
87-
return try dev.writev(&.{datagram});
81+
pub fn write(dev: I2C_Device, address: Address, datagram: []const u8) InterfaceError!void {
82+
return try dev.writev(address, &.{datagram});
8883
}
8984

9085
/// Writes multiple `datagrams` to the device.
91-
pub fn writev(dev: I2C_Device, datagrams: []const []const u8) InterfaceError!void {
86+
pub fn writev(dev: I2C_Device, address: Address, datagrams: []const []const u8) InterfaceError!void {
9287
const writev_fn = dev.vtable.writev_fn orelse return InterfaceError.Unsupported;
93-
return writev_fn(dev.ptr, datagrams);
88+
return writev_fn(dev.ptr, address, datagrams);
9489
}
9590

9691
/// Writes then reads a single `datagram` to the device.
97-
pub fn write_then_read(dev: I2C_Device, src: []const u8, dst: []u8) InterfaceError!void {
98-
return try dev.writev_then_readv(&.{src}, &.{dst});
92+
pub fn write_then_read(dev: I2C_Device, address: Address, src: []const u8, dst: []u8) InterfaceError!void {
93+
return try dev.writev_then_readv(address, &.{src}, &.{dst});
9994
}
10095

10196
/// Writes a slice of datagrams to the device, then reads back into another slice of datagrams
10297
pub fn writev_then_readv(
10398
dev: I2C_Device,
99+
address: Address,
104100
write_chunks: []const []const u8,
105101
read_chunks: []const []u8,
106102
) InterfaceError!void {
107103
const writev_then_readv_fn = dev.vtable.writev_then_readv_fn orelse return InterfaceError.Unsupported;
108-
return writev_then_readv_fn(dev.ptr, write_chunks, read_chunks);
104+
return writev_then_readv_fn(dev.ptr, address, write_chunks, read_chunks);
109105
}
110106

111107
/// Reads a single `datagram` from the device.
112108
/// Function returns the number of bytes written in `datagram`.
113-
pub fn read(dev: I2C_Device, datagram: []u8) InterfaceError!usize {
114-
return try dev.readv(&.{datagram});
109+
pub fn read(dev: I2C_Device, address: Address, datagram: []u8) InterfaceError!usize {
110+
return try dev.readv(address, &.{datagram});
115111
}
116112

117113
/// Reads multiple `datagrams` from the device.
118114
/// Function returns the number of bytes written in `datagrams`.
119-
pub fn readv(dev: I2C_Device, datagrams: []const []u8) InterfaceError!usize {
115+
pub fn readv(dev: I2C_Device, address: Address, datagrams: []const []u8) InterfaceError!usize {
120116
const readv_fn = dev.vtable.readv_fn orelse return InterfaceError.Unsupported;
121-
return readv_fn(dev.ptr, datagrams);
117+
return readv_fn(dev.ptr, address, datagrams);
122118
}
123119

124-
pub const Allow_Reserved = enum { allow_general, allow_reserved, dont_allow_reserved };
125-
126120
pub const VTable = struct {
127-
set_address_fn: ?*const fn (*anyopaque, Address, Allow_Reserved) InterfaceError!void,
128-
writev_fn: ?*const fn (*anyopaque, datagrams: []const []const u8) InterfaceError!void,
129-
readv_fn: ?*const fn (*anyopaque, datagrams: []const []u8) InterfaceError!usize,
121+
writev_fn: ?*const fn (*anyopaque, Address, datagrams: []const []const u8) InterfaceError!void,
122+
readv_fn: ?*const fn (*anyopaque, Address, datagrams: []const []u8) InterfaceError!usize,
130123
writev_then_readv_fn: ?*const fn (
131124
*anyopaque,
125+
Address,
132126
write_chunks: []const []const u8,
133127
read_chunks: []const []u8,
134128
) InterfaceError!void = null,
@@ -191,18 +185,9 @@ pub const Test_Device = struct {
191185
};
192186
}
193187

194-
fn set_address(ctx: *anyopaque, addr: Address, allow_reserved: Allow_Reserved) InterfaceError!void {
195-
const td: *Test_Device = @ptrCast(@alignCast(ctx));
196-
if (allow_reserved == .dont_allow_reserved)
197-
addr.is_reserved() catch return Error.IllegalAddress
198-
else if (allow_reserved == .allow_general)
199-
addr.is_reserved() catch |err| if (err != Address.Error.GeneralCall)
200-
return Error.IllegalAddress;
201-
td.addr = addr;
202-
}
203-
204-
fn writev(ctx: *anyopaque, datagrams: []const []const u8) InterfaceError!void {
188+
fn writev(ctx: *anyopaque, address: Address, datagrams: []const []const u8) InterfaceError!void {
205189
const td: *Test_Device = @ptrCast(@alignCast(ctx));
190+
_ = address;
206191

207192
if (!td.write_enabled) {
208193
return error.Unsupported;
@@ -231,8 +216,9 @@ pub const Test_Device = struct {
231216
td.packets.append(dg) catch return error.UnknownAbort;
232217
}
233218

234-
fn readv(ctx: *anyopaque, datagrams: []const []u8) InterfaceError!usize {
219+
fn readv(ctx: *anyopaque, address: Address, datagrams: []const []u8) InterfaceError!usize {
235220
const td: *Test_Device = @ptrCast(@alignCast(ctx));
221+
_ = address;
236222

237223
const inputs = td.input_sequence orelse return error.Unsupported;
238224

@@ -271,20 +257,19 @@ pub const Test_Device = struct {
271257
return written;
272258
}
273259

274-
fn writev_then_readv(ctx: *anyopaque, write_chunks: []const []const u8, read_chunks: []const []u8) InterfaceError!void {
275-
try Test_Device.writev(ctx, write_chunks);
276-
_ = try Test_Device.readv(ctx, read_chunks);
260+
fn writev_then_readv(ctx: *anyopaque, address: Address, write_chunks: []const []const u8, read_chunks: []const []u8) InterfaceError!void {
261+
try Test_Device.writev(ctx, address, write_chunks);
262+
_ = try Test_Device.readv(ctx, address, read_chunks);
277263
}
278264

279265
const vtable = I2C_Device.VTable{
280-
.set_address_fn = Test_Device.set_address,
281266
.writev_fn = Test_Device.writev,
282267
.readv_fn = Test_Device.readv,
283268
.writev_then_readv_fn = Test_Device.writev_then_readv,
284269
};
285270
};
286271

287-
test "Address.is_reserved returns correct error types" {
272+
test "Address.check_reserved returns correct error types" {
288273
const TestCase = struct {
289274
address: u7,
290275
expected_error: ?Address.Error,
@@ -310,15 +295,15 @@ test "Address.is_reserved returns correct error types" {
310295
for (test_cases) |test_case| {
311296
const addr: Address = @enumFromInt(test_case.address);
312297
if (test_case.expected_error) |expected_error| {
313-
std.testing.expectError(expected_error, addr.is_reserved()) catch |err| {
298+
std.testing.expectError(expected_error, addr.check_reserved()) catch |err| {
314299
std.debug.print(
315300
"Failed test case: {s} (address 0x{X:0>2})\n",
316301
.{ test_case.description, test_case.address },
317302
);
318303
return err;
319304
};
320305
} else {
321-
addr.is_reserved() catch |err| {
306+
addr.check_reserved() catch |err| {
322307
std.debug.print(
323308
"Expected valid address but got error for: {s} (address 0x{X:0>2})\n",
324309
.{ test_case.description, test_case.address },
@@ -339,34 +324,35 @@ test Test_Device {
339324

340325
var buffer: [16]u8 = undefined;
341326

342-
const dd = td.i2c_device();
327+
const id = td.i2c_device();
328+
const addr: Address = @enumFromInt(0);
343329

344330
{
345331
// The first input datagram will be received here:
346-
const recv_len = try dd.read(&buffer);
332+
const recv_len = try id.read(addr, &buffer);
347333
try std.testing.expectEqualStrings("first datagram", buffer[0..recv_len]);
348334
}
349335

350336
{
351337
// The second one here:
352-
const recv_len = try dd.read(&buffer);
338+
const recv_len = try id.read(addr, &buffer);
353339
try std.testing.expectEqualStrings("second datagram", buffer[0..recv_len]);
354340
}
355341

356342
{
357343
// The third datagram will overrun our buffer, so we're receiving an error
358344
// which tells us that the whole buffer is filled, but there's data that
359345
// was discarded:
360-
try std.testing.expectError(error.BufferOverrun, dd.read(&buffer));
346+
try std.testing.expectError(error.BufferOverrun, id.read(addr, &buffer));
361347
try std.testing.expectEqualStrings("the very third d", &buffer);
362348
}
363349

364350
// As there's no fourth datagram available, the test device will yield
365351
// an `IoError` for when no datagrams are available anymore:
366-
try std.testing.expectError(error.NoData, dd.read(&buffer));
352+
try std.testing.expectError(error.NoData, id.read(addr, &buffer));
367353

368-
try dd.write("Hello, World!");
369-
try dd.writev(&.{ "See", " you ", "soon!" });
354+
try id.write(addr, "Hello, World!");
355+
try id.writev(addr, &.{ "See", " you ", "soon!" });
370356

371357
// Check if we had exactly these datagrams:
372358
try td.expect_sent(&.{

drivers/io_expander/pcf8574.zig

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,22 @@ const SetBiasError = Digital_IO.SetBiasError;
77
const SetDirError = Digital_IO.SetDirError;
88
const WriteError = Digital_IO.WriteError;
99

10+
const I2CAddress = mdf.base.I2C_Device.Address;
11+
1012
pub const PCF8574_Config = struct {
11-
Datagram_Device: type = mdf.base.Datagram_Device,
13+
I2C_Device: type = mdf.base.I2C_Device,
1214
};
1315

1416
pub fn PCF8574(comptime config: PCF8574_Config) type {
1517
return struct {
1618
const Self = @This();
17-
interface: config.Datagram_Device,
19+
interface: config.I2C_Device,
20+
address: mdf.base.I2C_Device.Address,
1821
pins: u8 = 0,
1922
pin_arr: [8]u8 = undefined,
2023

2124
inline fn update(self: *const Self) !void {
22-
try self.interface.write(&[1]u8{self.pins});
25+
try self.interface.write(self.address, &[1]u8{self.pins});
2326
}
2427

2528
pub fn put(self: *Self, pin: u3, st: State) !void {
@@ -42,12 +45,12 @@ pub fn PCF8574(comptime config: PCF8574_Config) type {
4245
pub fn read(self: *Self, pin: u3) !State {
4346
const mask: u8 = @as(u8, 1) << pin;
4447
var pkg: [1]u8 = .{mask | self.pins};
45-
const read_value = try self.interface.read(&pkg); //keep pullup enable until next call
48+
const read_value = try self.interface.read(self.address, &pkg); //keep pullup enable until next call
4649
return if (read_value & mask != 0) State.high else State.low;
4750
}
4851

49-
pub fn init(datagram: config.Datagram_Device) Self {
50-
var obj = Self{ .interface = datagram };
52+
pub fn init(datagram: config.I2C_Device, address: I2CAddress) Self {
53+
var obj = Self{ .interface = datagram, .address = address };
5154
for (0..obj.pin_arr.len) |index| {
5255
obj.pin_arr[index] = @intCast(index);
5356
}

drivers/sensor/ICM-20948.zig

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
//! Example usage:
88
//! ```zig
99
//! var sensor = try ICM_20948.init(
10-
//! i2c_device.datagram_device(),
10+
//! i2c_device.i2c_device(),
11+
//! @enumFromInt(0x69),
1112
//! clock.clock_device(),
1213
//! .{
1314
//! .accel_range = .gs4,
@@ -62,7 +63,8 @@ pub const ICM_20948 = struct {
6263
const MAG_READ_DELAY_US = 10_000;
6364
const MAG_RESET_DELAY_US = 100_000;
6465

65-
dev: mdf.base.Datagram_Device,
66+
dev: mdf.base.I2C_Device,
67+
address: mdf.base.I2C_Device.Address,
6668
clock: mdf.base.Clock_Device,
6769
config: Config,
6870
current_bank: ?u2 = null,
@@ -320,8 +322,13 @@ pub const ICM_20948 = struct {
320322
i2c_slv0_en: u1 = 0,
321323
};
322324

323-
pub fn init(dev: mdf.base.Datagram_Device, clock: mdf.base.Clock_Device, config: Config) Error!Self {
324-
return Self{ .dev = dev, .clock = clock, .config = config };
325+
pub fn init(
326+
dev: mdf.base.I2C_Device,
327+
address: mdf.base.I2C_Device.Address,
328+
clock: mdf.base.Clock_Device,
329+
config: Config,
330+
) Error!Self {
331+
return Self{ .dev = dev, .address = address, .clock = clock, .config = config };
325332
}
326333

327334
pub fn setup(self: *Self) Error!void {
@@ -399,7 +406,7 @@ pub const ICM_20948 = struct {
399406

400407
try self.set_bank(reg.bank());
401408

402-
self.dev.writev_then_readv(&.{&.{reg.value()}}, &.{buf}) catch |err| {
409+
self.dev.writev_then_readv(self.address, &.{&.{reg.value()}}, &.{buf}) catch |err| {
403410
log.err("Failed to read register 0x{X:02}: {}", .{ reg.value(), err });
404411
return Error.ReadError;
405412
};
@@ -416,7 +423,7 @@ pub const ICM_20948 = struct {
416423
pub inline fn write_byte(self: *Self, reg: Self.Register, val: u8) Error!void {
417424
try self.set_bank(reg.bank());
418425

419-
self.dev.write(&.{ reg.value(), val }) catch |err| {
426+
self.dev.write(self.address, &.{ reg.value(), val }) catch |err| {
420427
log.err("Failed to write register 0x{X:02} = 0x{X:02}: {}", .{ reg.value(), val, err });
421428
return Error.WriteError;
422429
};
@@ -452,7 +459,7 @@ pub const ICM_20948 = struct {
452459
if (bank == self.current_bank) return;
453460

454461
// Bits 5:4 - directly write to bank0 register without recursion
455-
self.dev.write(&.{ 0x7F, @as(u8, bank) << 4 }) catch |err| {
462+
self.dev.write(self.address, &.{ 0x7F, @as(u8, bank) << 4 }) catch |err| {
456463
log.err("Failed to switch to bank {}: {}", .{ bank, err });
457464
return Error.BankSwitchFailed;
458465
};
@@ -889,17 +896,16 @@ pub const ICM_20948 = struct {
889896
};
890897

891898
// Testing
892-
const TestDatagramDevice = mdf.base.Datagram_Device.Test_Device;
899+
const TestI2CDevice = mdf.base.I2C_Device.Test_Device;
893900
const TestTime = mdf.base.Clock_Device.Test_Device;
894901

895902
test "set_bank" {
896903
var ttd = TestTime.init();
897-
var d = TestDatagramDevice.init(null, true);
904+
var d = TestI2CDevice.init(null, true);
898905
defer d.deinit();
899-
const dd = d.datagram_device();
900-
try dd.connect();
906+
const id = d.i2c_device();
901907

902-
var dev = try ICM_20948.init(dd, ttd.clock_device(), .{});
908+
var dev = try ICM_20948.init(id, @enumFromInt(0), ttd.clock_device(), .{});
903909

904910
// Nothing is sent in init
905911
try d.expect_sent(&.{});
@@ -923,12 +929,11 @@ test "set_bank" {
923929

924930
test "reset" {
925931
var ttd = TestTime.init();
926-
var d = TestDatagramDevice.init(null, true);
932+
var d = TestI2CDevice.init(null, true);
927933
defer d.deinit();
928-
const dd = d.datagram_device();
929-
try dd.connect();
934+
const id = d.i2c_device();
930935

931-
var dev = try ICM_20948.init(dd, ttd.clock_device(), .{});
936+
var dev = try ICM_20948.init(id, @enumFromInt(0), ttd.clock_device(), .{});
932937

933938
// Nothing is sent in init
934939
try d.expect_sent(&.{});
@@ -942,12 +947,11 @@ test "reset" {
942947

943948
test "read_byte" {
944949
var ttd = TestTime.init();
945-
var d = TestDatagramDevice.init(null, true);
950+
var d = TestI2CDevice.init(null, true);
946951
defer d.deinit();
947-
const dd = d.datagram_device();
948-
try dd.connect();
952+
const id = d.i2c_device();
949953

950-
var dev = try ICM_20948.init(dd, ttd.clock_device(), .{});
954+
var dev = try ICM_20948.init(id, @enumFromInt(0), ttd.clock_device(), .{});
951955

952956
// Read byte will set the bank
953957
// -- Put in the values it expects to read
@@ -960,12 +964,11 @@ test "read_byte" {
960964

961965
test "error handling in setup" {
962966
var ttd = TestTime.init();
963-
var d = TestDatagramDevice.init(null, true);
967+
var d = TestI2CDevice.init(null, true);
964968
defer d.deinit();
965-
const dd = d.datagram_device();
966-
try dd.connect();
969+
const id = d.i2c_device();
967970

968-
var dev = try ICM_20948.init(dd, ttd.clock_device(), .{});
971+
var dev = try ICM_20948.init(id, @enumFromInt(0), ttd.clock_device(), .{});
969972

970973
// Test wrong WHO_AM_I response, first byte is read during reset()
971974
d.input_sequence = &.{ &.{0x00}, &.{0xFF} }; // Wrong ID after reset
@@ -975,12 +978,11 @@ test "error handling in setup" {
975978

976979
test "device responsiveness check" {
977980
var ttd = TestTime.init();
978-
var d = TestDatagramDevice.init(null, true);
981+
var d = TestI2CDevice.init(null, true);
979982
defer d.deinit();
980-
const dd = d.datagram_device();
981-
try dd.connect();
983+
const id = d.i2c_device();
982984

983-
var dev = try ICM_20948.init(dd, ttd.clock_device(), .{});
985+
var dev = try ICM_20948.init(id, @enumFromInt(0), ttd.clock_device(), .{});
984986

985987
// Test with correct WHO_AM_I
986988
d.input_sequence = &.{&.{ICM_20948.WHOAMI}};

0 commit comments

Comments
 (0)