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.

210 lines
6.5 KiB
Zig

const std = @import("std");
const micro = @import("microzig");
const peripherals = micro.chip.peripherals;
const GPIO = peripherals.GPIO;
const PINCONNECT = peripherals.PINCONNECT;
const UART0 = peripherals.UART0;
const UART1 = peripherals.UART1;
const UART2 = peripherals.UART2;
const UART3 = peripherals.UART3;
const SYSCON = peripherals.SYSCON;
pub const clock = struct {
pub const Domain = enum {
cpu,
};
};
pub const clock_frequencies = .{
.cpu = 100_000_000, // 100 Mhz
};
pub const PinTarget = enum(u2) {
func00 = 0b00,
func01 = 0b01,
func10 = 0b10,
func11 = 0b11,
};
pub fn parse_pin(comptime spec: []const u8) type {
const invalid_format_msg = "The given pin '" ++ spec ++ "' has an invalid format. Pins must follow the format \"P{Port}.{Pin}\" scheme.";
if (spec[0] != 'P')
@compileError(invalid_format_msg);
const index = std.mem.indexOfScalar(u8, spec, '.') orelse @compileError(invalid_format_msg);
const _port: comptime_int = std.fmt.parseInt(u3, spec[1..index], 10) catch @compileError(invalid_format_msg);
const _pin: comptime_int = std.fmt.parseInt(u5, spec[index + 1 ..], 10) catch @compileError(invalid_format_msg);
const sel_reg_name = std.fmt.comptimePrint("PINSEL{d}", .{(2 * _port + _pin / 16)});
const _regs = struct {
const name_suffix = std.fmt.comptimePrint("{d}", .{_port});
const pinsel_reg = @field(PINCONNECT, sel_reg_name);
const pinsel_field = std.fmt.comptimePrint("P{d}_{d}", .{ _port, _pin });
const dir = @field(GPIO, "DIR" ++ name_suffix);
const pin = @field(GPIO, "PIN" ++ name_suffix);
const set = @field(GPIO, "SET" ++ name_suffix);
const clr = @field(GPIO, "CLR" ++ name_suffix);
const mask = @field(GPIO, "MASK" ++ name_suffix);
};
return struct {
pub const port: u3 = _port;
pub const pin: u5 = _pin;
pub const regs = _regs;
const gpio_mask: u32 = (1 << pin);
pub const Targets = PinTarget;
};
}
pub fn route_pin(comptime pin: type, function: PinTarget) void {
var val = pin.regs.pinsel_reg.read();
@field(val, pin.regs.pinsel_field) = @intFromEnum(function);
pin.regs.pinsel_reg.write(val);
}
pub const gpio = struct {
pub fn set_output(comptime pin: type) void {
pin.regs.dir.raw |= pin.gpio_mask;
}
pub fn set_input(comptime pin: type) void {
pin.regs.dir.raw &= ~pin.gpio_mask;
}
pub fn read(comptime pin: type) micro.gpio.State {
return if ((pin.regs.pin.raw & pin.gpio_mask) != 0)
micro.gpio.State.high
else
micro.gpio.State.low;
}
pub fn write(comptime pin: type, state: micro.gpio.State) void {
if (state == .high) {
pin.regs.set.raw = pin.gpio_mask;
} else {
pin.regs.clr.raw = pin.gpio_mask;
}
}
};
pub const uart = struct {
pub const DataBits = enum(u2) {
five = 0,
six = 1,
seven = 2,
eight = 3,
};
pub const StopBits = enum(u1) {
one = 0,
two = 1,
};
pub const Parity = enum(u2) {
odd = 0,
even = 1,
mark = 2,
space = 3,
};
pub const CClkDiv = enum(u2) {
four = 0,
one = 1,
two = 2,
eight = 3,
};
};
pub fn Uart(comptime index: usize, comptime pins: micro.uart.Pins) type {
if (pins.tx != null or pins.rx != null)
@compileError("TODO: custom pins are not currently supported");
return struct {
const UARTn = switch (index) {
0 => UART0,
1 => UART1,
2 => UART2,
3 => UART3,
else => @compileError("LPC1768 has 4 UARTs available."),
};
const Self = @This();
pub fn init(config: micro.uart.Config) !Self {
micro.debug.write("0");
switch (index) {
0 => {
SYSCON.PCONP.modify(.{ .PCUART0 = 1 });
SYSCON.PCLKSEL0.modify(.{ .PCLK_UART0 = @intFromEnum(uart.CClkDiv.four) });
},
1 => {
SYSCON.PCONP.modify(.{ .PCUART1 = 1 });
SYSCON.PCLKSEL0.modify(.{ .PCLK_UART1 = @intFromEnum(uart.CClkDiv.four) });
},
2 => {
SYSCON.PCONP.modify(.{ .PCUART2 = 1 });
SYSCON.PCLKSEL1.modify(.{ .PCLK_UART2 = @intFromEnum(uart.CClkDiv.four) });
},
3 => {
SYSCON.PCONP.modify(.{ .PCUART3 = 1 });
SYSCON.PCLKSEL1.modify(.{ .PCLK_UART3 = @intFromEnum(uart.CClkDiv.four) });
},
else => unreachable,
}
micro.debug.write("1");
UARTn.LCR.modify(.{
// 8N1
.WLS = @intFromEnum(config.data_bits),
.SBS = @intFromEnum(config.stop_bits),
.PE = if (config.parity != null) @as(u1, 1) else @as(u1, 0),
.PS = if (config.parity) |p| @intFromEnum(p) else @intFromEnum(uart.Parity.odd),
.BC = 0,
.DLAB = 1,
});
micro.debug.write("2");
// TODO: UARTN_FIFOS_ARE_DISA is not available in all uarts
//UARTn.FCR.modify(.{ .FIFOEN = .UARTN_FIFOS_ARE_DISA });
micro.debug.writer().print("clock: {} baud: {} ", .{
micro.clock.get().cpu,
config.baud_rate,
}) catch {};
const pclk = micro.clock.get().cpu / 4;
const divider = (pclk / (16 * config.baud_rate));
const regval = std.math.cast(u16, divider) orelse return error.UnsupportedBaudRate;
UARTn.DLL.modify(.{ .DLLSB = @as(u8, @truncate(regval >> 0x00)) });
UARTn.DLM.modify(.{ .DLMSB = @as(u8, @truncate(regval >> 0x08)) });
UARTn.LCR.modify(.{ .DLAB = 0 });
return Self{};
}
pub fn can_write(self: Self) bool {
_ = self;
return (UARTn.LSR.read().THRE == 1);
}
pub fn tx(self: Self, ch: u8) void {
while (!self.can_write()) {} // Wait for Previous transmission
UARTn.THR.raw = ch; // Load the data to be transmitted
}
pub fn can_read(self: Self) bool {
_ = self;
return (UARTn.LSR.read().RDR == 1);
}
pub fn rx(self: Self) u8 {
while (!self.can_read()) {} // Wait till the data is received
return UARTn.RBR.read().RBR; // Read received data
}
};
}