├── .gitignore ├── .travis.yml ├── README.md ├── ci ├── install.sh └── script.sh ├── example-stm32f042k6 ├── .cargo │ └── config ├── Cargo.toml ├── build.rs ├── memory.x ├── openocd.cfg ├── openocd.gdb └── src │ └── main.rs ├── example-stm32f072rb ├── .cargo │ └── config ├── Cargo.toml ├── build.rs ├── memory.x ├── openocd.cfg ├── openocd.gdb └── src │ └── main.rs ├── example-stm32f103c8 ├── .cargo │ └── config ├── Cargo.toml ├── assets │ └── blinky.html ├── build.rs ├── examples │ ├── serial_interrupt.rs │ ├── serial_rtfm.rs │ ├── usb_mouse_rtfm.rs │ └── webusb_blinky.rs ├── memory.x ├── openocd.cfg ├── openocd.gdb └── src │ └── main.rs ├── example-stm32f303vc ├── .cargo │ └── config ├── Cargo.toml ├── build.rs ├── memory.x ├── openocd.cfg ├── openocd.gdb └── src │ └── main.rs └── example-stm32l432kc ├── .cargo └── config ├── Cargo.toml ├── build.rs ├── memory.x ├── openocd.cfg ├── openocd.gdb └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | 4 | .gdb_history 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - nightly 5 | - stable 6 | 7 | cache: cargo 8 | 9 | install: 10 | - ci/install.sh 11 | 12 | script: 13 | - ci/script.sh 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/stm32-rs/stm32-usbd-examples.svg?branch=master)](https://travis-ci.org/stm32-rs/stm32-usbd-examples) 2 | 3 | # `stm32-usbd-examples` 4 | 5 | > A collection of examples for [`stm32-usbd`](https://github.com/stm32-rs/stm32-usbd). 6 | 7 | ## Cloning the repository 8 | 9 | git clone --recursive https://github.com/stm32-rs/stm32-usbd-examples 10 | cd stm32-usbd-examples 11 | 12 | ## Supported targets 13 | 14 | ### STM32F103C8 ([Blue Pill](https://wiki.stm32duino.com/index.php?title=Blue_Pill) board) 15 | 16 | rustup target add thumbv7m-none-eabi 17 | cd example-stm32f103c8 18 | openocd -f openocd.cfg & 19 | cargo run --release 20 | 21 | ### STM32F303VC ([STM32F3DISCOVERY](https://www.st.com/en/evaluation-tools/stm32f3discovery.html) board) 22 | 23 | rustup target add thumbv7em-none-eabihf 24 | cd example-stm32f303vc 25 | openocd -f openocd.cfg & 26 | cargo run --release 27 | 28 | ### STM32F042K6 ([NUCLEO-F042K6](https://www.st.com/en/evaluation-tools/nucleo-f042k6.html) board) 29 | 30 | rustup target add thumbv6m-none-eabi 31 | cd example-stm32f042k6 32 | openocd -f openocd.cfg & 33 | cargo run --release 34 | 35 | ### STM32F072RB ([32F072BDISCOVERY](https://www.st.com/en/evaluation-tools/32f072bdiscovery.html) board) 36 | 37 | rustup target add thumbv6m-none-eabi 38 | cd example-stm32f072rb 39 | openocd -f openocd.cfg & 40 | cargo run --release 41 | 42 | ### STM32L432KC ([NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) board) 43 | 44 | rustup target add thumbv7em-none-eabihf 45 | cd example-stm32l432kc 46 | openocd -f openocd.cfg & 47 | cargo run --release 48 | -------------------------------------------------------------------------------- /ci/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | rustup target add thumbv6m-none-eabi 6 | rustup target add thumbv7m-none-eabi 7 | rustup target add thumbv7em-none-eabihf 8 | -------------------------------------------------------------------------------- /ci/script.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | REPO=$(dirname $(readlink -f $0))/.. 4 | cd "$REPO" 5 | 6 | set -euxo pipefail 7 | 8 | for pkg in $(ls -d example-*); do 9 | cd "$REPO/$pkg" 10 | cargo build --release 11 | cargo build --release --examples 12 | done 13 | -------------------------------------------------------------------------------- /example-stm32f042k6/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv6m-none-eabi] 2 | runner = "arm-none-eabi-gdb -q -x openocd.gdb" 3 | rustflags = [ 4 | "-C", "linker=rust-lld", 5 | "-C", "link-arg=-Tlink.x", 6 | ] 7 | 8 | [build] 9 | target = "thumbv6m-none-eabi" 10 | -------------------------------------------------------------------------------- /example-stm32f042k6/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-stm32f042k6" 3 | version = "0.1.0" 4 | authors = ["Vadim Kaushan "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | cortex-m = "0.6" 9 | cortex-m-rt = "0.6" 10 | stm32f0xx-hal = { version = "0.15.2", features = ["rt", "stm32f042"] } 11 | panic-semihosting = "0.5" 12 | usb-device = "0.2.1" 13 | usbd-serial = "0.1" 14 | stm32-usbd = { version = "0.4.0", features = ["stm32f042xx"] } 15 | -------------------------------------------------------------------------------- /example-stm32f042k6/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | // Put the linker script somewhere the linker can find it 8 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 9 | fs::File::create(out_dir.join("memory.x")) 10 | .unwrap() 11 | .write_all(include_bytes!("memory.x")) 12 | .unwrap(); 13 | println!("cargo:rustc-link-search={}", out_dir.display()); 14 | println!("cargo:rerun-if-changed=memory.x"); 15 | } 16 | -------------------------------------------------------------------------------- /example-stm32f042k6/memory.x: -------------------------------------------------------------------------------- 1 | /* STM32F042K6 */ 2 | MEMORY 3 | { 4 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K 5 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 6K 6 | } 7 | -------------------------------------------------------------------------------- /example-stm32f042k6/openocd.cfg: -------------------------------------------------------------------------------- 1 | source [find interface/stlink-v2-1.cfg] 2 | source [find target/stm32f0x.cfg] 3 | targets 4 | # reset halt 5 | # halt 6 | -------------------------------------------------------------------------------- /example-stm32f042k6/openocd.gdb: -------------------------------------------------------------------------------- 1 | set history save on 2 | set confirm off 3 | target extended-remote :3333 4 | set print asm-demangle on 5 | monitor arm semihosting enable 6 | monitor reset halt 7 | load 8 | # monitor verify 9 | # monitor reset 10 | # quit 11 | continue 12 | -------------------------------------------------------------------------------- /example-stm32f042k6/src/main.rs: -------------------------------------------------------------------------------- 1 | //! CDC-ACM serial port example using polling in a busy loop. 2 | #![no_std] 3 | #![no_main] 4 | 5 | extern crate panic_semihosting; 6 | 7 | use cortex_m_rt::entry; 8 | use stm32_usbd::UsbBus; 9 | use stm32f0xx_hal::{prelude::*, stm32}; 10 | use usb_device::prelude::*; 11 | use usbd_serial::{SerialPort, USB_CLASS_CDC}; 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | let mut dp = stm32::Peripherals::take().unwrap(); 16 | 17 | /* Uncomment the following lines if you have a chip in TSSOP20 (STM32F042F) 18 | or UFQFPN28 (STM32F042G) package 19 | This code enables clock for SYSCFG and remaps USB pins to PA9 and PA10. 20 | */ 21 | //dp.RCC.apb2enr.modify(|_, w| w.syscfgen().set_bit()); 22 | //dp.SYSCFG.cfgr1.modify(|_, w| w.pa11_pa12_rmp().remapped()); 23 | 24 | let mut rcc = dp 25 | .RCC 26 | .configure() 27 | .hsi48() 28 | .enable_crs(dp.CRS) 29 | .sysclk(48.mhz()) 30 | .pclk(24.mhz()) 31 | .freeze(&mut dp.FLASH); 32 | 33 | // Configure the on-board LED (LD3, green) 34 | let gpiob = dp.GPIOB.split(&mut rcc); 35 | let mut led = cortex_m::interrupt::free(|cs| { 36 | gpiob.pb3.into_push_pull_output(cs) 37 | }); 38 | led.set_low(); // Turn off 39 | 40 | let gpioa = dp.GPIOA.split(&mut rcc); 41 | 42 | let usb_dm = gpioa.pa11; 43 | let usb_dp = gpioa.pa12; 44 | 45 | let usb_bus = UsbBus::new(dp.USB, (usb_dm, usb_dp)); 46 | 47 | let mut serial = SerialPort::new(&usb_bus); 48 | 49 | let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) 50 | .manufacturer("Fake company") 51 | .product("Serial port") 52 | .serial_number("TEST") 53 | .device_class(USB_CLASS_CDC) 54 | .build(); 55 | 56 | loop { 57 | if !usb_dev.poll(&mut [&mut serial]) { 58 | continue; 59 | } 60 | 61 | let mut buf = [0u8; 64]; 62 | 63 | match serial.read(&mut buf) { 64 | Ok(count) if count > 0 => { 65 | led.set_high(); // Turn on 66 | 67 | // Echo back in upper case 68 | for c in buf[0..count].iter_mut() { 69 | if 0x61 <= *c && *c <= 0x7a { 70 | *c &= !0x20; 71 | } 72 | } 73 | 74 | let mut write_offset = 0; 75 | while write_offset < count { 76 | match serial.write(&buf[write_offset..count]) { 77 | Ok(len) if len > 0 => { 78 | write_offset += len; 79 | }, 80 | _ => {}, 81 | } 82 | } 83 | } 84 | _ => {} 85 | } 86 | 87 | led.set_low(); // Turn off 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /example-stm32f072rb/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv6m-none-eabi] 2 | runner = "arm-none-eabi-gdb -q -x openocd.gdb" 3 | rustflags = [ 4 | "-C", "linker=rust-lld", 5 | "-C", "link-arg=-Tlink.x", 6 | ] 7 | 8 | [build] 9 | target = "thumbv6m-none-eabi" 10 | -------------------------------------------------------------------------------- /example-stm32f072rb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-stm32f072rb" 3 | version = "0.1.0" 4 | authors = ["Vadim Kaushan "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | cortex-m = "0.6" 9 | cortex-m-rt = "0.6" 10 | stm32f0xx-hal = { version = "0.15.2", features = ["rt", "stm32f072"] } 11 | panic-semihosting = "0.5" 12 | usb-device = "0.2.1" 13 | usbd-serial = "0.1" 14 | stm32-usbd = { version = "0.4.0", features = ["stm32f072xx"] } 15 | -------------------------------------------------------------------------------- /example-stm32f072rb/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | // Put the linker script somewhere the linker can find it 8 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 9 | fs::File::create(out_dir.join("memory.x")) 10 | .unwrap() 11 | .write_all(include_bytes!("memory.x")) 12 | .unwrap(); 13 | println!("cargo:rustc-link-search={}", out_dir.display()); 14 | println!("cargo:rerun-if-changed=memory.x"); 15 | } 16 | -------------------------------------------------------------------------------- /example-stm32f072rb/memory.x: -------------------------------------------------------------------------------- 1 | /* STM32F072RB */ 2 | MEMORY 3 | { 4 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K 5 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K 6 | } 7 | -------------------------------------------------------------------------------- /example-stm32f072rb/openocd.cfg: -------------------------------------------------------------------------------- 1 | source [find interface/stlink-v2-1.cfg] 2 | source [find target/stm32f0x.cfg] 3 | targets 4 | # reset halt 5 | # halt 6 | -------------------------------------------------------------------------------- /example-stm32f072rb/openocd.gdb: -------------------------------------------------------------------------------- 1 | set history save on 2 | set confirm off 3 | target extended-remote :3333 4 | set print asm-demangle on 5 | monitor arm semihosting enable 6 | monitor reset halt 7 | load 8 | # monitor verify 9 | # monitor reset 10 | # quit 11 | continue 12 | -------------------------------------------------------------------------------- /example-stm32f072rb/src/main.rs: -------------------------------------------------------------------------------- 1 | //! CDC-ACM serial port example using polling in a busy loop. 2 | #![no_std] 3 | #![no_main] 4 | 5 | extern crate panic_semihosting; 6 | 7 | use cortex_m_rt::entry; 8 | use stm32_usbd::UsbBus; 9 | use stm32f0xx_hal::{prelude::*, stm32}; 10 | use usb_device::prelude::*; 11 | use usbd_serial::{SerialPort, USB_CLASS_CDC}; 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | let mut dp = stm32::Peripherals::take().unwrap(); 16 | 17 | let mut rcc = dp 18 | .RCC 19 | .configure() 20 | .hsi48() 21 | .enable_crs(dp.CRS) 22 | .sysclk(48.mhz()) 23 | .pclk(24.mhz()) 24 | .freeze(&mut dp.FLASH); 25 | 26 | // Configure the on-board LED (LD6, blue) 27 | let gpioc = dp.GPIOC.split(&mut rcc); 28 | let mut led = cortex_m::interrupt::free(|cs| { 29 | gpioc.pc7.into_push_pull_output(cs) 30 | }); 31 | led.set_low(); // Turn off 32 | 33 | let gpioa = dp.GPIOA.split(&mut rcc); 34 | 35 | let usb_dm = gpioa.pa11; 36 | let usb_dp = gpioa.pa12; 37 | 38 | let usb_bus = UsbBus::new(dp.USB, (usb_dm, usb_dp)); 39 | 40 | let mut serial = SerialPort::new(&usb_bus); 41 | 42 | let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) 43 | .manufacturer("Fake company") 44 | .product("Serial port") 45 | .serial_number("TEST") 46 | .device_class(USB_CLASS_CDC) 47 | .build(); 48 | 49 | loop { 50 | if !usb_dev.poll(&mut [&mut serial]) { 51 | continue; 52 | } 53 | 54 | let mut buf = [0u8; 64]; 55 | 56 | match serial.read(&mut buf) { 57 | Ok(count) if count > 0 => { 58 | led.set_high(); // Turn on 59 | 60 | // Echo back in upper case 61 | for c in buf[0..count].iter_mut() { 62 | if 0x61 <= *c && *c <= 0x7a { 63 | *c &= !0x20; 64 | } 65 | } 66 | 67 | let mut write_offset = 0; 68 | while write_offset < count { 69 | match serial.write(&buf[write_offset..count]) { 70 | Ok(len) if len > 0 => { 71 | write_offset += len; 72 | }, 73 | _ => {}, 74 | } 75 | } 76 | } 77 | _ => {} 78 | } 79 | 80 | led.set_low(); // Turn off 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /example-stm32f103c8/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv7m-none-eabi] 2 | runner = "arm-none-eabi-gdb -q -x openocd.gdb" 3 | rustflags = [ 4 | "-C", "linker=rust-lld", 5 | "-C", "link-arg=-Tlink.x", 6 | ] 7 | 8 | [build] 9 | target = "thumbv7m-none-eabi" 10 | -------------------------------------------------------------------------------- /example-stm32f103c8/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-stm32f103c8" 3 | version = "0.1.0" 4 | authors = ["Vadim Kaushan ", "Matti Virkkunen "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | cortex-m = "0.6" 9 | cortex-m-rt = "0.6" 10 | stm32f1xx-hal = { version = "0.5", features = ["stm32f103", "stm32-usbd", "rt"] } 11 | panic-semihosting = "0.5" 12 | usb-device = "0.2.4" 13 | usbd-serial = "0.1" 14 | usbd-webusb = "1.0.0" 15 | embedded-hal = "0.2.3" 16 | 17 | [dev-dependencies] 18 | cortex-m-rtfm = "0.5" 19 | -------------------------------------------------------------------------------- /example-stm32f103c8/assets/blinky.html: -------------------------------------------------------------------------------- 1 | Web Blinky 2 | 3 |
4 | Not connected. 5 | 6 |
7 | 10 | 44 | 45 | -------------------------------------------------------------------------------- /example-stm32f103c8/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | // Put the linker script somewhere the linker can find it 8 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 9 | fs::File::create(out_dir.join("memory.x")) 10 | .unwrap() 11 | .write_all(include_bytes!("memory.x")) 12 | .unwrap(); 13 | println!("cargo:rustc-link-search={}", out_dir.display()); 14 | println!("cargo:rerun-if-changed=memory.x"); 15 | } 16 | -------------------------------------------------------------------------------- /example-stm32f103c8/examples/serial_interrupt.rs: -------------------------------------------------------------------------------- 1 | //! CDC-ACM serial port example using interrupts. 2 | #![no_std] 3 | #![no_main] 4 | 5 | extern crate panic_semihosting; 6 | 7 | use cortex_m::asm::{delay, wfi}; 8 | use cortex_m_rt::entry; 9 | use embedded_hal::digital::v2::OutputPin; 10 | use stm32f1xx_hal::stm32::{interrupt, Interrupt}; 11 | use stm32f1xx_hal::usb::{Peripheral, UsbBus, UsbBusType}; 12 | use stm32f1xx_hal::{prelude::*, stm32}; 13 | use usb_device::{bus::UsbBusAllocator, prelude::*}; 14 | use usbd_serial::{SerialPort, USB_CLASS_CDC}; 15 | 16 | static mut USB_BUS: Option> = None; 17 | static mut USB_SERIAL: Option> = None; 18 | static mut USB_DEVICE: Option> = None; 19 | 20 | #[entry] 21 | fn main() -> ! { 22 | let p = cortex_m::Peripherals::take().unwrap(); 23 | let dp = stm32::Peripherals::take().unwrap(); 24 | 25 | let mut flash = dp.FLASH.constrain(); 26 | let mut rcc = dp.RCC.constrain(); 27 | 28 | let clocks = rcc 29 | .cfgr 30 | .use_hse(8.mhz()) 31 | .sysclk(48.mhz()) 32 | .pclk1(24.mhz()) 33 | .freeze(&mut flash.acr); 34 | 35 | assert!(clocks.usbclk_valid()); 36 | 37 | let mut gpioa = dp.GPIOA.split(&mut rcc.apb2); 38 | 39 | // BluePill board has a pull-up resistor on the D+ line. 40 | // Pull the D+ pin down to send a RESET condition to the USB bus. 41 | // This forced reset is needed only for development, without it host 42 | // will not reset your device when you upload new firmware. 43 | let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh); 44 | usb_dp.set_low(); 45 | delay(clocks.sysclk().0 / 100); 46 | 47 | let usb_dm = gpioa.pa11; 48 | let usb_dp = usb_dp.into_floating_input(&mut gpioa.crh); 49 | 50 | let usb = Peripheral { 51 | usb: dp.USB, 52 | pin_dm: usb_dm, 53 | pin_dp: usb_dp, 54 | }; 55 | 56 | // Unsafe to allow access to static variables 57 | unsafe { 58 | let bus = UsbBus::new(usb); 59 | 60 | USB_BUS = Some(bus); 61 | 62 | USB_SERIAL = Some(SerialPort::new(USB_BUS.as_ref().unwrap())); 63 | 64 | let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27dd)) 65 | .manufacturer("Fake company") 66 | .product("Serial port") 67 | .serial_number("TEST") 68 | .device_class(USB_CLASS_CDC) 69 | .build(); 70 | 71 | USB_DEVICE = Some(usb_dev); 72 | } 73 | 74 | let mut nvic = p.NVIC; 75 | 76 | nvic.enable(Interrupt::USB_HP_CAN_TX); 77 | nvic.enable(Interrupt::USB_LP_CAN_RX0); 78 | 79 | loop { 80 | wfi(); 81 | } 82 | } 83 | 84 | #[interrupt] 85 | fn USB_HP_CAN_TX() { 86 | usb_interrupt(); 87 | } 88 | 89 | #[interrupt] 90 | fn USB_LP_CAN_RX0() { 91 | usb_interrupt(); 92 | } 93 | 94 | fn usb_interrupt() { 95 | let usb_dev = unsafe { USB_DEVICE.as_mut().unwrap() }; 96 | let serial = unsafe { USB_SERIAL.as_mut().unwrap() }; 97 | 98 | if !usb_dev.poll(&mut [serial]) { 99 | return; 100 | } 101 | 102 | let mut buf = [0u8; 8]; 103 | 104 | match serial.read(&mut buf) { 105 | Ok(count) if count > 0 => { 106 | // Echo back in upper case 107 | for c in buf[0..count].iter_mut() { 108 | if 0x61 <= *c && *c <= 0x7a { 109 | *c &= !0x20; 110 | } 111 | } 112 | 113 | serial.write(&buf[0..count]).ok(); 114 | } 115 | _ => {} 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /example-stm32f103c8/examples/serial_rtfm.rs: -------------------------------------------------------------------------------- 1 | //! CDC-ACM serial port example using cortex-m-rtfm. 2 | #![no_main] 3 | #![no_std] 4 | #![allow(non_snake_case)] 5 | 6 | extern crate panic_semihosting; 7 | 8 | use cortex_m::asm::delay; 9 | use embedded_hal::digital::v2::OutputPin; 10 | use rtfm::app; 11 | use stm32f1xx_hal::prelude::*; 12 | use stm32f1xx_hal::usb::{Peripheral, UsbBus, UsbBusType}; 13 | use usb_device::bus; 14 | use usb_device::prelude::*; 15 | use usbd_serial::{SerialPort, USB_CLASS_CDC}; 16 | 17 | #[app(device = stm32f1xx_hal::stm32, peripherals = true)] 18 | const APP: () = { 19 | struct Resources { 20 | usb_dev: UsbDevice<'static, UsbBusType>, 21 | serial: SerialPort<'static, UsbBusType>, 22 | } 23 | 24 | #[init] 25 | fn init(cx: init::Context) -> init::LateResources { 26 | static mut USB_BUS: Option> = None; 27 | 28 | let mut flash = cx.device.FLASH.constrain(); 29 | let mut rcc = cx.device.RCC.constrain(); 30 | 31 | let clocks = rcc 32 | .cfgr 33 | .use_hse(8.mhz()) 34 | .sysclk(48.mhz()) 35 | .pclk1(24.mhz()) 36 | .freeze(&mut flash.acr); 37 | 38 | assert!(clocks.usbclk_valid()); 39 | 40 | let mut gpioa = cx.device.GPIOA.split(&mut rcc.apb2); 41 | 42 | // BluePill board has a pull-up resistor on the D+ line. 43 | // Pull the D+ pin down to send a RESET condition to the USB bus. 44 | // This forced reset is needed only for development, without it host 45 | // will not reset your device when you upload new firmware. 46 | let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh); 47 | usb_dp.set_low().unwrap(); 48 | delay(clocks.sysclk().0 / 100); 49 | 50 | let usb_dm = gpioa.pa11; 51 | let usb_dp = usb_dp.into_floating_input(&mut gpioa.crh); 52 | 53 | let usb = Peripheral { 54 | usb: cx.device.USB, 55 | pin_dm: usb_dm, 56 | pin_dp: usb_dp, 57 | }; 58 | 59 | *USB_BUS = Some(UsbBus::new(usb)); 60 | 61 | let serial = SerialPort::new(USB_BUS.as_ref().unwrap()); 62 | 63 | let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27dd)) 64 | .manufacturer("Fake company") 65 | .product("Serial port") 66 | .serial_number("TEST") 67 | .device_class(USB_CLASS_CDC) 68 | .build(); 69 | 70 | init::LateResources { usb_dev, serial } 71 | } 72 | 73 | #[task(binds = USB_HP_CAN_TX, resources = [usb_dev, serial])] 74 | fn usb_tx(mut cx: usb_tx::Context) { 75 | usb_poll(&mut cx.resources.usb_dev, &mut cx.resources.serial); 76 | } 77 | 78 | #[task(binds = USB_LP_CAN_RX0, resources = [usb_dev, serial])] 79 | fn usb_rx0(mut cx: usb_rx0::Context) { 80 | usb_poll(&mut cx.resources.usb_dev, &mut cx.resources.serial); 81 | } 82 | }; 83 | 84 | fn usb_poll( 85 | usb_dev: &mut UsbDevice<'static, B>, 86 | serial: &mut SerialPort<'static, B>, 87 | ) { 88 | if !usb_dev.poll(&mut [serial]) { 89 | return; 90 | } 91 | 92 | let mut buf = [0u8; 8]; 93 | 94 | match serial.read(&mut buf) { 95 | Ok(count) if count > 0 => { 96 | // Echo back in upper case 97 | for c in buf[0..count].iter_mut() { 98 | if 0x61 <= *c && *c <= 0x7a { 99 | *c &= !0x20; 100 | } 101 | } 102 | 103 | serial.write(&buf[0..count]).ok(); 104 | } 105 | _ => {} 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /example-stm32f103c8/examples/usb_mouse_rtfm.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate panic_semihosting; 5 | 6 | use cortex_m::{asm::delay, peripheral::DWT}; 7 | use embedded_hal::digital::v2::OutputPin; 8 | use rtfm::cyccnt::{Instant, U32Ext as _}; 9 | use stm32f1xx_hal::usb::{Peripheral, UsbBus, UsbBusType}; 10 | use stm32f1xx_hal::{gpio, prelude::*}; 11 | use usb_device::bus; 12 | use usb_device::prelude::*; 13 | 14 | #[allow(unused)] 15 | pub mod hid { 16 | use usb_device::class_prelude::*; 17 | use usb_device::Result; 18 | 19 | pub const USB_CLASS_HID: u8 = 0x03; 20 | 21 | const USB_SUBCLASS_NONE: u8 = 0x00; 22 | const USB_SUBCLASS_BOOT: u8 = 0x01; 23 | 24 | const USB_INTERFACE_NONE: u8 = 0x00; 25 | const USB_INTERFACE_KEYBOARD: u8 = 0x01; 26 | const USB_INTERFACE_MOUSE: u8 = 0x02; 27 | 28 | const REQ_GET_REPORT: u8 = 0x01; 29 | const REQ_GET_IDLE: u8 = 0x02; 30 | const REQ_GET_PROTOCOL: u8 = 0x03; 31 | const REQ_SET_REPORT: u8 = 0x09; 32 | const REQ_SET_IDLE: u8 = 0x0a; 33 | const REQ_SET_PROTOCOL: u8 = 0x0b; 34 | 35 | // https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/mouse-collection-report-descriptor 36 | const REPORT_DESCR: &[u8] = &[ 37 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 38 | 0x09, 0x02, // USAGE (Mouse) 39 | 0xa1, 0x01, // COLLECTION (Application) 40 | 0x09, 0x01, // USAGE (Pointer) 41 | 0xa1, 0x00, // COLLECTION (Physical) 42 | 0x05, 0x09, // USAGE_PAGE (Button) 43 | 0x19, 0x01, // USAGE_MINIMUM (Button 1) 44 | 0x29, 0x03, // USAGE_MAXIMUM (Button 3) 45 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 46 | 0x25, 0x01, // LOGICAL_MAXIMUM (1) 47 | 0x95, 0x03, // REPORT_COUNT (3) 48 | 0x75, 0x01, // REPORT_SIZE (1) 49 | 0x81, 0x02, // INPUT (Data,Var,Abs) 50 | 0x95, 0x01, // REPORT_COUNT (1) 51 | 0x75, 0x05, // REPORT_SIZE (5) 52 | 0x81, 0x03, // INPUT (Cnst,Var,Abs) 53 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 54 | 0x09, 0x30, // USAGE (X) 55 | 0x09, 0x31, // USAGE (Y) 56 | 0x15, 0x81, // LOGICAL_MINIMUM (-127) 57 | 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 58 | 0x75, 0x08, // REPORT_SIZE (8) 59 | 0x95, 0x02, // REPORT_COUNT (2) 60 | 0x81, 0x06, // INPUT (Data,Var,Rel) 61 | 0xc0, // END_COLLECTION 62 | 0xc0, // END_COLLECTION 63 | ]; 64 | 65 | pub fn report(x: i8, y: i8) -> [u8; 3] { 66 | [ 67 | 0x00, // button: none 68 | x as u8, // x-axis 69 | y as u8, // y-axis 70 | ] 71 | } 72 | 73 | pub struct HIDClass<'a, B: UsbBus> { 74 | report_if: InterfaceNumber, 75 | report_ep: EndpointIn<'a, B>, 76 | } 77 | 78 | impl HIDClass<'_, B> { 79 | /// Creates a new HIDClass with the provided UsbBus and max_packet_size in bytes. For 80 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. 81 | pub fn new(alloc: &UsbBusAllocator) -> HIDClass<'_, B> { 82 | HIDClass { 83 | report_if: alloc.interface(), 84 | report_ep: alloc.interrupt(8, 10), 85 | } 86 | } 87 | 88 | pub fn write(&mut self, data: &[u8]) { 89 | self.report_ep.write(data).ok(); 90 | } 91 | } 92 | 93 | impl UsbClass for HIDClass<'_, B> { 94 | fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> { 95 | writer.interface( 96 | self.report_if, 97 | USB_CLASS_HID, 98 | USB_SUBCLASS_NONE, 99 | USB_INTERFACE_MOUSE, 100 | )?; 101 | 102 | let descr_len: u16 = REPORT_DESCR.len() as u16; 103 | writer.write( 104 | 0x21, 105 | &[ 106 | 0x01, // bcdHID 107 | 0x01, // bcdHID 108 | 0x00, // bContryCode 109 | 0x01, // bNumDescriptors 110 | 0x22, // bDescriptorType 111 | descr_len as u8, // wDescriptorLength 112 | (descr_len >> 8) as u8, // wDescriptorLength 113 | ], 114 | )?; 115 | 116 | writer.endpoint(&self.report_ep)?; 117 | 118 | Ok(()) 119 | } 120 | 121 | fn control_in(&mut self, xfer: ControlIn) { 122 | let req = xfer.request(); 123 | 124 | if req.request_type == control::RequestType::Standard { 125 | match (req.recipient, req.request) { 126 | (control::Recipient::Interface, control::Request::GET_DESCRIPTOR) => { 127 | let (dtype, _index) = req.descriptor_type_index(); 128 | if dtype == 0x21 { 129 | // HID descriptor 130 | cortex_m::asm::bkpt(); 131 | let descr_len: u16 = REPORT_DESCR.len() as u16; 132 | 133 | // HID descriptor 134 | let descr = &[ 135 | 0x09, // length 136 | 0x21, // descriptor type 137 | 0x01, // bcdHID 138 | 0x01, // bcdHID 139 | 0x00, // bCountryCode 140 | 0x01, // bNumDescriptors 141 | 0x22, // bDescriptorType 142 | descr_len as u8, // wDescriptorLength 143 | (descr_len >> 8) as u8, // wDescriptorLength 144 | ]; 145 | 146 | xfer.accept_with(descr).ok(); 147 | return; 148 | } else if dtype == 0x22 { 149 | // Report descriptor 150 | xfer.accept_with(REPORT_DESCR).ok(); 151 | return; 152 | } 153 | } 154 | _ => { 155 | return; 156 | } 157 | }; 158 | } 159 | 160 | if !(req.request_type == control::RequestType::Class 161 | && req.recipient == control::Recipient::Interface 162 | && req.index == u8::from(self.report_if) as u16) 163 | { 164 | return; 165 | } 166 | 167 | match req.request { 168 | REQ_GET_REPORT => { 169 | // USB host requests for report 170 | // I'm not sure what should we do here, so just send empty report 171 | xfer.accept_with(&report(0, 0)).ok(); 172 | } 173 | _ => { 174 | xfer.reject().ok(); 175 | } 176 | } 177 | } 178 | 179 | fn control_out(&mut self, xfer: ControlOut) { 180 | let req = xfer.request(); 181 | 182 | if !(req.request_type == control::RequestType::Class 183 | && req.recipient == control::Recipient::Interface 184 | && req.index == u8::from(self.report_if) as u16) 185 | { 186 | return; 187 | } 188 | 189 | xfer.reject().ok(); 190 | } 191 | } 192 | } 193 | 194 | use hid::HIDClass; 195 | 196 | type LED = gpio::gpioc::PC13>; 197 | 198 | const PERIOD: u32 = 8_000_000; 199 | 200 | #[rtfm::app(device = stm32f1xx_hal::stm32, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] 201 | const APP: () = { 202 | struct Resources { 203 | counter: u8, 204 | led: LED, 205 | 206 | usb_dev: UsbDevice<'static, UsbBusType>, 207 | hid: HIDClass<'static, UsbBusType>, 208 | } 209 | 210 | #[init(schedule = [on_tick])] 211 | fn init(mut cx: init::Context) -> init::LateResources { 212 | static mut USB_BUS: Option> = None; 213 | 214 | cx.core.DCB.enable_trace(); 215 | DWT::unlock(); 216 | cx.core.DWT.enable_cycle_counter(); 217 | 218 | let mut flash = cx.device.FLASH.constrain(); 219 | let mut rcc = cx.device.RCC.constrain(); 220 | 221 | let mut gpioc = cx.device.GPIOC.split(&mut rcc.apb2); 222 | let led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); 223 | 224 | let clocks = rcc 225 | .cfgr 226 | .use_hse(8.mhz()) 227 | .sysclk(48.mhz()) 228 | .pclk1(24.mhz()) 229 | .freeze(&mut flash.acr); 230 | 231 | assert!(clocks.usbclk_valid()); 232 | 233 | let mut gpioa = cx.device.GPIOA.split(&mut rcc.apb2); 234 | 235 | // BluePill board has a pull-up resistor on the D+ line. 236 | // Pull the D+ pin down to send a RESET condition to the USB bus. 237 | let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh); 238 | usb_dp.set_low().ok(); 239 | delay(clocks.sysclk().0 / 100); 240 | 241 | let usb_dm = gpioa.pa11; 242 | let usb_dp = usb_dp.into_floating_input(&mut gpioa.crh); 243 | 244 | let usb = Peripheral { 245 | usb: cx.device.USB, 246 | pin_dm: usb_dm, 247 | pin_dp: usb_dp, 248 | }; 249 | 250 | *USB_BUS = Some(UsbBus::new(usb)); 251 | 252 | let hid = HIDClass::new(USB_BUS.as_ref().unwrap()); 253 | 254 | let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0xc410, 0x0000)) 255 | .manufacturer("Fake company") 256 | .product("mouse") 257 | .serial_number("TEST") 258 | .device_class(0) 259 | .build(); 260 | 261 | cx.schedule.on_tick(cx.start + PERIOD.cycles()).ok(); 262 | 263 | init::LateResources { 264 | counter: 0, 265 | led, 266 | 267 | usb_dev, 268 | hid, 269 | } 270 | } 271 | 272 | #[task(schedule = [on_tick], resources = [counter, led, hid])] 273 | fn on_tick(mut cx: on_tick::Context) { 274 | cx.schedule.on_tick(Instant::now() + PERIOD.cycles()).ok(); 275 | 276 | let counter: &mut u8 = &mut cx.resources.counter; 277 | let led = &mut cx.resources.led; 278 | let hid = &mut cx.resources.hid; 279 | 280 | const P: u8 = 2; 281 | *counter = (*counter + 1) % P; 282 | 283 | // move mouse cursor horizontally (x-axis) while blinking LED 284 | if *counter < P / 2 { 285 | led.set_high().ok(); 286 | hid.write(&hid::report(10, 0)); 287 | } else { 288 | led.set_low().ok(); 289 | hid.write(&hid::report(-10, 0)); 290 | } 291 | } 292 | 293 | #[task(binds=USB_HP_CAN_TX, resources = [counter, led, usb_dev, hid])] 294 | fn usb_tx(mut cx: usb_tx::Context) { 295 | usb_poll( 296 | &mut cx.resources.counter, 297 | &mut cx.resources.led, 298 | &mut cx.resources.usb_dev, 299 | &mut cx.resources.hid, 300 | ); 301 | } 302 | 303 | #[task(binds=USB_LP_CAN_RX0, resources = [counter, led, usb_dev, hid])] 304 | fn usb_rx(mut cx: usb_rx::Context) { 305 | usb_poll( 306 | &mut cx.resources.counter, 307 | &mut cx.resources.led, 308 | &mut cx.resources.usb_dev, 309 | &mut cx.resources.hid, 310 | ); 311 | } 312 | 313 | extern "C" { 314 | fn EXTI0(); 315 | } 316 | }; 317 | 318 | fn usb_poll( 319 | _counter: &mut u8, 320 | _led: &mut LED, 321 | usb_dev: &mut UsbDevice<'static, B>, 322 | hid: &mut HIDClass<'static, B>, 323 | ) { 324 | if !usb_dev.poll(&mut [hid]) { 325 | return; 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /example-stm32f103c8/examples/webusb_blinky.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | // matti@miya:/etc/udev/rules.d$ cat 99-usb-test.rules 5 | // SUBSYSTEMS=="usb", ATTR{idVendor}=="16c0", ATTR{idProduct}=="27d8", MODE:="0666" 6 | 7 | extern crate panic_semihosting; 8 | 9 | use cortex_m::asm::delay; 10 | use rtfm::app; 11 | use embedded_hal::digital::v2::OutputPin; 12 | use stm32f1xx_hal::{ 13 | prelude::*, 14 | usb::{Peripheral, UsbBus, UsbBusType}, 15 | gpio::{gpioc::*, Output, PushPull}, 16 | }; 17 | use usb_device::bus; 18 | use usb_device::prelude::*; 19 | use usbd_webusb::WebUsb; 20 | 21 | mod blinky { 22 | use core::marker::PhantomData; 23 | use embedded_hal::digital::v2::OutputPin; 24 | use usb_device::class_prelude::*; 25 | 26 | pub struct BlinkyClass { 27 | spooky: core::marker::PhantomData, 28 | led: LED, 29 | } 30 | 31 | impl BlinkyClass { 32 | pub fn new(_alloc: &UsbBusAllocator, led: LED) -> Self { 33 | Self { 34 | spooky: PhantomData, 35 | led, 36 | } 37 | } 38 | } 39 | 40 | impl UsbClass for BlinkyClass { 41 | fn control_out(&mut self, xfer: ControlOut) { 42 | let req = xfer.request(); 43 | 44 | if req.request_type == control::RequestType::Vendor 45 | && req.recipient == control::Recipient::Device 46 | && req.request == 1 47 | { 48 | if req.value > 0 { 49 | self.led.set_low().ok(); 50 | } else { 51 | self.led.set_high().ok(); 52 | } 53 | } 54 | } 55 | } 56 | } 57 | 58 | #[app(device = stm32f1xx_hal::stm32, peripherals = true)] 59 | const APP: () = { 60 | struct Resources { 61 | usb_dev: UsbDevice<'static, UsbBusType>, 62 | blinky: blinky::BlinkyClass>>, 63 | webusb: WebUsb, 64 | } 65 | 66 | #[init] 67 | fn init(cx: init::Context) -> init::LateResources { 68 | static mut USB_BUS: Option> = None; 69 | 70 | let mut flash = cx.device.FLASH.constrain(); 71 | let mut rcc = cx.device.RCC.constrain(); 72 | 73 | let clocks = rcc 74 | .cfgr 75 | .use_hse(8.mhz()) 76 | .sysclk(48.mhz()) 77 | .pclk1(24.mhz()) 78 | .freeze(&mut flash.acr); 79 | 80 | assert!(clocks.usbclk_valid()); 81 | 82 | let mut gpioc = cx.device.GPIOC.split(&mut rcc.apb2); 83 | 84 | let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); 85 | led.set_high().ok(); 86 | 87 | let mut gpioa = cx.device.GPIOA.split(&mut rcc.apb2); 88 | 89 | // BluePill board has a pull-up resistor on the D+ line. 90 | // Pull the D+ pin down to send a RESET condition to the USB bus. 91 | // This forced reset is needed only for development, without it host 92 | // will not reset your device when you upload new firmware. 93 | let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh); 94 | usb_dp.set_low().unwrap(); 95 | delay(clocks.sysclk().0 / 100); 96 | 97 | let usb_dm = gpioa.pa11; 98 | let usb_dp = usb_dp.into_floating_input(&mut gpioa.crh); 99 | 100 | let usb = Peripheral { 101 | usb: cx.device.USB, 102 | pin_dm: usb_dm, 103 | pin_dp: usb_dp, 104 | }; 105 | 106 | *USB_BUS = Some(UsbBus::new(usb)); 107 | 108 | let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27d8)) 109 | .manufacturer("Fake Company") 110 | .product("Web Blinky") 111 | .serial_number("TEST") 112 | .build(); 113 | 114 | init::LateResources { 115 | usb_dev, 116 | blinky: blinky::BlinkyClass::new(USB_BUS.as_ref().unwrap(), led), 117 | webusb: WebUsb::new( 118 | USB_BUS.as_ref().unwrap(), 119 | usbd_webusb::url_scheme::HTTPS, 120 | "virkkunen.net/b/blinky.html"), 121 | } 122 | } 123 | 124 | #[task(binds = USB_LP_CAN_RX0, resources = [usb_dev, webusb, blinky])] 125 | fn usb_lp(cx: usb_lp::Context) { 126 | cx.resources.usb_dev.poll(&mut [ 127 | cx.resources.webusb, 128 | cx.resources.blinky 129 | ]); 130 | } 131 | }; 132 | -------------------------------------------------------------------------------- /example-stm32f103c8/memory.x: -------------------------------------------------------------------------------- 1 | /* STM32F103C8T6 */ 2 | MEMORY 3 | { 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 64K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 20K 6 | } 7 | -------------------------------------------------------------------------------- /example-stm32f103c8/openocd.cfg: -------------------------------------------------------------------------------- 1 | source [find interface/stlink-v2-1.cfg] 2 | source [find target/stm32f1x.cfg] 3 | targets 4 | # reset halt 5 | # halt 6 | -------------------------------------------------------------------------------- /example-stm32f103c8/openocd.gdb: -------------------------------------------------------------------------------- 1 | set history save on 2 | set confirm off 3 | target extended-remote :3333 4 | set print asm-demangle on 5 | monitor arm semihosting enable 6 | monitor reset halt 7 | load 8 | # monitor verify 9 | # monitor reset 10 | # quit 11 | continue 12 | -------------------------------------------------------------------------------- /example-stm32f103c8/src/main.rs: -------------------------------------------------------------------------------- 1 | //! CDC-ACM serial port example using polling in a busy loop. 2 | #![no_std] 3 | #![no_main] 4 | 5 | extern crate panic_semihosting; 6 | 7 | use cortex_m::asm::delay; 8 | use cortex_m_rt::entry; 9 | use embedded_hal::digital::v2::OutputPin; 10 | use stm32f1xx_hal::usb::{Peripheral, UsbBus}; 11 | use stm32f1xx_hal::{prelude::*, stm32}; 12 | use usb_device::prelude::*; 13 | use usbd_serial::{SerialPort, USB_CLASS_CDC}; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | let dp = stm32::Peripherals::take().unwrap(); 18 | 19 | let mut flash = dp.FLASH.constrain(); 20 | let mut rcc = dp.RCC.constrain(); 21 | 22 | let clocks = rcc 23 | .cfgr 24 | .use_hse(8.mhz()) 25 | .sysclk(48.mhz()) 26 | .pclk1(24.mhz()) 27 | .freeze(&mut flash.acr); 28 | 29 | assert!(clocks.usbclk_valid()); 30 | 31 | // Configure the on-board LED (PC13, green) 32 | let mut gpioc = dp.GPIOC.split(&mut rcc.apb2); 33 | let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); 34 | led.set_high().ok(); // Turn off 35 | 36 | let mut gpioa = dp.GPIOA.split(&mut rcc.apb2); 37 | 38 | // BluePill board has a pull-up resistor on the D+ line. 39 | // Pull the D+ pin down to send a RESET condition to the USB bus. 40 | // This forced reset is needed only for development, without it host 41 | // will not reset your device when you upload new firmware. 42 | let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh); 43 | usb_dp.set_low().ok(); 44 | delay(clocks.sysclk().0 / 100); 45 | 46 | let usb = Peripheral { 47 | usb: dp.USB, 48 | pin_dm: gpioa.pa11, 49 | pin_dp: usb_dp.into_floating_input(&mut gpioa.crh), 50 | }; 51 | let usb_bus = UsbBus::new(usb); 52 | 53 | let mut serial = SerialPort::new(&usb_bus); 54 | 55 | let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) 56 | .manufacturer("Fake company") 57 | .product("Serial port") 58 | .serial_number("TEST") 59 | .device_class(USB_CLASS_CDC) 60 | .build(); 61 | 62 | loop { 63 | if !usb_dev.poll(&mut [&mut serial]) { 64 | continue; 65 | } 66 | 67 | let mut buf = [0u8; 64]; 68 | 69 | match serial.read(&mut buf) { 70 | Ok(count) if count > 0 => { 71 | led.set_low().ok(); // Turn on 72 | 73 | // Echo back in upper case 74 | for c in buf[0..count].iter_mut() { 75 | if 0x61 <= *c && *c <= 0x7a { 76 | *c &= !0x20; 77 | } 78 | } 79 | 80 | let mut write_offset = 0; 81 | while write_offset < count { 82 | match serial.write(&buf[write_offset..count]) { 83 | Ok(len) if len > 0 => { 84 | write_offset += len; 85 | } 86 | _ => {} 87 | } 88 | } 89 | } 90 | _ => {} 91 | } 92 | 93 | led.set_high().ok(); // Turn off 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /example-stm32f303vc/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv7em-none-eabihf] 2 | runner = "arm-none-eabi-gdb -q -x openocd.gdb" 3 | rustflags = [ 4 | "-C", "link-arg=-Tlink.x", 5 | ] 6 | 7 | [build] 8 | target = "thumbv7em-none-eabihf" 9 | -------------------------------------------------------------------------------- /example-stm32f303vc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-stm32f303vc" 3 | version = "0.1.0" 4 | authors = ["Vadim Kaushan "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | cortex-m = "0.6" 9 | cortex-m-rt = "0.6" 10 | stm32f3xx-hal = { version = "0.4", features = ["stm32f303xc", "stm32-usbd", "rt"] } 11 | panic-semihosting = "0.5" 12 | usb-device = "0.2.4" 13 | usbd-serial = "0.1" 14 | -------------------------------------------------------------------------------- /example-stm32f303vc/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | // Put the linker script somewhere the linker can find it 8 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 9 | fs::File::create(out_dir.join("memory.x")) 10 | .unwrap() 11 | .write_all(include_bytes!("memory.x")) 12 | .unwrap(); 13 | println!("cargo:rustc-link-search={}", out_dir.display()); 14 | println!("cargo:rerun-if-changed=memory.x"); 15 | } 16 | -------------------------------------------------------------------------------- /example-stm32f303vc/memory.x: -------------------------------------------------------------------------------- 1 | /* Linker script for the STM32F303VCT6 */ 2 | MEMORY 3 | { 4 | CCRAM : ORIGIN = 0x10000000, LENGTH = 8K 5 | FLASH : ORIGIN = 0x08000000, LENGTH = 256K 6 | RAM : ORIGIN = 0x20000000, LENGTH = 40K 7 | } 8 | 9 | _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM); 10 | -------------------------------------------------------------------------------- /example-stm32f303vc/openocd.cfg: -------------------------------------------------------------------------------- 1 | # Sample OpenOCD configuration for the STM32F3DISCOVERY development board 2 | 3 | source [find interface/stlink.cfg] 4 | 5 | source [find target/stm32f3x.cfg] 6 | -------------------------------------------------------------------------------- /example-stm32f303vc/openocd.gdb: -------------------------------------------------------------------------------- 1 | set history save on 2 | set confirm off 3 | target extended-remote :3333 4 | set print asm-demangle on 5 | monitor arm semihosting enable 6 | monitor reset halt 7 | load 8 | # monitor verify 9 | # monitor reset 10 | # quit 11 | continue 12 | -------------------------------------------------------------------------------- /example-stm32f303vc/src/main.rs: -------------------------------------------------------------------------------- 1 | //! CDC-ACM serial port example using polling in a busy loop. 2 | #![no_std] 3 | #![no_main] 4 | 5 | extern crate panic_semihosting; 6 | 7 | use cortex_m::asm::delay; 8 | use cortex_m_rt::entry; 9 | use stm32f3xx_hal::usb::{Peripheral, UsbBus}; 10 | use stm32f3xx_hal::{hal::digital::v2::OutputPin, prelude::*, stm32}; 11 | use usb_device::prelude::*; 12 | use usbd_serial::{SerialPort, USB_CLASS_CDC}; 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | let dp = stm32::Peripherals::take().unwrap(); 17 | 18 | let mut flash = dp.FLASH.constrain(); 19 | let mut rcc = dp.RCC.constrain(); 20 | 21 | let clocks = rcc 22 | .cfgr 23 | .use_hse(8.mhz()) 24 | .sysclk(48.mhz()) 25 | .pclk1(24.mhz()) 26 | .pclk2(24.mhz()) 27 | .freeze(&mut flash.acr); 28 | 29 | assert!(clocks.usbclk_valid()); 30 | 31 | // Configure the on-board LED (LD10, south red) 32 | let mut gpioe = dp.GPIOE.split(&mut rcc.ahb); 33 | let mut led = gpioe 34 | .pe13 35 | .into_push_pull_output(&mut gpioe.moder, &mut gpioe.otyper); 36 | led.set_low().ok(); // Turn off 37 | 38 | let mut gpioa = dp.GPIOA.split(&mut rcc.ahb); 39 | 40 | // F3 Discovery board has a pull-up resistor on the D+ line. 41 | // Pull the D+ pin down to send a RESET condition to the USB bus. 42 | // This forced reset is needed only for development, without it host 43 | // will not reset your device when you upload new firmware. 44 | let mut usb_dp = gpioa 45 | .pa12 46 | .into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper); 47 | usb_dp.set_low().ok(); 48 | delay(clocks.sysclk().0 / 100); 49 | 50 | let usb_dm = gpioa.pa11.into_af14(&mut gpioa.moder, &mut gpioa.afrh); 51 | let usb_dp = usb_dp.into_af14(&mut gpioa.moder, &mut gpioa.afrh); 52 | 53 | let usb = Peripheral { 54 | usb: dp.USB, 55 | pin_dm: usb_dm, 56 | pin_dp: usb_dp, 57 | }; 58 | let usb_bus = UsbBus::new(usb); 59 | 60 | let mut serial = SerialPort::new(&usb_bus); 61 | 62 | let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) 63 | .manufacturer("Fake company") 64 | .product("Serial port") 65 | .serial_number("TEST") 66 | .device_class(USB_CLASS_CDC) 67 | .build(); 68 | 69 | loop { 70 | if !usb_dev.poll(&mut [&mut serial]) { 71 | continue; 72 | } 73 | 74 | let mut buf = [0u8; 64]; 75 | 76 | match serial.read(&mut buf) { 77 | Ok(count) if count > 0 => { 78 | led.set_high().ok(); // Turn on 79 | 80 | // Echo back in upper case 81 | for c in buf[0..count].iter_mut() { 82 | if 0x61 <= *c && *c <= 0x7a { 83 | *c &= !0x20; 84 | } 85 | } 86 | 87 | let mut write_offset = 0; 88 | while write_offset < count { 89 | match serial.write(&buf[write_offset..count]) { 90 | Ok(len) if len > 0 => { 91 | write_offset += len; 92 | } 93 | _ => {} 94 | } 95 | } 96 | } 97 | _ => {} 98 | } 99 | 100 | led.set_low().ok(); // Turn off 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /example-stm32l432kc/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv7em-none-eabihf] 2 | runner = "arm-none-eabi-gdb -q -x openocd.gdb" 3 | rustflags = [ 4 | "-C", "linker=rust-lld", 5 | "-C", "link-arg=-Tlink.x", 6 | ] 7 | 8 | [build] 9 | target = "thumbv7em-none-eabihf" 10 | -------------------------------------------------------------------------------- /example-stm32l432kc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-stm32l432kc" 3 | version = "0.1.0" 4 | authors = ["Vadim Kaushan "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | cortex-m-rt = "0.6" 9 | stm32l4xx-hal = { version = "0.5", features = ["rt", "stm32l4x2"] } 10 | panic-semihosting = "0.5" 11 | usb-device = "0.2.1" 12 | usbd-serial = "0.1" 13 | stm32-usbd = { version = "0.4.0", features = ["stm32l4x2xx"] } 14 | -------------------------------------------------------------------------------- /example-stm32l432kc/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | // Put the linker script somewhere the linker can find it 8 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 9 | fs::File::create(out_dir.join("memory.x")) 10 | .unwrap() 11 | .write_all(include_bytes!("memory.x")) 12 | .unwrap(); 13 | println!("cargo:rustc-link-search={}", out_dir.display()); 14 | println!("cargo:rerun-if-changed=memory.x"); 15 | } 16 | -------------------------------------------------------------------------------- /example-stm32l432kc/memory.x: -------------------------------------------------------------------------------- 1 | /* STM32L432KC */ 2 | MEMORY 3 | { 4 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K 5 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 48K 6 | SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K 7 | } 8 | -------------------------------------------------------------------------------- /example-stm32l432kc/openocd.cfg: -------------------------------------------------------------------------------- 1 | source [find interface/stlink-v2-1.cfg] 2 | source [find target/stm32l4x.cfg] 3 | targets 4 | # reset halt 5 | # halt 6 | -------------------------------------------------------------------------------- /example-stm32l432kc/openocd.gdb: -------------------------------------------------------------------------------- 1 | set history save on 2 | set confirm off 3 | target extended-remote :3333 4 | set print asm-demangle on 5 | monitor arm semihosting enable 6 | monitor reset halt 7 | load 8 | # monitor verify 9 | # monitor reset 10 | # quit 11 | continue 12 | -------------------------------------------------------------------------------- /example-stm32l432kc/src/main.rs: -------------------------------------------------------------------------------- 1 | //! CDC-ACM serial port example using polling in a busy loop. 2 | #![no_std] 3 | #![no_main] 4 | 5 | extern crate panic_semihosting; 6 | 7 | use cortex_m_rt::entry; 8 | use stm32_usbd::UsbBus; 9 | use stm32l4xx_hal::{prelude::*, stm32}; 10 | use usb_device::prelude::*; 11 | use usbd_serial::{SerialPort, USB_CLASS_CDC}; 12 | 13 | fn enable_crs() { 14 | let rcc = unsafe { &(*stm32::RCC::ptr()) }; 15 | rcc.apb1enr1.modify(|_, w| w.crsen().set_bit()); 16 | let crs = unsafe { &(*stm32::CRS::ptr()) }; 17 | // Initialize clock recovery 18 | // Set autotrim enabled. 19 | crs.cr.modify(|_, w| w.autotrimen().set_bit()); 20 | // Enable CR 21 | crs.cr.modify(|_, w| w.cen().set_bit()); 22 | } 23 | 24 | /// Enables VddUSB power supply 25 | fn enable_usb_pwr() { 26 | // Enable PWR peripheral 27 | let rcc = unsafe { &(*stm32::RCC::ptr()) }; 28 | rcc.apb1enr1.modify(|_, w| w.pwren().set_bit()); 29 | 30 | // Enable VddUSB 31 | let pwr = unsafe { &*stm32::PWR::ptr() }; 32 | pwr.cr2.modify(|_, w| w.usv().set_bit()); 33 | } 34 | 35 | #[entry] 36 | fn main() -> ! { 37 | let dp = stm32::Peripherals::take().unwrap(); 38 | 39 | let mut flash = dp.FLASH.constrain(); 40 | let mut rcc = dp.RCC.constrain(); 41 | 42 | let _clocks = rcc 43 | .cfgr 44 | .hsi48(true) 45 | .sysclk(48.mhz()) 46 | .pclk1(24.mhz()) 47 | .pclk2(24.mhz()) 48 | .freeze(&mut flash.acr); 49 | 50 | enable_crs(); 51 | 52 | // disable Vddusb power isolation 53 | enable_usb_pwr(); 54 | 55 | // Configure the on-board LED (LD3, green) 56 | let mut gpiob = dp.GPIOB.split(&mut rcc.ahb2); 57 | let mut led = gpiob.pb3.into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper); 58 | led.set_low(); // Turn off 59 | 60 | let mut gpioa = dp.GPIOA.split(&mut rcc.ahb2); 61 | 62 | let usb_dm = gpioa.pa11.into_af10(&mut gpioa.moder, &mut gpioa.afrh); 63 | let usb_dp = gpioa.pa12.into_af10(&mut gpioa.moder, &mut gpioa.afrh); 64 | 65 | let usb_bus = UsbBus::new(dp.USB, (usb_dm, usb_dp)); 66 | 67 | let mut serial = SerialPort::new(&usb_bus); 68 | 69 | let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) 70 | .manufacturer("Fake company") 71 | .product("Serial port") 72 | .serial_number("TEST") 73 | .device_class(USB_CLASS_CDC) 74 | .build(); 75 | 76 | loop { 77 | if !usb_dev.poll(&mut [&mut serial]) { 78 | continue; 79 | } 80 | 81 | let mut buf = [0u8; 64]; 82 | 83 | match serial.read(&mut buf) { 84 | Ok(count) if count > 0 => { 85 | led.set_high(); // Turn on 86 | 87 | // Echo back in upper case 88 | for c in buf[0..count].iter_mut() { 89 | if 0x61 <= *c && *c <= 0x7a { 90 | *c &= !0x20; 91 | } 92 | } 93 | 94 | let mut write_offset = 0; 95 | while write_offset < count { 96 | match serial.write(&buf[write_offset..count]) { 97 | Ok(len) if len > 0 => { 98 | write_offset += len; 99 | }, 100 | _ => {}, 101 | } 102 | } 103 | } 104 | _ => {} 105 | } 106 | 107 | led.set_low(); // Turn off 108 | } 109 | } 110 | --------------------------------------------------------------------------------