Răsfoiți Sursa

Add USB driver and examples

Vadim Kaushan 4 ani în urmă
părinte
comite
5ea743f317
7 a modificat fișierele cu 384 adăugiri și 0 ștergeri
  1. 1 0
      CHANGELOG.md
  2. 19 0
      Cargo.toml
  3. 94 0
      examples/usb_serial.rs
  4. 116 0
      examples/usb_serial_interrupt.rs
  5. 105 0
      examples/usb_serial_rtfm.rs
  6. 5 0
      src/lib.rs
  7. 44 0
      src/usb.rs

+ 1 - 0
CHANGELOG.md

@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 - Added `micros_since` and `reset` methods to timer
 - Added `select_frequency` method to RTC
 - Unidirectional DMA support for SPI (TX only)
+- Added USB driver for `stm32f102` and `stm32f103` devices
 
 ### Breaking changes
 

+ 19 - 0
Cargo.toml

@@ -37,6 +37,11 @@ version = "0.2.2"
 version = "0.2.3"
 features = ["unproven"]
 
+[dependencies.stm32-usbd]
+version = "0.5.0"
+features = ["ram_access_1x16"]
+optional = true
+
 [dev-dependencies]
 panic-halt = "0.2.0"
 panic-semihosting = "0.5.2"
@@ -48,6 +53,8 @@ heapless = "0.4.3"
 m = "0.1.1"
 mfrc522 = "0.2.0"
 serde_derive = "1.0.90"
+usb-device = "0.2.3"
+usbd-serial = "0.1.0"
 
 [dev-dependencies.byteorder]
 default-features = false
@@ -108,3 +115,15 @@ codegen-units = 1
 codegen-units = 1
 debug = true
 lto = true
+
+[[example]]
+name = "usb_serial"
+required-features = ["rt", "stm32f103", "stm32-usbd"]
+
+[[example]]
+name = "usb_serial_interrupt"
+required-features = ["rt", "stm32f103", "stm32-usbd"]
+
+[[example]]
+name = "usb_serial_rtfm"
+required-features = ["rt", "stm32f103", "stm32-usbd"]

+ 94 - 0
examples/usb_serial.rs

@@ -0,0 +1,94 @@
+//! CDC-ACM serial port example using polling in a busy loop.
+//! Target board: Blue Pill
+#![no_std]
+#![no_main]
+
+extern crate panic_semihosting;
+
+use cortex_m::asm::delay;
+use cortex_m_rt::entry;
+use embedded_hal::digital::v2::OutputPin;
+use stm32f1xx_hal::usb::{Peripheral, UsbBus};
+use stm32f1xx_hal::{prelude::*, stm32};
+use usb_device::prelude::*;
+use usbd_serial::{SerialPort, USB_CLASS_CDC};
+
+#[entry]
+fn main() -> ! {
+    let dp = stm32::Peripherals::take().unwrap();
+
+    let mut flash = dp.FLASH.constrain();
+    let mut rcc = dp.RCC.constrain();
+
+    let clocks = rcc
+        .cfgr
+        .use_hse(8.mhz())
+        .sysclk(48.mhz())
+        .pclk1(24.mhz())
+        .freeze(&mut flash.acr);
+
+    assert!(clocks.usbclk_valid());
+
+    // Configure the on-board LED (PC13, green)
+    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
+    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
+    led.set_high(); // Turn off
+
+    let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
+
+    // BluePill board has a pull-up resistor on the D+ line.
+    // Pull the D+ pin down to send a RESET condition to the USB bus.
+    let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh);
+    usb_dp.set_low();
+    delay(clocks.sysclk().0 / 100);
+
+    let usb = Peripheral {
+        usb: dp.USB,
+        pin_dm: gpioa.pa11,
+        pin_dp: usb_dp.into_floating_input(&mut gpioa.crh),
+    };
+    let usb_bus = UsbBus::new(usb);
+
+    let mut serial = SerialPort::new(&usb_bus);
+
+    let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
+        .manufacturer("Fake company")
+        .product("Serial port")
+        .serial_number("TEST")
+        .device_class(USB_CLASS_CDC)
+        .build();
+
+    loop {
+        if !usb_dev.poll(&mut [&mut serial]) {
+            continue;
+        }
+
+        let mut buf = [0u8; 64];
+
+        match serial.read(&mut buf) {
+            Ok(count) if count > 0 => {
+                led.set_low(); // Turn on
+
+                // Echo back in upper case
+                for c in buf[0..count].iter_mut() {
+                    if 0x61 <= *c && *c <= 0x7a {
+                        *c &= !0x20;
+                    }
+                }
+
+                let mut write_offset = 0;
+                while write_offset < count {
+                    match serial.write(&buf[write_offset..count]) {
+                        Ok(len) if len > 0 => {
+                            write_offset += len;
+                        }
+                        _ => {}
+                    }
+                }
+            }
+            _ => {}
+        }
+
+        led.set_high(); // Turn off
+    }
+}

+ 116 - 0
examples/usb_serial_interrupt.rs

@@ -0,0 +1,116 @@
+//! CDC-ACM serial port example using interrupts.
+//! Target board: Blue Pill
+#![no_std]
+#![no_main]
+
+extern crate panic_semihosting;
+
+use cortex_m::asm::{delay, wfi};
+use cortex_m_rt::entry;
+use embedded_hal::digital::v2::OutputPin;
+use stm32f1xx_hal::stm32::{interrupt, Interrupt};
+use stm32f1xx_hal::usb::{Peripheral, UsbBus, UsbBusType};
+use stm32f1xx_hal::{prelude::*, stm32};
+use usb_device::{bus::UsbBusAllocator, prelude::*};
+use usbd_serial::{SerialPort, USB_CLASS_CDC};
+
+static mut USB_BUS: Option<UsbBusAllocator<UsbBusType>> = None;
+static mut USB_SERIAL: Option<usbd_serial::SerialPort<UsbBusType>> = None;
+static mut USB_DEVICE: Option<UsbDevice<UsbBusType>> = None;
+
+#[entry]
+fn main() -> ! {
+    let p = cortex_m::Peripherals::take().unwrap();
+    let dp = stm32::Peripherals::take().unwrap();
+
+    let mut flash = dp.FLASH.constrain();
+    let mut rcc = dp.RCC.constrain();
+
+    let clocks = rcc
+        .cfgr
+        .use_hse(8.mhz())
+        .sysclk(48.mhz())
+        .pclk1(24.mhz())
+        .freeze(&mut flash.acr);
+
+    assert!(clocks.usbclk_valid());
+
+    let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
+
+    // BluePill board has a pull-up resistor on the D+ line.
+    // Pull the D+ pin down to send a RESET condition to the USB bus.
+    let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh);
+    usb_dp.set_low();
+    delay(clocks.sysclk().0 / 100);
+
+    let usb_dm = gpioa.pa11;
+    let usb_dp = usb_dp.into_floating_input(&mut gpioa.crh);
+
+    let usb = Peripheral {
+        usb: dp.USB,
+        pin_dm: usb_dm,
+        pin_dp: usb_dp,
+    };
+
+    // Unsafe to allow access to static variables
+    unsafe {
+        let bus = UsbBus::new(usb);
+
+        USB_BUS = Some(bus);
+
+        USB_SERIAL = Some(SerialPort::new(USB_BUS.as_ref().unwrap()));
+
+        let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27dd))
+            .manufacturer("Fake company")
+            .product("Serial port")
+            .serial_number("TEST")
+            .device_class(USB_CLASS_CDC)
+            .build();
+
+        USB_DEVICE = Some(usb_dev);
+    }
+
+    let mut nvic = p.NVIC;
+
+    nvic.enable(Interrupt::USB_HP_CAN_TX);
+    nvic.enable(Interrupt::USB_LP_CAN_RX0);
+
+    loop {
+        wfi();
+    }
+}
+
+#[interrupt]
+fn USB_HP_CAN_TX() {
+    usb_interrupt();
+}
+
+#[interrupt]
+fn USB_LP_CAN_RX0() {
+    usb_interrupt();
+}
+
+fn usb_interrupt() {
+    let usb_dev = unsafe { USB_DEVICE.as_mut().unwrap() };
+    let serial = unsafe { USB_SERIAL.as_mut().unwrap() };
+
+    if !usb_dev.poll(&mut [serial]) {
+        return;
+    }
+
+    let mut buf = [0u8; 8];
+
+    match serial.read(&mut buf) {
+        Ok(count) if count > 0 => {
+            // Echo back in upper case
+            for c in buf[0..count].iter_mut() {
+                if 0x61 <= *c && *c <= 0x7a {
+                    *c &= !0x20;
+                }
+            }
+
+            serial.write(&buf[0..count]).ok();
+        }
+        _ => {}
+    }
+}

+ 105 - 0
examples/usb_serial_rtfm.rs

@@ -0,0 +1,105 @@
+//! CDC-ACM serial port example using cortex-m-rtfm.
+//! Target board: Blue Pill
+#![no_main]
+#![no_std]
+#![allow(non_snake_case)]
+
+extern crate panic_semihosting;
+
+use cortex_m::asm::delay;
+use embedded_hal::digital::v2::OutputPin;
+use rtfm::app;
+use stm32f1xx_hal::prelude::*;
+use stm32f1xx_hal::usb::{Peripheral, UsbBus, UsbBusType};
+use usb_device::bus;
+use usb_device::prelude::*;
+use usbd_serial::{SerialPort, USB_CLASS_CDC};
+
+#[app(device = stm32f1xx_hal::stm32)]
+const APP: () = {
+    static mut USB_DEV: UsbDevice<'static, UsbBusType> = ();
+    static mut SERIAL: SerialPort<'static, UsbBusType> = ();
+
+    #[init]
+    fn init() {
+        static mut USB_BUS: Option<bus::UsbBusAllocator<UsbBusType>> = None;
+
+        let mut flash = device.FLASH.constrain();
+        let mut rcc = device.RCC.constrain();
+
+        let clocks = rcc
+            .cfgr
+            .use_hse(8.mhz())
+            .sysclk(48.mhz())
+            .pclk1(24.mhz())
+            .freeze(&mut flash.acr);
+
+        assert!(clocks.usbclk_valid());
+
+        let mut gpioa = device.GPIOA.split(&mut rcc.apb2);
+
+        // BluePill board has a pull-up resistor on the D+ line.
+        // Pull the D+ pin down to send a RESET condition to the USB bus.
+        let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh);
+        usb_dp.set_low();
+        delay(clocks.sysclk().0 / 100);
+
+        let usb_dm = gpioa.pa11;
+        let usb_dp = usb_dp.into_floating_input(&mut gpioa.crh);
+
+        let usb = Peripheral {
+            usb: device.USB,
+            pin_dm: usb_dm,
+            pin_dp: usb_dp,
+        };
+
+        *USB_BUS = Some(UsbBus::new(usb));
+
+        let serial = SerialPort::new(USB_BUS.as_ref().unwrap());
+
+        let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27dd))
+            .manufacturer("Fake company")
+            .product("Serial port")
+            .serial_number("TEST")
+            .device_class(USB_CLASS_CDC)
+            .build();
+
+        USB_DEV = usb_dev;
+        SERIAL = serial;
+    }
+
+    #[interrupt(resources = [USB_DEV, SERIAL])]
+    fn USB_HP_CAN_TX() {
+        usb_poll(&mut resources.USB_DEV, &mut resources.SERIAL);
+    }
+
+    #[interrupt(resources = [USB_DEV, SERIAL])]
+    fn USB_LP_CAN_RX0() {
+        usb_poll(&mut resources.USB_DEV, &mut resources.SERIAL);
+    }
+};
+
+fn usb_poll<B: bus::UsbBus>(
+    usb_dev: &mut UsbDevice<'static, B>,
+    serial: &mut SerialPort<'static, B>,
+) {
+    if !usb_dev.poll(&mut [serial]) {
+        return;
+    }
+
+    let mut buf = [0u8; 8];
+
+    match serial.read(&mut buf) {
+        Ok(count) if count > 0 => {
+            // Echo back in upper case
+            for c in buf[0..count].iter_mut() {
+                if 0x61 <= *c && *c <= 0x7a {
+                    *c &= !0x20;
+                }
+            }
+
+            serial.write(&buf[0..count]).ok();
+        }
+        _ => {}
+    }
+}

+ 5 - 0
src/lib.rs

@@ -160,5 +160,10 @@ pub mod spi;
 pub mod time;
 #[cfg(feature = "device-selected")]
 pub mod timer;
+#[cfg(all(
+    feature = "stm32-usbd",
+    any(feature = "stm32f102", feature = "stm32f103")
+))]
+pub mod usb;
 #[cfg(feature = "device-selected")]
 pub mod watchdog;

+ 44 - 0
src/usb.rs

@@ -0,0 +1,44 @@
+//! USB peripheral
+
+use crate::pac::{RCC, USB};
+use stm32_usbd::UsbPeripheral;
+
+use crate::gpio::gpioa::{PA11, PA12};
+use crate::gpio::{Floating, Input};
+pub use stm32_usbd::UsbBus;
+
+pub struct Peripheral {
+    pub usb: USB,
+    pub pin_dm: PA11<Input<Floating>>,
+    pub pin_dp: PA12<Input<Floating>>,
+}
+
+unsafe impl Sync for Peripheral {}
+
+unsafe impl UsbPeripheral for Peripheral {
+    const REGISTERS: *const () = USB::ptr() as *const ();
+    const DP_PULL_UP_FEATURE: bool = false;
+    const EP_MEMORY: *const () = 0x4000_6000 as _;
+    const EP_MEMORY_SIZE: usize = 512;
+
+    fn enable() {
+        let rcc = unsafe { (&*RCC::ptr()) };
+
+        cortex_m::interrupt::free(|_| {
+            // Enable USB peripheral
+            rcc.apb1enr.modify(|_, w| w.usben().set_bit());
+
+            // Reset USB peripheral
+            rcc.apb1rstr.modify(|_, w| w.usbrst().set_bit());
+            rcc.apb1rstr.modify(|_, w| w.usbrst().clear_bit());
+        });
+    }
+
+    fn startup_delay() {
+        // There is a chip specific startup delay. For STM32F103xx it's 1µs and this should wait for
+        // at least that long.
+        cortex_m::asm::delay(72);
+    }
+}
+
+pub type UsbBusType = UsbBus<Peripheral>;