Skip to content

Instantly share code, notes, and snippets.

@mpasternacki
Last active November 24, 2019 20:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mpasternacki/93c73d2f94630fbc356e28436e7f70ac to your computer and use it in GitHub Desktop.
Save mpasternacki/93c73d2f94630fbc356e28436e7f70ac to your computer and use it in GitHub Desktop.
#[task(resources=[i2c])]
fn init_display(cx: init_display::Context) {
let lcd = cx.resources.i2c.lcd_mut();
lcd.init(2, 16).unwrap();
lcd.backlight(true).unwrap();
lcd.home().unwrap();
uwrite!(lcd, "Hello?").ok();
}
// 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) }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment