Skip to content

Commit ce0678f

Browse files
authored
nRF5x: Add i2c hal + datagram device (#561)
1 parent 884a4fa commit ce0678f

9 files changed

Lines changed: 1198 additions & 13 deletions

File tree

examples/nordic/nrf5x/build.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ pub fn build(b: *std.Build) void {
2222
.{ .target = nrf52840_mdk, .name = "blinky", .file = "src/blinky.zig" },
2323
.{ .target = pca10040, .name = "uart", .file = "src/uart.zig" },
2424
.{ .target = nrf52840_mdk, .name = "uart", .file = "src/uart.zig" },
25+
.{ .target = pca10040, .name = "i2c_bus_scan", .file = "src/i2c_bus_scan.zig" },
26+
.{ .target = nrf52840_mdk, .name = "i2c_bus_scan", .file = "src/i2c_bus_scan.zig" },
27+
.{ .target = pca10040, .name = "i2c_temp", .file = "src/i2c_temp.zig" },
28+
.{ .target = nrf52840_mdk, .name = "i2c_temp", .file = "src/i2c_temp.zig" },
2529
};
2630

2731
for (available_examples) |example| {
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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+
const i2cdma = nrf.i2cdma;
10+
11+
const uart = nrf.uart.num(0);
12+
const i2c0 = i2c.num(0);
13+
const i2c0dma = i2cdma.num(0);
14+
15+
const sleep_ms = nrf.time.sleep_ms;
16+
17+
pub const microzig_options = microzig.Options{
18+
.log_level = .debug,
19+
.logFn = nrf.uart.logFn,
20+
};
21+
22+
pub fn main() !void {
23+
board.init();
24+
25+
uart.apply(.{
26+
.tx_pin = board.uart_tx,
27+
.rx_pin = board.uart_rx,
28+
.baud_rate = .@"115200",
29+
});
30+
31+
nrf.uart.init_logger(uart);
32+
33+
defer i2c0.reset();
34+
defer i2c0dma.reset();
35+
36+
// ------------------------ BUS SCAN ------------------------
37+
std.log.info("I2C bus scan", .{});
38+
39+
try i2c0.apply(.{
40+
.scl_pin = gpio.num(0, 9),
41+
.sda_pin = gpio.num(0, 10),
42+
});
43+
for (0..std.math.maxInt(u7)) |addr| {
44+
const a: i2c.Address = @enumFromInt(addr);
45+
46+
var rx_data: [1]u8 = undefined;
47+
_ = i2c0.read_blocking(a, &rx_data, null) catch |e| {
48+
if (e != i2c.TransactionError.DeviceNotPresent and
49+
e != i2c.TransactionError.TargetAddressReserved)
50+
std.log.info("Error {any}", .{e});
51+
continue;
52+
};
53+
54+
std.log.info("I2C device found at address {X}.", .{addr});
55+
}
56+
i2c0.reset();
57+
58+
// ------------------------ BUS SCAN (DMA) ------------------------
59+
std.log.info("I2C bus scan (DMA)", .{});
60+
61+
try i2c0dma.apply(.{
62+
.scl_pin = gpio.num(0, 9),
63+
.sda_pin = gpio.num(0, 10),
64+
});
65+
for (0..std.math.maxInt(u7)) |addr| {
66+
const a: i2cdma.Address = @enumFromInt(addr);
67+
68+
var rx_data: [1]u8 = undefined;
69+
_ = i2c0dma.read_blocking(a, &rx_data, null) catch |e| {
70+
if (e != i2c.TransactionError.DeviceNotPresent and
71+
e != i2c.TransactionError.TargetAddressReserved)
72+
std.log.info("Error {any}", .{e});
73+
continue;
74+
};
75+
76+
std.log.info("I2C device found at address {X}.", .{addr});
77+
}
78+
i2c0dma.reset();
79+
80+
std.log.info("Done!", .{});
81+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
const std = @import("std");
2+
const microzig = @import("microzig");
3+
const time = microzig.drivers.time;
4+
const board = microzig.board;
5+
6+
const nrf = microzig.hal;
7+
const TMP117 = microzig.drivers.sensor.TMP117;
8+
9+
const i2c = nrf.i2c;
10+
const i2cdma = nrf.i2cdma;
11+
const gpio = nrf.gpio;
12+
const peripherals = microzig.chip.peripherals;
13+
const I2C_Device = nrf.drivers.I2C_Device;
14+
15+
const uart = nrf.uart.num(0);
16+
const i2c0 = i2c.num(0);
17+
const i2c0dma = i2cdma.num(0);
18+
19+
const sleep_ms = nrf.time.sleep_ms;
20+
21+
pub const microzig_options = microzig.Options{
22+
.log_level = .debug,
23+
.logFn = nrf.uart.logFn,
24+
};
25+
26+
pub fn main() !void {
27+
board.init();
28+
29+
uart.apply(.{
30+
.tx_pin = board.uart_tx,
31+
.rx_pin = board.uart_rx,
32+
.baud_rate = .@"115200",
33+
});
34+
35+
nrf.uart.init_logger(uart);
36+
defer i2c0.reset();
37+
defer i2c0dma.reset();
38+
39+
// ------------------------ TMP117 w/driver ------------------------
40+
std.log.info("I2C temp sensor (using driver)", .{});
41+
try i2c0.apply(.{
42+
.scl_pin = gpio.num(0, 9),
43+
.sda_pin = gpio.num(0, 10),
44+
});
45+
46+
// Create i2c datagram device
47+
var i2c_device = I2C_Device.init(i2c0, @enumFromInt(0x48));
48+
// Pass i2c device to driver to create sensor instance
49+
const temp_sensor = try TMP117.init(i2c_device.datagram_device());
50+
51+
const temp_sensor_address = 0x48;
52+
var temp_buf: [2]u8 = undefined;
53+
const cbuf = [3]u8{ 1, 0, 0 };
54+
55+
std.log.info("Writing config", .{});
56+
try temp_sensor.write_configuration(.{});
57+
58+
std.log.info("Reading device ID config", .{});
59+
const id = try temp_sensor.read_device_id();
60+
std.log.info("ID: {any}", .{id});
61+
62+
for (0..5) |_| {
63+
const temp = try temp_sensor.read_temperature();
64+
std.log.info("Temp: {d:0.2}°C", .{temp});
65+
sleep_ms(500);
66+
}
67+
i2c0.reset();
68+
69+
// ------------------------ TMP117 raw ------------------------
70+
std.log.info("I2C temp sensor (raw writes)", .{});
71+
72+
try i2c0.apply(.{
73+
.scl_pin = gpio.num(0, 9),
74+
.sda_pin = gpio.num(0, 10),
75+
});
76+
77+
std.log.info("Writing config", .{});
78+
try i2c0.writev_blocking(@enumFromInt(temp_sensor_address), &.{&cbuf}, null);
79+
80+
std.log.info("Reading temp", .{});
81+
for (0..5) |_| {
82+
const write_buf = [_]u8{0}; // Read temperature register
83+
try i2c0.writev_then_readv_blocking(@enumFromInt(temp_sensor_address), &.{&write_buf}, &.{&temp_buf}, null);
84+
const temp_u = std.mem.readInt(u16, &temp_buf, .big);
85+
const temp = @as(f32, @floatFromInt(temp_u)) * 7.8125E-3;
86+
std.log.info("Temp: {d:0.2}°C", .{temp});
87+
sleep_ms(500);
88+
}
89+
i2c0.reset();
90+
91+
// ------------------------ TMP117 raw (DMA) ------------------------
92+
std.log.info("I2C temp sensor (raw writes w/DMA)", .{});
93+
94+
try i2c0dma.apply(.{
95+
.scl_pin = gpio.num(0, 9),
96+
.sda_pin = gpio.num(0, 10),
97+
});
98+
99+
std.log.info("Writing config", .{});
100+
try i2c0dma.write_blocking(@enumFromInt(temp_sensor_address), &cbuf, null);
101+
102+
std.log.info("Reading temp", .{});
103+
for (0..5) |_| {
104+
const write_buf = [_]u8{0}; // Read temperature register
105+
try i2c0dma.write_then_read_blocking(@enumFromInt(temp_sensor_address), &write_buf, &temp_buf, null);
106+
const temp_u = std.mem.readInt(u16, &temp_buf, .big);
107+
const temp = @as(f32, @floatFromInt(temp_u)) * 7.8125E-3;
108+
std.log.info("Temp: {d:0.2}°C", .{temp});
109+
sleep_ms(500);
110+
}
111+
i2c0dma.reset();
112+
}

port/nordic/nrf5x/src/hal.zig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ const microzig = @import("microzig");
22

33
pub const compatibility = @import("hal/compatibility.zig");
44
pub const gpio = @import("hal/gpio.zig");
5+
pub const i2c = @import("hal/i2c.zig");
6+
pub const i2cdma = @import("hal/i2cdma.zig");
57
pub const time = @import("hal/time.zig");
68
pub const uart = @import("hal/uart.zig");
9+
pub const drivers = @import("hal/drivers.zig");
710
// TODO: adc, timers, pwm, rng, rtc, spi, interrupts, i2c, wdt, wifi, nfc, bt, zigbee
811

912
pub fn init() void {
@@ -12,6 +15,10 @@ pub fn init() void {
1215

1316
test "hal tests" {
1417
_ = gpio;
18+
_ = i2c;
19+
_ = i2cdma;
1520
_ = time;
1621
_ = uart;
22+
23+
_ = drivers;
1724
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//!
2+
//! This file implements driver abstractions based on HAL devices.
3+
//!
4+
5+
const std = @import("std");
6+
const microzig = @import("microzig");
7+
const hal = @import("../hal.zig");
8+
9+
const drivers = microzig.drivers.base;
10+
const time = microzig.drivers.time;
11+
12+
const Datagram_Device = drivers.Datagram_Device;
13+
const Stream_Device = drivers.Stream_Device;
14+
const Digital_IO = drivers.Digital_IO;
15+
const Clock_Device = drivers.Clock_Device;
16+
17+
///
18+
/// A datagram device attached to an I²C bus.
19+
///
20+
pub const I2C_Device = struct {
21+
pub const ConnectError = Datagram_Device.ConnectError;
22+
pub const WriteError = Datagram_Device.WriteError;
23+
pub const ReadError = Datagram_Device.ReadError;
24+
25+
/// Selects I²C bus should be used.
26+
bus: hal.i2c.I2C,
27+
28+
/// The address of our I²C device.
29+
address: hal.i2c.Address,
30+
31+
pub fn init(bus: hal.i2c.I2C, address: hal.i2c.Address) I2C_Device {
32+
return .{
33+
.bus = bus,
34+
.address = address,
35+
};
36+
}
37+
38+
pub fn datagram_device(dev: *I2C_Device) Datagram_Device {
39+
return .{
40+
.ptr = dev,
41+
.vtable = &vtable,
42+
};
43+
}
44+
45+
pub fn connect(dev: I2C_Device) ConnectError!void {
46+
_ = dev;
47+
}
48+
49+
pub fn disconnect(dev: I2C_Device) void {
50+
_ = dev;
51+
}
52+
53+
pub fn write(dev: I2C_Device, datagram: []const u8) !void {
54+
try dev.bus.write_blocking(dev.address, datagram, null);
55+
}
56+
57+
pub fn writev(dev: I2C_Device, datagrams: []const []const u8) !void {
58+
try dev.bus.writev_blocking(dev.address, datagrams, null);
59+
}
60+
61+
pub fn read(dev: I2C_Device, datagram: []u8) !usize {
62+
try dev.bus.read_blocking(dev.address, datagram, null);
63+
return datagram.len;
64+
}
65+
66+
pub fn readv(dev: I2C_Device, datagrams: []const []u8) !usize {
67+
try dev.bus.readv_blocking(dev.address, datagrams, null);
68+
return microzig.utilities.Slice_Vector([]u8).init(datagrams).size();
69+
}
70+
71+
const vtable = Datagram_Device.VTable{
72+
.connect_fn = null,
73+
.disconnect_fn = null,
74+
.writev_fn = writev_fn,
75+
.readv_fn = readv_fn,
76+
};
77+
78+
fn writev_fn(dd: *anyopaque, chunks: []const []const u8) WriteError!void {
79+
const dev: *I2C_Device = @ptrCast(@alignCast(dd));
80+
return dev.writev(chunks) catch |err| switch (err) {
81+
error.DeviceNotPresent,
82+
error.NoAcknowledge,
83+
error.TargetAddressReserved,
84+
=> return error.Unsupported,
85+
86+
error.UnknownAbort,
87+
error.Overrun,
88+
=> return error.IoError,
89+
90+
error.Timeout => return error.Timeout,
91+
error.NoData => {},
92+
};
93+
}
94+
95+
fn readv_fn(dd: *anyopaque, chunks: []const []u8) ReadError!usize {
96+
const dev: *I2C_Device = @ptrCast(@alignCast(dd));
97+
return dev.readv(chunks) catch |err| switch (err) {
98+
error.DeviceNotPresent,
99+
error.NoAcknowledge,
100+
error.TargetAddressReserved,
101+
=> return error.Unsupported,
102+
103+
error.UnknownAbort,
104+
error.Overrun,
105+
=> return error.IoError,
106+
107+
error.Timeout => return error.Timeout,
108+
error.NoData => return 0,
109+
};
110+
}
111+
};

0 commit comments

Comments
 (0)