Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support USB-DEVICE on ESP32-S2/S3 #226

Merged
merged 1 commit into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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