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;