Improve STM32F407 Uart and introduce GPIO Alternate Function (#44)

wch-ch32v003
Riccardo Binetti 2 years ago committed by GitHub
parent 8b04413e55
commit b9cd24ae96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -8,6 +8,7 @@ pub const Mode = enum {
input_output, input_output,
open_drain, open_drain,
generic, generic,
alternate_function,
}; };
pub const State = enum(u1) { pub const State = enum(u1) {
@ -57,6 +58,14 @@ pub fn Gpio(comptime pin: type, config: anytype) type {
else => @compileError("An open_drain pin requires initial_state to be either .floating or .driven"), else => @compileError("An open_drain pin requires initial_state to be either .floating or .driven"),
}); });
}, },
.alternate_function => {
if (comptime @hasDecl(chip.gpio, "AlternateFunction")) {
const alternate_function = @as(chip.gpio.AlternateFunction, config.alternate_function);
setAlternateFunction(alternate_function);
} else {
@compileError("Alternate Function not supported yet");
}
},
} }
} }
@ -112,6 +121,11 @@ pub fn Gpio(comptime pin: type, config: anytype) type {
fn getDrive() Drive { fn getDrive() Drive {
@compileError("open drain not implemented yet!"); @compileError("open drain not implemented yet!");
} }
// alternate function
fn setAlternateFunction(af: chip.gpio.AlternateFunction) void {
chip.gpio.setAlternateFunction(pin.source_pin, af);
}
}; };
// return only a subset of Generic for the requested pin. // return only a subset of Generic for the requested pin.
switch (mode) { switch (mode) {
@ -142,6 +156,9 @@ pub fn Gpio(comptime pin: type, config: anytype) type {
pub const setDrive = Generic.setDrive; pub const setDrive = Generic.setDrive;
pub const getDrive = Generic.getDrive; pub const getDrive = Generic.getDrive;
}, },
.alternate_function => return struct {
pub const init = Generic.init;
},
.generic => Generic, .generic => Generic,
} }
} }

@ -53,9 +53,8 @@ pub fn parsePin(comptime spec: []const u8) type {
if (spec[1] < 'A' or spec[1] > 'I') if (spec[1] < 'A' or spec[1] > 'I')
@compileError(invalid_format_msg); @compileError(invalid_format_msg);
const pin_number: comptime_int = std.fmt.parseInt(u4, spec[2..], 10) catch @compileError(invalid_format_msg);
return struct { return struct {
const pin_number: comptime_int = std.fmt.parseInt(u4, spec[2..], 10) catch @compileError(invalid_format_msg);
/// 'A'...'I' /// 'A'...'I'
const gpio_port_name = spec[1..2]; const gpio_port_name = spec[1..2];
const gpio_port = @field(regs, "GPIO" ++ gpio_port_name); const gpio_port = @field(regs, "GPIO" ++ gpio_port_name);
@ -70,6 +69,25 @@ fn setRegField(reg: anytype, comptime field_name: anytype, value: anytype) void
} }
pub const gpio = struct { pub const gpio = struct {
pub const AlternateFunction = enum(u4) {
af0,
af1,
af2,
af3,
af4,
af5,
af6,
af7,
af8,
af9,
af10,
af11,
af12,
af13,
af14,
af15,
};
pub fn setOutput(comptime pin: type) void { pub fn setOutput(comptime pin: type) void {
setRegField(regs.RCC.AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); setRegField(regs.RCC.AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1);
setRegField(@field(pin.gpio_port, "MODER"), "MODER" ++ pin.suffix, 0b01); setRegField(@field(pin.gpio_port, "MODER"), "MODER" ++ pin.suffix, 0b01);
@ -80,6 +98,16 @@ pub const gpio = struct {
setRegField(@field(pin.gpio_port, "MODER"), "MODER" ++ pin.suffix, 0b00); setRegField(@field(pin.gpio_port, "MODER"), "MODER" ++ pin.suffix, 0b00);
} }
pub fn setAlternateFunction(comptime pin: type, af: AlternateFunction) void {
setRegField(regs.RCC.AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1);
setRegField(@field(pin.gpio_port, "MODER"), "MODER" ++ pin.suffix, 0b10);
if (pin.pin_number < 8) {
setRegField(@field(pin.gpio_port, "AFRL"), "AFRL" ++ pin.suffix, @enumToInt(af));
} else {
setRegField(@field(pin.gpio_port, "AFRH"), "AFRH" ++ pin.suffix, @enumToInt(af));
}
}
pub fn read(comptime pin: type) micro.gpio.State { pub fn read(comptime pin: type) micro.gpio.State {
const idr_reg = pin.gpio_port.IDR; const idr_reg = pin.gpio_port.IDR;
const reg_value = @field(idr_reg.read(), "IDR" ++ pin.suffix); // TODO extract to getRegField()? const reg_value = @field(idr_reg.read(), "IDR" ++ pin.suffix); // TODO extract to getRegField()?
@ -118,7 +146,27 @@ pub const uart = struct {
}; };
pub fn Uart(comptime index: usize) type { pub fn Uart(comptime index: usize) type {
if (!(index == 2)) @compileError("TODO: only USART2 is currently supported"); if (index < 1 or index > 6) @compileError("Valid USART index are 1..6");
const usart_name = std.fmt.comptimePrint("USART{d}", .{index});
// TODO: support alternative pins
const pins = switch (index) {
1 => .{ .tx = micro.Pin("PA9"), .rx = micro.Pin("PA10") },
2 => .{ .tx = micro.Pin("PA2"), .rx = micro.Pin("PA3") },
3 => .{ .tx = micro.Pin("PD8"), .rx = micro.Pin("PD9") },
4 => .{ .tx = micro.Pin("PC10"), .rx = micro.Pin("PC11") },
5 => .{ .tx = micro.Pin("PC12"), .rx = micro.Pin("PD2") },
6 => .{ .tx = micro.Pin("PC6"), .rx = micro.Pin("PC7") },
else => unreachable,
};
const tx_gpio = micro.Gpio(pins.tx, .{
.mode = .alternate_function,
.alternate_function = .af7,
});
const rx_gpio = micro.Gpio(pins.rx, .{
.mode = .alternate_function,
.alternate_function = .af7,
});
return struct { return struct {
parity_read_mask: u8, parity_read_mask: u8,
@ -127,23 +175,26 @@ pub fn Uart(comptime index: usize) type {
pub fn init(config: micro.uart.Config) !Self { pub fn init(config: micro.uart.Config) !Self {
// The following must all be written when the USART is disabled (UE=0). // The following must all be written when the USART is disabled (UE=0).
if (regs.USART2.CR1.read().UE == 1) if (@field(regs, usart_name).CR1.read().UE == 1)
@panic("Trying to initialize USART2 while it is already enabled"); @panic("Trying to initialize " ++ usart_name ++ " while it is already enabled");
// LATER: Alternatively, set UE=0 at this point? Then wait for something? // LATER: Alternatively, set UE=0 at this point? Then wait for something?
// Or add a destroy() function which disables the USART? // Or add a destroy() function which disables the USART?
// enable the USART2 clock // enable the USART clock
regs.RCC.APB1ENR.modify(.{ .USART2EN = 1 }); const clk_enable_reg = switch (index) {
// enable GPIOA clock 1, 6 => regs.RCC.APB2ENR,
regs.RCC.AHB1ENR.modify(.{ .GPIOAEN = 1 }); 2...5 => regs.RCC.APB1ENR,
// set PA2+PA3 to alternate function 7, USART2_TX + USART2_RX else => unreachable,
regs.GPIOA.MODER.modify(.{ .MODER2 = 0b10, .MODER3 = 0b10 }); };
regs.GPIOA.AFRL.modify(.{ .AFRL2 = 7, .AFRL3 = 7 }); setRegField(clk_enable_reg, usart_name ++ "EN", 1);
tx_gpio.init();
rx_gpio.init();
// clear USART2 configuration to its default // clear USART configuration to its default
regs.USART2.CR1.raw = 0; @field(regs, usart_name).CR1.raw = 0;
regs.USART2.CR2.raw = 0; @field(regs, usart_name).CR2.raw = 0;
regs.USART2.CR3.raw = 0; @field(regs, usart_name).CR3.raw = 0;
// Return error for unsupported combinations // Return error for unsupported combinations
if (config.data_bits == .nine and config.parity != null) { if (config.data_bits == .nine and config.parity != null) {
@ -159,15 +210,15 @@ pub fn Uart(comptime index: usize) type {
// - 0: 1 start bit, 8 data bits (7 data + 1 parity, or 8 data), n stop bits, the chip default // - 0: 1 start bit, 8 data bits (7 data + 1 parity, or 8 data), n stop bits, the chip default
// - 1: 1 start bit, 9 data bits (8 data + 1 parity, or 9 data), n stop bits // - 1: 1 start bit, 9 data bits (8 data + 1 parity, or 9 data), n stop bits
const m: u1 = if (config.data_bits == .nine or (config.data_bits == .eight and config.parity != null)) 1 else 0; const m: u1 = if (config.data_bits == .nine or (config.data_bits == .eight and config.parity != null)) 1 else 0;
regs.USART2.CR1.modify(.{ .M = m }); @field(regs, usart_name).CR1.modify(.{ .M = m });
// set parity // set parity
if (config.parity) |parity| { if (config.parity) |parity| {
regs.USART2.CR1.modify(.{ .PCE = 1, .PS = @enumToInt(parity) }); @field(regs, usart_name).CR1.modify(.{ .PCE = 1, .PS = @enumToInt(parity) });
} // otherwise, no need to set no parity since we reset Control Registers above, and it's the default } // otherwise, no need to set no parity since we reset Control Registers above, and it's the default
// set number of stop bits // set number of stop bits
regs.USART2.CR2.modify(.{ .STOP = @enumToInt(config.stop_bits) }); @field(regs, usart_name).CR2.modify(.{ .STOP = @enumToInt(config.stop_bits) });
// set the baud rate // set the baud rate
// Despite the reference manual talking about fractional calculation and other buzzwords, // Despite the reference manual talking about fractional calculation and other buzzwords,
@ -177,12 +228,12 @@ pub fn Uart(comptime index: usize) type {
// TODO: Do some checks to see if the baud rate is too high (or perhaps too low) // TODO: Do some checks to see if the baud rate is too high (or perhaps too low)
// TODO: Do a rounding div, instead of a truncating div? // TODO: Do a rounding div, instead of a truncating div?
const usartdiv = @intCast(u16, @divTrunc(apb1_frequency, config.baud_rate)); const usartdiv = @intCast(u16, @divTrunc(apb1_frequency, config.baud_rate));
regs.USART2.BRR.raw = usartdiv; @field(regs, usart_name).BRR.raw = usartdiv;
// enable USART2, and its transmitter and receiver // enable USART, and its transmitter and receiver
regs.USART2.CR1.modify(.{ .UE = 1 }); @field(regs, usart_name).CR1.modify(.{ .UE = 1 });
regs.USART2.CR1.modify(.{ .TE = 1 }); @field(regs, usart_name).CR1.modify(.{ .TE = 1 });
regs.USART2.CR1.modify(.{ .RE = 1 }); @field(regs, usart_name).CR1.modify(.{ .RE = 1 });
// For code simplicity, at cost of one or more register reads, // For code simplicity, at cost of one or more register reads,
// we read back the actual configuration from the registers, // we read back the actual configuration from the registers,
@ -191,7 +242,7 @@ pub fn Uart(comptime index: usize) type {
} }
pub fn getOrInit(config: micro.uart.Config) !Self { pub fn getOrInit(config: micro.uart.Config) !Self {
if (regs.USART2.CR1.read().UE == 1) { if (@field(regs, usart_name).CR1.read().UE == 1) {
// UART1 already enabled, don't reinitialize and disturb things; // UART1 already enabled, don't reinitialize and disturb things;
// instead read and use the actual configuration. // instead read and use the actual configuration.
return readFromRegisters(); return readFromRegisters();
@ -199,7 +250,7 @@ pub fn Uart(comptime index: usize) type {
} }
fn readFromRegisters() Self { fn readFromRegisters() Self {
const cr1 = regs.USART2.CR1.read(); const cr1 = @field(regs, usart_name).CR1.read();
// As documented in `init()`, M0==1 means 'the 9th bit (not the 8th bit) is the parity bit'. // As documented in `init()`, M0==1 means 'the 9th bit (not the 8th bit) is the parity bit'.
// So we always mask away the 9th bit, and if parity is enabled and it is in the 8th bit, // So we always mask away the 9th bit, and if parity is enabled and it is in the 8th bit,
// then we also mask away the 8th bit. // then we also mask away the 8th bit.
@ -208,7 +259,7 @@ pub fn Uart(comptime index: usize) type {
pub fn canWrite(self: Self) bool { pub fn canWrite(self: Self) bool {
_ = self; _ = self;
return switch (regs.USART2.SR.read().TXE) { return switch (@field(regs, usart_name).SR.read().TXE) {
1 => true, 1 => true,
0 => false, 0 => false,
}; };
@ -216,16 +267,16 @@ pub fn Uart(comptime index: usize) type {
pub fn tx(self: Self, ch: u8) void { pub fn tx(self: Self, ch: u8) void {
while (!self.canWrite()) {} // Wait for Previous transmission while (!self.canWrite()) {} // Wait for Previous transmission
regs.USART2.DR.modify(ch); @field(regs, usart_name).DR.modify(ch);
} }
pub fn txflush(_: Self) void { pub fn txflush(_: Self) void {
while (regs.USART2.SR.read().TC == 0) {} while (@field(regs, usart_name).SR.read().TC == 0) {}
} }
pub fn canRead(self: Self) bool { pub fn canRead(self: Self) bool {
_ = self; _ = self;
return switch (regs.USART2.SR.read().RXNE) { return switch (@field(regs, usart_name).SR.read().RXNE) {
1 => true, 1 => true,
0 => false, 0 => false,
}; };
@ -233,7 +284,7 @@ pub fn Uart(comptime index: usize) type {
pub fn rx(self: Self) u8 { pub fn rx(self: Self) u8 {
while (!self.canRead()) {} // Wait till the data is received while (!self.canRead()) {} // Wait till the data is received
const data_with_parity_bit: u9 = regs.USART2.DR.read(); const data_with_parity_bit: u9 = @field(regs, usart_name).DR.read();
return @intCast(u8, data_with_parity_bit & self.parity_read_mask); return @intCast(u8, data_with_parity_bit & self.parity_read_mask);
} }
}; };

@ -1,43 +1,26 @@
const micro = @import("microzig"); const micro = @import("microzig");
// Configures the led_pin to a hardware pin // Configures the led_pin and uart index
const led_pin = if (micro.config.has_board) const cfg = if (micro.config.has_board)
switch (micro.config.board_name) { switch (micro.config.board_name) {
.@"Arduino Nano" => micro.Pin("D13"), .@"mbed LPC1768" => .{ .led_pin = micro.Pin("LED-1"), .uart_idx = 1 },
.@"mbed LPC1768" => micro.Pin("LED-1"), .@"STM32F3DISCOVERY" => .{ .led_pin = micro.Pin("LD3"), .uart_idx = 1 },
.@"STM32F3DISCOVERY" => micro.Pin("LD3"), .@"STM32F4DISCOVERY" => .{ .led_pin = micro.Pin("LD5"), .uart_idx = 2 },
.@"STM32F4DISCOVERY" => micro.Pin("LD5"),
.@"STM32F429IDISCOVERY" => micro.Pin("LD4"),
else => @compileError("unknown board"), else => @compileError("unknown board"),
} }
else switch (micro.config.chip_name) { else switch (micro.config.chip_name) {
.@"ATmega328p" => micro.Pin("PB5"), .@"NXP LPC1768" => .{ .led_pin = micro.Pin("P1.18"), .uart_idx = 1 },
.@"NXP LPC1768" => micro.Pin("P1.18"),
.@"STM32F103x8" => micro.Pin("PC13"),
else => @compileError("unknown chip"),
};
// Configures the uart index
const uart_idx = if (micro.config.has_board)
switch (micro.config.board_name) {
.@"mbed LPC1768" => 1,
.@"STM32F3DISCOVERY" => 1,
.@"STM32F4DISCOVERY" => 2,
else => @compileError("unknown board"),
}
else switch (micro.config.chip_name) {
.@"NXP LPC1768" => 1,
else => @compileError("unknown chip"), else => @compileError("unknown chip"),
}; };
pub fn main() !void { pub fn main() !void {
const led = micro.Gpio(led_pin, .{ const led = micro.Gpio(cfg.led_pin, .{
.mode = .output, .mode = .output,
.initial_state = .low, .initial_state = .low,
}); });
led.init(); led.init();
var uart = micro.Uart(uart_idx).init(.{ var uart = micro.Uart(cfg.uart_idx).init(.{
.baud_rate = 9600, .baud_rate = 9600,
.stop_bits = .one, .stop_bits = .one,
.parity = null, .parity = null,

Loading…
Cancel
Save