Mockup a Tessel API covering the same basic APIs as Tessel JS
// | |
// 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