Skip to content

Commit f332679

Browse files
committed
Expand the macro syntax to support easy string usage and array auto-sizing.
Ref #3
1 parent 307d2ba commit f332679

File tree

6 files changed

+325
-74
lines changed

6 files changed

+325
-74
lines changed

examples/printer/mod.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,20 @@
77
//! Quite useful for examples.
88
//!
99
10+
// This a shared model, so there are function only used in specific cases
11+
#![allow(dead_code)]
1012

1113
// Import the Arduino libraries, interestingly they don't cause problems perse
1214
// on other architectures. Through, we will not use there.
13-
use arduino_hal::port::mode::AnyInput;
14-
use arduino_hal::port::mode::Input;
15-
use arduino_hal::port::mode::Output;
16-
use arduino_hal::port::Pin;
17-
use arduino_hal::prelude::*;
15+
cfg_if::cfg_if! {
16+
if #[cfg(target_arch = "avr")] {
17+
use arduino_hal::port::mode::AnyInput;
18+
use arduino_hal::port::mode::Input;
19+
use arduino_hal::port::mode::Output;
20+
use arduino_hal::port::Pin;
21+
use arduino_hal::prelude::*;
22+
}
23+
}
1824

1925

2026
#[cfg(target_arch = "avr")]

examples/uno-auto-sizing.rs

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
//
2+
// This file provides a example on how to use auto-sizing statics on an
3+
// Arduino Uno.
4+
//
5+
6+
7+
// Define no_std only for AVR
8+
#![cfg_attr(target_arch = "avr", no_std)]
9+
#![cfg_attr(target_arch = "avr", no_main)]
10+
//
11+
// To unwrap the Option in const context
12+
#![feature(const_option)]
13+
//
14+
#![feature(extended_key_value_attributes)]
15+
16+
17+
use avr_progmem::progmem; // The macro
18+
#[cfg(target_arch = "avr")]
19+
use panic_halt as _; // halting panic implementation for AVR
20+
21+
22+
progmem! {
23+
// Simple Auto-sizing
24+
// This is equivalent to:
25+
// `static progmem ARR: [u8; 3] = [1,2,3];`
26+
// But you don't have to count the elements yourself, and you get an nice
27+
// constant `ARR_LEN` to refer to the length of the array (e.g. to name the
28+
// type).
29+
static progmem<const ARR_LEN: usize> ARR: [u8; ARR_LEN] = [1,2,3];
30+
31+
// The size in the array type is automatically derived from the value.
32+
// As note: don't confuse this with text, see `uno-string.rs` for those!
33+
static progmem<const LONG_LEN: usize> LONG: [u8; LONG_LEN] =
34+
*include_bytes!("./test_text.txt");
35+
}
36+
37+
// Include a fancy printer supporting Arduino Uno's USB-Serial output as well
38+
// as stdout on non-AVR targets.
39+
mod printer;
40+
use printer::Printer;
41+
42+
#[cfg_attr(target_arch = "avr", arduino_hal::entry)]
43+
fn main() -> ! {
44+
let mut printer = {
45+
#[cfg(target_arch = "avr")]
46+
{
47+
// Initialize the USB-Serial output on the Arduino Uno
48+
49+
let dp = arduino_hal::Peripherals::take().unwrap();
50+
let pins = arduino_hal::pins!(dp);
51+
let serial = arduino_hal::default_serial!(dp, pins, 9600);
52+
53+
Printer(serial)
54+
}
55+
#[cfg(not(target_arch = "avr"))]
56+
{
57+
// Just use stdout for non-AVR targets
58+
Printer
59+
}
60+
};
61+
62+
// Print some introduction text
63+
printer.println("Hello from Arduino!");
64+
printer.println("");
65+
printer.println("--------------------------");
66+
printer.println("");
67+
68+
//
69+
// Using auto-sized arrays
70+
//
71+
72+
// We get the const to refer to the size of the array
73+
ufmt::uwriteln!(&mut printer, "The array is {} bytes long\r", ARR_LEN).unwrap();
74+
printer.println("");
75+
76+
// So we can easily name the type of the array, and load it as a whole:
77+
let arr: [u8; ARR_LEN] = ARR.load();
78+
ufmt::uwriteln!(&mut printer, "{:?}\r", arr).unwrap();
79+
printer.println("");
80+
81+
// We can of course still use the array element accessors:
82+
assert_eq!(2, ARR.load_at(1));
83+
84+
// Particularly useful is this auto-sizing with data from an external file.
85+
ufmt::uwriteln!(&mut printer, "The long data is {} bytes long\r", LONG_LEN).unwrap();
86+
87+
// For longer data, it can easily become problematic to load it all at once,
88+
// instead you can use a byte-by-byte iterator to get the only load them
89+
// one at a time.
90+
for b in LONG.iter() {
91+
// assuming its all ASCII, so we actually print it,
92+
// but if you happen to have text, better use strings
93+
// see: `uno-string.rs`
94+
printer.print(b as char);
95+
}
96+
97+
98+
// Print some final lines
99+
printer.println("");
100+
printer.println("--------------------------");
101+
printer.println("");
102+
printer.println("DONE");
103+
104+
// It is very convenient to just exit on non-AVR platforms, otherwise users
105+
// might get the impression that the program hangs, whereas it already
106+
// succeeded.
107+
#[cfg(not(target_arch = "avr"))]
108+
std::process::exit(0);
109+
110+
// Otherwise, that is on AVR, just go into an infinite loop, because on AVR
111+
// we just can't exit!
112+
loop {
113+
// Done, just do nothing
114+
}
115+
}

examples/uno-string.rs

+61-40
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,33 @@
1212

1313

1414
use avr_progmem::progmem; // The macro
15-
use avr_progmem::string::LoadedString; // Helper for storing strings
1615
#[cfg(target_arch = "avr")]
1716
use panic_halt as _; // halting panic implementation for AVR
1817

1918

19+
//
20+
// Defining strings as statics in progmem
21+
//
22+
2023
progmem! {
21-
/// The static data to be stored in program code section
22-
/// Notice the usage of `LoadedString`, to store a string as `[u8;N]`,
23-
/// because you can't store a `str` and storing a `&str` wouldn't have
24-
/// much of an effect.
25-
static progmem SOME_TEXT: LoadedString<191> = LoadedString::new("
26-
A long test string literal, that is stored in progmem instead of DRAM.
27-
However, to use it, it needs to be temporarily load into DRAM,
28-
so an individual `LoadedString` shouldn't be too long.
29-
").unwrap();
30-
31-
/// More data to be stored in program code section
32-
static progmem MORE_TEXT: LoadedString<102> = LoadedString::new("
33-
However, you easily store your strings individual, limiting the amount of
34-
temporary DRAM necessary.
35-
").unwrap();
36-
37-
/// Unicode works of course as expected
38-
///
39-
static progmem UNICODE_TEXT: LoadedString<137> = LoadedString::new(
40-
"dai 大賢者 kenja, Völlerei lässt grüßen, le garçon de théâtre, Ελληνική Δημοκρατία, Слава Україні"
41-
).unwrap();
24+
/// A static string to be stored in program code section.
25+
/// Notice the usage of special `string` modifier, that accepts a `&str`
26+
/// as value and will wrap it in a `PmString` instead of a
27+
/// standard `ProgMem`.
28+
static progmem string SOME_TEXT = concat!(
29+
"A long test string literal, that is stored in progmem instead of RAM. ",
30+
"If used via `load`, it is load as `LoadedString` entirely into RAM, ",
31+
"so for those use-case an individual string shouldn't be too long."
32+
);
33+
34+
/// You can also load a file as string via the std `include_str` macro.
35+
/// And because this is stored in progmem, it can be big,
36+
/// e.g. this one is over 2 KiB is size.
37+
static progmem string MUCH_LONGER_TEXT = include_str!("./test_text.txt");
38+
39+
/// Of course, Unicode works as expected.
40+
static progmem string UNICODE_TEXT =
41+
"dai 大賢者 kenja, Völlerei lässt grüßen, le garçon de théâtre, Ελληνική Δημοκρατία, Слава Україні";
4242
}
4343

4444
// Include a fancy printer supporting Arduino Uno's USB-Serial output as well
@@ -72,41 +72,62 @@ fn main() -> ! {
7272
printer.println("--------------------------");
7373
printer.println("");
7474

75-
// Scope to limit the lifetime of `text_buffer`
75+
76+
//
77+
// Using string from progmem
78+
//
79+
80+
// Option 1)
81+
// We can load the entire string at once and use references to the resulting
82+
// `LoadedString` everywhere where a `&str` is expected.
83+
// However, the string must of limited in size to not exceed RAM.
7684
{
77-
// The temporary DRAM buffer for the string
85+
// Scope to limit the lifetime of `text_buffer`, since it is big.
86+
87+
// The temporary DRAM buffer for the string of type `LoadedString`
7888
let text_buffer = SOME_TEXT.load();
79-
let text: &str = &text_buffer; // Just derefs to `str`
80-
printer.println(text);
89+
// Just derefs to `str`
90+
let _text: &str = &text_buffer;
91+
// This function only accepts `&str`, deref makes this possible:
92+
printer.println(&text_buffer);
8193
}
8294

83-
// Or just using temporaries
84-
printer.println(&MORE_TEXT.load());
85-
printer.println(&UNICODE_TEXT.load());
8695

87-
// Even more convenient: use a one-off in-place progmem static via `progmem_str`
88-
printer.println(avr_progmem::progmem_str!("Just a lone literal progmem str"));
96+
// Option 2)
97+
// We can use the `char`-iterator to access the text iteratively,
98+
// this has the advantage of limiting the stack usage.
99+
for c in MUCH_LONGER_TEXT.chars() {
100+
// Here, `c` is just a `char`
101+
let _c: char = c;
102+
printer.print(c);
103+
}
104+
printer.println("");
105+
106+
107+
// Option 3)
108+
// We directly use the Display/uDisplay impl, which uses the char-iterator.
109+
#[cfg(feature = "ufmt")] // this however requires the `ufmt` a crate feature
110+
ufmt::uwriteln!(&mut printer, "{}\r", UNICODE_TEXT).unwrap();
111+
112+
113+
// Option 4)
114+
// We can use a in-place immediate string, similar to the popular C macro:
89115
use avr_progmem::progmem_str as F;
90-
printer.println(F!("And another one"));
116+
ufmt::uwriteln!(&mut printer, "{}\r", F!("Some immediate string")).unwrap();
117+
91118

92-
// Using the ufmt impl
93-
#[cfg(feature = "ufmt")]
94-
ufmt::uwriteln!(&mut printer, "{}\r", UNICODE_TEXT.load()).unwrap();
95119

96120
// Print some final lines
97121
printer.println("");
98122
printer.println("--------------------------");
99123
printer.println("");
100124
printer.println("DONE");
101125

102-
// It is very convenient to just exit on non-AVR platforms, otherwise users
103-
// might get the impression that the program hangs, whereas it already
104-
// succeeded.
126+
// It is convenient to just exit on non-AVR platforms.
105127
#[cfg(not(target_arch = "avr"))]
106128
std::process::exit(0);
107129

108-
// Otherwise, that is on AVR, just go into an infinite loop, because on AVR
109-
// we just can't exit!
130+
// Otherwise, that is on AVR, just go into an infinite loop.
110131
loop {
111132
// Done, just do nothing
112133
}

src/raw.rs

+3
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,9 @@ unsafe fn read_asm_loop_raw<T>(p_addr: *const T, out: *mut T, len: u8) {
271271
// We just use normal data or text segment, and thus that it is
272272
// actually save to just access the data.
273273

274+
// Ignore the unused vars:
275+
let _ = size_bytes;
276+
274277
// Now, just copy the bytes from p_addr to out
275278
// It is save by the way, because we require the user to give use
276279
// pointer valid for exactly that case.

src/string.rs

+40-16
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,27 @@
2626
//! converting a normal string literal into a [`PmString`]:
2727
//!
2828
//! ```rust
29-
//! // TODO: code
29+
//! #![feature(const_option)]
30+
//!
31+
//! # use std::iter::FromIterator;
32+
//! use avr_progmem::progmem;
33+
//!
34+
//! progmem! {
35+
//! // A simple Unicode string in progmem, internally stored as fix-sized
36+
//! // byte array.
37+
//! static progmem string TEXT = "Hello 大賢者";
38+
//! }
39+
//!
40+
//! // You can load it all at once (like a `ProgMem`)
41+
//! let buffer = TEXT.load();
42+
//! // and use that as `&str`
43+
//! assert_eq!("Hello 大賢者", &*buffer);
44+
//!
45+
//! // Or you load it one char at a time (limits RAM usage) via the
46+
//! // chars-iterator
47+
//! let chars_iter = TEXT.chars(); // impl Iterator<Item=char>
48+
//! let exp = ['H', 'e', 'l', 'l', 'o', ' ', '大', '賢', '者'];
49+
//! assert_eq!(&exp, &*Vec::from_iter(chars_iter));
3050
//! ```
3151
//!
3252
@@ -64,20 +84,21 @@ pub struct InvalidLengthError;
6484
/// #![feature(const_option)]
6585
///
6686
/// use avr_progmem::progmem;
87+
/// use avr_progmem::string::PmString;
6788
/// use avr_progmem::string::LoadedString;
6889
///
6990
/// progmem! {
7091
/// // Stores a string as a byte array, i.e. `[u8;19]`, but makes it usable
7192
/// // as `&str` (via `Deref`)
72-
/// static progmem TEXT: LoadedString<19> = LoadedString::new(
73-
/// "dai 大賢者 kenja"
74-
/// ).unwrap();
93+
/// static progmem string TEXT = "dai 大賢者 kenja";
7594
/// }
7695
///
77-
/// // usage:
78-
/// let text_buffer = TEXT.load(); // The temporary DRAM buffer for `TEXT`
79-
/// let text: &str = &text_buffer; // Just derefs to `str`
80-
/// assert_eq!(text, "dai 大賢者 kenja")
96+
/// // The static has type `PmString`
97+
/// let text: &PmString<19> = &TEXT;
98+
/// // The loaded RAM string has type `LoadedString`
99+
/// let loaded: LoadedString<19> = text.load();
100+
/// // Which derefs to `&str`
101+
/// assert_eq!("dai 大賢者 kenja", &*loaded)
81102
/// ```
82103
///
83104
/// # Safety
@@ -434,37 +455,40 @@ impl<'a, const N: usize> Iterator for PmChars<'a, N> {
434455
/// This is a short-cut macro to create an ad-hoc static storing the given
435456
/// string literal as by [`LoadedString`] and load it here from progmem into a
436457
/// temporary and return it as `&str`.
458+
/// This is similar to the `F` macro available in Arduino.
459+
///
460+
/// Similar to the C marco, this will load the full string into RAM at once
461+
/// and thus the string should be of limited size, to not exceed the space
462+
/// available in RAM.
437463
///
438464
/// This macro allows to conveniently put literal string into progmem exactly,
439465
/// where they are used. However, since they are directly loaded into a
440466
/// temporary you don't get a `&'static str` back, and must use the `&str`
441467
/// immediately (i.e. pass it as a function parameter).
442468
/// You can't even store the returned `&str` in a local `let` assignment.
443469
///
470+
///
444471
/// # Example
445472
///
446473
/// ```rust
447474
/// #![feature(const_option)]
448475
///
449-
/// use avr_progmem::progmem_str as S;
476+
/// use avr_progmem::progmem_str as F;
450477
///
451478
/// fn print(s: &str) {
452479
/// // -- snip --
453480
/// # assert_eq!(s, "dai 大賢者 kenja")
454481
/// }
455482
///
456-
/// // Put the literal as byte array into progmem and load it here as `&str`
457-
/// print(S!("dai 大賢者 kenja"));
483+
/// // Put the literal `str` into progmem and load it here as `&str`
484+
/// print(F!("dai 大賢者 kenja"));
458485
/// ```
486+
///
459487
#[macro_export]
460488
macro_rules! progmem_str {
461489
($text:literal) => {{
462-
const TEXT_LEN: usize = <str>::as_bytes($text).len();
463490
$crate::progmem! {
464-
// TODO: use PmString
465-
static progmem TEXT: $crate::string::LoadedString<TEXT_LEN> = $crate::string::LoadedString::new(
466-
$text
467-
).unwrap();
491+
static progmem string TEXT = $text;
468492
}
469493
&*TEXT.load()
470494
}};

0 commit comments

Comments
 (0)