Skip to content

Commit

Permalink
Support USB-DEVICE on ESP32-S3 and ESP32-S2
Browse files Browse the repository at this point in the history
  • Loading branch information
bjoernQ committed Nov 2, 2022
1 parent e040b55 commit cbbbe97
Show file tree
Hide file tree
Showing 18 changed files with 359 additions and 19 deletions.
34 changes: 18 additions & 16 deletions esp-hal-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,20 @@ repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"

[dependencies]
cfg-if = "1.0.0"
critical-section = "1.1.0"
embedded-hal = { version = "0.2.7", features = ["unproven"] }
embedded-hal-1 = { version = "=1.0.0-alpha.9", optional = true, package = "embedded-hal" }
embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true }
fugit = "0.3.6"
lock_api = { version = "0.4.8", optional = true }
nb = "1.0.0"
paste = "=1.0.8"
procmacros = { version = "0.1.0", package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
void = { version = "1.0.2", default-features = false }
embedded-dma = "0.2.0"
cfg-if = "1.0.0"
critical-section = "1.1.1"
embedded-hal = { version = "0.2.7", features = ["unproven"] }
embedded-hal-1 = { version = "=1.0.0-alpha.9", optional = true, package = "embedded-hal" }
embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true }
fugit = "0.3.6"
lock_api = { version = "0.4.8", optional = true }
nb = "1.0.0"
paste = "=1.0.8"
procmacros = { version = "0.1.0", package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
void = { version = "1.0.2", default-features = false }
embedded-dma = "0.2.0"
esp-synopsys-usb-otg = { version = "0.3.1", optional = true, features = ["fs", "esp32sx"] }
usb-device = { version = "0.2.3", optional = true }

# RISC-V
riscv = { version = "0.9.0", optional = true }
Expand All @@ -46,15 +48,15 @@ ufmt-write = { version = "0.1.0", optional = true }
esp32 = { version = "0.14.0", features = ["critical-section"], optional = true }
esp32c2 = { version = "0.3.0", features = ["critical-section"], optional = true }
esp32c3 = { version = "0.6.0", features = ["critical-section"], optional = true }
esp32s2 = { version = "0.4.0", features = ["critical-section"], optional = true }
esp32s3 = { version = "0.4.0", features = ["critical-section"], optional = true }
esp32s2 = { version = "0.5.0", features = ["critical-section"], optional = true }
esp32s3 = { version = "0.5.0", features = ["critical-section"], optional = true }

[features]
esp32 = ["esp32/rt" , "procmacros/xtensa", "xtensa-lx-rt/esp32", "xtensa-lx/esp32", "critical-section/restore-state-u32", "lock_api"]
esp32c2 = ["esp32c2/rt", "procmacros/riscv" , "riscv", "riscv-atomic-emulation-trap", "critical-section/restore-state-u8"]
esp32c3 = ["esp32c3/rt", "procmacros/riscv" , "riscv", "riscv-atomic-emulation-trap", "critical-section/restore-state-u8"]
esp32s2 = ["esp32s2/rt", "procmacros/xtensa", "xtensa-lx-rt/esp32s2", "xtensa-lx/esp32s2", "critical-section/restore-state-u32"]
esp32s3 = ["esp32s3/rt", "procmacros/xtensa", "xtensa-lx-rt/esp32s3", "xtensa-lx/esp32s3", "critical-section/restore-state-u32", "lock_api"]
esp32s2 = ["esp32s2/rt", "procmacros/xtensa", "xtensa-lx-rt/esp32s2", "xtensa-lx/esp32s2", "critical-section/restore-state-u32", "esp-synopsys-usb-otg", "usb-device"]
esp32s3 = ["esp32s3/rt", "procmacros/xtensa", "xtensa-lx-rt/esp32s3", "xtensa-lx/esp32s3", "critical-section/restore-state-u32", "lock_api", "esp-synopsys-usb-otg", "usb-device"]

# Implement the `embedded-hal==1.0.0-alpha.x` traits
eh1 = ["embedded-hal-1", "embedded-hal-nb"]
Expand Down
6 changes: 3 additions & 3 deletions esp-hal-common/src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ pub fn connect_low_to_peripheral(signal: InputSignal) {
.in_inv_sel()
.bit(false)
.in_sel()
.bits(0x1f)
.bits(ZERO_INPUT)
});
}

Expand All @@ -430,7 +430,7 @@ pub fn connect_high_to_peripheral(signal: InputSignal) {
.in_inv_sel()
.bit(false)
.in_sel()
.bits(0x1e)
.bits(ONE_INPUT)
});
}

Expand Down Expand Up @@ -1311,4 +1311,4 @@ pub use impl_interrupt_status_register_access;
pub use impl_output;
pub use impl_output_wrap;

use self::types::{InputSignal, OutputSignal};
use self::types::{InputSignal, OutputSignal, ONE_INPUT, ZERO_INPUT};
3 changes: 3 additions & 0 deletions esp-hal-common/src/gpio/esp32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ pub type OutputSignalType = u16;
pub const OUTPUT_SIGNAL_MAX: u16 = 548;
pub const INPUT_SIGNAL_MAX: u16 = 539;

pub const ONE_INPUT: u8 = 0x38;
pub const ZERO_INPUT: u8 = 0x30;

/// Peripheral input signals for the GPIO mux
#[allow(non_camel_case_types)]
#[derive(PartialEq, Copy, Clone)]
Expand Down
3 changes: 3 additions & 0 deletions esp-hal-common/src/gpio/esp32c2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ pub type OutputSignalType = u8;
pub const OUTPUT_SIGNAL_MAX: u8 = 128;
pub const INPUT_SIGNAL_MAX: u8 = 100;

pub const ONE_INPUT: u8 = 0x1e;
pub const ZERO_INPUT: u8 = 0x1f;

#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq)]
pub enum InputSignal {
Expand Down
3 changes: 3 additions & 0 deletions esp-hal-common/src/gpio/esp32c3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ pub type OutputSignalType = u8;
pub const OUTPUT_SIGNAL_MAX: u8 = 128;
pub const INPUT_SIGNAL_MAX: u8 = 100;

pub const ONE_INPUT: u8 = 0x1e;
pub const ZERO_INPUT: u8 = 0x1f;

#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq)]
pub enum InputSignal {
Expand Down
3 changes: 3 additions & 0 deletions esp-hal-common/src/gpio/esp32s2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ pub type OutputSignalType = u16;
pub const OUTPUT_SIGNAL_MAX: u16 = 256;
pub const INPUT_SIGNAL_MAX: u16 = 204;

pub const ONE_INPUT: u8 = 0x38;
pub const ZERO_INPUT: u8 = 0x3c;

/// Peripheral input signals for the GPIO mux
#[allow(non_camel_case_types)]
#[derive(PartialEq, Copy, Clone)]
Expand Down
3 changes: 3 additions & 0 deletions esp-hal-common/src/gpio/esp32s3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ pub type OutputSignalType = u16;
pub const OUTPUT_SIGNAL_MAX: u16 = 256;
pub const INPUT_SIGNAL_MAX: u16 = 189;

pub const ONE_INPUT: u8 = 0x38;
pub const ZERO_INPUT: u8 = 0x3c;

/// Peripheral input signals for the GPIO mux
#[allow(non_camel_case_types)]
#[derive(PartialEq, Copy, Clone)]
Expand Down
3 changes: 3 additions & 0 deletions esp-hal-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ pub mod efuse;
#[cfg_attr(xtensa, path = "interrupt/xtensa.rs")]
pub mod interrupt;

#[cfg(any(esp32s3, esp32s2))]
pub mod otg_fs;

/// Enumeration of CPU cores
/// The actual number of available cores depends on the target.
pub enum Cpu {
Expand Down
123 changes: 123 additions & 0 deletions esp-hal-common/src/otg_fs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//! USB OTG full-speed peripheral

pub use esp_synopsys_usb_otg::UsbBus;
use esp_synopsys_usb_otg::UsbPeripheral;

use crate::{
pac,
system::{Peripheral, PeripheralClockControl},
types::InputSignal,
};

#[doc(hidden)]
pub trait UsbSel {}

#[doc(hidden)]
pub trait UsbDp {}

#[doc(hidden)]
pub trait UsbDm {}

pub struct USB<S, P, M>
where
S: UsbSel + Send + Sync,
P: UsbDp + Send + Sync,
M: UsbDm + Send + Sync,
{
_usb0: pac::USB0,
_usb_sel: S,
_usb_dp: P,
_usb_dm: M,
}

impl<S, P, M> USB<S, P, M>
where
S: UsbSel + Send + Sync,
P: UsbDp + Send + Sync,
M: UsbDm + Send + Sync,
{
pub fn new(
usb0: pac::USB0,
usb_sel: S,
usb_dp: P,
usb_dm: M,
peripheral_clock_control: &mut PeripheralClockControl,
) -> Self {
peripheral_clock_control.enable(Peripheral::Usb);
Self {
_usb0: usb0,
_usb_sel: usb_sel,
_usb_dp: usb_dp,
_usb_dm: usb_dm,
}
}
}

unsafe impl<S, P, M> Sync for USB<S, P, M>
where
S: UsbSel + Send + Sync,
P: UsbDp + Send + Sync,
M: UsbDm + Send + Sync,
{
}

unsafe impl<S, P, M> UsbPeripheral for USB<S, P, M>
where
S: UsbSel + Send + Sync,
P: UsbDp + Send + Sync,
M: UsbDm + Send + Sync,
{
const REGISTERS: *const () = pac::USB0::ptr() as *const ();

const HIGH_SPEED: bool = false;
const FIFO_DEPTH_WORDS: usize = 256;
const ENDPOINT_COUNT: usize = 5;

fn enable() {
unsafe {
let usb_wrap = &*pac::USB_WRAP::PTR;
usb_wrap.otg_conf.modify(|_, w| {
w.usb_pad_enable()
.set_bit()
.phy_sel()
.clear_bit()
.clk_en()
.set_bit()
.ahb_clk_force_on()
.set_bit()
.phy_clk_force_on()
.set_bit()
});

#[cfg(esp32s3)]
{
let rtc = &*pac::RTC_CNTL::PTR;
rtc.usb_conf
.modify(|_, w| w.sw_hw_usb_phy_sel().set_bit().sw_usb_phy_sel().set_bit());
}

crate::gpio::connect_high_to_peripheral(InputSignal::USB_OTG_IDDIG); // connected connector is mini-B side
crate::gpio::connect_high_to_peripheral(InputSignal::USB_SRP_BVALID); // HIGH to force USB device mode
crate::gpio::connect_high_to_peripheral(InputSignal::USB_OTG_VBUSVALID); // receiving a valid Vbus from device
crate::gpio::connect_low_to_peripheral(InputSignal::USB_OTG_AVALID);

usb_wrap.otg_conf.modify(|_, w| {
w.pad_pull_override()
.set_bit()
.dp_pullup()
.set_bit()
.dp_pulldown()
.clear_bit()
.dm_pullup()
.clear_bit()
.dm_pulldown()
.clear_bit()
});
}
}

fn ahb_frequency_hz(&self) -> u32 {
// unused
80_000_000
}
}
7 changes: 7 additions & 0 deletions esp-hal-common/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub enum Peripheral {
Gdma,
#[cfg(any(esp32, esp32s2))]
Dma,
#[cfg(any(esp32s2, esp32s3))]
Usb,
}

/// Controls the enablement of peripheral clocks.
Expand Down Expand Up @@ -106,6 +108,11 @@ impl PeripheralClockControl {
perip_clk_en0.modify(|_, w| w.spi3_dma_clk_en().set_bit());
perip_rst_en0.modify(|_, w| w.spi3_dma_rst().clear_bit());
}
#[cfg(any(esp32s2, esp32s3))]
Peripheral::Usb => {
perip_clk_en0.modify(|_, w| w.usb_clk_en().set_bit());
perip_rst_en0.modify(|_, w| w.usb_rst().clear_bit());
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions esp32s2-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ esp-backtrace = { version = "0.2.0", features = ["esp32s2", "panic-handler",
esp-println = { version = "0.3.0", features = ["esp32s2"] }
smart-leds = "0.3.0"
ssd1306 = "0.7.1"
usb-device = { version = "0.2.3" }
usbd-serial = "0.1.1"

[features]
default = ["rt", "vectored"]
Expand Down
87 changes: 87 additions & 0 deletions esp32s2-hal/examples/usb_serial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//! CDC-ACM serial port example using polling in a busy loop.
//!
//! This example should be built in release mode.

#![no_std]
#![no_main]

use esp32s2_hal::{
clock::{ClockControl, CpuClock},
otg_fs::{UsbBus, USB},
pac::Peripherals,
prelude::*,
timer::TimerGroup,
Rtc,
IO,
};
use esp_backtrace as _;
use usb_device::prelude::{UsbDeviceBuilder, UsbVidPid};
use xtensa_lx_rt::entry;

static mut EP_MEMORY: [u32; 1024] = [0; 1024];

#[entry]
fn main() -> ! {
let peripherals = Peripherals::take().unwrap();
let mut system = peripherals.SYSTEM.split();
let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock240MHz).freeze();

let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
let mut wdt = timer_group0.wdt;
let mut rtc = Rtc::new(peripherals.RTC_CNTL);

// Disable MWDT and RWDT (Watchdog) flash boot protection
wdt.disable();
rtc.rwdt.disable();

let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);

let usb = USB::new(
peripherals.USB0,
io.pins.gpio18,
io.pins.gpio19,
io.pins.gpio20,
&mut system.peripheral_clock_control,
);

let usb_bus = UsbBus::new(usb, unsafe { &mut EP_MEMORY });

let mut serial = usbd_serial::SerialPort::new(&usb_bus);

let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
.manufacturer("esp-hal")
.product("esp-hal")
.serial_number("12345678")
.device_class(usbd_serial::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 => {
// 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;
}
_ => {}
}
}
}
_ => {}
}
}
}
5 changes: 5 additions & 0 deletions esp32s2-hal/src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,8 @@ analog! {
Gpio20: (20, rtc_pad20, rtc_pad20_hold, mux_sel, fun_sel, fun_ie, slp_ie, slp_sel, rue, rde, drv, slp_oe),
Gpio21: (21, rtc_pad21, rtc_pad21_hold, mux_sel, fun_sel, fun_ie, slp_ie, slp_sel, rue, rde, drv, slp_oe),
}

// implement marker traits on USB pins
impl<T> esp_hal_common::otg_fs::UsbSel for Gpio18<T> {}
impl<T> esp_hal_common::otg_fs::UsbDp for Gpio19<T> {}
impl<T> esp_hal_common::otg_fs::UsbDm for Gpio20<T> {}
1 change: 1 addition & 0 deletions esp32s2-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub use esp_hal_common::{
interrupt,
ledc,
macros,
otg_fs,
pac,
prelude,
pulse_control,
Expand Down
Loading

0 comments on commit cbbbe97

Please sign in to comment.