Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
d272589
neccessary -> necessary and fix linter bot issue
piotrfila Jan 10, 2026
3e3b6de
scan unhandled buffer linearly
piotrfila Jan 10, 2026
b587b46
redo endpoint handling
piotrfila Jan 10, 2026
a653711
separate getting usb rx data and requesting more
piotrfila Jan 10, 2026
9504866
make CdcClassDriver rx interrupt-safe
piotrfila Jan 10, 2026
52ea727
add usb packet length type
piotrfila Jan 10, 2026
b655989
make CdcClass driver tx interrupt-safe
piotrfila Jan 10, 2026
503c998
allow partial usb tx and require calling ep_writev and ep_listen only…
piotrfila Jan 10, 2026
93ce596
documentation and configurable handler names
piotrfila Jan 10, 2026
f210076
first draft of an example driver that explains the comptime driver in…
piotrfila Jan 10, 2026
942aa42
more error checking around interfaces and string descriptors
piotrfila Jan 10, 2026
340d9c3
endpoint_open -> ep_open for consistency
piotrfila Jan 10, 2026
9eabcab
style fixes
piotrfila Jan 10, 2026
870f0d6
appease linter bot
piotrfila Jan 11, 2026
2a79c78
add u32 little endian wrapper
piotrfila Jan 11, 2026
f553d34
respect endianness in setup packet and shortcuts for bulk and interru…
piotrfila Jan 11, 2026
27edab3
start at ch32v usb
Copper280z Jan 11, 2026
2ed6b4d
cleanum usb examples
piotrfila Jan 13, 2026
cd4e348
change the example usb driver to a simple echo
piotrfila Jan 13, 2026
0959d37
assign endpoints and interfaces (more) automatically
piotrfila Jan 13, 2026
36f02ea
better handling of device class, subclass and protocol
piotrfila Jan 13, 2026
b7bed05
separate usb device and controller
piotrfila Jan 13, 2026
93cbc87
more convenient descriptor creation
piotrfila Jan 13, 2026
55921cc
handle bus resets correctly
piotrfila Jan 13, 2026
f373714
fix startup
Copper280z Jan 17, 2026
bbe968f
more examples cleanup
piotrfila Jan 13, 2026
baee980
add BOS descriptor
piotrfila Jan 17, 2026
ae7d203
simplify setup packet handling
piotrfila Jan 17, 2026
a6e8c89
add usb device logging
piotrfila Jan 17, 2026
66f0116
better use scoped logging
piotrfila Jan 17, 2026
f07fe5c
reorganize ClassSubclassProtocol
piotrfila Jan 17, 2026
c40f907
adhere more to style guidelines
piotrfila Jan 17, 2026
b9f61f5
...again
piotrfila Jan 17, 2026
8dd3a7b
add USBHD/USBHS device backend implementation
Copper280z Jan 17, 2026
4c399ef
add enums to CDC
piotrfila Jan 18, 2026
3f494d0
cleanup
piotrfila Jan 18, 2026
5079a5b
Merge branch 'main' into usb-driver-rework
piotrfila Jan 18, 2026
6474eab
EpNum -> EP_Num and fix pins in examples
piotrfila Jan 18, 2026
4e00819
usb periph is alive and responding to host, but not enumerating corre…
Copper280z Jan 18, 2026
94c4e64
change to work with v307 evt
Copper280z Jan 18, 2026
32feeb7
Logging updates and call on_buffer at end of setup flag block
Copper280z Jan 18, 2026
5fff85c
add fifo overflow warning and reduce SOF spam
Copper280z Jan 18, 2026
efefeae
logging swirl
Copper280z Jan 18, 2026
6f6ba02
implement GetStatus
piotrfila Jan 18, 2026
c233851
make it to config descriptor but fail to send 2nd packet.
Copper280z Jan 18, 2026
872e794
driver handler type safety
piotrfila Jan 19, 2026
52396d6
successful enumeration!
Copper280z Jan 19, 2026
aa8034d
inline get_setup_packet()
piotrfila Jan 19, 2026
7667ea7
fix ep_writev only using the first buffer
piotrfila Jan 19, 2026
1a1aab8
add rp2xxx reset interface
piotrfila Jan 21, 2026
6c89d64
start at ch32v usb
Copper280z Jan 11, 2026
64d16ce
fix startup
Copper280z Jan 17, 2026
e8c6a40
add USBHD/USBHS device backend implementation
Copper280z Jan 17, 2026
8f4fdcc
usb periph is alive and responding to host, but not enumerating corre…
Copper280z Jan 18, 2026
d36e85c
change to work with v307 evt
Copper280z Jan 18, 2026
a3673d8
Logging updates and call on_buffer at end of setup flag block
Copper280z Jan 18, 2026
321c317
add fifo overflow warning and reduce SOF spam
Copper280z Jan 18, 2026
59450b4
logging swirl
Copper280z Jan 18, 2026
07bfc32
make it to config descriptor but fail to send 2nd packet.
Copper280z Jan 18, 2026
2bbaf5b
successful enumeration!
Copper280z Jan 19, 2026
66e9c32
minor changes while hunting down why cdc transfers don't work.
Copper280z Jan 22, 2026
00fe33b
Fix data toggle config
Copper280z Jan 22, 2026
2011590
Fixed data toggles, CDC Data flows!
Copper280z Jan 22, 2026
9865fe3
comment out some logging
Copper280z Jan 22, 2026
985d6e8
Merge branch 'origin/umain' of https://github.com/Copper280z/microzig…
Copper280z Jan 22, 2026
c337146
Refactor ch32 USB logging and enhance clock configuration for CH32V b…
Copper280z Jan 25, 2026
baed799
Refactor USBHS interrupt handling and switch toggle management to man…
Copper280z Jan 25, 2026
df4e181
Refactor USB polling logic into a separate function and clean up unus…
Copper280z Jan 25, 2026
dc8ec01
Merge remote-tracking branch 'upstream/main' into ch32_usb
Copper280z Jan 31, 2026
ad686c3
update for latest usb core changes
Copper280z Jan 31, 2026
fce6646
remove unfinished usbd driver
Copper280z Jan 31, 2026
8a0e752
revert to upstream with minimal updates for usbhs
Copper280z Jan 31, 2026
57f498f
revert changes to uart_log
Copper280z Jan 31, 2026
8b9704c
remove old example driver
Copper280z Jan 31, 2026
0049e03
cleanup
Copper280z Jan 31, 2026
01624dc
cleanup usbhs clock setup
Copper280z Jan 31, 2026
19cc7f3
more cleanup
Copper280z Jan 31, 2026
493c041
revert board clock setup to one that works on current upstream.
Copper280z Jan 31, 2026
02ce566
revert most of clocks.zig to upstream.
Copper280z Jan 31, 2026
349fb6a
mirror rp2xxx better
Copper280z Jan 31, 2026
e0cef46
revert usart changes, cleanup example to match rp2xxx better
Copper280z Jan 31, 2026
0717b51
adjust comment
Copper280z Jan 31, 2026
b9892ab
get rid of regs function, change a log call
Copper280z Jan 31, 2026
0a2bde0
usbfs works
Copper280z Feb 3, 2026
e322393
Add usbfs clock setup function, add support for v203 chips and nanoch…
Copper280z Feb 5, 2026
026395f
Add more example build defs, clean up dev noise, add usb product stri…
Copper280z Feb 6, 2026
a4927cc
remove zigscient rootid tag
Copper280z Feb 6, 2026
0480805
clear IRQ flags after work is done to ensure clean NAK/ACK transition.
Copper280z Feb 6, 2026
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
2 changes: 1 addition & 1 deletion core/src/core/usb.zig
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ pub fn DeviceController(config: Config, driver_args: config.DriverArgs()) type {
const desc_device: descriptor.Device = .{
.bcd_usb = config.bcd_usb,
.device_triple = config.device_triple,
.max_packet_size0 = @max(config.max_supported_packet_size, 64),
.max_packet_size0 = @min(config.max_supported_packet_size, 64),
.vendor = .from(config.vendor.id),
.product = .from(config.product.id),
.bcd_device = config.bcd_device,
Expand Down
4 changes: 4 additions & 0 deletions examples/wch/ch32v/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ pub fn build(b: *std.Build) void {
.{ .target = mb.ports.ch32v.chips.ch32v203x6, .name = "blinky_ch32v203", .file = "src/blinky.zig" },
.{ .target = mb.ports.ch32v.chips.ch32v203x6, .name = "blinky_systick_ch32v203", .file = "src/blinky_systick.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v203.nano_ch32v203, .name = "nano_ch32v203_blinky", .file = "src/board_blinky.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v203.nano_ch32v203, .name = "nano_ch32v203_usb_cdc", .file = "src/usb_cdc.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v203.suzuduino_uno_v1b, .name = "suzuduino_blinky", .file = "src/board_blinky.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v203.suzuduino_uno_v1b, .name = "suzuduino_usb_cdc", .file = "src/usb_cdc.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_dma", .file = "src/dma.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" },
Expand All @@ -46,13 +48,15 @@ pub fn build(b: *std.Build) void {
.{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_spi_loopback", .file = "src/spi_loopback.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_spi_flash_w25q", .file = "src/spi_flash_w25q.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_sharp_niceview", .file = "src/sharp_niceview.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_usb_cdc", .file = "src/usb_cdc.zig" },

// CH32V30x
.{ .target = mb.ports.ch32v.chips.ch32v303xb, .name = "empty_ch32v303", .file = "src/empty.zig" },
.{ .target = mb.ports.ch32v.chips.ch32v303xb, .name = "blinky_ch32v303", .file = "src/blinky.zig" },
.{ .target = mb.ports.ch32v.chips.ch32v303xb, .name = "blinky_systick_ch32v303", .file = "src/blinky_systick.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v305.nano_ch32v305, .name = "nano_ch32v305_blinky", .file = "src/board_blinky.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v307.ch32v307v_r1_1v0, .name = "ch32v307v_r1_1v0_blinky", .file = "src/blinky.zig" },
.{ .target = mb.ports.ch32v.boards.ch32v307.ch32v307v_r1_1v0, .name = "ch32v307v_r1_1v0_usb_cdc", .file = "src/usb_cdc.zig" },
};

for (available_examples) |example| {
Expand Down
139 changes: 139 additions & 0 deletions examples/wch/ch32v/src/usb_cdc.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
const std = @import("std");
const microzig = @import("microzig");

const hal = microzig.hal;
const board = microzig.board;
const time = hal.time;
const gpio = hal.gpio;
const usb = microzig.core.usb;
const USB_Serial = usb.drivers.CDC;

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

const usart = hal.usart.instance.USART1;

const usart_tx_pin = gpio.Pin.init(0, 9); // PA9
const func_pin = gpio.Pin.init(0, 10); // PA10
const tog_pin = gpio.Pin.init(0, 14);
const mco_pin = gpio.Pin.init(0, 8);

pub const microzig_options = microzig.Options{
.logFn = hal.usart.log,
.log_level = .debug,
.log_scope_levels = &.{
.{ .scope = .usb_dev, .level = .warn },
.{ .scope = .usb_ctrl, .level = .warn },
.{ .scope = .usb_cdc, .level = .warn },
},
};

const USBController = usb.DeviceController(.{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Rename USBController to USB_Controller, it should be more in line with our style guidelines. This automation is not perfect so take it with a grain of salt.

.bcd_usb = .v2_00,
.device_triple = .unspecified,
.vendor = .{ .id = 0x2E8A, .str = "MicroZig" },
.product = .{ .id = 0x000A, .str = board.product_string },
.bcd_device = .v1_00,
.serial = "someserial",
.max_supported_packet_size = hal.usb.max_packet_size,
.configurations = &.{.{
.attributes = .{ .self_powered = false },
.max_current_ma = 50,
.Drivers = struct { serial: USB_Serial },
}},
}, .{.{
.serial = .{ .itf_notifi = "Board CDC", .itf_data = "Board CDC Data" },
}});

pub var usb_dev: hal.usb.Polled(
.{},
) = undefined;

var usb_controller: USBController = .init;

pub fn main() !void {
// Board brings up clocks and time
microzig.board.init();
microzig.hal.init();
// Enable peripheral clocks for USART1 and GPIOA
RCC.APB2PCENR.modify(.{
.IOPAEN = 1, // Enable GPIOA clock
.IOPCEN = 1,
.AFIOEN = 1, // Enable AFIO clock
.USART1EN = 1, // Enable USART1 clock
});
// Configure TX pin as alternate function push-pull
usart_tx_pin.set_output_mode(.alternate_function_push_pull, .max_50MHz);

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

hal.usart.init_logger(usart);
std.log.info("UART logging initialized.", .{});

std.log.info("Initializing USB device.", .{});

usb_dev = .init();

var i: u32 = 0;
var old: u64 = time.get_time_since_boot().to_us();
var new: u64 = 0;

while (true) {
if (usb_controller.drivers()) |drivers| {
new = time.get_time_since_boot().to_us();
if (new - old > 500000) {
old = new;
i += 1;
std.log.info("cdc test: {}", .{i});

usb_cdc_write(&drivers.serial, "This is very very very long text sent from ch32 by USB CDC to your device: {}\r\n", .{i});
}

// read and print host command if present
const message = usb_cdc_read(&drivers.serial);
if (message.len > 0) {
usb_cdc_write(&drivers.serial, "Your message to me was: {s}\r\n", .{message});
}
}
usb_dev.poll(false, &usb_controller);
}
}

var usb_tx_buff: [1024]u8 = undefined;

// Transfer data to host
// NOTE: After each USB chunk transfer, we have to call the USB task so that bus TX events can be handled
pub fn usb_cdc_write(serial: *USB_Serial, comptime fmt: []const u8, args: anytype) void {
const text = std.fmt.bufPrint(&usb_tx_buff, fmt, args) catch &.{};
var write_buff = text;
while (write_buff.len > 0) {
write_buff = write_buff[serial.write(write_buff)..];
while (!serial.flush()) {
// pins.tog.put(0);
usb_dev.poll(false, &usb_controller);
// pins.tog.put(1);
}
}
}

var usb_rx_buff: [1024]u8 = undefined;

// Receive data from host
// NOTE: Read code was not tested extensively. In case of issues, try to call USB task before every read operation
pub fn usb_cdc_read(
serial: *USB_Serial,
) []const u8 {
var total_read: usize = 0;
var read_buff: []u8 = usb_rx_buff[0..];

while (true) {
const len = serial.read(read_buff);
read_buff = read_buff[len..];
total_read += len;
if (len == 0) break;
}

return usb_rx_buff[0..total_read];
}
5 changes: 4 additions & 1 deletion port/wch/ch32v/src/boards/CH32V307V-R1-1v0.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ pub const microzig = @import("microzig");
pub const chip = @import("chip");
const ch32v = microzig.hal;

pub const product_string = "ch32v307 Demo Device";

/// Clock configuration for this board
pub const clock_config: ch32v.clocks.Config = .{
.source = .hsi,
.source = .hse,
.hse_frequency = 8_000_000,
.target_frequency = 48_000_000,
};

Expand Down
2 changes: 2 additions & 0 deletions port/wch/ch32v/src/boards/CH32Vx03C-R0-1v0.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pub const microzig = @import("microzig");
pub const chip = @import("chip");
const ch32v = microzig.hal;

pub const product_string = "ch32vx03c Demo Device";

/// Clock configuration for this board
pub const clock_config: ch32v.clocks.Config = .{
.source = .hsi,
Expand Down
2 changes: 2 additions & 0 deletions port/wch/ch32v/src/boards/LANA_TNY.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub const microzig = @import("microzig");
pub const chip = @import("chip");
const ch32v = microzig.hal;

pub const product_string = "LANA TNY";

/// Clock configuration for this board
pub const clock_config: ch32v.clocks.Config = .{
.source = .hsi,
Expand Down
2 changes: 2 additions & 0 deletions port/wch/ch32v/src/boards/Suzuduino_Uno_V1b.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub const microzig = @import("microzig");
pub const chip = @import("chip");
const ch32v = microzig.hal;

pub const product_string = "Suzuduino Uno";

/// Clock configuration for this board
pub const clock_config: ch32v.clocks.Config = .{
.source = .hsi,
Expand Down
3 changes: 3 additions & 0 deletions port/wch/ch32v/src/boards/nanoCH32V203.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub const microzig = @import("microzig");
pub const chip = @import("chip");
const ch32v = microzig.hal;

pub const product_string = "nanoCH32v203";

/// Clock configuration for this board
pub const clock_config: ch32v.clocks.Config = .{
.source = .hse,
Expand All @@ -19,6 +21,7 @@ pub const cpu_frequency = clock_config.target_frequency;
pub fn init() void {
ch32v.clocks.init(clock_config);
ch32v.time.init();
ch32v.clocks.enable_usbfs_clock();
}

pub const pin_config = ch32v.pins.GlobalConfiguration{
Expand Down
2 changes: 2 additions & 0 deletions port/wch/ch32v/src/boards/nanoCH32V305.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub const chip = @import("chip");
pub const microzig = @import("microzig");
const ch32v = microzig.hal;

pub const product_string = "nanoCH32v305";

/// Clock configuration for this board
pub const clock_config: ch32v.clocks.Config = .{
.source = .hsi,
Expand Down
1 change: 1 addition & 0 deletions port/wch/ch32v/src/hals/ch32v20x.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub const i2c = @import("i2c.zig");
pub const usart = @import("usart.zig");
pub const spi = @import("spi.zig");
pub const dma = @import("dma.zig");
pub const usb = @import("usbfs.zig");

/// HSI (High Speed Internal) oscillator frequency
/// This is the fixed internal RC oscillator frequency for CH32V20x
Expand Down
14 changes: 14 additions & 0 deletions port/wch/ch32v/src/hals/ch32v30x.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,32 @@ pub const clocks = @import("clocks.zig");
pub const time = @import("time.zig");
pub const i2c = @import("i2c.zig");
pub const usart = @import("usart.zig");
const std = @import("std");
pub const usb = @import("usbhs.zig");
pub const usbfs = @import("usbfs.zig");

/// HSI (High Speed Internal) oscillator frequency
/// This is the fixed internal RC oscillator frequency for CH32V30x
pub const hsi_frequency: u32 = 8_000_000; // 8 MHz
pub const hse_frequency: u32 = 8_000_000; // 8 MHz

/// Default interrupt handlers provided by the HAL
pub const default_interrupts: microzig.cpu.InterruptOptions = .{
.TIM2 = time.tim2_handler,
// .USBHS = usb.usbhs_interrupt_handler,
};

/// Initialize HAL subsystems used by default
pub fn init() void {
// Configure TIM2 timing driver
time.init();
clocks.init(.{
.source = .hse,
.hse_frequency = hse_frequency,
.target_frequency = 48_000_000,
});
clocks.enable_usbhs_clock(.{
.ref_source_hz = hse_frequency,
.ref_source = .hse,
});
}
Loading
Loading