-
Notifications
You must be signed in to change notification settings - Fork 164
ch32 USBHD/USBHS driver implementation #893
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Copper280z
wants to merge
83
commits into
ZigEmbeddedGroup:main
Choose a base branch
from
Copper280z:ch32_usb
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
83 commits
Select commit
Hold shift + click to select a range
d272589
neccessary -> necessary and fix linter bot issue
piotrfila 3e3b6de
scan unhandled buffer linearly
piotrfila b587b46
redo endpoint handling
piotrfila a653711
separate getting usb rx data and requesting more
piotrfila 9504866
make CdcClassDriver rx interrupt-safe
piotrfila 52ea727
add usb packet length type
piotrfila b655989
make CdcClass driver tx interrupt-safe
piotrfila 503c998
allow partial usb tx and require calling ep_writev and ep_listen only…
piotrfila 93ce596
documentation and configurable handler names
piotrfila f210076
first draft of an example driver that explains the comptime driver in…
piotrfila 942aa42
more error checking around interfaces and string descriptors
piotrfila 340d9c3
endpoint_open -> ep_open for consistency
piotrfila 9eabcab
style fixes
piotrfila 870f0d6
appease linter bot
piotrfila 2a79c78
add u32 little endian wrapper
piotrfila f553d34
respect endianness in setup packet and shortcuts for bulk and interru…
piotrfila 27edab3
start at ch32v usb
Copper280z 2ed6b4d
cleanum usb examples
piotrfila cd4e348
change the example usb driver to a simple echo
piotrfila 0959d37
assign endpoints and interfaces (more) automatically
piotrfila 36f02ea
better handling of device class, subclass and protocol
piotrfila b7bed05
separate usb device and controller
piotrfila 93cbc87
more convenient descriptor creation
piotrfila 55921cc
handle bus resets correctly
piotrfila f373714
fix startup
Copper280z bbe968f
more examples cleanup
piotrfila baee980
add BOS descriptor
piotrfila ae7d203
simplify setup packet handling
piotrfila a6e8c89
add usb device logging
piotrfila 66f0116
better use scoped logging
piotrfila f07fe5c
reorganize ClassSubclassProtocol
piotrfila c40f907
adhere more to style guidelines
piotrfila b9f61f5
...again
piotrfila 8dd3a7b
add USBHD/USBHS device backend implementation
Copper280z 4c399ef
add enums to CDC
piotrfila 3f494d0
cleanup
piotrfila 5079a5b
Merge branch 'main' into usb-driver-rework
piotrfila 6474eab
EpNum -> EP_Num and fix pins in examples
piotrfila 4e00819
usb periph is alive and responding to host, but not enumerating corre…
Copper280z 94c4e64
change to work with v307 evt
Copper280z 32feeb7
Logging updates and call on_buffer at end of setup flag block
Copper280z 5fff85c
add fifo overflow warning and reduce SOF spam
Copper280z efefeae
logging swirl
Copper280z 6f6ba02
implement GetStatus
piotrfila c233851
make it to config descriptor but fail to send 2nd packet.
Copper280z 872e794
driver handler type safety
piotrfila 52396d6
successful enumeration!
Copper280z aa8034d
inline get_setup_packet()
piotrfila 7667ea7
fix ep_writev only using the first buffer
piotrfila 1a1aab8
add rp2xxx reset interface
piotrfila 6c89d64
start at ch32v usb
Copper280z 64d16ce
fix startup
Copper280z e8c6a40
add USBHD/USBHS device backend implementation
Copper280z 8f4fdcc
usb periph is alive and responding to host, but not enumerating corre…
Copper280z d36e85c
change to work with v307 evt
Copper280z a3673d8
Logging updates and call on_buffer at end of setup flag block
Copper280z 321c317
add fifo overflow warning and reduce SOF spam
Copper280z 59450b4
logging swirl
Copper280z 07bfc32
make it to config descriptor but fail to send 2nd packet.
Copper280z 2bbaf5b
successful enumeration!
Copper280z 66e9c32
minor changes while hunting down why cdc transfers don't work.
Copper280z 00fe33b
Fix data toggle config
Copper280z 2011590
Fixed data toggles, CDC Data flows!
Copper280z 9865fe3
comment out some logging
Copper280z 985d6e8
Merge branch 'origin/umain' of https://github.com/Copper280z/microzig…
Copper280z c337146
Refactor ch32 USB logging and enhance clock configuration for CH32V b…
Copper280z baed799
Refactor USBHS interrupt handling and switch toggle management to man…
Copper280z df4e181
Refactor USB polling logic into a separate function and clean up unus…
Copper280z dc8ec01
Merge remote-tracking branch 'upstream/main' into ch32_usb
Copper280z ad686c3
update for latest usb core changes
Copper280z fce6646
remove unfinished usbd driver
Copper280z 8a0e752
revert to upstream with minimal updates for usbhs
Copper280z 57f498f
revert changes to uart_log
Copper280z 8b9704c
remove old example driver
Copper280z 0049e03
cleanup
Copper280z 01624dc
cleanup usbhs clock setup
Copper280z 19cc7f3
more cleanup
Copper280z 493c041
revert board clock setup to one that works on current upstream.
Copper280z 02ce566
revert most of clocks.zig to upstream.
Copper280z 349fb6a
mirror rp2xxx better
Copper280z e0cef46
revert usart changes, cleanup example to match rp2xxx better
Copper280z 0717b51
adjust comment
Copper280z b9892ab
get rid of regs function, change a log call
Copper280z File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| const std = @import("std"); | ||
| const microzig = @import("microzig"); | ||
|
|
||
| const hal = microzig.hal; | ||
| 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 | ||
|
|
||
| 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(.{ | ||
| .bcd_usb = .v2_00, | ||
| .device_triple = .unspecified, | ||
| .vendor = .{ .id = 0x2E8A, .str = "MicroZig" }, | ||
| .product = .{ .id = 0x000A, .str = "ch32v307 Test Device" }, | ||
| .bcd_device = .v1_00, | ||
| .serial = "someserial", | ||
| .max_supported_packet_size = 512, | ||
| .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.usbhs.Polled( | ||
| .{ .prefer_high_speed = true }, | ||
| ) = 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 | ||
| .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 very very very very very long text sent from ch32v30x 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()) | ||
| usb_dev.poll(false, &usb_controller); | ||
| } | ||
| } | ||
|
|
||
| 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]; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -413,6 +413,150 @@ pub fn get_freqs() ClockSpeeds { | |
| }; | ||
| } | ||
|
|
||
| // ============================================================================ | ||
| // Enable + configure USBHS clocks. | ||
| // ============================================================================ | ||
|
|
||
| /// This configures RCC_CFGR2 fields for the USBHS PHY PLL reference (if present), | ||
| /// selects whether the USBHS 48MHz clock comes from the system PLL clock or the USB PHY, | ||
| /// and enables the AHB clock gate for USBHS. | ||
| /// | ||
| /// Note: The SVD names bit31 as `USBFSSRC`, but the reference manual | ||
| /// describes it as `USBHSSRC` ("USBHS 48MHz clock source selection"). | ||
| pub const UsbHsClockConfig = struct { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: Rename |
||
| pub const RefSource = enum { hse, hsi }; | ||
| /// Desired PHY PLL reference frequency (the USBHSCLK field selects one of these). | ||
| /// If null, we'll pick the highest one we can generate exactly: 8MHz, then 5MHz, 4MHz, 3MHz. | ||
| pub const RefFreq = enum(u2) { | ||
| mhz3 = 0b00, | ||
| mhz4 = 0b01, | ||
| mhz8 = 0b10, | ||
| mhz5 = 0b11, | ||
| }; | ||
|
|
||
| /// If true select USB PHY as the 48MHz source and enable the PHY internal PLL. | ||
| /// If false, select PLL CLK as the 48MHz source (you must ensure a valid 48MHz PLL clock exists). | ||
| use_phy_48mhz: bool = true, | ||
|
|
||
| /// PHY PLL reference source (only used when use_phy_48mhz). | ||
| ref_source: RefSource = .hse, | ||
|
|
||
| /// Frequency of the chosen ref_source, in Hz. | ||
| /// Typical: HSE crystal (e.g. 8_000_000, 12_000_000, 24_000_000) or HSI (often 8_000_000). | ||
| ref_source_hz: u32, | ||
|
|
||
| ref_freq: ?RefFreq = null, | ||
| }; | ||
|
|
||
| fn div_to_usbhsdiv(div: u32) u3 { | ||
| // RCC_CFGR2 USBHSDIV encoding: | ||
| // 000: /1, 001: /2, ... 111: /8 | ||
| return @as(u3, @intCast(div - 1)); | ||
| } | ||
|
|
||
| fn hz_for_ref(ref: UsbHsClockConfig.RefFreq) u32 { | ||
| return switch (ref) { | ||
| .mhz3 => 3_000_000, | ||
| .mhz4 => 4_000_000, | ||
| .mhz5 => 5_000_000, | ||
| .mhz8 => 8_000_000, | ||
| }; | ||
| } | ||
|
|
||
| const HSPLLSRC = enum(u2) { | ||
| hse = 0, | ||
| hsi = 1, | ||
| }; | ||
|
|
||
| /// Selects USBHS Clock source, options are: | ||
| /// - "PLL CLK" 48MHz source (cfg.use_phy_48mhz = false), or | ||
| /// - "USB PHY" 48MHz source (cfg.use_phy_48mhz = true), | ||
| /// in which case it also configures the PHY PLL reference (USBHSCLK/USBHSPLLSRC/USBHSDIV) | ||
| /// and enables the PHY internal PLL (USBHSPLL). | ||
| pub fn enable_usbhs_clock(comptime cfg: UsbHsClockConfig) void { | ||
| // Turn on the AHB clock gate for the USBHS peripheral block. | ||
|
|
||
| // If caller prefers PLL CLK, set PLL CLK selection and keep PHY PLL off. | ||
| if (!cfg.use_phy_48mhz) { | ||
| RCC.CFGR2.modify(.{ | ||
| // SVD name mismatch: USBFSSRC field == USBHS 48MHz source select per RM. | ||
| .USBFSSRC = 0, // 0: PLL CLK | ||
| .USBHSPLL = 0, // PHY internal PLL disabled | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| // Ensure the selected oscillator is on (best-effort; usually already enabled by system clock init). | ||
| switch (cfg.ref_source) { | ||
| .hse => { | ||
| if (RCC.CTLR.read().HSEON == 0) RCC.CTLR.modify(.{ .HSEON = 1 }); | ||
| while (RCC.CTLR.read().HSERDY == 0) {} | ||
| }, | ||
| .hsi => { | ||
| if (RCC.CTLR.read().HSION == 0) RCC.CTLR.modify(.{ .HSION = 1 }); | ||
| while (RCC.CTLR.read().HSIRDY == 0) {} | ||
| }, | ||
| } | ||
|
|
||
| // Choose a reference frequency and divider that is exactly achievable. | ||
| const candidates = [_]UsbHsClockConfig.RefFreq{ .mhz8, .mhz5, .mhz4, .mhz3 }; | ||
|
|
||
| comptime var chosen_ref: UsbHsClockConfig.RefFreq = undefined; | ||
| comptime var chosen_div: u32 = 0; | ||
| comptime { | ||
| if (cfg.ref_freq) |forced| { | ||
| const want = hz_for_ref(forced); | ||
| var found = false; | ||
| for (1..9) |div| { | ||
| if (cfg.ref_source_hz % div == 0 and (cfg.ref_source_hz / div) == want) { | ||
| chosen_ref = forced; | ||
| chosen_div = div; | ||
| found = true; | ||
| break; | ||
| } | ||
| } | ||
| if (!found) { | ||
| @compileError("USBHS PHY PLL ref cannot be generated exactly from ref_source_hz with /1..8 prescaler."); | ||
| } | ||
| } else { | ||
| var found = false; | ||
| for (candidates) |ref| { | ||
| const want = hz_for_ref(ref); | ||
| for (1..9) |div| { | ||
| if (cfg.ref_source_hz % div == 0 and (cfg.ref_source_hz / div) == want) { | ||
| chosen_ref = ref; | ||
| chosen_div = div; | ||
| found = true; | ||
| break; | ||
| } | ||
| } | ||
| if (found) break; | ||
| } | ||
| if (!found) { | ||
| @compileError("USBHS PHY PLL ref cannot be generated: need 3/4/5/8MHz from ref_source_hz using /1..8 prescaler."); | ||
| } | ||
| } | ||
| } | ||
| // Program CFGR2: | ||
| // - USBHSPLLSRC: 0=HSE, 1=HSI | ||
| // - USBHSDIV: prescaler (/1..8) | ||
| // - USBHSCLK: selects which ref freq the PHY PLL expects (3/4/8/5 MHz) | ||
| // - USBHSPLL: enable PHY internal PLL | ||
| // - USBHSSRC (SVD calls it USBFSSRC): 1=USB PHY as 48MHz source | ||
| RCC.CFGR2.modify(.{ | ||
| .USBHSPLLSRC = switch (cfg.ref_source) { | ||
| .hse => 0, | ||
| .hsi => 1, | ||
| }, | ||
| .USBHSDIV = div_to_usbhsdiv(chosen_div), | ||
| .USBHSCLK = @as(u2, @intFromEnum(chosen_ref)), | ||
| .USBHSPLL = 1, | ||
| .USBFSSRC = 1, // RM: USBHS 48MHz clock source = USB PHY | ||
| }); | ||
|
|
||
| RCC.AHBPCENR.modify(.{ .USBHS_EN = 1 }); | ||
| } | ||
|
|
||
| // ============================================================================ | ||
| // Convenience Functions for HAL Modules | ||
| // ============================================================================ | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Rename
USBControllertoUSB_Controller, it should be more in line with our style guidelines. This automation is not perfect so take it with a grain of salt.