Skip to content

Commit 9733eae

Browse files
committed
[hdsc] HC32L110 i2c write
1 parent e6b44f7 commit 9733eae

File tree

4 files changed

+380
-3
lines changed

4 files changed

+380
-3
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const std = @import("std");
2+
const microzig = @import("microzig");
3+
const time = microzig.drivers.time;
4+
5+
const hal = microzig.hal;
6+
const i2c = hal.i2c;
7+
const gpio = hal.gpio;
8+
const clocks = hal.clocks;
9+
const peripherals = microzig.chip.peripherals;
10+
11+
const drivers = microzig.drivers.base;
12+
const Datagram_Device = drivers.Datagram_Device;
13+
14+
const I2C_Device_Hal = hal.drivers.I2C_Device;
15+
16+
const i2c0 = i2c.instance.num(0);
17+
const i2c_device = I2C_Device_Hal.init(i2c0, @enumFromInt(0x78));
18+
19+
const SSD1306 = microzig.drivers.display.ssd1306.SSD1306_Generic(.{
20+
.mode = .i2c,
21+
.Datagram_Device = I2C_Device_Hal,
22+
});
23+
24+
const sda_pin = gpio.num(0, 1);
25+
const scl_pin = gpio.num(0, 2);
26+
27+
const data: [128 * 8]u8 = blk: {
28+
var tmp: [128 * 8]u8 = undefined;
29+
@memset(&tmp, 0xFF);
30+
break :blk tmp;
31+
};
32+
33+
const data2: [128 * 8]u8 = blk: {
34+
var tmp: [128 * 8]u8 = undefined;
35+
@memset(&tmp, 0x00);
36+
break :blk tmp;
37+
};
38+
39+
pub fn main() !void {
40+
clocks.gate.enable(.Gpio);
41+
clocks.gate.enable(.I2c);
42+
43+
inline for (&.{ scl_pin, sda_pin }) |pin| {
44+
pin.set_direction(.out);
45+
pin.set_pull(.disabled);
46+
pin.set_open_drain(.enabled);
47+
pin.set_drive_strength(.normal);
48+
pin.set_analog(.disabled);
49+
}
50+
// TODO:
51+
sda_pin.set_function(2);
52+
scl_pin.set_function(2);
53+
54+
try i2c0.apply(.{ .baud = 2 });
55+
i2c0.enable();
56+
57+
var display = try SSD1306.init(i2c_device);
58+
59+
while (true) {
60+
try display.write_full_display(&data);
61+
hal.time.sleep_ms(100);
62+
try display.write_full_display(&data2);
63+
hal.time.sleep_ms(100);
64+
}
65+
}

port/hdsc/hc32l110/src/hal.zig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const chip = microzig.chip;
55

66
pub const time = @import("hal/time.zig");
77
pub const gpio = @import("hal/gpio.zig");
8-
pub const clock = @import("hal/clocks.zig");
8+
pub const clocks = @import("hal/clocks.zig");
99
pub const crc16 = @import("hal/crc16.zig");
1010
pub const spi = @import("hal/spi.zig");
1111
pub const uart = @import("hal/uart.zig");
@@ -14,8 +14,8 @@ pub const drivers = @import("hal/drivers.zig");
1414

1515
pub inline fn init() void {
1616
const CLOCK = chip.peripherals.CLOCK;
17-
clock.set_rch_frequency(.@"4MHz");
18-
clock.enable(.InternalHighSpeed, true);
17+
clocks.set_rch_frequency(.@"24MHz");
18+
clocks.enable(.InternalHighSpeed, true);
1919
// TODO: hide pins on 20-pin mcu
2020

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

port/hdsc/hc32l110/src/hal/i2c.zig

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
const std = @import("std");
2+
const microzig = @import("microzig");
3+
const mdf = microzig.drivers;
4+
const peripherals = microzig.chip.peripherals;
5+
6+
const I2C0 = peripherals.I2C;
7+
const RESET = microzig.chip.peripherals.RESET;
8+
9+
pub const Config = struct {
10+
baud: u8,
11+
};
12+
13+
///
14+
/// 7-bit I²C address, without the read/write bit.
15+
///
16+
pub const Address = enum(u7) {
17+
/// The general call addresses all devices on the bus using the I²C address 0.
18+
pub const general_call: Address = @enumFromInt(0x00);
19+
20+
_,
21+
22+
pub fn new(addr: u7) Address {
23+
const a = @as(Address, @enumFromInt(addr));
24+
// std.debug.assert(!a.is_reserved());
25+
return a;
26+
}
27+
28+
///
29+
/// Returns `true` if the Address is a reserved I²C address.
30+
///
31+
/// Reserved addresses are ones that match `0b0000XXX` or `0b1111XXX`.
32+
///
33+
/// See more here: https://www.i2c-bus.org/addressing/
34+
pub fn is_reserved(addr: Address) bool {
35+
const value: u7 = @intFromEnum(addr);
36+
return ((value & 0x78) == 0) or ((value & 0x78) == 0x78);
37+
}
38+
39+
pub fn format(addr: Address, fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
40+
_ = fmt;
41+
_ = options;
42+
try writer.print("I2C(0x{X:0>2})", .{@intFromEnum(addr)});
43+
}
44+
};
45+
46+
const i2c = @This();
47+
48+
pub const instance = struct {
49+
pub const I2C: i2c.I2C = @enumFromInt(0);
50+
pub fn num(instance_number: u1) i2c.I2C {
51+
std.debug.assert(instance_number == 0);
52+
return @enumFromInt(instance_number);
53+
}
54+
};
55+
56+
pub const TransactionError = error{
57+
Timeout,
58+
TargetAddressReserved,
59+
NoData,
60+
};
61+
62+
pub const ConfigError = error{
63+
UnsupportedBaudRate,
64+
};
65+
66+
pub const I2C = enum(u1) {
67+
_,
68+
69+
pub fn apply(self: I2C, comptime config: Config) ConfigError!void {
70+
self.reset();
71+
I2C0.TM.write(.{ .TM = config.baud });
72+
I2C0.ADDR.write(.{
73+
.I2CADR = 0,
74+
.GC = 0,
75+
});
76+
}
77+
78+
pub inline fn reset(_: I2C) void {
79+
RESET.PREI_RESET.modify(.{
80+
.I2C = 0,
81+
});
82+
RESET.PREI_RESET.modify(.{
83+
.I2C = 1,
84+
});
85+
}
86+
87+
pub inline fn enable(_: I2C) void {
88+
I2C0.TMRUN.write(.{
89+
// enable baudrate generation
90+
.TME = 1,
91+
});
92+
93+
I2C0.CR.modify(.{
94+
// enable i2c module
95+
.ENS = 1,
96+
// enable high speed mode
97+
.H1M = 1,
98+
});
99+
}
100+
101+
pub inline fn disable(_: I2C) void {
102+
I2C0.CR.modify(.{
103+
.TMRUN = 0,
104+
.ENS = 0,
105+
.H1M = 0,
106+
});
107+
}
108+
109+
pub fn writev_blocking(self: I2C, addr: Address, buffers: []const []const u8, timeout: ?mdf.time.Duration) TransactionError!void {
110+
// TODO: timeouts
111+
_ = timeout;
112+
113+
const write_vec = microzig.utilities.Slice_Vector([]const u8).init(buffers);
114+
if (write_vec.size() == 0)
115+
return TransactionError.NoData;
116+
117+
self.set_start();
118+
self.wait_for_irq();
119+
120+
// send address
121+
self.wait_for_any_state(&.{ 0x08, 0x10 });
122+
self.clear_start();
123+
self.write_data(@intFromEnum(addr));
124+
self.clear_irq();
125+
126+
self.wait_for_irq();
127+
// @breakpoint();
128+
self.wait_for_any_state(&.{ 0x18, 0x28 });
129+
130+
var iter = write_vec.iterator();
131+
while (iter.next_element()) |element| {
132+
self.write_data(element.value);
133+
self.clear_irq();
134+
135+
self.wait_for_irq();
136+
self.wait_for_any_state(&.{ 0x18, 0x28 });
137+
}
138+
139+
self.set_stop();
140+
self.clear_irq();
141+
}
142+
143+
pub fn write_blocking(self: I2C, addr: Address, data: []const u8, timeout: ?mdf.time.Duration) TransactionError!void {
144+
return self.writev_blocking(addr, &.{data}, timeout);
145+
}
146+
147+
pub fn readv_blocking(self: I2C, addr: Address, buffers: []const []u8, timeout: ?mdf.time.Duration) TransactionError!void {
148+
// TODO: timeouts
149+
_ = timeout;
150+
_ = addr;
151+
_ = buffers;
152+
_ = self;
153+
}
154+
155+
pub fn read_blocking(self: I2C, addr: Address, dst: []u8, timeout: ?mdf.time.Duration) TransactionError!void {
156+
return try self.readv_blocking(addr, &.{dst}, timeout);
157+
}
158+
159+
inline fn set_start(_: I2C) void {
160+
I2C0.CR.modify(.{ .STA = 1 });
161+
}
162+
163+
inline fn clear_start(_: I2C) void {
164+
I2C0.CR.modify(.{ .STA = 0 });
165+
}
166+
167+
inline fn set_stop(_: I2C) void {
168+
I2C0.CR.modify(.{ .STO = 1 });
169+
}
170+
171+
inline fn clear_stop(_: I2C) void {
172+
I2C0.CR.modify(.{ .STO = 0 });
173+
}
174+
175+
inline fn set_ack(_: I2C) void {
176+
I2C0.CR.modify(.{ .AA = 1 });
177+
}
178+
179+
inline fn clear_ack(_: I2C) void {
180+
I2C0.CR.modify(.{ .AA = 0 });
181+
}
182+
183+
inline fn write_data(_: I2C, data: u8) void {
184+
I2C0.DATA.write(.{ .I2CDAT = data });
185+
}
186+
187+
fn wait_for_any_state(_: I2C, comptime states: []const u8) void {
188+
while (true) {
189+
const state = I2C0.STAT.read().I2CSTA;
190+
inline for (states) |s| {
191+
if (s == state)
192+
return;
193+
}
194+
}
195+
}
196+
197+
fn wait_for_irq(self: I2C) void {
198+
while (!self.is_irq_set()) {}
199+
}
200+
201+
inline fn is_irq_set(_: I2C) bool {
202+
return I2C0.CR.read().SI == 1;
203+
}
204+
205+
inline fn clear_irq(_: I2C) void {
206+
I2C0.CR.modify(.{ .SI = 0 });
207+
}
208+
};

0 commit comments

Comments
 (0)