Skip to content

Commit 5ea743f

Browse files
committed
Add USB driver and examples
1 parent 420243a commit 5ea743f

File tree

7 files changed

+384
-0
lines changed

7 files changed

+384
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1313
- Added `micros_since` and `reset` methods to timer
1414
- Added `select_frequency` method to RTC
1515
- Unidirectional DMA support for SPI (TX only)
16+
- Added USB driver for `stm32f102` and `stm32f103` devices
1617

1718
### Breaking changes
1819

Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ version = "0.2.2"
3737
version = "0.2.3"
3838
features = ["unproven"]
3939

40+
[dependencies.stm32-usbd]
41+
version = "0.5.0"
42+
features = ["ram_access_1x16"]
43+
optional = true
44+
4045
[dev-dependencies]
4146
panic-halt = "0.2.0"
4247
panic-semihosting = "0.5.2"
@@ -48,6 +53,8 @@ heapless = "0.4.3"
4853
m = "0.1.1"
4954
mfrc522 = "0.2.0"
5055
serde_derive = "1.0.90"
56+
usb-device = "0.2.3"
57+
usbd-serial = "0.1.0"
5158

5259
[dev-dependencies.byteorder]
5360
default-features = false
@@ -108,3 +115,15 @@ codegen-units = 1
108115
codegen-units = 1
109116
debug = true
110117
lto = true
118+
119+
[[example]]
120+
name = "usb_serial"
121+
required-features = ["rt", "stm32f103", "stm32-usbd"]
122+
123+
[[example]]
124+
name = "usb_serial_interrupt"
125+
required-features = ["rt", "stm32f103", "stm32-usbd"]
126+
127+
[[example]]
128+
name = "usb_serial_rtfm"
129+
required-features = ["rt", "stm32f103", "stm32-usbd"]

examples/usb_serial.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//! CDC-ACM serial port example using polling in a busy loop.
2+
//! Target board: Blue Pill
3+
#![no_std]
4+
#![no_main]
5+
6+
extern crate panic_semihosting;
7+
8+
use cortex_m::asm::delay;
9+
use cortex_m_rt::entry;
10+
use embedded_hal::digital::v2::OutputPin;
11+
use stm32f1xx_hal::usb::{Peripheral, UsbBus};
12+
use stm32f1xx_hal::{prelude::*, stm32};
13+
use usb_device::prelude::*;
14+
use usbd_serial::{SerialPort, USB_CLASS_CDC};
15+
16+
#[entry]
17+
fn main() -> ! {
18+
let dp = stm32::Peripherals::take().unwrap();
19+
20+
let mut flash = dp.FLASH.constrain();
21+
let mut rcc = dp.RCC.constrain();
22+
23+
let clocks = rcc
24+
.cfgr
25+
.use_hse(8.mhz())
26+
.sysclk(48.mhz())
27+
.pclk1(24.mhz())
28+
.freeze(&mut flash.acr);
29+
30+
assert!(clocks.usbclk_valid());
31+
32+
// Configure the on-board LED (PC13, green)
33+
let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
34+
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
35+
led.set_high(); // Turn off
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+
let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh);
42+
usb_dp.set_low();
43+
delay(clocks.sysclk().0 / 100);
44+
45+
let usb = Peripheral {
46+
usb: dp.USB,
47+
pin_dm: gpioa.pa11,
48+
pin_dp: usb_dp.into_floating_input(&mut gpioa.crh),
49+
};
50+
let usb_bus = UsbBus::new(usb);
51+
52+
let mut serial = SerialPort::new(&usb_bus);
53+
54+
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
55+
.manufacturer("Fake company")
56+
.product("Serial port")
57+
.serial_number("TEST")
58+
.device_class(USB_CLASS_CDC)
59+
.build();
60+
61+
loop {
62+
if !usb_dev.poll(&mut [&mut serial]) {
63+
continue;
64+
}
65+
66+
let mut buf = [0u8; 64];
67+
68+
match serial.read(&mut buf) {
69+
Ok(count) if count > 0 => {
70+
led.set_low(); // Turn on
71+
72+
// Echo back in upper case
73+
for c in buf[0..count].iter_mut() {
74+
if 0x61 <= *c && *c <= 0x7a {
75+
*c &= !0x20;
76+
}
77+
}
78+
79+
let mut write_offset = 0;
80+
while write_offset < count {
81+
match serial.write(&buf[write_offset..count]) {
82+
Ok(len) if len > 0 => {
83+
write_offset += len;
84+
}
85+
_ => {}
86+
}
87+
}
88+
}
89+
_ => {}
90+
}
91+
92+
led.set_high(); // Turn off
93+
}
94+
}

examples/usb_serial_interrupt.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//! CDC-ACM serial port example using interrupts.
2+
//! Target board: Blue Pill
3+
#![no_std]
4+
#![no_main]
5+
6+
extern crate panic_semihosting;
7+
8+
use cortex_m::asm::{delay, wfi};
9+
use cortex_m_rt::entry;
10+
use embedded_hal::digital::v2::OutputPin;
11+
use stm32f1xx_hal::stm32::{interrupt, Interrupt};
12+
use stm32f1xx_hal::usb::{Peripheral, UsbBus, UsbBusType};
13+
use stm32f1xx_hal::{prelude::*, stm32};
14+
use usb_device::{bus::UsbBusAllocator, prelude::*};
15+
use usbd_serial::{SerialPort, USB_CLASS_CDC};
16+
17+
static mut USB_BUS: Option<UsbBusAllocator<UsbBusType>> = None;
18+
static mut USB_SERIAL: Option<usbd_serial::SerialPort<UsbBusType>> = None;
19+
static mut USB_DEVICE: Option<UsbDevice<UsbBusType>> = None;
20+
21+
#[entry]
22+
fn main() -> ! {
23+
let p = cortex_m::Peripherals::take().unwrap();
24+
let dp = stm32::Peripherals::take().unwrap();
25+
26+
let mut flash = dp.FLASH.constrain();
27+
let mut rcc = dp.RCC.constrain();
28+
29+
let clocks = rcc
30+
.cfgr
31+
.use_hse(8.mhz())
32+
.sysclk(48.mhz())
33+
.pclk1(24.mhz())
34+
.freeze(&mut flash.acr);
35+
36+
assert!(clocks.usbclk_valid());
37+
38+
let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
39+
40+
// BluePill 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+
let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh);
43+
usb_dp.set_low();
44+
delay(clocks.sysclk().0 / 100);
45+
46+
let usb_dm = gpioa.pa11;
47+
let usb_dp = usb_dp.into_floating_input(&mut gpioa.crh);
48+
49+
let usb = Peripheral {
50+
usb: dp.USB,
51+
pin_dm: usb_dm,
52+
pin_dp: usb_dp,
53+
};
54+
55+
// Unsafe to allow access to static variables
56+
unsafe {
57+
let bus = UsbBus::new(usb);
58+
59+
USB_BUS = Some(bus);
60+
61+
USB_SERIAL = Some(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+
USB_DEVICE = Some(usb_dev);
71+
}
72+
73+
let mut nvic = p.NVIC;
74+
75+
nvic.enable(Interrupt::USB_HP_CAN_TX);
76+
nvic.enable(Interrupt::USB_LP_CAN_RX0);
77+
78+
loop {
79+
wfi();
80+
}
81+
}
82+
83+
#[interrupt]
84+
fn USB_HP_CAN_TX() {
85+
usb_interrupt();
86+
}
87+
88+
#[interrupt]
89+
fn USB_LP_CAN_RX0() {
90+
usb_interrupt();
91+
}
92+
93+
fn usb_interrupt() {
94+
let usb_dev = unsafe { USB_DEVICE.as_mut().unwrap() };
95+
let serial = unsafe { USB_SERIAL.as_mut().unwrap() };
96+
97+
if !usb_dev.poll(&mut [serial]) {
98+
return;
99+
}
100+
101+
let mut buf = [0u8; 8];
102+
103+
match serial.read(&mut buf) {
104+
Ok(count) if count > 0 => {
105+
// Echo back in upper case
106+
for c in buf[0..count].iter_mut() {
107+
if 0x61 <= *c && *c <= 0x7a {
108+
*c &= !0x20;
109+
}
110+
}
111+
112+
serial.write(&buf[0..count]).ok();
113+
}
114+
_ => {}
115+
}
116+
}

examples/usb_serial_rtfm.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//! CDC-ACM serial port example using cortex-m-rtfm.
2+
//! Target board: Blue Pill
3+
#![no_main]
4+
#![no_std]
5+
#![allow(non_snake_case)]
6+
7+
extern crate panic_semihosting;
8+
9+
use cortex_m::asm::delay;
10+
use embedded_hal::digital::v2::OutputPin;
11+
use rtfm::app;
12+
use stm32f1xx_hal::prelude::*;
13+
use stm32f1xx_hal::usb::{Peripheral, UsbBus, UsbBusType};
14+
use usb_device::bus;
15+
use usb_device::prelude::*;
16+
use usbd_serial::{SerialPort, USB_CLASS_CDC};
17+
18+
#[app(device = stm32f1xx_hal::stm32)]
19+
const APP: () = {
20+
static mut USB_DEV: UsbDevice<'static, UsbBusType> = ();
21+
static mut SERIAL: SerialPort<'static, UsbBusType> = ();
22+
23+
#[init]
24+
fn init() {
25+
static mut USB_BUS: Option<bus::UsbBusAllocator<UsbBusType>> = None;
26+
27+
let mut flash = device.FLASH.constrain();
28+
let mut rcc = device.RCC.constrain();
29+
30+
let clocks = rcc
31+
.cfgr
32+
.use_hse(8.mhz())
33+
.sysclk(48.mhz())
34+
.pclk1(24.mhz())
35+
.freeze(&mut flash.acr);
36+
37+
assert!(clocks.usbclk_valid());
38+
39+
let mut gpioa = device.GPIOA.split(&mut rcc.apb2);
40+
41+
// BluePill board has a pull-up resistor on the D+ line.
42+
// Pull the D+ pin down to send a RESET condition to the USB bus.
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: device.USB,
52+
pin_dm: usb_dm,
53+
pin_dp: usb_dp,
54+
};
55+
56+
*USB_BUS = Some(UsbBus::new(usb));
57+
58+
let serial = SerialPort::new(USB_BUS.as_ref().unwrap());
59+
60+
let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27dd))
61+
.manufacturer("Fake company")
62+
.product("Serial port")
63+
.serial_number("TEST")
64+
.device_class(USB_CLASS_CDC)
65+
.build();
66+
67+
USB_DEV = usb_dev;
68+
SERIAL = serial;
69+
}
70+
71+
#[interrupt(resources = [USB_DEV, SERIAL])]
72+
fn USB_HP_CAN_TX() {
73+
usb_poll(&mut resources.USB_DEV, &mut resources.SERIAL);
74+
}
75+
76+
#[interrupt(resources = [USB_DEV, SERIAL])]
77+
fn USB_LP_CAN_RX0() {
78+
usb_poll(&mut resources.USB_DEV, &mut resources.SERIAL);
79+
}
80+
};
81+
82+
fn usb_poll<B: bus::UsbBus>(
83+
usb_dev: &mut UsbDevice<'static, B>,
84+
serial: &mut SerialPort<'static, B>,
85+
) {
86+
if !usb_dev.poll(&mut [serial]) {
87+
return;
88+
}
89+
90+
let mut buf = [0u8; 8];
91+
92+
match serial.read(&mut buf) {
93+
Ok(count) if count > 0 => {
94+
// Echo back in upper case
95+
for c in buf[0..count].iter_mut() {
96+
if 0x61 <= *c && *c <= 0x7a {
97+
*c &= !0x20;
98+
}
99+
}
100+
101+
serial.write(&buf[0..count]).ok();
102+
}
103+
_ => {}
104+
}
105+
}

src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,5 +160,10 @@ pub mod spi;
160160
pub mod time;
161161
#[cfg(feature = "device-selected")]
162162
pub mod timer;
163+
#[cfg(all(
164+
feature = "stm32-usbd",
165+
any(feature = "stm32f102", feature = "stm32f103")
166+
))]
167+
pub mod usb;
163168
#[cfg(feature = "device-selected")]
164169
pub mod watchdog;

0 commit comments

Comments
 (0)