ramlink/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
//! RAM-based, producer-consumer, one-way communication using a ring buffer
//!
//! This no-std crate provides a way to transmit information from a producer (for example
//! a microcontroller) to a consumer (a debbuging host) using a shared RAM memory.
//! Usually, it is possible to read the RAM of microcontrollers using debugging
//! interfaces (JTAG, UPDI, ..).
//!
//! This way, it's possible to transmit information through the debugging interface
//! without relying on an additional UART.
//!
//! # Example
//! ## Producer (AVR, PIC, microcontroller, ...)
//! Add this crate to your project, don't forget to enable the `producer` feature:
//!
//! `cargo add ramlink -F producer`
//!
//! Then create an ring buffer of size 5.
//!
//! In order to access it safely, we wrap it around a Mutex and a RefCell:
//! ```
//! use avr_device::interrupt::{self, Mutex};
//! use core::cell::{Cell, RefCell};
//! use ramlink::producer::RB;
//!
//! static RING_BUF: Mutex<RefCell<RB<5>>> = Mutex::new(RefCell::new(RB::<5>::new()));
//! ```
//! you can then send data to your consumer:
//! ```
//! interrupt::free(|cs| {
//! RING_BUF
//! .borrow(cs)
//! .borrow_mut()
//! .send_bytes_blocking(&[temperature, current]);
//! });
//!```
//! ## Consumer (laptop with JTAG/UPDI/… interface)
//! Add this crate to you project, don't forget to enable the `consumer` feature:
//!
//! `cargo add ramlink -F consumer`
//!
//! Implement the trait for your specific device
//! ```
//! struct mk2<'a> {
//! dev: JtagIceMkii<'a>,
//! }
//!
//! impl<'a> ramlink::consumer::MemoryReader for mk2<'a> {
//! fn read_memory(&mut self, address: usize, buffer: &mut [u8]) -> Result<(), String> {
//! for i in 0..buffer.len() {
//! let byte = self.dev.read_ram_byte((address + i) as u16).unwrap();
//! buffer[i] = byte;
//! }
//! Ok(())
//! }
//! fn write_memory(&mut self, address: usize, value: u8) -> Result<(), String> {
//! self.dev.write_ram_byte(address as u16, value);
//! Ok(())
//! }
//! }
//!```
//! Initialize it. In this example, the producer device is an AVR Attiny402, and the RB struct
//! is stored at address `0x3f0e`
//! ```
//! let mm = mk2 { dev: dgr };
//!
//! let mut rb = ramlink::consumer::ProducerDevice::new(Box::new(mm), 0x3f0e).unwrap();
//! ```
//! and start reading:
//! ```
//! while true {
//! let r = rb.read_bytes();
//! if r.len() > 0 {
//! println!("I READ {:02x?}", r);
//! }
//! }
//! ```
#![no_std]
#![allow(incomplete_features)]
#![feature(generic_const_exprs)]
#[allow(dead_code)]
const RB_MAGIC: [u8; 3] = [0x88, 0x88, 0x88]; // XXX to share amongst prod/cons
#[cfg(feature = "consumer")]
pub mod consumer;
#[cfg(feature = "producer")]
pub mod producer;