Last active
March 6, 2017 21:34
-
-
Save mzgoddard/98d1b27979ef86cf2a7effe4bd201984 to your computer and use it in GitHub Desktop.
Mockup a Tessel API covering the same basic APIs as Tessel JS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Examples | |
// | |
mod examples { | |
fn main() { | |
// Different ways to get a single pwm pin. | |
// | |
// These earlier ones are a bit wasteful as they also create the leds | |
// and other port to start. Meaning both ports need to be created and | |
// then with proper scoping one should shutdown. We should be able to | |
// use scoping to automatically control if a port is open or closed. If | |
// no pins are in use for a port, it should automatically close. | |
// | |
// Each of these must be unwrapped. If there is already a Tessel, Port, | |
// or Pin instance for what we want the first part of these calls will | |
// be an error instead of the object. Each object can only be owned at | |
// one time. If multiple things need to access the same physical | |
// resource they should share a Mutex or other relevant object between | |
// them containing the resource. We shouldn't hide the fact that they | |
// are a limited resource by automatically providing a cloning | |
// interface. Either the user can do that or we can provide a more | |
// explicit even higher level object, like a TesselProxy, that owns the | |
// single resource. | |
let mut pwm = Tessel::new().expect("a Tessel object").ports.a.pin[5].into_pwm().expect("a PWM pin"); | |
let mut pwm = Tessel::new().expect("a Tessel object").ports.a.into_pwm().expect("a PWM pin"); | |
let mut pwm = Tessel::ports().expect("a Tessel object").a.pin[5].into_pwm().expect("a PWM pin"); | |
let mut pwm = Tessel::ports().expect("a Tessel object").a.into_pwm().expect("a PWM pin"); | |
// These next two open a specific port to access the pin. | |
let mut pwm = Port::new("a").expect("a Port A object").pin[5].into_pwm().expect("a PWM pin"); | |
let mut pwm = Port::new("a").expect("a Port A object").into_pwm().expect("a PWM ping"); | |
// This last one opens the specific pin. | |
let mut pwm = Pin::new("a", 5).expect("the A5 Pin").into_pwm().expect("a PWM pin"); | |
// A port must be available for global calls like pwm_frequency. | |
// Otherwise it will return an error. | |
Tessel::pwm_frequency(1000).expect("PWM frequency to be set"); | |
let mut duty = 0.0; | |
let mut dir = 0.016; | |
loop { | |
pwm.duty_cycle(duty).expect("PWM duty cycle to be updated"); | |
duty += dir; | |
if duty > 1 { | |
duty = 1; | |
dir = -0.016; | |
} | |
if duty < 0 { | |
duty = 0; | |
dir = 0.016; | |
} | |
sleep(Duration::from_millis(16)); | |
} | |
} | |
mod relay_mono { | |
pub struct RelayArray { | |
items: [RelayItem; 2], | |
} | |
pub struct RelayItem { | |
pin: tessel::Pin, | |
state: Option<bool>, | |
} | |
impl RelayArray { | |
pub fn new(port: tessel::Port) -> tessel::Result<RelayArray> { | |
// let (_, _, _, _, _, pin1, pin2, _) = port.into_pins(); | |
let pins = port.pin.drain(5..7).map(|pin| { | |
Ok(RelayItem { | |
pin: try!(pin.into_digital()), | |
state: false, | |
}) | |
}); | |
RelayArray { | |
items: [ | |
pins.next()?, | |
pins.next()?, | |
], | |
} | |
} | |
pub fn connect(&mut self) -> tessel::Result<()> { | |
// Set Digitals as outputs. | |
for item in self.iter_mut() { | |
try!(item.set(0)); | |
} | |
Ok(()) | |
} | |
pub fn len(&self) -> usize { | |
self.items.len() | |
} | |
pub fn iter(&self) -> slice::Iter<RelayItem> { | |
self.items.iter() | |
} | |
pub fn iter_mut(&mut self) -> slice::IterMut<RelayItem> { | |
self.items.iter_mut() | |
} | |
} | |
impl Index<usize> for RelayArray { | |
type Output = RelayItem; | |
fn index(&self, index: usize) -> &Self::Output { | |
self.items[index] | |
} | |
} | |
impl IndexMut<usize> for RelayArray { | |
type Output = RelayItem; | |
fn index_mut(&mut self, index: usize) -> &mut Self::Output { | |
self.items[index] | |
} | |
} | |
impl RelayItem { | |
pub fn get(&self) -> Result<usize> { | |
self.state.ok_or(Err(tessel::Error::new(tessel::ErrorKind::Uninitialized, "RelayArray item is not initialized."))) | |
} | |
pub fn set(&mut self, value: usize) -> tessel::Result<()> { | |
if value > 1 { | |
return Err(tessel::Error::new(tessel::ErrorKind::Range, "Cannot set RelayArray item to a value higher than 1.")); | |
} | |
self.pin.output(value); | |
self.state = Some(value); | |
} | |
} | |
} | |
fn main() { | |
// A Ports object can be destructed so its a and b port can be owned by | |
// seperate objects or threads. | |
let Ports { a: mut a, b: mut b } = Tessel::ports().expect("a Ports object"); | |
thread::spawn(move || { | |
loop { | |
// Reference instances can be an easy way to reuse a port or | |
// pin. Once the reference is dropped the port or pin can be | |
// used for another. | |
a.interrupt().expect("a Interrupt pin").wait_rise().expect("the Interrupt pin rose"); | |
let mut i2c = a.i2c().expect("a I2c pin"); | |
// Do i2c data transfer | |
} | |
}); | |
thread::spawn(move || { | |
Tessel::pwm_frequency(1000).expect("that PWM frequency is set"); | |
let mut pwm = b.into_pwm().expect("a PWM pin"); | |
// Light up a LED | |
}); | |
} | |
fn main() { | |
let port = Port::new("b").expect("a Port B object"); | |
// Pull off the pins taking ownership of them. | |
let interrupt = port.pin.remove(0).into_interrupt().expect("an Interrupt pin"); | |
let digital = port.pin.remove(1).into_digital().expect("a Digital pin"); | |
// Multi pin communication protocols require us to have mutable | |
// references or ownership of all needed ports. I2C and UART need two | |
// pins or one port (consuming the port, so using I2C and UART at the | |
// same time or other pins means we need to destruct the port by taking | |
// Pins from it). Since we are removing pins here the "index" will be | |
// changing as the Port cannot retain a reference. | |
let uart_pins = (port.pin.remove(3), port.pin.remove(3)); | |
// Different vector methods or order of removing can make this easier to | |
// understand. | |
let uart_pins = port.pin.drain(3..4).collect(); | |
let uart = uart_pins.into_uart().expect("a Uart pin wrapper"); | |
loop { | |
interrupt.wait_rise().expect("the Interrupt pin rose"); | |
if digital.read().expect("the Digital pin was read") { | |
uart.write(&[0x01, 0x02]).expect("Uart was written to"); | |
} | |
else { | |
uart.write(&[0x03, 0x04]).expect("Uart was written to"); | |
} | |
} | |
} | |
fn main() { | |
let Tessel { led: mut leds, .. } = Tessel::new().expect("a Tessel object"); | |
let mut leds = Tessel::leds().expect("the Tessel Leds"); | |
led[2].on().expect("led 2 was updated"); | |
loop { | |
led[2].toggle().expect("led 2 was updated"); | |
led[3].toggle().expect("led 3 was updated"); | |
sleep(Duration::from_millis(100)); | |
} | |
} | |
fn main() { | |
// The faster protocols should support normal rust Read and Write | |
// interfaces, letting the user use utilities like io::copy and using | |
// non-blocking behaviour with Read::read and Write::write. This | |
// non-blocking behaviour could be facilitated by having all socket | |
// reading and writing on their own thread and using sync types to | |
// communicate. | |
let mut uart = Port::new("a").expect("a Port A object").into_uart().expect("a Uart Pin wrapper"); | |
io::copy(&mut File::create("/app/my-file.txt").expect("my-file.txt opened"), &mut uart); | |
} | |
} | |
// | |
// Tessel | |
// | |
mod tessel { | |
pub struct Error {} | |
pub enum ErrorKind {} | |
impl Error { | |
pub fn kind(&self) -> ErrorKind {} | |
pub fn message(&self) -> &'static str {} | |
} | |
// The TesselInner type holds neccessary handles, like mutexes and other | |
// sync types to communicate with whatever holds the Sockets to communicate | |
// pwm_frequency info. Since TesselInner is only used for static method | |
// Tessel does not need to own it. A Tessel may not even exist if more | |
// specific constructors are used to get Leds, Ports, or Pins. In those | |
// cases a TesselInner instance will still be created if pwm_frequency is | |
// called. | |
struct TesselInner {} | |
pub struct Tessel { | |
pub led: Vec<Led>, | |
pub port: Ports, | |
} | |
impl Tessel { | |
// These constructors are all convenience methods for the encased types. | |
// The top one, Tessel, can only be instantiated if all LEDs and Ports | |
// are not currently owned. Otherwise it returns an Error. In the case | |
// some Led, Pin, or Port is owned the user needs to instantiate the | |
// specific object they need by a specific constructor and unwrap the | |
// object in case they receive an error because the object they want is | |
// in fact owned. | |
pub fn new() -> Result<Tessel> {} | |
// Like the Tessel::new, leds returns an error if any of the leds is | |
// owned. | |
pub fn leds() -> Result<Vec<Led>> {} | |
// Like the Tessel::new function, ports returns an error if any Port or | |
// Pin is owned. | |
pub fn ports() -> Result<Ports> {} | |
// pwm_frequency is a static function. As long as a Pin or Port is owned | |
// it should succeed. | |
pub fn pwm_frequency(freq) -> Result<()> {} | |
} | |
} | |
// | |
// LED | |
// | |
mod led { | |
// Owns any objects needed to communicate to the hardware LED resource. May | |
// only be owned by one Led instance. If another owns it, creating a new Led | |
// will return an error. | |
struct LedInner {} | |
impl LedInner { | |
// For testing a non public interface can let tests create LedInner | |
// instances holding objects other than what Led::new may use. In | |
// production Led::new can must use into_led so that it finds or creates | |
// the LedInner instance as a standard process. | |
fn into_led() -> Led {} | |
} | |
pub struct Led { | |
inner: LedInner, | |
} | |
// Return the LedInner instance to somewhere or reset whatever value lets a | |
// future Led::new call know that the object can be created. | |
impl Drop for Led {} | |
impl Led { | |
pub fn new(color, kind) -> Result<Led> {} | |
pub fn on(&mut self) -> Result<()> {} | |
pub fn off(&mut self) -> Result<()> {} | |
pub fn toggle(&mut self) -> Result<()> {} | |
// Blocking function. Returns a result of the value read for | |
// consistency. | |
pub fn read(&self) -> Result<usize> {} | |
// Blocking function. Writes a value to the hardware resource. | |
pub fn write(&mut self, value: usize) -> Result<()> {} | |
} | |
} | |
// | |
// Port | |
// | |
mod port { | |
// Wrap the pair of Ports used by the Tessel object like it is in Node. | |
pub struct Ports { | |
a: Port, | |
b: Port, | |
} | |
// Data used by a thread to write to a port stream. The UnixStream can be | |
// blocking or nonblocking. Blocking in a separate thread is easier of the | |
// two paths. Primarily with nonblocking we wouldn't have a good path | |
// currently to know when we can next write. We would just have to | |
// occasionally try and succeed or fail. With a blocking thread doing the | |
// writing we can send instructions to it and it'll be able to write them as | |
// fast as the thread executes and the underlying stream lets it. When | |
// nothing is being written it can lock on however it receives new | |
// instructions wasting no cpu resources until then. | |
struct PortWriteThread {} | |
// Data used by a thread to "read" from the port stream. This thread needs | |
// to communicate with the write thread. It sends read commands through it | |
// from the read thread so there is one source to queue pins waiting for | |
// feedback and queue instructions to be written. This may need to be two | |
// threads. One that queues up new commands and sends instructions to the | |
// write thread. And a second that does the actual reading. | |
// | |
// Instead of callbacks like in Node, Pins should operate in threads or | |
// tasks through libraries like futures and fibers. Calls to write or read a | |
// pin (or set of pins through i2c, uart, and spi protocols) send commands | |
// to the appropriate PortThread (digital, interrupt, analog) and then block | |
// or fill a shared buffer (i2c, spi, uart) and block if that buffer fills | |
// up. | |
struct PortReadThread {} | |
struct PortInner {} | |
pub struct Port { | |
inner: PortInner, | |
pub pin: Vec<Pin>, | |
} | |
// Return the PortInner instance or reset whatever value lets future | |
// Port::new calls discover if they can be created. | |
impl Drop for Port {} | |
impl Port { | |
pub fn new(name: &'static str) -> Result<Port> {} | |
} | |
} | |
// | |
// Pin | |
// | |
mod pin { | |
// Hold objects to communicate with its PortStreams. If all of the PinInner | |
// objects are for a port are not owned, that port is turned off. And the | |
// reverse if any pin is owned, the port is turned on. | |
struct PinInner {} | |
pub struct Pin { | |
inner: PinInner, | |
} | |
impl Pin { | |
pub fn new(port_name: &'static str, index: usize) -> Result<Pin> {} | |
pub fn port_name(&self) -> &'static str {} | |
pub fn port_index(&self) -> index {} | |
pub fn is_digital(&self) -> bool {} | |
pub fn is_interrupt(&self) -> bool {} | |
pub fn is_analog(&self) -> bool {} | |
pub fn is_analog_read(&self) -> bool {} | |
pub fn is_analog_write(&self) -> bool {} | |
pub fn is_pwm(&self) -> bool {} | |
pub fn is_i2c(&self) -> bool {} | |
pub fn is_uart(&self) -> bool {} | |
pub fn is_spi(&self) -> bool {} | |
} | |
} | |
// | |
// Digital | |
// | |
mod digital { | |
// This is the first pin protocol in this document. The general idiom | |
// established here is that Pins know what they are but do not expose | |
// methods to perform work with a certain protocol. Since the multi pin | |
// protocols should own the pins so should the single pin protocols. Since | |
// all the protocols own the pin, the Pin type itself should give up all | |
// methods for communication to the respective protocols. This helps build | |
// a consistent interface for using pins to communicate with components of a | |
// project. | |
// | |
// With that established we need interfaces to convert pins into pin owning | |
// protocol types. The first two are below and are repeated for other | |
// protocols. The first creates a type that allows temporary access to the | |
// pin. Both have the same costs, but the first lets helper functions | |
// receive references to pins if not the protocol they need. Otherwise a | |
// helper function would need to own the relevant pin and return it or drop | |
// it. The second lets us convert into permanent protocol objects that can | |
// be returned from other functions or give a better sense of permanence in | |
// containing types. | |
// Return a temporary reference to a digital protocol holding a reference to | |
// a pin or return an error if the pin doesn't support digital (not possible | |
// for digital, but again this is for consistency of the api). | |
pub trait RefDigital { | |
fn digital<'a>(self) -> Result<DigitalRef<'a>> {} | |
} | |
// Return a digital protocol object that owns the underlying pin. | |
pub trait IntoDigital { | |
fn into_digital(self) -> Result<Digital> {} | |
} | |
// References to a Port can create a digital reference. As part of the api | |
// it will always use the same pin, like for example pin 0. If that pin has | |
// been pulled out of the Port object an error will be returned. | |
impl RefDigital for &mut Port {} | |
// A port itself can be converted into a digital protocol. It consumes the | |
// port and all other pins are dropped. As part of the api it will always | |
// use the same pin, like for example pin 0. | |
impl IntoDigital for Port {} | |
// One of the straight forward ones. Turn a Pin reference into a Digital | |
// reference. | |
impl RefDigital for &mut Pin {} | |
// The other straight forward one. Turn a Pin into a Digital object. | |
impl IntoDigital for Pin {} | |
// We can provide a lot of convenience implementations too. Like for | |
// iterators over pins taking the first item and erroring if the iterator is | |
// already at the end. | |
impl<T> RefDigital for T where T : Iterator<Item=&mut Pin> {} | |
impl<T> IntoDigital for T where T : Iterator<Item=Pin> {} | |
// A private trait. DigitalRef and Digital can implement this exposing a Pin | |
// reference. With this trait to get the reference another trait can | |
// implement all the methods for Digital once since it'll go through internal | |
// details the Pin has. This is all about the internal implementation. | |
trait BorrowDigitalPin { | |
fn borrow_pin(&mut self) -> &mut Pin; | |
} | |
// A public trait with the commands a Digital sends. | |
pub trait DigitalCommands : BorrowDigitalPin { | |
// Read the pin receiving a 0 or 1. This method is blocking. | |
fn read(&mut self) -> Result<usize>; | |
// Write a 0 or 1 to the pin. This method is blocking. | |
fn write(&mut self, value: usize) -> Result<()>; | |
} | |
// The generic implementation of DigitalCommands for anything implementing | |
// the private trait that lets us borrow the pin to access private internal | |
// details to read and write on the Digital. This way we only implement the | |
// methods once for a digital reference of pin owning digital. With this | |
// being a trait users or downstream libraries could do further wrapping and | |
// expose the same interface. Functions instead of needing a Digital or | |
// DigitalRef specifically can take a &DigitalCommands &mut DigitalCommands | |
// or Box<DigitalCommands>. | |
impl<T> DigitalCommands for T where T : BorrowDigitalPin {} | |
// The Digital refence object. Instead of owning a Pin it owns a mutable | |
// reference to a Pin. | |
struct DigitalRef<'a> { | |
pin: &'a mut Pin, | |
} | |
// The Digital object. | |
struct Digital { | |
pin: Pin, | |
} | |
// Implement the borrow trait letting Digital ref implement the methods for | |
// reading and writing the pin. | |
impl<'a> BorrowDigitalPin for DigitalRef<'a> {} | |
// Implement the borrow trait letting Digital implement the methods for | |
// reading and writing the pin. | |
impl BorrowDigitalPin for Digital {} | |
impl<'a> DigitalRef<'a> { | |
pub fn pin(&mut self) -> &mut Pin {} | |
} | |
impl Digital { | |
// Along with the trait methods, Digital also lets you borrow the | |
// internal pin normally or turn it back into the pin. These methods | |
// cannot error or panic since the Pin is the more abstract object that | |
// is required to even make Digital. We already know that we can safely | |
// return these values. | |
pub fn pin(&mut self) -> &mut Pin {} | |
pub fn into_pin(self) -> Pin {} | |
} | |
} | |
// | |
// Interrupt | |
// | |
mod interrupt { | |
// Interrupt follows the idioms for single pin protocols laid out by Digital. | |
pub trait RefInterrupt { | |
fn interrupt<'a>(&mut self) -> InterruptRef<'a>; | |
} | |
pub trait IntoInterrupt { | |
fn into_interrupt(self) -> Interrupt; | |
} | |
impl RefInterrupt for &mut Port {} | |
impl IntoInterrupt for Port {} | |
impl RefInterrupt for &mut Pin {} | |
impl IntoInterrupt for Pin {} | |
trait BorrowInterruptPin { | |
fn borrow_pin(&mut self) -> &mut Pin; | |
} | |
// Each of these methods is blocking and waits for some interrupt change. | |
// They don't repeatedly wait, each time you want to wait on the interrupt, | |
// you need to call the corresponding wait again. | |
trait InterruptCommands { | |
// Wait for the pin to be read as 1. | |
fn wait_rise(&mut self) -> Result<()>; | |
// Wait for the pin to be read as 0. | |
fn wait_fall(&mut self) -> Result<()>; | |
// Wait for the pin to be a different value. The value is returned. | |
fn wait(&mut self) -> Result<bool>; | |
} | |
impl<T> InterruptCommands for T where T : BorrowInterruptPin {} | |
pub struct InterruptRef<'a> {} | |
pub struct Interrupt {} | |
impl<'a> BorrowInterruptPin for InterruptRef<'a> {} | |
impl BorrowInterruptPin for Interrupt {} | |
} | |
// | |
// Analog | |
// | |
mod analog { | |
// To keep protocol types from being to complicated and letting Analog be | |
// thought of like other protocols it has read and write methods. Turning a | |
// pin into a Analog object only requires that the pin can be read. Pin | |
// writability is only checked when actually writing to the pin. If the pin | |
// can not be written to the call will error. | |
pub trait RefAnalog { | |
fn analog<'a>(self) -> Result<AnalogRef<'a>> {} | |
} | |
pub trait IntoAnalog { | |
fn into_analog(self) -> Result<Analog> {} | |
} | |
impl RefAnalog for &mut Port {} | |
impl IntoAnalog for Port {} | |
impl RefAnalog for &mut Pin {} | |
impl IntoAnalog for Pin {} | |
trait BorrowAnalogPin { | |
fn borrow_pin(&mut self) -> &mut Pin; | |
} | |
pub trait AnalogCommands { | |
// Read an analog pin returning its value. This method is blocking. | |
fn read(&mut self) -> Result<f32> {} | |
// Write a value to an analog pin. If the pin cannot be written to it | |
// will error. This method is blocking. | |
fn write(&mut self, value: f32) -> Result<()> {} | |
} | |
impl<T> AnalogCommands for T where T : BorrowAnalogPin {} | |
pub struct AnalogRef<'a> {} | |
impl BorrowAnalogPin for AnalogRef {} | |
pub struct Analog {} | |
impl BorrowAnalogPin for Analog {} | |
} | |
// | |
// PWM | |
// | |
mod pwm { | |
pub trait RefPwm { | |
fn pwm<'a>(self) -> Result<PwmRef<'a>> {} | |
} | |
pub trait IntoPwm { | |
fn into_pwm(self) -> Result<Pwm> {} | |
} | |
impl RefPwm for &mut Port {} | |
impl IntoPwm for Port {} | |
impl RefPwm for &mut Pin {} | |
impl IntoPwm for Pin {} | |
trait BorrowPwmPin { | |
fn borrow_pin(&mut self) -> &mut Pin; | |
} | |
pub trait PwmCommands { | |
// Change the duty_cycle for this pin. This method is not blocking. | |
fn duty_cycle(&mut self, cycle: f32) -> Result<()>; | |
} | |
impl<T> PwmCommands for T where T : BorrowPwmPin {} | |
impl<T> Drop for T where T : BorrowPwmPin {} | |
struct PwmRef<'a> { | |
pin: &'a mut Pin, | |
} | |
impl<'a> BorrowPwmPin for PwmRef<'a> {} | |
struct Pwm { | |
pin: Pin, | |
} | |
impl BorrowPwmPin for Pwm {} | |
impl Pwm { | |
pub fn pin(&mut self) -> &mut Pin {} | |
pub fn into_pin(self) -> Pin {} | |
} | |
} | |
// | |
// Transfer | |
// | |
mod transfer { | |
// Two multi pin protocols share an idea, transfering data, that is reading | |
// and writing blocks of data at the same time. For rust this is like | |
// combining Write::write_all and Read::read_exact together, leading this | |
// design to be blocking while I2C, SPI, and UART should otherwise be | |
// possible to not block when Write::write and Read::read are used (unless | |
// their underlying buffer is filled). | |
pub trait Transfer { | |
fn transfer(&mut self, write: &[u8], read: &mut [u8]) -> Result<()>; | |
} | |
} | |
// | |
// I2C | |
// | |
mod i2c { | |
// I2c is the first multi pin protocol in this document. Multi pin protocols | |
// follow most of the same ideas the single pin protocols do. The biggest | |
// differences are that these protocols take multiple pins and implement | |
// Rust's std Read and Write traits. As those traits can be (but don't | |
// guarentee) non-blocking, multiple messages can be queued up depending on | |
// the underlying way those messages are passed to the co-processor. | |
// Since I2c and Spi can talk to multiple devices their interfaces are | |
// further wrapped by Slave types. These Slave types are like Rust's Arc | |
// referring to the same underlying objects to communicate with the | |
// co-processor and be on different separate threads. While they hold those | |
// references the original pins cannot be reused. Instead of blocking | |
// dropping, which could create race conditions, those pins cannot be turned | |
// into another protocol other than I2c until all slaves are also dropped. A | |
// pin in use by an I2c slave will return an error when trying to turn it | |
// into any protocol other than I2c. | |
// | |
// Not being able to turn into a protocol because its in use by a slave does | |
// make this interface a little awkward. This is about finding a happy | |
// middle ground. One option for this API would be slaves with lifetimes. | |
// Those slaves could exist as long as the I2c type, meaning they can only | |
// live in the same scope. This would exclude sending slaves to different | |
// threads to communicate with devices. A second option would be blocking at | |
// some point. We could block on dropping the I2c object. We could block | |
// when trying to turn the pin into a communications object. Both blocking | |
// options can easily end up in deadlock scenarios if the same thread owns | |
// one of the slaves. The third option is this one. Slaves can be sent to | |
// other threads and you can't easily deadlock the thread. A way to make | |
// this last option less awkward will be to have a method on Pin that can in | |
// its name and documentation blocks until the Pin can be turned into a | |
// given type. Otherwise users can poll occasionally to see if the Pin can | |
// be turned into a given protocol. | |
// Non-blocking writing with a supporting mechanism underneath is easy. Send | |
// the message to be written through a buffer until the buffer is filled. | |
// Once filled we should block until we can write more. | |
// Non-blocking reading is more tricky. (This does not apply to Uart.) Since | |
// I2c and Spi read by instruction to their devices the easy option for this | |
// API would be to be blocking. We can present a non-blocking interface by | |
// taking a read call as instruction to send a rx command if one currently | |
// isn't being fulfilled. After that command is sent, any further reads can | |
// return any data recevied from the co-processor until the next read is | |
// made with the buffer empty and the last command being fulfilled. | |
pub trait RefI2c { | |
fn i2c(self) -> Result<I2cRef>; | |
} | |
pub trait IntoI2c { | |
fn into_i2c(self) -> Result<I2c>; | |
} | |
// These are some examples. We could also support Iterator of &mut Pin and | |
// Pin or Vec or slices of the same. | |
impl RefI2c for &mut Port {} | |
impl RefI2c for (&mut Pin, &mut Pin) {} | |
impl IntoI2c for Port {} | |
impl IntoI2c for (Pin, Pin) {} | |
trait BorrowI2cPin { | |
fn borrow_pin(&mut self) -> &mut Pin; | |
} | |
impl<T> io::Read for T where T : BorrowI2cPin {} | |
impl<T> io::Write for T where T : BorrowI2cPin {} | |
impl<T> Transfer for T where T : BorrowI2cPin {} | |
trait SlaveOf { | |
type Slave; | |
// Up to the addressable limit number of slaves can exist. But no two | |
// may share the same address. | |
fn slave(&self, addr: u8) -> Result<Self::Slave>; | |
} | |
pub struct I2cRef<'a> {} | |
pub struct I2cRefSlave<'a> {} | |
impl SlaveOf for I2cRef {} | |
pub struct I2c {} | |
pub struct I2cSlave {} | |
impl SlaveOf for I2c {} | |
impl I2c { | |
pub fn into_pins(self) -> (Pin, Pin) {} | |
} | |
impl Drop for I2cRef {} | |
impl BorrowI2cPin for I2cRefSlave {} | |
impl Drop for I2c {} | |
impl BorrowI2cPin for I2cSlave {} | |
} | |
// | |
// UART | |
// | |
mod uart { | |
// Uart is similar to I2c. It uses multiple pins. It can Read and Write. | |
// Since it can only communicate with one device it doesn't need to lock | |
// pins like I2c slaves. Reading a Uart is easier since its delivered | |
// asynchrounously instead of on demand like I2c and Uart. If on read | |
// nothing has yet been delivered by Uart, read will immediately return. | |
pub trait RefUart { | |
fn uart(&mut self) -> Result<RefUart<'a>>; | |
} | |
pub trait IntoUart { | |
fn into_uart(self) -> Result<Uart>; | |
} | |
impl RefUart for &mut Port {} | |
impl RefUart for (&mut Pin, &mut Pin) {} | |
impl IntoUart for Port {} | |
impl IntoUart for (Pin, Pin) {} | |
trait BorrowUartPin { | |
fn borrow_uart_pin(&mut self) -> &mut Pin; | |
} | |
impl<T> Read for T where T : BorrowUartPin {} | |
impl<T> Write for T where T : BorrowUartPin {} | |
pub struct UartRef<'a> {} | |
impl BorrowUartPin for UartRef {} | |
pub struct Uart {} | |
impl BorrowUartPin for Uart {} | |
} | |
// | |
// SPI | |
// | |
mod spi { | |
pub trait RefSpi { | |
fn spi(&mut self) -> Result<SpiRef>; | |
} | |
pub trait IntoSpi { | |
fn into_spi(self) -> Result<Spi>; | |
} | |
impl RefSpi for Port {} | |
impl RefSpi for (&mut Pin, &mut Pin, &mut Pin) {} | |
impl IntoSpi for Port {} | |
impl IntoSpi for (Pin, Pin, Pin) {} | |
trait BorrowSpiPin { | |
fn borrow_pin(&mut self) -> &mut Pin; | |
} | |
impl<T> io::Read for T where T : BorrowSpiPin {} | |
impl<T> io::Write for T where T : BorrowSpiPin {} | |
impl<T> Transfer for T where T : BorrowSpiPin {} | |
trait SlaveOf { | |
type Slave; | |
fn slave(&self, chip_select: Pin) -> Result<Self::Slave>; | |
} | |
pub struct SpiRef<'a> {} | |
pub struct SpiRefSlave<'a> {} | |
pub struct Spi {} | |
pub struct SpiSlave {} | |
impl SlaveOf for SpiRef {} | |
impl SlaveOf for Spi {} | |
impl BorrowSpiPin for SpiRefSlave {} | |
impl BorrowSpiPin for SpiSlave {} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment