diff --git a/src/modules/chips/atmega328p/atmega328p.zig b/src/modules/chips/atmega328p/atmega328p.zig index 32ffbb6..43022a8 100644 --- a/src/modules/chips/atmega328p/atmega328p.zig +++ b/src/modules/chips/atmega328p/atmega328p.zig @@ -1,6 +1,8 @@ const std = @import("std"); const micro = @import("microzig"); + pub usingnamespace @import("registers.zig"); +const regz = @import("registers.zig").registers; pub const cpu = micro.cpu; const Port = enum(u8) { @@ -26,13 +28,15 @@ pub fn parsePin(comptime spec: []const u8) type { pub const gpio = struct { fn regs(comptime desc: type) type { return struct { - const pin_addr = 3 * @enumToInt(desc.port) + 0x00; - const dir_addr = 3 * @enumToInt(desc.port) + 0x01; - const port_addr = 3 * @enumToInt(desc.port) + 0x02; + // io address + const pin_addr: u5 = 3 * @enumToInt(desc.port) + 0x00; + const dir_addr: u5 = 3 * @enumToInt(desc.port) + 0x01; + const port_addr: u5 = 3 * @enumToInt(desc.port) + 0x02; - const pin = @intToPtr(*volatile u8, pin_addr); - const dir = @intToPtr(*volatile u8, dir_addr); - const port = @intToPtr(*volatile u8, port_addr); + // ram mapping + const pin = @intToPtr(*volatile u8, 0x20 + @as(usize, pin_addr)); + const dir = @intToPtr(*volatile u8, 0x20 + @as(usize, dir_addr)); + const port = @intToPtr(*volatile u8, 0x20 + @as(usize, port_addr)); }; } @@ -63,3 +67,116 @@ pub const gpio = struct { cpu.sbi(regs(pin).pin_addr, pin.pin); } }; + +pub const uart = struct { + pub const DataBits = enum { + five, + six, + seven, + eight, + nine, + }; + + pub const StopBits = enum { + one, + two, + }; + + pub const Parity = enum { + odd, + even, + }; +}; + +pub fn Uart(comptime index: usize) type { + if (index != 0) @compileError("Atmega328p only has a single uart!"); + return struct { + const Self = @This(); + + fn computeDivider(baud_rate: u32) !u12 { + const pclk = micro.clock.get(); + const divider = ((pclk + (8 * baud_rate)) / (16 * baud_rate)) - 1; + + return std.math.cast(u12, divider) catch return error.UnsupportedBaudRate; + } + + fn computeBaudRate(divider: u12) u32 { + return micro.clock.get() / (16 * @as(u32, divider) + 1); + } + + pub fn init(config: micro.uart.Config) !Self { + const ucsz: u3 = switch (config.data_bits) { + .five => 0b000, + .six => 0b001, + .seven => 0b010, + .eight => 0b011, + .nine => return error.UnsupportedWordSize, // 0b111 + }; + + const upm: u2 = if (config.parity) |parity| switch (parity) { + .even => @as(u2, 0b10), // even + .odd => @as(u2, 0b11), // odd + } else 0b00; // parity disabled + + const usbs: u1 = switch (config.stop_bits) { + .one => 0b0, + .two => 0b1, + }; + + const umsel: u2 = 0b00; // Asynchronous USART + + // baud is computed like this: + // f(osc) + // BAUD = ---------------- + // 16 * (UBRRn + 1) + + const ubrr_val = try computeDivider(config.baud_rate); + + regz.USART0.UCSR0A.modify(.{ + .MPCM0 = 0, + .U2X0 = 0, + }); + regz.USART0.UCSR0B.write(.{ + .TXB80 = 0, // we don't care about these btw + .RXB80 = 0, // we don't care about these btw + .UCSZ02 = @truncate(u1, (ucsz & 0x04) >> 2), + .TXEN0 = 1, + .RXEN0 = 1, + .UDRIE0 = 0, // no interrupts + .TXCIE0 = 0, // no interrupts + .RXCIE0 = 0, // no interrupts + }); + regz.USART0.UCSR0C.write(.{ + .UCPOL0 = 0, // async mode + .UCSZ0 = @truncate(u2, (ucsz & 0x03) >> 0), + .USBS0 = usbs, + .UPM0 = upm, + .UMSEL0 = umsel, + }); + + regz.USART0.UBRR0.modify(ubrr_val); + + return Self{}; + } + + pub fn canWrite(self: Self) bool { + _ = self; + return (regz.USART0.UCSR0A.read().UDRE0 == 1); + } + + pub fn tx(self: Self, ch: u8) void { + while (!self.canWrite()) {} // Wait for Previous transmission + regz.USART0.UDR0.* = ch; // Load the data to be transmitted + } + + pub fn canRead(self: Self) bool { + _ = self; + return (regz.USART0.UCSR0A.read().RXC0 == 1); + } + + pub fn rx(self: Self) u8 { + while (!self.canRead()) {} // Wait till the data is received + return regz.USART0.UDR0.*; // Read received data + } + }; +} diff --git a/src/modules/chips/atmega328p/registers.zig b/src/modules/chips/atmega328p/registers.zig index 06d4880..0c27f21 100644 --- a/src/modules/chips/atmega328p/registers.zig +++ b/src/modules/chips/atmega328p/registers.zig @@ -279,7 +279,7 @@ pub const registers = struct { /// address: 0xc4 /// USART Baud Rate Register Bytes - pub const UBRR0 = @intToPtr(*volatile u12, 0xc4); + pub const UBRR0 = @intToPtr(*volatile MmioInt(16, u12), 0xc4); }; /// Two Wire Serial Interface @@ -1110,7 +1110,7 @@ pub const registers = struct { /// address: 0x5d /// Stack Pointer - pub const SP = @intToPtr(*volatile u12, 0x5d); + pub const SP = @intToPtr(*volatile MmioInt(16, u12), 0x5d); /// address: 0x57 /// Store Program Memory Control and Status Register