avr-hal
avr-hal copied to clipboard
make serial port, ADC, and SPI examples portable across targets
extract the serial port example into a portable function that demonstrates how to declare the serial port using generics such that it will compile on multiple targets.
I think folks can benefit from seeing concrete examples of how to write cross-platform fuctions like
pub fn report<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK>(
serial: &mut Usart<H, USART, RX, TX, CLOCK>,
) {
arduino_portable::report(&mut serial);
Uhm, I like the idea, but ...
having arduino
in the name is something that I dislike. (note that I have no approval privilege, that I'm just a passenger, surely not a crew member)
The word arduino has too many meanings. It was once a PCB with an avr328
and it was/is an "IDE".
I think ( I suggest) that portable across targets is also portable outside the arduino island. Think ARM (STM32) and RISC-V.
Sweet platypus on a unicycle : I forgot to add the new directory to the repo. While I'm fixing that, I'll rename it to avr-portable.
Hey, thanks a lot for this PR!
I like the idea of including examples which show how to write "generic" code ontop of avr-hal
. That said, there are two dimensions to this:
-
One one hand, there are "real" generics. This is what your code is making use of. Generics are best used when trying to either write a library (which then works with any peripheral from any supported MCU) or when writing application code that works with any of multiple similar peripherals (e.g. any of the USART blocks). In code:
pub fn read_command<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK>( serial: &mut UsartReader<H, USART, RX, TX, CLOCK>, buffer: &mut [u8], ) -> Result<(), Error> { // reads a command from the given UART }
-
On the other hand, the crates of
avr-hal
are designed to be (where possible) drop-in replacements for each other. This means you could use cargo-features to switch between different boards. The application code would, in this case, not require any generics at all. Instead, it would use type-aliases and cfg-directives. To make it more clear, consider this example code:# Cargo.toml [dependencies] cfg-if = "0.1.10" [dependencies.arduino-hal] git = "https://github.com/rahix/avr-hal" rev = "todotodo" [features] # Build for Arduino-Uno uno = ["arduino-hal/arduino-uno"] # Build for Arduino-Leonardo leonardo = ["arduino-hal/arduino-leonardo"]
// src/main.rs #![no_std] #![no_main] use cfg_if::cfg_if; use panic_halt as _; use arduino_hal::hal::port; // Let's say on the Uno we want to use pin d0 and on Leonardo pin d4 for something cfg_if! { if #[cfg(feature = "uno")] { // D0 type FooPin = port::Pin<port::mode::Output, port::PD0>; } else if #[cfg(feature = "leonardo")] { // D4 type FooPin = port::Pin<port::mode::Output, port::PD4>; } else { compile_error!("Select uno or leonardo!"); } }; fn do_foo(pin: &mut FooPin) { pin.toggle(); } #[arduino_hal::entry] fn main() -> ! { let dp = arduino_hal::Peripherals::take().unwrap(); let pins = arduino_hal::pins!(dp); // D13 is an LED on both uno & leonardo so this just works (tm) let mut led = pins.d13.into_output(); led.set_high(); let mut foo_pin: FooPin = cfg_if! { if #[cfg(feature = "uno")] { pins.d0.into_output() } else if #[cfg(feature = "leonardo")] { pins.d4.into_output() } }; loop { led.toggle(); do_foo(&mut foo_pin); arduino_hal::delay_ms(100); } }
You can compile this program for both uno and leonardo so it is also "portable" in that sense. This kind of "portability" is, what I imagine most users will need. The big advantage is that it does not require any kind of generic juggling.
That said, there of course still is a use-case for the former as well. So I would like to include your examples to demonstrate it. However, I don't think we should modify the existing examples to make use of it. Instead I would just add your avr-portable
crate and add some binaries to call into the library to it. You can then make it work for a number of boards using the cargo-feature mechanism shown above.
Does the existence of #264 allow to close this merge request?
I'm asking because dangling merge requests do obstruct the flow of evolution.
If #264 is accepted, this merge request becomes superfluous.