ramlink/
lib.rs

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