Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/wch/ch32v/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub fn build(b: *std.Build) void {
.{ .target = mb.ports.ch32v.chips.ch32v203x6, .name = "blinky_systick_ch32v203", .file = "src/blinky_systick.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v203.suzuduino_uno_v1b, .name = "suzuduino_blinky", .file = "src/board_blinky.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_ws2812", .file = "src/ws2812.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_uart_log", .file = "src/uart_log.zig" },

// CH32V30x
.{ .target = mb.ports.ch32v.chips.ch32v303xb, .name = "empty_ch32v303", .file = "src/empty.zig" },
Expand Down
47 changes: 47 additions & 0 deletions examples/wch/ch32v/src/uart_log.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const std = @import("std");
const microzig = @import("microzig");
const hal = microzig.hal;
const time = hal.time;
const gpio = hal.gpio;

const RCC = microzig.chip.peripherals.RCC;
const AFIO = microzig.chip.peripherals.AFIO;

const usart = hal.usart.instance.USART2;
const usart_tx_pin = gpio.Pin.init(0, 2); // PA2

pub const microzig_options = microzig.Options{
.log_level = .debug,
.logFn = hal.usart.log,
};

pub fn main() !void {
// Board brings up clocks and time
microzig.board.init();

// Enable peripheral clocks for USART2 and GPIOA
RCC.APB2PCENR.modify(.{
.IOPAEN = 1, // Enable GPIOA clock
.AFIOEN = 1, // Enable AFIO clock
});
RCC.APB1PCENR.modify(.{
.USART2EN = 1, // Enable USART2 clock
});

// Ensure USART2 is NOT remapped (default PA2/PA3, not PD5/PD6)
AFIO.PCFR1.modify(.{ .USART2_RM = 0 });

// Configure PA2 as alternate function push-pull for USART2 TX
usart_tx_pin.set_output_mode(.alternate_function_push_pull, .max_50MHz);

// Initialize USART2 at 115200 baud
usart.apply(.{ .baud_rate = 115200 });

hal.usart.init_logger(usart);

var i: u32 = 0;
while (true) : (i += 1) {
std.log.info("what {}", .{i});
time.sleep_ms(1000);
}
}
1 change: 1 addition & 0 deletions port/wch/ch32v/src/hals/ch32v103.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub const pins = @import("pins.zig");
pub const gpio = @import("gpio.zig");
pub const clocks = @import("clocks.zig");
pub const time = @import("time.zig");
pub const usart = @import("usart.zig");

/// Initialize HAL subsystems used by default
/// CH32V103: set clock to 48 MHz via HSI PLL; SysTick driver differs on 103, so time is not enabled here.
Expand Down
2 changes: 2 additions & 0 deletions port/wch/ch32v/src/hals/ch32v20x.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub const pins = @import("pins.zig");
pub const gpio = @import("gpio.zig");
pub const clocks = @import("clocks.zig");
pub const time = @import("time.zig");
pub const usart = @import("usart.zig");

pub const default_interrupts: microzig.cpu.InterruptOptions = .{
// Default TIM2 handler provided by the HAL for 1ms timekeeping
Expand All @@ -18,4 +19,5 @@ test "hal tests" {
_ = clocks;
_ = gpio;
_ = time;
_ = usart;
}
1 change: 1 addition & 0 deletions port/wch/ch32v/src/hals/ch32v30x.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub const pins = @import("pins.zig");
pub const gpio = @import("gpio.zig");
pub const clocks = @import("clocks.zig");
pub const time = @import("time.zig");
pub const usart = @import("usart.zig");

/// Default interrupt handlers provided by the HAL
pub const default_interrupts: microzig.cpu.InterruptOptions = .{
Expand Down
184 changes: 182 additions & 2 deletions port/wch/ch32v/src/hals/clocks.zig
Original file line number Diff line number Diff line change
@@ -1,9 +1,85 @@
const microzig = @import("microzig");

const RCC = microzig.chip.peripherals.RCC;
const EXTEND = microzig.chip.peripherals.EXTEND;

const gpioBlock = enum {
A,
B,
C,
D,
};

pub fn enable_gpio_clock(block: gpioBlock) void {
// TODO: How do we know if we need to set the AFIOEN?
switch (block) {
.A => RCC.APB2PCENR.modify(.{
.IOPAEN = 1,
.AFIOEN = 1,
}),
.B => RCC.APB2PCENR.modify(.{
.IOPBEN = 1,
.AFIOEN = 1,
}),
.C => RCC.APB2PCENR.modify(.{
.IOPCEN = 1,
.AFIOEN = 1,
}),
.D => RCC.APB2PCENR.modify(.{
.IOPDEN = 1,
.AFIOEN = 1,
}),
}
}

const peripheral = enum {
// APB2 peripherals
USART1,
SPI1,
ADC1,
ADC2,
TIM1,

// APB1 peripherals
USART2,
USART3,
I2C1,
I2C2,
SPI2,
TIM2,
TIM3,
TIM4,
};

pub fn enable_peripheral_clock(p: peripheral) void {
switch (p) {
// APB2 peripherals (high-speed bus)
.USART1 => RCC.APB2PCENR.modify(.{ .USART1EN = 1 }),
.SPI1 => RCC.APB2PCENR.modify(.{ .SPI1EN = 1 }),
.ADC1 => RCC.APB2PCENR.modify(.{ .ADC1EN = 1 }),
.ADC2 => RCC.APB2PCENR.modify(.{ .ADC2EN = 1 }),
.TIM1 => RCC.APB2PCENR.modify(.{ .TIM1EN = 1 }),

// APB1 peripherals (low-speed bus)
.USART2 => RCC.APB1PCENR.modify(.{ .USART2EN = 1 }),
.USART3 => RCC.APB1PCENR.modify(.{ .USART3EN = 1 }),
.I2C1 => RCC.APB1PCENR.modify(.{ .I2C1EN = 1 }),
.I2C2 => RCC.APB1PCENR.modify(.{ .I2C2EN = 1 }),
.SPI2 => RCC.APB1PCENR.modify(.{ .SPI2EN = 1 }),
.TIM2 => RCC.APB1PCENR.modify(.{ .TIM2EN = 1 }),
.TIM3 => RCC.APB1PCENR.modify(.{ .TIM3EN = 1 }),
.TIM4 => RCC.APB1PCENR.modify(.{ .TIM4EN = 1 }),
}
}

/// Enable AFIO (Alternate Function I/O) clock
/// Required for pin remapping and external interrupt configuration
pub fn enable_afio_clock() void {
RCC.APB2PCENR.modify(.{ .AFIOEN = 1 });
}

/// Initialize system clock to 48 MHz using HSI
pub fn init_48mhz_hsi() void {
const RCC = microzig.chip.peripherals.RCC;
const EXTEND = microzig.chip.peripherals.EXTEND;
// Enable PLL HSI prescaler (HSIPRE bit)
EXTEND.EXTEND_CTR.modify(.{ .HSIPRE = 1 });

Expand All @@ -29,3 +105,107 @@ pub fn init_48mhz_hsi() void {
// Wait for PLL to be used as system clock (SWS should be 2)
while (RCC.CFGR0.read().SWS != 2) {}
}

const ClockSpeeds = struct {
sysclk: u32,
hclk: u32,
pclk1: u32,
pclk2: u32,
adcclk: u32,
};

// Clock constants
const HSI_VALUE: u32 = 8_000_000; // 8 MHz internal oscillator
const HSE_VALUE: u32 = 8_000_000; // 8 MHz external oscillator (board-dependent)

// Prescaler lookup table for AHB/APB buses
// Index into this table with the HPRE/PPRE bits, result is the shift amount
const prescaler_table = [16]u8{ 0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 3, 4, 6, 7, 8, 9 };

// ADC prescaler lookup table (divisor values)
const adc_prescaler_table = [4]u8{ 2, 4, 6, 8 };

/// Get the current clock frequencies by reading RCC registers
/// Based on WCH's RCC_GetClocksFreq() implementation
pub fn get_freqs() ClockSpeeds {
var sysclk: u32 = 0;

// Determine system clock source from SWS (System Clock Switch Status)
const sws = RCC.CFGR0.read().SWS;

switch (sws) {
0 => {
// HSI used as system clock
sysclk = HSI_VALUE;
},
1 => {
// HSE used as system clock
sysclk = HSE_VALUE;
},
2 => {
// PLL used as system clock
const cfgr0 = RCC.CFGR0.read();
const pllmul_bits = cfgr0.PLLMUL;
const pllsrc = cfgr0.PLLSRC;

// PLL multiplication factor: PLLMUL bits + 2
// Special case: if result is 17, it's actually 18
var pllmul: u32 = @as(u32, pllmul_bits) + 2;
if (pllmul == 17) pllmul = 18;

if (pllsrc == 0) {
// PLL source is HSI
const hsipre = EXTEND.EXTEND_CTR.read().HSIPRE;
if (hsipre == 1) {
// HSI not divided
sysclk = HSI_VALUE * pllmul;
} else {
// HSI divided by 2
sysclk = (HSI_VALUE >> 1) * pllmul;
}
} else {
// PLL source is HSE
const pllxtpre = cfgr0.PLLXTPRE;
if (pllxtpre == 1) {
// HSE divided by 2 before PLL
sysclk = (HSE_VALUE >> 1) * pllmul;
} else {
// HSE not divided
sysclk = HSE_VALUE * pllmul;
}
}
},
else => {
// Default to HSI
sysclk = HSI_VALUE;
},
}

// Calculate AHB clock (HCLK) from SYSCLK using HPRE prescaler
const hpre = RCC.CFGR0.read().HPRE;
const hpre_shift = prescaler_table[hpre];
const hclk = sysclk >> @as(u5, @intCast(hpre_shift));

// Calculate APB1 clock (PCLK1) from HCLK using PPRE1 prescaler
const ppre1 = RCC.CFGR0.read().PPRE1;
const ppre1_shift = prescaler_table[ppre1];
const pclk1 = hclk >> @as(u5, @intCast(ppre1_shift));

// Calculate APB2 clock (PCLK2) from HCLK using PPRE2 prescaler
const ppre2 = RCC.CFGR0.read().PPRE2;
const ppre2_shift = prescaler_table[ppre2];
const pclk2 = hclk >> @as(u5, @intCast(ppre2_shift));

// Calculate ADC clock from PCLK2 using ADCPRE
const adcpre = RCC.CFGR0.read().ADCPRE;
const adc_divisor: u32 = adc_prescaler_table[adcpre];
const adcclk = pclk2 / adc_divisor;

return .{
.sysclk = sysclk,
.hclk = hclk,
.pclk1 = pclk1,
.pclk2 = pclk2,
.adcclk = adcclk,
};
}
31 changes: 31 additions & 0 deletions port/wch/ch32v/src/hals/gpio.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const microzig = @import("microzig");
pub const peripherals = microzig.chip.peripherals;
const clocks = microzig.hal.clocks;

const GPIOA = peripherals.GPIOA;
const GPIOB = peripherals.GPIOB;
Expand Down Expand Up @@ -44,6 +45,11 @@ pub const Pull = enum {
down,
};

pub const AlternateFunctionMode = enum {
push_pull,
open_drain,
};

// NOTE: With this current setup, every time we want to do anythting we go through a switch
// Do we want this?
// NOTE: How about to use port_config_mask, _value as previous apply function?
Expand Down Expand Up @@ -109,6 +115,31 @@ pub const Pin = packed struct(u8) {
gpio.write_pin_config(config);
}

pub inline fn enable_clock(gpio: Pin) void {
// TODO: Cleanup!
clocks.enable_gpio_clock(switch (gpio.get_port()) {
GPIOA => .A,
GPIOB => .B,
GPIOC => .C,
GPIOD => .D,
else => unreachable,
});
}

/// Configure pin for alternate function use (e.g., USART, I2C, SPI)
/// Combines enable_clock + set_output_mode for convenience
pub inline fn configure_alternate_function(
gpio: Pin,
mode: AlternateFunctionMode,
speed: Speed,
) void {
gpio.enable_clock();
switch (mode) {
.push_pull => gpio.set_output_mode(.alternate_function_push_pull, speed),
.open_drain => gpio.set_output_mode(.alternate_function_open_drain, speed),
}
}

pub inline fn set_pull(gpio: Pin, pull: Pull) void {
var port = gpio.get_port();
switch (pull) {
Expand Down
3 changes: 2 additions & 1 deletion port/wch/ch32v/src/hals/pins.zig
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ pub const GlobalConfiguration = struct {
const used_gpios = comptime input_gpios | output_gpios;

if (used_gpios != 0) {
// Figure out IO port enable bit from name
const offset = @as(u3, @intFromEnum(@field(Port, port_field.name))) + 2;
RCC.APB2PCENR.raw |= @as(u32, 1 << offset);
}
Expand All @@ -242,7 +243,7 @@ pub const GlobalConfiguration = struct {
}
}

// Set upll-up and pull-down.
// Set pull-up and pull-down.
if (input_gpios != 0) {
inline for (@typeInfo(Port.Configuration).@"struct".fields) |field|
if (@field(port_config, field.name)) |pin_config| {
Expand Down
Loading
Loading