You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
6.0 KiB
Zig

Initial I2C support (#26) * build.zig: Trivial rename around UART test * mmio: Add writeRaw() to set a full register * UART: Add TODO for auto baud rate detection * STM32F30x Initial USART1 output/transmit support All code assumes default chip clock configuration. Code assumes STM32F303xB / STM32F3030xC. Code supports only 8 data bits, 1 stop bit. * stm32f3discovery @panic() to UART1 This is done by implementing `debugWrite()` for the board, which only initializes UART1 if that was not yet done, and flushes afterwards to make sure the host receives all. * stm32f303: Support UART1 reader This is done by implementing `rx()` and `canRead()`. * stm32f303 UART1 correctly support 7 and 8 bits This includes correctly masking the parity bit on reads. * stm32f3 UART1 support 0.5/1.5/2 stop bits * stm32f303 UART1 simplify parity code * stm32f303 I2C rough initial code Allows only writing and reading single bytes. * stm32f3 i2c: enable debug 'logging' * Add a few comments * I2C API changes, STM32F303 I2C multi-byte transfers Now using controller/device terminology, instead of master/slave. Now using 'transfer objects' to make STOPs and re-STARTs explicit, and allow using Writer and Reader APIs. Added 'register' abstraction. STM32F303 I2C now supports this new API, and multi-byte transfers. Now waiting for I2C_ISR.BUSY == 0, after setting I2C_CR2.STOP == 1. Without this, the sequence write-stop-write caused an additional STOP to be sent immediately the START and address of the second write. * Make work with regz-generated registers.zig change * Updated to match regz-generated update * After #23 repair Reset on stm32, lpc1768 * Clean-up I2C `readRegisters()`. * Refactor to separate read/write states * On STM32F303, make second read call fail Also doc comments to clarify the new API. * STM32 I2C: Properly support multiple write calls * I2C STM32: Fix release mode compile error ...on top of an earlier commit on this branch. * I2C Add 'write register' shorthand functions
3 years ago
const std = @import("std");
const micro = @import("microzig.zig");
const chip = @import("chip");
pub fn I2CController(comptime index: usize) type {
const SystemI2CController = chip.I2CController(index);
const I2CDevice = struct {
const Device = @This();
internal: SystemI2CController,
address: u7,
const Direction = enum { read, write };
fn Transfer(comptime direction: Direction) type {
return switch (direction) {
.read => struct {
const Self = @This();
state: SystemI2CController.ReadState,
pub const Reader = std.io.Reader(*Self, ReadError, readSome);
/// NOTE that some platforms, notably most (all?) STM32 microcontrollers,
/// allow only a single read call per transfer.
pub fn reader(self: *Self) Reader {
return Reader{ .context = self };
}
fn readSome(self: *Self, buffer: []u8) ReadError!usize {
try self.state.readNoEof(buffer);
return buffer.len;
}
/// STOP the transfer, invalidating this object.
pub fn stop(self: *Self) !void {
try self.state.stop();
}
/// RESTART to a new transfer, invalidating this object.
/// Note that some platforms set the repeated START condition
/// on the first read or write call.
pub fn restartTransfer(self: *Self, comptime new_direction: Direction) !Transfer(new_direction) {
return Transfer(direction){ .state = try self.state.restartTransfer(new_direction) };
}
},
.write => struct {
const Self = @This();
state: SystemI2CController.WriteState,
pub const Writer = std.io.Writer(*Self, WriteError, writeSome);
/// NOTE that some platforms, notably most (all?) STM32 microcontrollers,
/// will not immediately write all bytes, but postpone that
/// to the next write call, or to the stop() call.
pub fn writer(self: *Self) Writer {
return Writer{ .context = self };
}
fn writeSome(self: *Self, buffer: []const u8) WriteError!usize {
try self.state.writeAll(buffer);
return buffer.len;
}
/// STOP the transfer, invalidating this object.
pub fn stop(self: *Self) !void {
try self.state.stop();
}
/// RESTART to a new transfer, invalidating this object.
/// Note that some platforms set the repeated START condition
/// on the first read or write call.
pub fn restartTransfer(self: *Self, comptime new_direction: Direction) !Transfer(new_direction) {
return switch (new_direction) {
.read => Transfer(new_direction){ .state = try self.state.restartRead() },
.write => Transfer(new_direction){ .state = try self.state.restartWrite() },
};
}
},
};
}
/// START a new transfer.
/// Note that some platforms set the START condition
/// on the first read or write call.
pub fn startTransfer(self: Device, comptime direction: Direction) !Transfer(direction) {
return switch (direction) {
.read => Transfer(direction){ .state = try SystemI2CController.ReadState.start(self.address) },
.write => Transfer(direction){ .state = try SystemI2CController.WriteState.start(self.address) },
};
}
/// Shorthand for 'register-based' devices
pub fn writeRegister(self: Device, register_address: u8, byte: u8) ReadError!void {
try self.writeRegisters(register_address, &.{byte});
}
/// Shorthand for 'register-based' devices
pub fn writeRegisters(self: Device, register_address: u8, buffer: []u8) ReadError!void {
var wt = try self.startTransfer(.write);
defer wt.stop() catch {};
try wt.writer().writeByte(register_address);
try wt.writer().writeAll(buffer);
}
/// Shorthand for 'register-based' devices
pub fn readRegister(self: Device, register_address: u8) ReadError!u8 {
var buffer: [1]u8 = undefined;
try self.readRegisters(register_address, &buffer);
return buffer[0];
}
/// Shorthand for 'register-based' devices
pub fn readRegisters(self: Device, register_address: u8, buffer: []u8) ReadError!void {
var rt = write_and_restart: {
var wt = try self.startTransfer(.write);
errdefer wt.stop() catch {};
try wt.writer().writeByte(1 << 7 | register_address); // MSB == 'keep sending until I STOP'
break :write_and_restart try wt.restartTransfer(.read);
};
defer rt.stop() catch {};
try rt.reader().readNoEof(buffer);
}
};
return struct {
const Self = @This();
internal: SystemI2CController,
pub fn init() InitError!Self {
return Self{
.internal = try SystemI2CController.init(),
};
}
pub fn device(self: Self, address: u7) I2CDevice {
return I2CDevice{ .internal = self.internal, .address = address };
}
};
}
pub const InitError = error{};
pub const WriteError = error{};
pub const ReadError = error{
EndOfStream,
TooFewBytesReceived,
TooManyBytesReceived,
};