Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add examples to atmega-hal documentation #591

Merged
merged 7 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions examples/atmega2560/src/bin/atmega2560-eeprom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#![no_std]
#![no_main]

use atmega_hal::delay::Delay;
use atmega_hal::usart::{Baudrate, Usart};
use atmega_hal::Eeprom;
use embedded_hal::delay::DelayNs;
use panic_halt as _;

// Define core clock in the root crate
type CoreClock = atmega_hal::clock::MHz16;

const BOOT_COUNT_OFFSET: u16 = 0;

#[avr_device::entry]
fn main() -> ! {
let dp = atmega_hal::Peripherals::take().unwrap();
let pins = atmega_hal::pins!(dp);

let mut delay = Delay::<crate::CoreClock>::new();

// set up serial interface for text output
let mut serial = Usart::new(
dp.USART0,
pins.pe0,
pins.pe1.into_output(),
Baudrate::<crate::CoreClock>::new(57600),
);

let mut eeprom = Eeprom::new(dp.EEPROM);

let mut boot_count = eeprom.read_byte(BOOT_COUNT_OFFSET);
boot_count = boot_count.wrapping_add(1);
eeprom.write_byte(BOOT_COUNT_OFFSET, boot_count);

ufmt::uwriteln!(&mut serial, "Boot count: {}", boot_count).unwrap();
Comment on lines +32 to +36
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A minor thing, but because people like copying example code for production use, let's talk about it:

I think this should be using a saturating_add() instead of wrapping_add() so the value doesn't cycle back to zero after 256 boots. The print statement should then display a different message on overflow. E.g.

Suggested change
let mut boot_count = eeprom.read_byte(BOOT_COUNT_OFFSET);
boot_count = boot_count.wrapping_add(1);
eeprom.write_byte(BOOT_COUNT_OFFSET, boot_count);
ufmt::uwriteln!(&mut serial, "Boot count: {}", boot_count).unwrap();
let mut boot_count = eeprom.read_byte(BOOT_COUNT_OFFSET);
boot_count = boot_count.saturating_add(1);
eeprom.write_byte(BOOT_COUNT_OFFSET, boot_count);
if boot_count == u8::MAX {
ufmt::uwriteln!(&mut serial, "Boot count: >={} (overflow)", boot_count).unwrap();
} else {
ufmt::uwriteln!(&mut serial, "Boot count: {}", boot_count).unwrap();
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, examples are how people learn, so we should try to show the best code.

The issue with EEPROM is that the data is 0xFF in the default/erased state, so we need to "initialize" it on first boot. A couple of possible solutions would be:

  • Check and stop counting at u8::MAX - 1
  • Use a larger integer (u32 should be enough for everyone™)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I see it is getting even more difficult... Tell you what, let's merge it as is and then maybe think about improvements in the future. I smell the solution being a custom "driver" to manage a bootcount, to contain all this complexity...


loop {}
}
24 changes: 24 additions & 0 deletions mcu/atmega-hal/src/adc.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
//! Analog-to-Digital Converter
//!
//! # Example
//!
//! Complete example source code can be found in the repository:
//! [`atmega2560-adc.rs`](https://github.com/Rahix/avr-hal/blob/main/examples/atmega2560/src/bin/atmega2560-adc.rs)
//!
//! ```
//! let dp = atmega_hal::Peripherals::take().unwrap();
//! let pins = atmega_hal::pins!(dp);
//!
//! let mut adc = Adc::new(dp.ADC, Default::default());
//!
//! let channels: [atmega_hal::adc::Channel; 4] = [
//! pins.pf0.into_analog_input(&mut adc).into_channel(),
//! pins.pf1.into_analog_input(&mut adc).into_channel(),
//! pins.pf2.into_analog_input(&mut adc).into_channel(),
//! pins.pf3.into_analog_input(&mut adc).into_channel(),
//! ];
//!
//! for (index, channel) in channels.iter().enumerate() {
//! let value = adc.read_blocking(channel);
//! ufmt::uwrite!(&mut serial, "CH{}: {} ", index, value).unwrap();
//! }
//! ```

use crate::port;
pub use avr_hal_generic::adc::{AdcChannel, AdcOps, ClockDivider};
Expand Down
20 changes: 20 additions & 0 deletions mcu/atmega-hal/src/eeprom.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
//! EEPROM
//!
//! # Example
//!
//! Complete example source code can be found in the repository:
//! [`atmega2560-eeprom.rs`](https://github.com/Rahix/avr-hal/blob/main/examples/atmega2560/src/bin/atmega2560-eeprom.rs)
//!
//! ```
//! const BOOT_COUNT_OFFSET: u16 = 0;
//!
//! let dp = atmega_hal::Peripherals::take().unwrap();
//! let mut eeprom = Eeprom::new(dp.EEPROM);
//!
//! let mut boot_count = eeprom.read_byte(BOOT_COUNT_OFFSET);
//! boot_count = boot_count.wrapping_add(1);
//! eeprom.write_byte(BOOT_COUNT_OFFSET, boot_count);
//!
//! ufmt::uwriteln!(&mut serial, "Boot count: {}", boot_count).unwrap();
//! ```

pub use avr_hal_generic::eeprom::{EepromOps, OutOfBoundsError};

pub type Eeprom = avr_hal_generic::eeprom::Eeprom<crate::Atmega, crate::pac::EEPROM>;
Expand Down
21 changes: 21 additions & 0 deletions mcu/atmega-hal/src/i2c.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
//! I2C
//!
//! # Example
//!
//! Complete example source code can be found in the repository:
//! [`atmega2560-i2cdetect.rs`](https://github.com/Rahix/avr-hal/blob/main/examples/atmega2560/src/bin/atmega2560-i2cdetect.rs)
//!
//! ```
//! let dp = atmega_hal::Peripherals::take().unwrap();
//! let pins = atmega_hal::pins!(dp);
//!
//! let mut i2c = I2c::new(
//! dp.TWI,
//! pins.pd1.into_pull_up_input(),
//! pins.pd0.into_pull_up_input(),
//! 50_000,
//! );
//!
//! i2c.i2cdetect(&mut serial, atmega_hal::i2c::Direction::Read).unwrap();
//! ```

#[allow(unused_imports)]
use crate::port;
pub use avr_hal_generic::i2c::*;
Expand Down
19 changes: 19 additions & 0 deletions mcu/atmega-hal/src/port.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
//! Port
//!
//! # Example
//!
//! Complete example source code can be found in the repository:
//! [`atmega2560-blink.rs`](https://github.com/Rahix/avr-hal/blob/main/examples/atmega2560/src/bin/atmega2560-blink.rs)
//!
//! ```
//! let dp = atmega_hal::Peripherals::take().unwrap();
//! let pins = atmega_hal::pins!(dp);
//!
//! let mut led = pins.pb7.into_output();
//!
//! loop {
//! led.toggle();
//! delay_ms(1000);
//! }
//! ```

pub use avr_hal_generic::port::{mode, PinMode, PinOps};

#[cfg(any(feature = "atmega48p", feature = "atmega168", feature = "atmega328p"))]
Expand Down
30 changes: 30 additions & 0 deletions mcu/atmega-hal/src/spi.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
//! SPI
//!
//! # Example
//!
//! Complete example source code can be found in the repository
//! [`atmega2560-spi-feedback.rs`](https://github.com/Rahix/avr-hal/blob/main/examples/atmega2560/src/bin/atmega2560-spi-feedback.rs)
//!
//! ```
//! let dp = atmega_hal::Peripherals::take().unwrap();
//! let pins = atmega_hal::pins!(dp);
//!
//! let (mut spi, mut cs) = spi::Spi::new(
//! dp.SPI,
//! pins.pb1.into_output(),
//! pins.pb2.into_output(),
//! pins.pb3.into_pull_up_input(),
//! pins.pb0.into_output(),
//! spi::Settings::default(),
//! );
//!
//! let data_out = b"Hello World!";
//! let mut data_in = [0u8; 12];
//!
//! cs.set_low().unwrap();
//! spi.transfer(&mut data_in, data_out).unwrap();
//! cs.set_high().unwrap();
//!
//! ufmt::uwriteln!(&mut serial, "data: {:?}", data_in).unwrap();
//! ```

#[allow(unused_imports)]
use crate::port;
pub use avr_hal_generic::spi::*;
Expand Down
31 changes: 31 additions & 0 deletions mcu/atmega-hal/src/usart.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
//! USART
//!
//! # Example
//!
//! Complete example source code can be found in the repository:
//! [`atmega2560-usart.rs`](https://github.com/Rahix/avr-hal/blob/main/examples/atmega2560/src/bin/atmega2560-usart.rs)
//!
//! *Note: [ufmt](https://crates.io/crates/ufmt/) is used instead of `core::fmt` because
//! `core::fmt` code quickly grows too large for AVR platforms.*
//!
//! ```
//! let dp = atmega_hal::Peripherals::take().unwrap();
//! let pins = atmega_hal::pins!(dp);
//!
//! let mut serial = Usart::new(
//! dp.USART0,
//! pins.pe0,
//! pins.pe1.into_output(),
//! Baudrate::<crate::CoreClock>::new(57600),
//! );
//!
//! ufmt::uwriteln!(&mut serial, "Hello from ATmega!").unwrap();
//!
//! loop {
//! // Read a byte from the serial connection
//! let b = nb::block!(serial.read()).unwrap();
//! // Answer
//! ufmt::uwriteln!(&mut serial, "Got {}!", b).unwrap();
//! }
//! ```

#[allow(unused_imports)]
use crate::port;
pub use avr_hal_generic::usart::*;
Expand Down