Skip to content

Commit

Permalink
example(ch32v307): add USB DFU example to CH32V307
Browse files Browse the repository at this point in the history
  • Loading branch information
Codetector1374 authored and andelf committed Oct 29, 2024
1 parent eb1c240 commit 1adfe02
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 3 deletions.
12 changes: 9 additions & 3 deletions examples/ch32v307/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@ ch32-hal = { path = "../../", features = [
] }
embassy-executor = { version = "0.6.0", features = [
"integrated-timers",
"arch-riscv32",
"arch-spin",
"executor-thread",
] }

critical-section = "1.2.0"

embassy-time = "0.3.2"
embassy-usb = "0.3.0"
nb = "1.1.0"

qingke = "0.4.0"
# qingke-rt = { version = "0.4.0", path = "../../../qingke/qingke-rt" }
# qingke = { version = "0.4.0", path = "../../../qingke" }
qingke-rt = "0.4.0"

# Not working for now
# defmt = "0.3"
Expand All @@ -39,6 +42,9 @@ ssd1306 = "0.9"
edrv-bmp180 = "0.0.1"
embedded-hal = "1.0.0"

# for usbfs_dfu
usb-dfu-target = "0.1.0"

[profile.release]
strip = false # symbols are not flashed to the microcontroller, so don't strip them.
lto = true
Expand Down
162 changes: 162 additions & 0 deletions examples/ch32v307/src/bin/usbfs_dfu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#![no_std]
#![no_main]

use panic_halt as _;
use ch32_hal::otg_fs::endpoint::EndpointDataBuffer;
use ch32_hal::otg_fs::{self, Driver};
use ch32_hal::{self as hal, bind_interrupts, peripherals, Config};
use embassy_executor::Spawner;
use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
use embassy_usb::{Builder, Handler};
use usb_dfu_target::consts::{DfuAttributes, DfuRequest};
use usb_dfu_target::{DfuHandler, UsbDfuDevice};

bind_interrupts!(struct Irq {
OTG_FS => otg_fs::InterruptHandler<peripherals::OTG_FS>;
});

const BLOCK_SIZE: usize = 64;

struct DfuDemoDevice;

impl DfuHandler for DfuDemoDevice {
fn write_data(&mut self, offset: usize, data: &[u8]) {
// Nothing
}

fn complete_download(&mut self) {
// TODO: nothing
}

fn upload(&self, buffer: &mut [u8], offset: usize) -> usize {
todo!()
}

fn is_write_complete(&self) -> bool {
true
}
}

#[embassy_executor::main(entry = "qingke_rt::entry")]
async fn main(spawner: Spawner) -> ! {
// setup clocks
let cfg = Config {
rcc: ch32_hal::rcc::Config::SYSCLK_FREQ_144MHZ_HSI,
..Default::default()
};
let p = hal::init(cfg);
hal::embassy::init();

/* USB DRIVER SECION */
let mut buffer: [EndpointDataBuffer; 1] = core::array::from_fn(|_| EndpointDataBuffer::default());
let driver = Driver::new(p.OTG_FS, p.PA12, p.PA11, &mut buffer);

// Create embassy-usb Config
let mut config = embassy_usb::Config::new(0x6666, 0xcafe);
config.manufacturer = Some("Embassy");
config.product = Some("USB DFU Demo");
config.serial_number = Some("12345678");
config.max_power = 100;
config.max_packet_size_0 = 64;

// Required for windows compatibility.
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
config.device_class = 0x00;
config.device_sub_class = 0x00;
config.device_protocol = 0x00;
config.composite_with_iads = false;

// Create embassy-usb DeviceBuilder using the driver and config.
// It needs some buffers for building the descriptors.
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
// You can also add a Microsoft OS descriptor.
let mut msos_descriptor = [0; 256];
let mut control_buf = [0; 64];

let mut dfu_device_handler = DfuDemoDevice;
let mut request_handler = DfuRequestHandler {
inner: UsbDfuDevice::new(&mut dfu_device_handler),
};

let mut builder = Builder::new(
driver,
config,
&mut config_descriptor,
&mut bos_descriptor,
&mut msos_descriptor,
&mut control_buf,
);

let mut func = builder.function(0x00, 0x00, 0x00);
let mut iface = func.interface();
let mut alt = {
use usb_dfu_target::consts::*;
iface.alt_setting(USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_DFU, None)
};
let mut attr = DfuAttributes::empty();
attr.set(DfuAttributes::CAN_DOWNLOAD, true);
alt.descriptor(
usb_dfu_target::consts::DESC_DFU_FUNCTIONAL,
&[
attr.bits(),
0xc4,
0x09, // 2500ms timeout, doesn't affect operation as DETACH not necessary in bootloader code
(BLOCK_SIZE & 0xff) as u8,
((BLOCK_SIZE & 0xff00) >> 8) as u8,
0x10,
0x01, // DFU 1.1
],
);

drop(func);
builder.handler(&mut request_handler);

// Build the builder.
let mut usb = builder.build();

// Run the USB device.
let usb_fut = usb.run();

// Run everything concurrently.
// If we had made everything `'static` above instead, we could do this using separate tasks instead.
usb_fut.await;
/* END USB DRIVER */
}

struct DfuRequestHandler<'h> {
inner: UsbDfuDevice<'h>,
}

impl<'h> Handler for DfuRequestHandler<'h> {
fn control_out(&mut self, req: embassy_usb::control::Request, buf: &[u8]) -> Option<OutResponse> {
if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) {
return None;
}

match DfuRequest::try_from(req.request) {
Ok(req) => match self.inner.handle_control_out(req, buf) {
Ok(_) => Some(OutResponse::Accepted),
Err(_) => Some(OutResponse::Rejected),
},
Err(_) => Some(OutResponse::Rejected),
}
}

fn control_in<'a>(
&'a mut self,
req: embassy_usb::control::Request,
buf: &'a mut [u8],
) -> Option<embassy_usb::control::InResponse<'a>> {
if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) {
return None;
}
match DfuRequest::try_from(req.request) {
Ok(req) => match self.inner.handle_control_in(req, buf) {
Ok(buf) => Some(InResponse::Accepted(buf)),
Err(_) => Some(InResponse::Rejected),
},
Err(_) => Some(InResponse::Rejected),
}
}
}

0 comments on commit 1adfe02

Please sign in to comment.