USBD peripheral driver
I've been working on getting a simple USBD CDC device going on ch32v203. The existing driver (based on stm32) is mostly working, but there are a few deficiencies that I have discovered:
- The driver does not enable its interrupt, this is a simple patch (or can be done in the application code via the PAC, but I don't think that is the idiom)
diff --git a/src/usbd.rs b/src/usbd.rs
index 55cdf97..aa26946 100644
--- a/src/usbd.rs
+++ b/src/usbd.rs
@@ -417,6 +417,11 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
w.set_ctrm(true);
});
+ unsafe {
+ use interrupt::typelevel::Interrupt;
+ T::Interrupt::enable()
+ };
+
crate::println!("enabled");
let mut ep_types = [EpType::BULK; EP_COUNT - 1];
- Wake from sleep does not work properly; this might also just require twiddling some bits, but I couldn't figure it out, manually setting up the USB wake event in EXTI didn't seem to help. Using
embassy-executorfeaturearch-spininstead ofarch-riscv32works. - The SDI printlns throughout the driver cause frequent enumeration failure when a probe is connected and hang completely when one is not.
With these changes I was able to port with very few changes the stm32u0 usb_serial example, and it works.
It is unfortunate that the USBD is connected to a type-A port on the ch32v203 eval board.
Unsure if this is related, but I'm getting a system hang on my CH32v203 whenever I initialize the USBD driver. I'm not sure if it has to do with interrupts not being enabled, but I've used SDI printing to pinpoint that the system stops at driver initialization. I'll try your interrupts patch and see if that fixes anything.
I can confirm that the failure of driver initialization was due to the lack of interrupts being enabled. Your fix worked perfectly. My only issue now is that device enumeration requires that I pull SWCLK to high temporarily before I continue on with standard operation (I have my SWDIO pins configured for GPIO).
Hi, I tried getting USBD to work on my CH32V203 EVT and windows, so far no luck. I tried adding the interrupt enabling code and changing from arch-riscv32 to arch-spin, but no luck. I used pretty much identical code to the Embassy stm32f0 example. @ktims Would you perhaps have a minimal example to get the USBD to enumerate?
@Noxime I don't have the setup handy on my bench right now to confirm this code is working, but I am 95% sure it does. You must use arch-spin and apply the interrupt patch at a minimum. If you don't have a debug probe connected and in SDIPrint mode to reap the SDI println queue, it will hang, and depending on timing it might fail to enumerate anyway. So you might also need to comment out or otherwise disable all the printlns throughout the driver.
I also found the documentation a bit confusing as to which port is USBD. On the EVT it is the USB-A port 'P6', connected to PB6/PB7, which makes it inconvenient to use on the EVT. The other port (which has a USB-C connector available) uses a different driver.
https://github.com/ktims/ch32-hal/blob/usbd_fixes/examples/ch32v203/src/bin/usb_serial.rs
Hm, I gave your branch a go and still nothing. I checked the EVT documentation and it does say that the 2 ports are connected to PA11 and PA12, which is the USBD peripheral. On a logic analyzer I don't see any activity on those pins after reset, just an initial 100ms pulldown on D+ that I added.
Does the USBFS H/D driver work better at the moment? its more inconvenient for me to test, but if it works then its worth it.
Hm, I gave your branch a go and still nothing. I checked the EVT documentation and it does say that the 2 ports are connected to PA11 and PA12, which is the
USBDperipheral. On a logic analyzer I don't see any activity on those pins after reset, just an initial 100ms pulldown on D+ that I added.
You're right, I must have misread the documentation when I was testing the driver on the EVT. Most of my work has been on a custom board. I am not sure what is different on the EVT, I poked around for a bit and couldn't get it working, and confirmed the usb_serial example as shown does work on my custom board. As far as I see there isn't any relevant difference in the USB hardware or HSE clock source setup so really not sure what is wrong on the EVT. Sorry :(.
Sigh, the tricksters at WCH got me! You were right originally. On most EVT boards the USB-C is indeed connected to PA11/PA12, but on the C8T6 board its on the other connector... 😓 I got it to enumerate, thank you!
Hi @Noxime , I just ran into this problem, the EVT documentation appears to just be super wrong? Amazing. -_-
Did you end up getting ktims example working past enumeration? I can't get around "Problem Code 43 (failed post start)".
@nemasu Yes, I managed to get it working, I've tried at least embassy-usb's implementation of ACM CDC. I removed all the printlns from the USB driver, i think that was interfering with the USB stack working. If I remember, I'll post my working state for you to check out later today.
@nemasu Hello, I pushed some changes to https://github.com/ch32-rs/ch32-hal/blob/usbd-example/examples/ch32v203/src/bin/usbd.rs
Before it can be merged to main, there is some issue that requires you to use arch-spin instead of arch-riscv. If you have the CH32V203 EVT board with C8T6 chip on it, you can jumper PA11-PB6 and PA12-PB7 to "hotwire" the P6 USB-A port into the P7 USB-C port for testing.
Thanks for the help @Noxime
I got my ch32v203 EVT board being recognized as a vendor defined HID now, successfully sending GPIO pin changes to the host. This is basically all I need I think, so far so good.
I'm using an AtomicBool for USB state management, but of course compiling for riscv32imc, so I'm guessing it's implementation is not using the atomic instructions.
Thanks for the work in this thread. Removing those print statements was the final piece needed to fix device enumeration on my keyboard. I'm not using an eval board, just the chip on a custom PCB, so it's nice to finally see everything working.
tl;dr it looks like the driver requires a 1MHz tick rate to be reliable for now. Lower tick rates YMMV.
I wanted to use a lower tick rate than the default in Embassy (which is 1MHz), so I was experimenting with a 10KHz tick rate instead. This caused the driver to become unstable - with continuous serial data at a few kbps it works for a few seconds but eventually the driver hangs up and serial data is no longer dispatched to the application.
I haven't had time to really dig into this, but I think it is one of two possibilities:
- The interrupt handler is setting the EPR register inappropriately during the period between the interrupt and the USB task handling the data. This interim period will have a longer maximum duration the lower the tick frequency.
- Since the synchronization primitive is the AtomicWaker, which only supports one event per registration, it is possible that a USB interrupt comes in before the waker is re-registered, which gets 'lost'. Since the bus is now in a stall/nak/disabled state after the ISR, no further interrupts will occur to wake it again. It seems to me that a Semaphore is a more appropriate primitive here.