Skip to content
Draft
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
16 changes: 16 additions & 0 deletions arduino-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,22 @@ pub mod eeprom {
#[cfg(feature = "board-selected")]
pub use eeprom::Eeprom;

#[doc(no_inline)]
#[cfg(any(
feature = "arduino-micro",
feature = "arduino-leonardo",
feature = "sparkfun-promicro"
))]
pub use atmega_hal::default_usb_bus_with_pll;

#[doc(no_inline)]
#[cfg(any(
feature = "arduino-micro",
feature = "arduino-leonardo",
feature = "sparkfun-promicro"
))]
pub use atmega_hal::default_usb_bus_with_pll_macro;

#[cfg(feature = "board-selected")]
pub mod simple_pwm {
#[cfg(feature = "mcu-atmega")]
Expand Down
2 changes: 2 additions & 0 deletions examples/arduino-leonardo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ panic-halt = "1.0.0"
ufmt = "0.2.0"
nb = "1.1.0"
embedded-hal = "1.0"
usb-device = "0.3.2"
usbd-serial = "0.2.2"

[dependencies.arduino-hal]
path = "../../arduino-hal/"
Expand Down
1 change: 1 addition & 0 deletions examples/arduino-leonardo/src/bin/micro-usb-serial.rs
2 changes: 2 additions & 0 deletions examples/arduino-micro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ publish = false
panic-halt = "1.0.0"
ufmt = "0.2.0"
nb = "1.1.0"
usb-device = "0.3.2"
usbd-serial = "0.2.2"

[dependencies.arduino-hal]
path = "../../arduino-hal/"
Expand Down
55 changes: 55 additions & 0 deletions examples/arduino-micro/src/bin/micro-usb-serial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#![no_std]
#![no_main]
use arduino_hal::Peripherals;
use panic_halt as _;
use usb_device::bus::UsbBusAllocator;
use usb_device::device::StringDescriptors;
use usb_device::device::UsbDeviceBuilder;
use usb_device::device::UsbVidPid;
use usb_device::LangID;
use usbd_serial::SerialPort;

#[arduino_hal::entry]
fn main() -> ! {
let dp: Peripherals = arduino_hal::Peripherals::take().unwrap();

let usb_bus = arduino_hal::default_usb_bus_with_pll_macro!(dp);
let usb_bus_allocator = UsbBusAllocator::new(usb_bus);
let mut serial = SerialPort::new(&usb_bus_allocator);

let string_descriptors = StringDescriptors::new(LangID::EN_US)
.manufacturer("test manufacturer")
.product("test product")
.serial_number("test serial number");

let rand_ids = UsbVidPid(0x1ea7, 0x4a09);

let mut usb_dev = UsbDeviceBuilder::new(&usb_bus_allocator, rand_ids)
.strings(&[string_descriptors])
.unwrap()
.max_packet_size_0(64)
.unwrap()
.device_class(usbd_serial::USB_CLASS_CDC)
.build();

loop {
// Wait until we have data
if !usb_dev.poll(&mut [&mut serial]) {
continue;
}

// Read the data into this buffer
let mut read_buf = [0u8; 10];
let Ok(read_count) = serial.read(&mut read_buf) else {
continue;
};
if read_count == 0 {
continue;
}

let len = serial
.write(&read_buf[0..read_count])
.expect("The host should be reading data faster than the arduino can write it");
assert_eq!(len, read_count);
}
}
2 changes: 2 additions & 0 deletions examples/sparkfun-promicro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ panic-halt = "1.0.0"
ufmt = "0.2.0"
nb = "1.1.0"
embedded-hal = "1.0"
usb-device = "0.3.2"
usbd-serial = "0.2.2"

[dependencies.arduino-hal]
path = "../../arduino-hal/"
Expand Down
1 change: 1 addition & 0 deletions examples/sparkfun-promicro/src/bin/micro-usb-serial.rs
3 changes: 2 additions & 1 deletion mcu/atmega-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ atmega168 = ["avr-device/atmega168", "device-selected"]
atmega328p = ["avr-device/atmega328p", "device-selected"]
atmega328pb = ["avr-device/atmega328pb", "device-selected"]
atmega32a = ["avr-device/atmega32a", "device-selected"]
atmega32u4 = ["avr-device/atmega32u4", "device-selected"]
atmega32u4 = ["avr-device/atmega32u4", "device-selected", "usb-device"]
atmega2560 = ["avr-device/atmega2560", "device-selected"]
atmega128a = ["avr-device/atmega128a", "device-selected"]
atmega1280 = ["avr-device/atmega1280", "device-selected"]
Expand All @@ -37,6 +37,7 @@ docsrs = ["atmega328p"]

[dependencies]
avr-hal-generic = { path = "../../avr-hal-generic/" }
usb-device = { version = "0.3.2", optional = true }

[dependencies.avr-device]
version = "0.8"
Expand Down
88 changes: 88 additions & 0 deletions mcu/atmega-hal/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![no_std]
#![feature(asm_experimental_arch)]

//! `atmega-hal`
//! =============
Expand Down Expand Up @@ -119,6 +120,93 @@ pub use avr_hal_generic::clock;
pub use avr_hal_generic::delay;
pub use avr_hal_generic::prelude;

#[cfg(feature = "atmega32u4")]
mod usb;

// TODO: fix bad usb-device::UsbBus link
/// This function provides a safe abstraction layer over the USB hardware, by way of the
/// [UsbBus](usb-device::UsbBus) trait.
///
/// There are a few notable limitations, however:
///
/// * This implementation requires exclusive access to the PLL, even though on a hardware
/// level it is possible for the PLL output to be used by both the USB controller and
/// the high-speed timer (TC4) simultaneously. Refer to GitHub issue #TBD for details.
///
/// TODO if Rahix agrees that this limitation isn't something we need to worry about
/// as part of PR, then create a GitHub issue so that someone else can fix it later:
///
/// > **Title**
/// >
/// > Allow the USB and TC4 hardware to both use the PLL output simultaneously
/// >
/// > **Description**
/// >
/// > Our current UsbBus implementation prevents TC4 from using PLL output, even though
/// > the hardware supports it. There are two main
/// > problems that we need to solve first:
/// >
/// > 1. The current UsbBus implementation sets the PLL output to 48MHz. This could
/// > cause problems if the user has already configured TC4 to expect a different
/// > clock speed from the PLL.
/// >
/// > 2. We need to make the USB suspend state configurable. Currently when the USB
/// > bus is idle for 3ms or longer, it will disable the PLL to reduce power usage.
/// > However, this may not be desirable if TC4 is also using the PLL.
/// >
/// > **Comment**
/// >
/// > I think we *might* be able to solve this by splitting the constructor's
/// > argument into two separate parts. Instead of passing ownership of the entire PLL
/// > configuration (`pll: avr_device::atmega32u4::PLL`), we'd have one argument for
/// > the registers that config the PLL clock speed (e.g. `pll_config: &PLLFRQ`) and one
/// > optional argument for the registers that we use to turn the PLL on and off
/// > (e.g. `pll_suspend: Option<&mut pllcsr>`). A value of `None` would indicate that
/// > the user wants us to keep the PLL running while USB is idle.
/// >
/// > A few disclaimers:
/// >
/// > * This is a simplification. Instead of `pll_suspend: Option<&mut pllcsr>` we'd
/// > probably want to define a new trait,
/// > similar to what is done [in the `agausmann/atmega-usbd` repo](https://github.com/agausmann/atmega-usbd/blob/5fc68ca813ce0a37dab65dd4d66efe1ec125f2a8/src/lib.rs#L590-L618).
/// >
/// > * This is just one possible solution; there are others.
/// >
/// > * I've not spent much time investigating this, so this proposed solution might not work.
///
/// * The current implementation does not attempt to minimize power usage. For details,
/// see GitHub issue #TBD.
///
/// TODO if Rahix agrees that this limitation isn't something we need to worry about
/// as part of PR, then create a GitHub issue so that someone else can fix it later:
///
/// * Add support for using interrupts, in addition to polling.
/// Similar to `agausmann/atmega-usbd`.
///
/// * Shutdown the PLL when the USB module is suspended (TODO: do in this PR?)
///
/// * and more?
///
/// * The underlying struct that implements `UsbBus` is private. This is done intentionally
/// in order to make it easier to address the other issues without breaking backwards
/// compatibility.
#[cfg(feature = "atmega32u4")]
pub fn default_usb_bus_with_pll(
usb: avr_device::atmega32u4::USB_DEVICE,
pll: avr_device::atmega32u4::PLL,
) -> impl usb_device::class_prelude::UsbBus {
return usb::UsbdBus::new(usb, pll);
}

/// This macro is exactly equivalent to [default_usb_bus_with_pll](default_usb_bus_with_pll).
#[cfg(feature = "atmega32u4")]
#[macro_export]
macro_rules! default_usb_bus_with_pll_macro {
($p:expr) => {
$crate::default_usb_bus_with_pll($p.USB_DEVICE, $p.PLL)
};
}

#[cfg(feature = "device-selected")]
pub mod adc;
#[cfg(feature = "device-selected")]
Expand Down
91 changes: 91 additions & 0 deletions mcu/atmega-hal/src/usb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use core::cell::Cell;

use avr_device::atmega32u4::PLL;
use avr_device::atmega32u4::USB_DEVICE;
use avr_device::interrupt::Mutex;
use usb_device::bus::PollResult;
use usb_device::bus::UsbBus;
use usb_device::endpoint::EndpointAddress;
use usb_device::endpoint::EndpointType;
use usb_device::UsbDirection;
use usb_device::UsbError;

const MAX_ENDPOINTS: usize = 7;

struct EndpointTableEntry {
_ep_type: EndpointType,
_direction: UsbDirection,
_max_packet_size: u16,
}

pub struct UsbdBus {
_usb: Mutex<USB_DEVICE>,
_pll: Mutex<PLL>,
_pending_ins: Mutex<Cell<u8>>,
_endpoints: [Option<EndpointTableEntry>; MAX_ENDPOINTS],
}

impl UsbdBus {
pub fn new(_usb: USB_DEVICE, _pll: PLL) -> Self {
todo!();
}
}

impl UsbBus for UsbdBus {
fn alloc_ep(
&mut self,
_direction: UsbDirection,
_ep_addr: Option<EndpointAddress>,
_ep_type: EndpointType,
_max_packet_size: u16,
_interval: u8,
) -> Result<EndpointAddress, UsbError> {
todo!();
}

fn enable(&mut self) {
todo!();
}

fn reset(&self) {
todo!();
}

fn set_device_address(&self, _addr: u8) {
todo!();
}

fn write(&self, _ep_addr: EndpointAddress, _buf: &[u8]) -> Result<usize, UsbError> {
todo!();
}

fn read(&self, _ep_addr: EndpointAddress, _buf: &mut [u8]) -> Result<usize, UsbError> {
todo!();
}

fn set_stalled(&self, _ep_addr: EndpointAddress, _stalled: bool) {
todo!();
}

fn is_stalled(&self, _ep_addr: EndpointAddress) -> bool {
todo!();
}

fn suspend(&self) {
todo!();
}

fn resume(&self) {
todo!();
}

fn poll(&self) -> PollResult {
todo!();
}
}

impl Drop for UsbdBus {
fn drop(&mut self) {
todo!()
}
}