5.4. Choosing an input/output library
To communicate with the outside world, you will need to use the AVR's built in IO registers to configure and access the electrical pins on the outside of the chip.
External resources
The problem space of IO on AVR
- Reading from or writing to an IO pin is as easy as reading a byte from or writing a byte to the correct location in memory
- The main issue is identifying which memory locations assigned to which physical pins and peripherals for each chip
Rather than using bare-metal IO tied to specific AVR chips, it is recommended to use an abstraction layer over the actual I/O register manipulation.
A bare-metal example of IO
This example uses hardcoded IO register locations for the ATMega328p. It may work for some other AVR devices in the same family, but others will use different IO register memory mappings and so will not produce the expected output when ran on these devices.
NOTE: Executables should prefer using an abstraction layer over the actual I/O such as embedded-hal
rather than writing to microcontroller-specific I/O registers directly.
#![no_std] #![no_main] extern crate avr_std_stub; /// The data direction register for PORT B, which is mapped to 0x24 in memory on the atmega328. const DDRB: *mut u8 = 0x24 as *mut u8; /// The pin status register for PORT B, which is mapped to 0x25 in memory on the atmega328. const PORTB: *mut u8 = 0x25 as *mut u8; #[no_mangle] pub extern fn main() { unsafe { // Set the upper four physical pins on PORT B to inputs, the lower four to outputs. // The AVR interprets '1' in the data direction register as 'output', '0' input // for the corresponding pin. core::ptr::write_volatile(DDRB, core::ptr::read_volatile(DDRB) | 0b00001111); // Write half of the output pins as high, the other half low. core::ptr::write_volatile(PORTB, 0b00001010); } }
High level libraries for IO on AVR
The embedded-hal
Hardware abstraction layer
The embedded-hal
project exposes a device-independent set of traits that can be implemented
by crates for specific devices. In this manner, one crate can be written to target multiple
different embedded device architectures like ARM, MSP430, and AVR.
embedded-hal implementations for AVR:
- Rahix/avr-hal
- Supports
atmega32u4
,attiny85
,atmega328p
,atmega1280
- Supports
The AVR-specific avr-rust/ruduino
crate
Caveat: This crate can and will only work for AVR. Depending on this crate directly will lock you out of targeting architectures other than AVR.
The ruduino library provides a high-level type-safe API for interacting with the AVR IO registers.
Driven from the official .atdf
AVR device specification files, this crate exposes all available
IO registers for any AVR device you wish to target.
At the moment, the high level bindings in ruduino
are limited. You have access to all of
the peripherals, but you will need to write to the expected IO registers manually.
Others?
Pull requests to github.com/avr-rust/book.avr-rust.com welcome!