Merge branch 'master' into spi-trait

This commit is contained in:
Dario Nieuwenhuis 2021-03-17 03:06:04 +01:00 committed by GitHub
commit 5bd705caa5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 368 additions and 39 deletions

View file

@ -45,7 +45,7 @@ GPIO pins are set up for the `nrf52840-dk` board (PCA10056)
- Install `probe-run` with defmt support.
```
cargo install --git https://github.com/knurling-rs/probe-run --branch main --features defmt
cargo install probe-run
```
- Run the example

View file

@ -157,17 +157,17 @@ pub fn interrupt_take(item: TokenStream) -> TokenStream {
static HANDLER: ::embassy::interrupt::Handler;
}
let func = HANDLER.func.load(::embassy::atomic::Ordering::Acquire);
let ctx = HANDLER.ctx.load(::embassy::atomic::Ordering::Acquire);
let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Acquire);
let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Acquire);
if !func.is_null() {
let func: fn(*mut ()) = ::core::mem::transmute(func);
func(ctx)
}
}
static TAKEN: ::embassy::atomic::AtomicBool = ::embassy::atomic::AtomicBool::new(false);
static TAKEN: ::embassy::export::atomic::AtomicBool = ::embassy::export::atomic::AtomicBool::new(false);
if TAKEN.compare_exchange(false, true, ::embassy::atomic::Ordering::AcqRel, ::embassy::atomic::Ordering::Acquire).is_err() {
if TAKEN.compare_exchange(false, true, ::embassy::export::atomic::Ordering::AcqRel, ::embassy::export::atomic::Ordering::Acquire).is_err() {
panic!("IRQ Already taken");
}

View file

@ -1,5 +1,7 @@
#![no_std]
#![no_main]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -1,5 +1,7 @@
#![no_std]
#![no_main]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -1,5 +1,7 @@
#![no_std]
#![no_main]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -1,5 +1,7 @@
#![no_std]
#![no_main]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -55,6 +55,8 @@
#![no_std]
#![no_main]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -1,5 +1,7 @@
#![no_std]
#![no_main]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -1,5 +1,7 @@
#![no_std]
#![no_main]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -1,5 +1,7 @@
#![no_std]
#![no_main]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -1,5 +1,7 @@
#![no_std]
#![no_main]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -1,5 +1,7 @@
#![no_std]
#![no_main]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -1,6 +1,8 @@
#![no_std]
#![feature(generic_associated_types)]
#![feature(asm)]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#![allow(incomplete_features)]

View file

@ -1,3 +1,5 @@
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../serial_port.rs"]

View file

@ -1,3 +1,5 @@
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
use embassy::executor::task;

View file

@ -1,6 +1,8 @@
#![no_std]
#![no_main]
#![feature(trait_alias)]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -1,6 +1,8 @@
#![no_std]
#![no_main]
#![feature(trait_alias)]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -1,5 +1,7 @@
#![no_std]
#![no_main]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -1,6 +1,8 @@
#![no_std]
#![no_main]
#![feature(trait_alias)]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#[path = "../example_common.rs"]

View file

@ -10,6 +10,7 @@ use crate::hal::gpio;
use crate::hal::gpio::{Edge, ExtiPin as HalExtiPin};
use crate::hal::syscfg::SysCfg;
use crate::pac::EXTI;
use embedded_hal::digital::v2 as digital;
use crate::interrupt;
@ -42,6 +43,52 @@ pub struct ExtiPin<T: HalExtiPin + WithInterrupt> {
_mgr: &'static ExtiManager,
}
impl<T: HalExtiPin + WithInterrupt + digital::OutputPin> digital::OutputPin for ExtiPin<T> {
type Error = T::Error;
fn set_low(&mut self) -> Result<(), Self::Error> {
self.pin.set_low()
}
fn set_high(&mut self) -> Result<(), Self::Error> {
self.pin.set_high()
}
}
impl<T: HalExtiPin + WithInterrupt + digital::StatefulOutputPin> digital::StatefulOutputPin
for ExtiPin<T>
{
fn is_set_low(&self) -> Result<bool, Self::Error> {
self.pin.is_set_low()
}
fn is_set_high(&self) -> Result<bool, Self::Error> {
self.pin.is_set_high()
}
}
impl<T: HalExtiPin + WithInterrupt + digital::ToggleableOutputPin> digital::ToggleableOutputPin
for ExtiPin<T>
{
type Error = T::Error;
fn toggle(&mut self) -> Result<(), Self::Error> {
self.pin.toggle()
}
}
impl<T: HalExtiPin + WithInterrupt + digital::InputPin> digital::InputPin for ExtiPin<T> {
type Error = T::Error;
fn is_high(&self) -> Result<bool, Self::Error> {
self.pin.is_high()
}
fn is_low(&self) -> Result<bool, Self::Error> {
self.pin.is_low()
}
}
/*
Irq Handler Description
EXTI0_IRQn EXTI0_IRQHandler Handler for pins connected to line 0
@ -482,7 +529,7 @@ exti!(gpioj, [
feature = "stm32f469",
feature = "stm32f479"
))]
exti!(gpioj, [
exti!(gpiok, [
EXTI0 => PK0,
EXTI1 => PK1,
EXTI2 => PK2,

View file

@ -1,6 +1,8 @@
#![no_std]
#![feature(generic_associated_types)]
#![feature(asm)]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
#![allow(incomplete_features)]
@ -313,5 +315,6 @@ pub(crate) mod fmt;
pub mod exti;
pub mod interrupt;
pub mod qei;
pub mod rtc;
pub mod serial;

View file

@ -0,0 +1,95 @@
use crate::interrupt;
use core::future::Future;
use core::pin::Pin;
use embassy::interrupt::Interrupt;
use embassy::traits::qei::WaitForRotate;
use embedded_hal::Direction;
use embedded_hal::Qei as THQei;
use stm32f4xx_hal::pac::TIM2;
use stm32f4xx_hal::qei::{Pins, Qei as HalQei};
pub struct Qei<T: Instance, PINS> {
qei: HalQei<T, PINS>,
int: T::Interrupt,
}
impl<PINS: Pins<TIM2>> Qei<TIM2, PINS> {
pub fn tim2(tim: TIM2, pins: PINS, interrupt: interrupt::TIM2) -> Self {
let qei = HalQei::tim2(tim, pins);
let tim = unsafe {
&mut *(stm32f4xx_hal::stm32::TIM2::ptr()
as *mut stm32f4xx_hal::stm32::tim2::RegisterBlock)
};
/*
enable qei interrupt
*/
tim.dier.write(|w| w.uie().set_bit());
Qei {
qei: qei,
int: interrupt,
}
}
}
impl<PINS: Pins<TIM2> + 'static> WaitForRotate for Qei<TIM2, PINS> {
type RotateFuture<'a> = impl Future<Output = Direction> + 'a;
fn wait_for_rotate<'a>(
self: Pin<&'a mut Self>,
count_down: u16,
count_up: u16,
) -> Self::RotateFuture<'a> {
let s = unsafe { self.get_unchecked_mut() };
let tim = unsafe {
&mut *(stm32f4xx_hal::stm32::TIM2::ptr()
as *mut stm32f4xx_hal::stm32::tim2::RegisterBlock)
};
/*
the interrupt will be reached at zero or the max count
write the total range to the qei.
*/
tim.arr
.write(|w| unsafe { w.bits((count_down + count_up) as u32) });
/*
set timer to the correct value in the range
*/
tim.cnt.write(|w| unsafe { w.bits(count_down as u32) });
/*
clear interrupt flag
*/
tim.sr.write(|w| w.uif().clear_bit());
async move {
embassy::util::InterruptFuture::new(&mut s.int).await;
if tim.cnt.read().bits() == 0 {
Direction::Downcounting
} else if tim.cnt.read() == count_down + count_up {
Direction::Upcounting
} else {
panic!("unexpected value")
}
}
}
}
mod sealed {
pub trait Sealed {}
}
pub trait Instance: sealed::Sealed {
type Interrupt: interrupt::Interrupt;
}
#[cfg(feature = "stm32f405")]
impl sealed::Sealed for TIM2 {}
#[cfg(feature = "stm32f405")]
impl Instance for TIM2 {
type Interrupt = interrupt::TIM2;
}

View file

@ -9,3 +9,4 @@ std = []
[dependencies]
defmt = { version = "0.2.0", optional = true }
embedded-hal = { version = "0.2.3", features = ["unproven"] }

View file

@ -91,8 +91,7 @@ impl AddressMode for SevenBitAddress {}
impl AddressMode for TenBitAddress {}
/// Blocking read
pub trait Read<A: AddressMode = SevenBitAddress> {
pub trait I2c<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;

View file

@ -4,11 +4,14 @@
#![feature(const_fn_fn_ptr_basics)]
#![feature(const_option)]
#![allow(incomplete_features)]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
pub mod delay;
pub mod flash;
pub mod gpio;
pub mod i2c;
pub mod qei;
pub mod spi;
pub mod uart;

22
embassy-traits/src/qei.rs Normal file
View file

@ -0,0 +1,22 @@
use core::future::Future;
use core::pin::Pin;
use embedded_hal::Direction;
// Wait for a specified number of rotations either up or down
pub trait WaitForRotate {
type RotateFuture<'a>: Future<Output = Direction> + 'a;
/// Wait for a specified number of rotations, in ticks, either up or down.
///
/// Return Direction::Upcounting if the high bound is reached.
/// Return Direction::Downcounting if the low bound is reached.
///
/// Number of ticks is encoder dependent. As an example, if we connect
/// the Bourns PEC11H-4120F-S0020, we have 20 ticks per full rotation.
/// Other encoders may vary.
fn wait_for_rotate<'a>(
self: Pin<&'a mut Self>,
count_down: u16,
count_up: u16,
) -> Self::RotateFuture<'a>;
}

View file

@ -13,6 +13,8 @@ defmt-info = []
defmt-warn = []
defmt-error = []
executor-agnostic = []
[dependencies]
defmt = { version = "0.2.0", optional = true }
log = { version = "0.4.11", optional = true }

View file

@ -1,5 +1,6 @@
pub use embassy_macros::task;
use atomic_polyfill::Ordering;
use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin;
@ -15,7 +16,6 @@ mod util;
mod waker;
use self::util::UninitCell;
use crate::atomic::Ordering;
use crate::fmt::panic;
use crate::interrupt::{Interrupt, InterruptExt};
use crate::time::Alarm;

View file

@ -1,3 +1,4 @@
use atomic_polyfill::{AtomicU32, Ordering};
use core::cell::Cell;
use core::cmp::min;
use core::marker::PhantomData;
@ -9,7 +10,6 @@ use super::run_queue::{RunQueue, RunQueueItem};
use super::timer_queue::{TimerQueue, TimerQueueItem};
use super::util::UninitCell;
use super::waker;
use crate::atomic::{AtomicU32, Ordering};
use crate::time::{Alarm, Instant};
/// Task is spawned (has a future)

View file

@ -1,8 +1,8 @@
use atomic_polyfill::{AtomicPtr, Ordering};
use core::ptr;
use core::ptr::NonNull;
use super::raw::Task;
use crate::atomic::{AtomicPtr, Ordering};
pub(crate) struct RunQueueItem {
next: AtomicPtr<Task>,

View file

@ -1,10 +1,10 @@
use atomic_polyfill::{AtomicPtr, Ordering};
use core::cell::Cell;
use core::cmp::min;
use core::ptr;
use core::ptr::NonNull;
use super::raw::{Task, STATE_TIMER_QUEUED};
use crate::atomic::{AtomicPtr, Ordering};
use crate::time::Instant;
pub(crate) struct TimerQueueItem {

View file

@ -1,7 +1,7 @@
use core::ptr;
use cortex_m::peripheral::NVIC;
use crate::atomic::{AtomicBool, AtomicPtr, Ordering};
use atomic_polyfill::{AtomicBool, AtomicPtr, Ordering};
pub use embassy_macros::interrupt_declare as declare;
pub use embassy_macros::interrupt_take as take;

View file

@ -4,6 +4,8 @@
#![feature(const_fn_fn_ptr_basics)]
#![feature(const_option)]
#![allow(incomplete_features)]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
#![feature(type_alias_impl_trait)]
// This mod MUST go first, so that the others see its macros.
@ -16,4 +18,9 @@ pub mod time;
pub mod util;
pub use embassy_traits as traits;
pub use atomic_polyfill as atomic;
#[doc(hidden)]
/// Implementation details for embassy macros. DO NOT USE.
pub mod export {
pub use atomic_polyfill as atomic;
}

View file

@ -1,7 +1,7 @@
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use crate::atomic::{AtomicBool, Ordering};
use atomic_polyfill::{AtomicBool, Ordering};
pub struct Forever<T> {
used: AtomicBool,

View file

@ -3,6 +3,8 @@ mod forever;
mod mutex;
mod portal;
mod signal;
#[cfg_attr(feature = "executor-agnostic", path = "waker_agnostic.rs")]
mod waker;
pub use drop_bomb::*;

View file

@ -1,11 +1,14 @@
use core::mem;
use core::task::Context;
use core::ptr::{self, NonNull};
use core::task::Waker;
use atomic_polyfill::{AtomicPtr, Ordering};
use crate::executor::raw::{task_from_waker, wake_task, Task};
/// Utility struct to register and wake a waker.
#[derive(Debug)]
pub struct WakerRegistration {
waker: Option<Waker>,
waker: Option<NonNull<Task>>,
}
impl WakerRegistration {
@ -15,37 +18,61 @@ impl WakerRegistration {
/// Register a waker. Overwrites the previous waker, if any.
pub fn register(&mut self, w: &Waker) {
let w = unsafe { task_from_waker(w) };
match self.waker {
// Optimization: If both the old and new Wakers wake the same task, we can simply
// keep the old waker, skipping the clone. (In most executor implementations,
// cloning a waker is somewhat expensive, comparable to cloning an Arc).
Some(ref w2) if (w2.will_wake(w)) => {}
_ => {
// clone the new waker and store it
if let Some(old_waker) = mem::replace(&mut self.waker, Some(w.clone())) {
// We had a waker registered for another task. Wake it, so the other task can
// reregister itself if it's still interested.
//
// If two tasks are waiting on the same thing concurrently, this will cause them
// to wake each other in a loop fighting over this WakerRegistration. This wastes
// CPU but things will still work.
//
// If the user wants to have two tasks waiting on the same thing they should use
// a more appropriate primitive that can store multiple wakers.
old_waker.wake()
}
// Optimization: If both the old and new Wakers wake the same task, do nothing.
Some(w2) if w == w2 => {}
Some(w2) => {
// We had a waker registered for another task. Wake it, so the other task can
// reregister itself if it's still interested.
//
// If two tasks are waiting on the same thing concurrently, this will cause them
// to wake each other in a loop fighting over this WakerRegistration. This wastes
// CPU but things will still work.
//
// If the user wants to have two tasks waiting on the same thing they should use
// a more appropriate primitive that can store multiple wakers.
unsafe { wake_task(w2) }
self.waker = Some(w);
}
None => self.waker = Some(w),
}
}
/// Wake the registered waker, if any.
pub fn wake(&mut self) {
if let Some(w) = self.waker.take() {
w.wake()
unsafe { wake_task(w) }
}
}
}
pub struct AtomicWakerRegistration {
waker: AtomicPtr<Task>,
}
impl AtomicWakerRegistration {
pub const fn new() -> Self {
Self {
waker: AtomicPtr::new(ptr::null_mut()),
}
}
pub fn context(&self) -> Option<Context<'_>> {
self.waker.as_ref().map(|w| Context::from_waker(w))
/// Register a waker. Overwrites the previous waker, if any.
pub fn register(&self, w: &Waker) {
let w = unsafe { task_from_waker(w) };
let w2 = self.waker.swap(w.as_ptr(), Ordering::Relaxed);
if !w2.is_null() && w2 != w.as_ptr() {
unsafe { wake_task(NonNull::new_unchecked(w2)) };
}
}
/// Wake the registered waker, if any.
pub fn wake(&self) {
let w2 = self.waker.swap(ptr::null_mut(), Ordering::Relaxed);
if !w2.is_null() {
unsafe { wake_task(NonNull::new_unchecked(w2)) };
}
}
}

View file

@ -0,0 +1,87 @@
use core::cell::Cell;
use core::mem;
use core::task::Waker;
use cortex_m::interrupt::Mutex;
/// Utility struct to register and wake a waker.
#[derive(Debug)]
pub struct WakerRegistration {
waker: Option<Waker>,
}
impl WakerRegistration {
pub const fn new() -> Self {
Self { waker: None }
}
/// Register a waker. Overwrites the previous waker, if any.
pub fn register(&mut self, w: &Waker) {
match self.waker {
// Optimization: If both the old and new Wakers wake the same task, we can simply
// keep the old waker, skipping the clone. (In most executor implementations,
// cloning a waker is somewhat expensive, comparable to cloning an Arc).
Some(ref w2) if (w2.will_wake(w)) => {}
_ => {
// clone the new waker and store it
if let Some(old_waker) = mem::replace(&mut self.waker, Some(w.clone())) {
// We had a waker registered for another task. Wake it, so the other task can
// reregister itself if it's still interested.
//
// If two tasks are waiting on the same thing concurrently, this will cause them
// to wake each other in a loop fighting over this WakerRegistration. This wastes
// CPU but things will still work.
//
// If the user wants to have two tasks waiting on the same thing they should use
// a more appropriate primitive that can store multiple wakers.
old_waker.wake()
}
}
}
}
/// Wake the registered waker, if any.
pub fn wake(&mut self) {
if let Some(w) = self.waker.take() {
w.wake()
}
}
}
/// Utility struct to register and wake a waker.
pub struct AtomicWakerRegistration {
waker: Mutex<Cell<Option<Waker>>>,
}
impl AtomicWakerRegistration {
pub const fn new() -> Self {
Self {
waker: Mutex::new(Cell::new(None)),
}
}
/// Register a waker. Overwrites the previous waker, if any.
pub fn register(&mut self, w: &Waker) {
cortex_m::interrupt::free(|cs| {
let cell = self.waker.borrow(cs);
cell.set(match cell.replace(None) {
Some(w2) if (w2.will_wake(w)) => Some(w2),
Some(w2) => {
w2.wake();
Some(w.clone())
}
None => Some(w.clone()),
})
})
}
/// Wake the registered waker, if any.
pub fn wake(&mut self) {
cortex_m::interrupt::free(|cs| {
let cell = self.waker.borrow(cs);
if let Some(w) = cell.replace(None) {
w.wake()
}
})
}
}