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

add initial embedded-hal v1 implementations for pins, delays & PWM #470

Merged
merged 5 commits into from
Dec 24, 2023
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
6 changes: 5 additions & 1 deletion arduino-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ nano168 = ["mcu-atmega", "atmega-hal/atmega168", "atmega-hal/enable-extra-adc",

[dependencies]
cfg-if = "1"
embedded-hal = "0.2.3"
embedded-hal = "1.0.0-rc.3"
ufmt = "0.2.0"

[dependencies.embedded-hal-v0]
version = "0.2.3"
package = "embedded-hal"

[dependencies.void]
version = "1.0.2"
default-features = false
Expand Down
2 changes: 1 addition & 1 deletion arduino-hal/src/delay.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
use embedded_hal_v0::blocking::delay::{DelayMs, DelayUs};

/// Delay type for `embedded-hal` compatibility.
///
Expand Down
4 changes: 3 additions & 1 deletion avr-hal-generic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ ufmt = "0.2.0"
paste = "1.0.0"
avr-device = "0.5.3"
embedded-storage = "0.2"
embedded-hal = "1.0.0-rc.3"

[dependencies.embedded-hal]
[dependencies.embedded-hal-v0]
version = "0.2.3"
package = "embedded-hal"
Comment on lines -15 to +18
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing it this way, it makes the transition so much easier :)

features = ["unproven"]

[dependencies.void]
Expand Down
79 changes: 52 additions & 27 deletions avr-hal-generic/src/delay.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Delay implementations

use core::marker;
use hal::blocking::delay;
use hal::blocking::delay as delay_v0;
use embedded_hal::delay::DelayNs;

#[cfg(all(target_arch = "avr", avr_hal_asm_macro))]
use core::arch::asm;
Expand Down Expand Up @@ -76,7 +77,7 @@ cfg_if::cfg_if! {
}

// Clock-Specific Delay Implementations ----------------------------------- {{{
impl delay::DelayUs<u16> for Delay<crate::clock::MHz24> {
impl delay_v0::DelayUs<u16> for Delay<crate::clock::MHz24> {
fn delay_us(&mut self, mut us: u16) {
// for the 24 crate::clock::MHz clock for the aventurous ones, trying to overclock

Expand All @@ -99,7 +100,7 @@ impl delay::DelayUs<u16> for Delay<crate::clock::MHz24> {
}
}

impl delay::DelayUs<u16> for Delay<crate::clock::MHz20> {
impl delay_v0::DelayUs<u16> for Delay<crate::clock::MHz20> {
fn delay_us(&mut self, mut us: u16) {
// for the 20 crate::clock::MHz clock on rare Arduino boards

Expand Down Expand Up @@ -133,7 +134,7 @@ impl delay::DelayUs<u16> for Delay<crate::clock::MHz20> {
}
}

impl delay::DelayUs<u16> for Delay<crate::clock::MHz16> {
impl delay_v0::DelayUs<u16> for Delay<crate::clock::MHz16> {
fn delay_us(&mut self, mut us: u16) {
// for the 16 crate::clock::MHz clock on most Arduino boards

Expand All @@ -157,7 +158,7 @@ impl delay::DelayUs<u16> for Delay<crate::clock::MHz16> {
}
}

impl delay::DelayUs<u16> for Delay<crate::clock::MHz12> {
impl delay_v0::DelayUs<u16> for Delay<crate::clock::MHz12> {
fn delay_us(&mut self, mut us: u16) {
// for the 12 crate::clock::MHz clock if somebody is working with USB

Expand All @@ -181,7 +182,7 @@ impl delay::DelayUs<u16> for Delay<crate::clock::MHz12> {
}
}

impl delay::DelayUs<u16> for Delay<crate::clock::MHz8> {
impl delay_v0::DelayUs<u16> for Delay<crate::clock::MHz8> {
fn delay_us(&mut self, mut us: u16) {
// for the 8 crate::clock::MHz internal clock

Expand All @@ -205,7 +206,7 @@ impl delay::DelayUs<u16> for Delay<crate::clock::MHz8> {
}
}

impl delay::DelayUs<u16> for Delay<crate::clock::MHz1> {
impl delay_v0::DelayUs<u16> for Delay<crate::clock::MHz1> {
fn delay_us(&mut self, mut us: u16) {
// for the 1 crate::clock::MHz internal clock (default settings for common Atmega microcontrollers)

Expand All @@ -219,9 +220,9 @@ impl delay::DelayUs<u16> for Delay<crate::clock::MHz1> {

// compensate for the time taken by the preceeding and next commands (about 22 cycles)
us -= 22; // = 2 cycles
// the following loop takes 4 microseconds (4 cycles)
// per iteration, so execute it us/4 times
// us is at least 4, divided by 4 gives us 1 (no zero delay bug)
// the following loop takes 4 microseconds (4 cycles)
// per iteration, so execute it us/4 times
// us is at least 4, divided by 4 gives us 1 (no zero delay bug)
us >>= 2; // us div 4, = 4 cycles

busy_loop(us);
Expand All @@ -230,18 +231,18 @@ impl delay::DelayUs<u16> for Delay<crate::clock::MHz1> {

// ------------------------------------------------------------------------ }}}

impl<SPEED> delay::DelayUs<u8> for Delay<SPEED>
where
Delay<SPEED>: delay::DelayUs<u16>,
impl<SPEED> delay_v0::DelayUs<u8> for Delay<SPEED>
where
Delay<SPEED>: delay_v0::DelayUs<u16>,
{
fn delay_us(&mut self, us: u8) {
delay::DelayUs::<u16>::delay_us(self, us as u16);
delay_v0::DelayUs::<u16>::delay_us(self, us as u16);
}
}

impl<SPEED> delay::DelayUs<u32> for Delay<SPEED>
where
Delay<SPEED>: delay::DelayUs<u16>,
impl<SPEED> delay_v0::DelayUs<u32> for Delay<SPEED>
where
Delay<SPEED>: delay_v0::DelayUs<u16>,
{
fn delay_us(&mut self, us: u32) {
// TODO: Somehow fix the overhead induced by this loop
Expand All @@ -252,27 +253,51 @@ where
let iters = us >> 12;
let mut i = 0;
while i < iters {
delay::DelayUs::<u16>::delay_us(self, 0xfff);
delay_v0::DelayUs::<u16>::delay_us(self, 0xfff);
i += 1;
}
delay::DelayUs::<u16>::delay_us(self, (us & 0xfff) as u16);
delay_v0::DelayUs::<u16>::delay_us(self, (us & 0xfff) as u16);
}
}

impl<SPEED> delay::DelayMs<u16> for Delay<SPEED>
where
Delay<SPEED>: delay::DelayUs<u32>,
impl<SPEED> delay_v0::DelayMs<u16> for Delay<SPEED>
where
Delay<SPEED>: delay_v0::DelayUs<u32>,
{
fn delay_ms(&mut self, ms: u16) {
delay::DelayUs::<u32>::delay_us(self, ms as u32 * 1000);
delay_v0::DelayUs::<u32>::delay_us(self, ms as u32 * 1000);
}
}

impl<SPEED> delay::DelayMs<u8> for Delay<SPEED>
where
Delay<SPEED>: delay::DelayMs<u16>,
impl<SPEED> delay_v0::DelayMs<u8> for Delay<SPEED>
where
Delay<SPEED>: delay_v0::DelayMs<u16>,
{
fn delay_ms(&mut self, ms: u8) {
delay::DelayMs::<u16>::delay_ms(self, ms as u16);
delay_v0::DelayMs::<u16>::delay_ms(self, ms as u16);
}
}

impl<SPEED> DelayNs for Delay<SPEED>
where
Delay<SPEED>: delay_v0::DelayUs<u16>, {
fn delay_ns(&mut self, ns: u32) {
// quick-win to get an initial implementation.
// note that the trait does not guarantee nanosecond-accuracy.

let mut us = ns / 1000;

// ensure that we wait _at least_ as long as specified.
// (we truncate the integer, so if we don't do this we'd wait 1us instead of 2us when specifying 1999ns).
let diff = ns - (us * 1000);
if diff > 0 {
us += 1;
}

delay_v0::DelayUs::<u32>::delay_us(self, us);
}

fn delay_us(&mut self, us: u32) {
delay_v0::DelayUs::<u32>::delay_us(self, us);
}
}
2 changes: 1 addition & 1 deletion avr-hal-generic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#![cfg_attr(avr_hal_asm_macro, feature(asm_experimental_arch))]
#![cfg_attr(not(avr_hal_asm_macro), feature(llvm_asm))]

pub extern crate embedded_hal as hal;
pub extern crate embedded_hal_v0 as hal;

#[doc(hidden)]
pub extern crate avr_device;
Expand Down
89 changes: 80 additions & 9 deletions avr-hal-generic/src/port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
//! Please take a look at the documentation for [`Pin`] for a detailed explanation.

use core::marker::PhantomData;
use embedded_hal::digital::v2::{InputPin, OutputPin};
use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
use embedded_hal_v0::digital::v2::{InputPin as InputPinV0, OutputPin as OutputPinV0};

pub trait PinMode: crate::Sealed {}
/// GPIO pin modes
Expand Down Expand Up @@ -318,8 +319,8 @@ impl<PIN: PinOps> Pin<mode::Output, PIN> {
}
}

// Implements OutputPin from embedded-hal to make sure external libraries work
impl<PIN: PinOps> OutputPin for Pin<mode::Output, PIN> {
// Implements OutputPinV0 from embedded-hal to make sure external libraries work
impl<PIN: PinOps> OutputPinV0 for Pin<mode::Output, PIN> {
type Error = core::convert::Infallible;

fn set_high(&mut self) -> Result<(), Self::Error> {
Expand All @@ -333,6 +334,30 @@ impl<PIN: PinOps> OutputPin for Pin<mode::Output, PIN> {
}
}

impl<PIN: PinOps> ErrorType for Pin<mode::Output, PIN> { type Error = core::convert::Infallible; }

impl<PIN: PinOps> OutputPin for Pin<mode::Output, PIN> {
fn set_low(&mut self) -> Result<(), Self::Error> {
self.set_low();
Ok(())
}

fn set_high(&mut self) -> Result<(), Self::Error> {
self.set_high();
Ok(())
}
}

impl<PIN: PinOps> StatefulOutputPin for Pin<mode::Output, PIN> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_set_high())
}

fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_set_low())
}
}

/// # Digital Output Open Drain
impl<PIN: PinOps> Pin<mode::OpenDrain, PIN> {
/// Set the pin high (Input without PullUp so it is floating)
Expand Down Expand Up @@ -364,8 +389,8 @@ impl<PIN: PinOps> Pin<mode::OpenDrain, PIN> {
}
}

// Implements OutputPin from embedded-hal to make sure external libraries work
impl<PIN: PinOps> OutputPin for Pin<mode::OpenDrain, PIN> {
// Implements OutputPinV0 from embedded-hal to make sure external libraries work
impl<PIN: PinOps> OutputPinV0 for Pin<mode::OpenDrain, PIN> {
type Error = core::convert::Infallible;

fn set_high(&mut self) -> Result<(), Self::Error> {
Expand All @@ -379,8 +404,30 @@ impl<PIN: PinOps> OutputPin for Pin<mode::OpenDrain, PIN> {
}
}

// Implements InputPin from embedded-hal to make sure external libraries work
impl<PIN: PinOps> InputPin for Pin<mode::OpenDrain, PIN> {
impl<PIN: PinOps> OutputPin for Pin<mode::OpenDrain, PIN> {
fn set_low(&mut self) -> Result<(), Self::Error> {
self.set_low();
Ok(())
}

fn set_high(&mut self) -> Result<(), Self::Error> {
self.set_high();
Ok(())
}
}

impl<PIN: PinOps> StatefulOutputPin for Pin<mode::OpenDrain, PIN> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_high())
}

fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_low())
}
}

// Implements InputPinV0 from embedded-hal to make sure external libraries work
impl<PIN: PinOps> InputPinV0 for Pin<mode::OpenDrain, PIN> {
type Error = core::convert::Infallible;

fn is_high(&self) -> Result<bool, Self::Error> {
Expand All @@ -392,8 +439,20 @@ impl<PIN: PinOps> InputPin for Pin<mode::OpenDrain, PIN> {
}
}

// Implements InputPin from embedded-hal to make sure external libraries work
impl<PIN: PinOps, IMODE: mode::InputMode> InputPin for Pin<mode::Input<IMODE>, PIN> {
impl<PIN: PinOps> ErrorType for Pin<mode::OpenDrain, PIN> { type Error = core::convert::Infallible; }

impl<PIN: PinOps> InputPin for Pin<mode::OpenDrain, PIN> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_high())
}

fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_low())
}
}

// Implements InputPinV0 from embedded-hal to make sure external libraries work
impl<PIN: PinOps, IMODE: mode::InputMode> InputPinV0 for Pin<mode::Input<IMODE>, PIN> {
type Error = core::convert::Infallible;

fn is_high(&self) -> Result<bool, Self::Error> {
Expand All @@ -405,6 +464,18 @@ impl<PIN: PinOps, IMODE: mode::InputMode> InputPin for Pin<mode::Input<IMODE>, P
}
}

impl<PIN: PinOps, IMODE: mode::InputMode> ErrorType for Pin<mode::Input<IMODE>, PIN> { type Error = core::convert::Infallible; }

impl<PIN: PinOps, IMODE: mode::InputMode> InputPin for Pin<mode::Input<IMODE>, PIN> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_high())
}

fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_low())
}
}

/// # Digital Input
impl<PIN: PinOps, IMODE: mode::InputMode> Pin<mode::Input<IMODE>, PIN> {
/// Check whether the pin is driven high.
Expand Down
31 changes: 31 additions & 0 deletions avr-hal-generic/src/simple_pwm.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! PWM Implementation

use core::marker::PhantomData;
use embedded_hal::pwm;
use embedded_hal::pwm::{ErrorKind, ErrorType, SetDutyCycle};

use crate::port::mode;
use crate::port::Pin;
Expand Down Expand Up @@ -82,6 +84,35 @@ impl<TC, PIN: PwmPinOps<TC>> Pin<mode::PwmOutput<TC>, PIN> {
}
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum PwmError {
/// `embedded-hal` supports duty cycles up to `u16`, however `avr` devices only support up to `u8`.
/// Passing a duty cycle larger than [`u8::MAX`] will result in this error.
DutyCycleTooLarge,
}

impl pwm::Error for PwmError {
fn kind(&self) -> ErrorKind {
ErrorKind::Other
}
}

impl<TC, PIN: PwmPinOps<TC>> ErrorType for Pin<mode::PwmOutput<TC>, PIN> { type Error = PwmError; }

impl<TC, PIN: PwmPinOps<TC, Duty=u8>> SetDutyCycle for Pin<mode::PwmOutput<TC>, PIN> {
fn max_duty_cycle(&self) -> u16 {
self.get_max_duty() as u16
}

fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
if duty > u8::MAX as u16 {
return Err(PwmError::DutyCycleTooLarge);
}
self.set_duty(duty as u8);
Ok(())
}
}

#[macro_export]
macro_rules! impl_simple_pwm {
(
Expand Down
2 changes: 1 addition & 1 deletion avr-hal-generic/src/spi.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! SPI Implementation
use crate::port;
use core::marker::PhantomData;
use embedded_hal::spi;
use embedded_hal_v0::spi;

/// Oscillator Clock Frequency division options.
///
Expand Down
Loading