Add Uart to STM32F4Discovery and rework uart-sync example (#43)

* stm32f407: add Uart support

* lpc1768: add cpu_frequency in board and chip

Allow using them with microzig Uart, which ensures the clock is present

* lpc1768: fix UART2 and UART3 peripheral clock register

They are in the PCLKSEL1 register, not PCLKSEL0

* uart-sync: use native microzig Uart, target different boards/chips

The previous example was lpc1768 specific, this new example should work with all
board and chips which implement the microzig Uart functionality. Instead of 4
LEDs, the new example uses a single LED to provide minimal debugging
capabilities if something does not work correctly.
This allows building this example for the stm32 f3 and f4 discovery boards.
wch-ch32v003
Riccardo Binetti 2 years ago committed by GitHub
parent c0e0423f5c
commit 8b04413e55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -21,8 +21,8 @@ pub fn build(b: *std.build.Builder) !void {
BuildConfig{ .name = "chips.atmega328p", .backing = Backing{ .chip = chips.atmega328p } },
BuildConfig{ .name = "chips.lpc1768", .backing = Backing{ .chip = chips.lpc1768 } },
//BuildConfig{ .name = "chips.stm32f103x8", .backing = Backing{ .chip = chips.stm32f103x8 } },
BuildConfig{ .name = "boards.stm32f3discovery", .backing = Backing{ .board = boards.stm32f3discovery }, .supports_uart_test = false },
BuildConfig{ .name = "boards.stm32f4discovery", .backing = Backing{ .board = boards.stm32f4discovery }, .supports_uart_test = false },
BuildConfig{ .name = "boards.stm32f3discovery", .backing = Backing{ .board = boards.stm32f3discovery } },
BuildConfig{ .name = "boards.stm32f4discovery", .backing = Backing{ .board = boards.stm32f4discovery } },
BuildConfig{ .name = "boards.stm32f429idiscovery", .backing = Backing{ .board = boards.stm32f429idiscovery }, .supports_uart_test = false },
};

@ -1,6 +1,8 @@
pub const chip = @import("chip");
pub const micro = @import("microzig");
pub const cpu_frequency: u32 = 100_000_000; // 100 MHz
pub fn debugWrite(string: []const u8) void {
const clk_pin = micro.Pin("DIP5");
const dat_pin = micro.Pin("DIP6");

@ -5,6 +5,8 @@ const regs = chip.registers;
pub usingnamespace chip;
pub const cpu_frequency: u32 = 100_000_000; // 100 MHz
pub const PinTarget = enum(u2) {
func00 = 0b00,
func01 = 0b01,
@ -129,11 +131,11 @@ pub fn Uart(comptime index: usize) type {
},
2 => {
regs.SYSCON.PCONP.modify(.{ .PCUART2 = 1 });
regs.SYSCON.PCLKSEL0.modify(.{ .PCLK_UART2 = @enumToInt(uart.CClkDiv.four) });
regs.SYSCON.PCLKSEL1.modify(.{ .PCLK_UART2 = @enumToInt(uart.CClkDiv.four) });
},
3 => {
regs.SYSCON.PCONP.modify(.{ .PCUART3 = 1 });
regs.SYSCON.PCLKSEL0.modify(.{ .PCLK_UART3 = @enumToInt(uart.CClkDiv.four) });
regs.SYSCON.PCLKSEL1.modify(.{ .PCLK_UART3 = @enumToInt(uart.CClkDiv.four) });
},
else => unreachable,
}

@ -19,7 +19,18 @@
//!
//! results in PCLK1 = 16 MHz.
//!
//! TODO: add more clock calculations when adding Uart
//! The above default configuration makes U(S)ART2..5
//! receive a 16 MHz clock by default.
//!
//! USART1 and USART6 use PCLK2, which uses the APB2 prescaler on HCLK,
//! default APB2 prescaler = /1:
//!
//! ```
//! regs.RCC.CFGR.modify(.{ .PPRE2 = 0 });
//! ```
//!
//! and therefore USART1 and USART6 receive a 16 MHz clock.
//!
const std = @import("std");
const micro = @import("microzig");
@ -28,6 +39,12 @@ const regs = chip.registers;
pub usingnamespace chip;
// Default clock frequencies after reset, see top comment for calculation
// TODO: these would need to change when we support multiple clock configurations
pub const ahb_frequency = 16_000_000;
pub const apb1_frequency = 16_000_000;
pub const apb2_frequency = 16_000_000;
pub fn parsePin(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.";
@ -77,3 +94,147 @@ pub const gpio = struct {
}
}
};
pub const uart = struct {
pub const DataBits = enum {
seven,
eight,
nine,
};
/// uses the values of USART_CR2.STOP
pub const StopBits = enum(u2) {
one = 0b00,
half = 0b01,
two = 0b10,
one_and_half = 0b11,
};
/// uses the values of USART_CR1.PS
pub const Parity = enum(u1) {
even = 0,
odd = 1,
};
};
pub fn Uart(comptime index: usize) type {
if (!(index == 2)) @compileError("TODO: only USART2 is currently supported");
return struct {
parity_read_mask: u8,
const Self = @This();
pub fn init(config: micro.uart.Config) !Self {
// The following must all be written when the USART is disabled (UE=0).
if (regs.USART2.CR1.read().UE == 1)
@panic("Trying to initialize USART2 while it is already enabled");
// LATER: Alternatively, set UE=0 at this point? Then wait for something?
// Or add a destroy() function which disables the USART?
// enable the USART2 clock
regs.RCC.APB1ENR.modify(.{ .USART2EN = 1 });
// enable GPIOA clock
regs.RCC.AHB1ENR.modify(.{ .GPIOAEN = 1 });
// set PA2+PA3 to alternate function 7, USART2_TX + USART2_RX
regs.GPIOA.MODER.modify(.{ .MODER2 = 0b10, .MODER3 = 0b10 });
regs.GPIOA.AFRL.modify(.{ .AFRL2 = 7, .AFRL3 = 7 });
// clear USART2 configuration to its default
regs.USART2.CR1.raw = 0;
regs.USART2.CR2.raw = 0;
regs.USART2.CR3.raw = 0;
// Return error for unsupported combinations
if (config.data_bits == .nine and config.parity != null) {
// TODO: should we consider this an unsupported word size or unsupported parity?
return error.UnsupportedWordSize;
} else if (config.data_bits == .seven and config.parity == null) {
// TODO: should we consider this an unsupported word size or unsupported parity?
return error.UnsupportedWordSize;
}
// set word length
// Per the reference manual, M means
// - 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
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 });
// set parity
if (config.parity) |parity| {
regs.USART2.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
// set number of stop bits
regs.USART2.CR2.modify(.{ .STOP = @enumToInt(config.stop_bits) });
// set the baud rate
// Despite the reference manual talking about fractional calculation and other buzzwords,
// it is actually just a simple divider. Just ignore DIV_Mantissa and DIV_Fraction and
// set the result of the division as the lower 16 bits of BRR.
// TODO: We assume the default OVER8=0 configuration above (i.e. 16x oversampling).
// 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?
const usartdiv = @intCast(u16, @divTrunc(apb1_frequency, config.baud_rate));
regs.USART2.BRR.raw = usartdiv;
// enable USART2, and its transmitter and receiver
regs.USART2.CR1.modify(.{ .UE = 1 });
regs.USART2.CR1.modify(.{ .TE = 1 });
regs.USART2.CR1.modify(.{ .RE = 1 });
// For code simplicity, at cost of one or more register reads,
// we read back the actual configuration from the registers,
// instead of using the `config` values.
return readFromRegisters();
}
pub fn getOrInit(config: micro.uart.Config) !Self {
if (regs.USART2.CR1.read().UE == 1) {
// UART1 already enabled, don't reinitialize and disturb things;
// instead read and use the actual configuration.
return readFromRegisters();
} else return init(config);
}
fn readFromRegisters() Self {
const cr1 = regs.USART2.CR1.read();
// 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,
// then we also mask away the 8th bit.
return Self{ .parity_read_mask = if (cr1.PCE == 1 and cr1.M == 0) 0x7F else 0xFF };
}
pub fn canWrite(self: Self) bool {
_ = self;
return switch (regs.USART2.SR.read().TXE) {
1 => true,
0 => false,
};
}
pub fn tx(self: Self, ch: u8) void {
while (!self.canWrite()) {} // Wait for Previous transmission
regs.USART2.DR.modify(ch);
}
pub fn txflush(_: Self) void {
while (regs.USART2.SR.read().TC == 0) {}
}
pub fn canRead(self: Self) bool {
_ = self;
return switch (regs.USART2.SR.read().RXNE) {
1 => true,
0 => false,
};
}
pub fn rx(self: Self) u8 {
while (!self.canRead()) {} // Wait till the data is received
const data_with_parity_bit: u9 = regs.USART2.DR.read();
return @intCast(u8, data_with_parity_bit & self.parity_read_mask);
}
};
}

@ -1,120 +1,76 @@
const micro = @import("microzig");
// Configures the led_pin to a hardware pin
const uart_txd_pin = micro.Pin("P0.15");
const uart_rxd_pin = micro.Pin("P0.16");
const debug_pin = micro.Pin("P0.17");
const led1_pin = micro.Pin("P1.18");
const led2_pin = micro.Pin("P1.20");
const led3_pin = micro.Pin("P1.21");
const led4_pin = micro.Pin("P1.23");
pub const cpu_frequency: u32 = 100_000_000; // 100 MHz
const PLL = struct {
fn init() void {
reset_overclocking();
const led_pin = if (micro.config.has_board)
switch (micro.config.board_name) {
.@"Arduino Nano" => micro.Pin("D13"),
.@"mbed LPC1768" => micro.Pin("LED-1"),
.@"STM32F3DISCOVERY" => micro.Pin("LD3"),
.@"STM32F4DISCOVERY" => micro.Pin("LD5"),
.@"STM32F429IDISCOVERY" => micro.Pin("LD4"),
else => @compileError("unknown board"),
}
else switch (micro.config.chip_name) {
.@"ATmega328p" => micro.Pin("PB5"),
.@"NXP LPC1768" => micro.Pin("P1.18"),
.@"STM32F103x8" => micro.Pin("PC13"),
else => @compileError("unknown chip"),
};
fn reset_overclocking() void {
overclock_flash(5); // 5 cycles access time
overclock_pll(3); // 100 MHz
}
fn overclock_flash(timing: u4) void {
micro.chip.registers.SYSCON.FLASHCFG.modify(.{
.FLASHTIM = timing - 1,
});
}
inline fn feed_pll() void {
micro.chip.registers.SYSCON.PLL0FEED.modify(.{ .PLL0FEED = 0xAA });
micro.chip.registers.SYSCON.PLL0FEED.modify(.{ .PLL0FEED = 0x55 });
}
fn overclock_pll(divider: u8) void {
// PLL einrichten für RC
micro.chip.registers.SYSCON.PLL0CON.modify(.{
.PLLE0 = 0,
.PLLC0 = 0,
});
feed_pll();
micro.chip.registers.SYSCON.CLKSRCSEL.modify(.{ .CLKSRC = 0 }); // RC-Oszillator als Quelle
micro.chip.registers.SYSCON.PLL0CFG.modify(.{
// SysClk = (4MHz / 2) * (2 * 75) = 300 MHz
.MSEL0 = 74,
.NSEL0 = 1,
});
// CPU Takt = SysClk / divider
micro.chip.registers.SYSCON.CCLKCFG.modify(.{ .CCLKSEL = divider - 1 });
feed_pll();
micro.chip.registers.SYSCON.PLL0CON.modify(.{ .PLLE0 = 1 }); // PLL einschalten
feed_pll();
var i: usize = 0;
while (i < 1_000) : (i += 1) {
micro.cpu.nop();
}
micro.chip.registers.SYSCON.PLL0CON.modify(.{ .PLLC0 = 1 });
feed_pll();
// 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"),
};
pub fn main() !void {
const gpio_init = .{ .mode = .output, .initial_state = .low };
const led1 = micro.Gpio(led1_pin, gpio_init);
const led2 = micro.Gpio(led2_pin, gpio_init);
const led3 = micro.Gpio(led3_pin, gpio_init);
const led4 = micro.Gpio(led4_pin, gpio_init);
const status = micro.Gpio(debug_pin, .{
const led = micro.Gpio(led_pin, .{
.mode = .output,
.initial_state = .high,
.initial_state = .low,
});
status.init();
led1.init();
led2.init();
led3.init();
led4.init();
led.init();
uart_txd_pin.route(.func01);
uart_rxd_pin.route(.func01);
PLL.init();
led1.setToHigh();
var debug_port = micro.Uart(1).init(.{
var uart = micro.Uart(uart_idx).init(.{
.baud_rate = 9600,
.stop_bits = .one,
.parity = null,
.data_bits = .eight,
}) catch |err| {
led1.write(if (err == error.UnsupportedBaudRate) micro.gpio.State.low else .high);
led2.write(if (err == error.UnsupportedParity) micro.gpio.State.low else .high);
led3.write(if (err == error.UnsupportedStopBitCount) micro.gpio.State.low else .high);
led4.write(if (err == error.UnsupportedWordSize) micro.gpio.State.low else .high);
blinkError(led, err);
micro.hang();
};
led2.setToHigh();
var out = debug_port.writer();
var in = debug_port.reader();
_ = in;
try out.writeAll("Please enter a sentence:\r\n");
led3.setToHigh();
var out = uart.writer();
while (true) {
try out.writeAll(".");
led4.toggle();
led.setToHigh();
try out.writeAll("Hello microzig!\r\n");
led.setToLow();
micro.debug.busySleep(100_000);
}
}
fn blinkError(led: anytype, err: micro.uart.InitError) void {
var blinks: u3 =
switch (err) {
error.UnsupportedBaudRate => 1,
error.UnsupportedParity => 2,
error.UnsupportedStopBitCount => 3,
error.UnsupportedWordSize => 4,
};
while (blinks > 0) : (blinks -= 1) {
led.setToHigh();
micro.debug.busySleep(1_000_000);
led.setToLow();
micro.debug.busySleep(1_000_000);
}
}

Loading…
Cancel
Save