|
// Trying to solve https://github.com/Rahix/shared-bus/issues/4 by |
|
// using a single struct with BusManager and all devices that share |
|
// the bus + a bit of lifetime hacking. The struct is RTFM shared |
|
// resource, so RTFM manages locks on the whole bus and the task can |
|
// access the bus safely without additional locking. |
|
|
|
use core::cell::RefCell; |
|
use core::mem::transmute; |
|
use core::ops::Deref; |
|
use shared_bus::{BusManager, BusProxy}; |
|
use teensy_4::lpi2c; |
|
|
|
// Device that uses the bus |
|
pub mod lcd; |
|
|
|
/// NOP mutex. It does nothing. We manage all I²C devices together |
|
/// with the manager in one struct. They are one RTFM resource, so |
|
/// that the whole bus is ceiling-analyzed and locked together by |
|
/// RTFM, lock is not needed. |
|
/// |
|
/// We use shared_bus only because external libraries consume the |
|
/// whole bus, and we want to use more devices on one bus and keep |
|
/// using existing libraries instead of writing new ones. |
|
pub struct NOPMutex<T>(T); |
|
|
|
impl<T> shared_bus::BusMutex<T> for NOPMutex<T> { |
|
fn create(v: T) -> Self { |
|
Self(v) |
|
} |
|
|
|
fn lock<R, F: FnOnce(&T) -> R>(&self, f: F) -> R { |
|
f(&self.0) |
|
} |
|
} |
|
|
|
// Newtype from HAL crate that implements I²C traits |
|
type Device<T> = lpi2c::LPI2C<T>; |
|
|
|
pub type Proxy<'a, T> = BusProxy<'a, NOPMutex<RefCell<Device<T>>>, Device<T>>; |
|
pub type Manager<T> = BusManager<NOPMutex<RefCell<Device<T>>>, Device<T>>; |
|
|
|
// Device types |
|
type LCD<'a, T> = lcd::LCD<Proxy<'a, T>>; |
|
|
|
fn init_manager<T>(i2c: T) -> Manager<T> |
|
where |
|
T: Deref<Target = lpi2c::RegisterBlock>, |
|
{ |
|
// Initializing the peripheral - skipped for brevity |
|
Manager::new(i2c.into()) |
|
} |
|
|
|
/// Structure that holds bus and all its devices together. Manager is |
|
/// inside the struct as an attempt to keep the lifetime synchronized. |
|
pub struct Bus<T: 'static> |
|
where |
|
T: Deref<Target = lpi2c::RegisterBlock>, |
|
{ |
|
_manager: Manager<T>, |
|
|
|
// We use 'static private struct members and then downcast them to |
|
// shorter lifetimes in public accessor methods. Trick described |
|
// in https://stackoverflow.com/a/33203128/16390 |
|
lcd: LCD<'static, T>, |
|
} |
|
|
|
// I'm not 100% confident in what I'm doing here, but Bus owns the |
|
// Manager (which has consumed the Device, which in turn has consumed |
|
// the Peripheral) and all the devices, and doesn't allow safe access |
|
// to the peripheral or manager, so it seems safe to send across |
|
// threads, right? |
|
unsafe impl<T> Send for Bus<T> where T: Deref<Target = lpi2c::RegisterBlock> {} |
|
|
|
impl<T> Bus<T> |
|
where |
|
T: Deref<Target = lpi2c::RegisterBlock>, |
|
{ |
|
/// Returns new I²C bus with all the devices used by project. |
|
pub fn new(i2c: T) -> Self { |
|
let manager = init_manager(i2c); |
|
let lcd: LCD<'static, _> = unsafe { transmute(LCD::new(manager.acquire())) }; |
|
Self { |
|
_manager: manager, |
|
lcd: lcd, |
|
} |
|
} |
|
|
|
pub fn lcd<'a>(&'a self) -> &'a LCD<'a, T> { |
|
&self.lcd |
|
} |
|
|
|
pub fn lcd_mut<'a>(&'a mut self) -> &'a mut LCD<'a, T> { |
|
// Need transmute to cast from Proxy<'static> to |
|
// Proxy<'a>. Why is transmute necessary to cast to a |
|
// _shorter_ lifetime? |
|
unsafe { transmute(&mut self.lcd) } |
|
} |
|
} |