Skip to content
Open
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ members = [
"examples/arduino-nano",
"examples/arduino-uno",
"examples/atmega2560",
"examples/avr-butterfly",
"examples/nano168",
"examples/sparkfun-promicro",
"examples/sparkfun-promini-3v3",
Expand Down
14 changes: 14 additions & 0 deletions avr-hal-generic/src/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ impl Clock for MHz8 {
const FREQ: u32 = 8_000_000;
}

/// 4 MHz Clock
#[derive(ufmt::derive::uDebug, Debug)]
pub struct MHz4;
impl Clock for MHz4 {
const FREQ: u32 = 4_000_000;
}

/// 2 MHz Clock
#[derive(ufmt::derive::uDebug, Debug)]
pub struct MHz2;
impl Clock for MHz2 {
const FREQ: u32 = 2_000_000;
}

/// 1 MHz Clock
#[derive(ufmt::derive::uDebug, Debug)]
pub struct MHz1;
Expand Down
9 changes: 9 additions & 0 deletions examples/avr-butterfly/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[build]
target = "avr-none"
rustflags = ["-C", "target-cpu=atmega169pa"]

[target.'cfg(target_arch = "avr")']
runner = "ravedude"

[unstable]
build-std = ["core"]
21 changes: 21 additions & 0 deletions examples/avr-butterfly/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "avr-butterfly-examples"
description = "Examples for the AVR Butterfly board with Atmega169V chip"
version = "0.0.0"
authors = ["Markus Kolb <[email protected]>"]
edition = "2024"
publish = false

[features]
critical-section-impl = ["avr-device/critical-section-impl"]

[dependencies]
panic-halt = "1.0.0"
ufmt = "0.2.0"
nb = "1.1.0"
embedded-hal = "1.0"
avr-device = { version = "0.7", features = ["rt"] }

[dependencies.atmega-hal]
path = "../../mcu/atmega-hal/"
features = ["atmega169pa"]
7 changes: 7 additions & 0 deletions examples/avr-butterfly/Ravedude.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[general]
board = "butterfly"
serial-baudrate = 57600
open-console = false

# For documentation about this file, check here:
# https://github.com/Rahix/avr-hal/blob/main/ravedude/README.md#ravedudetoml-format
54 changes: 54 additions & 0 deletions examples/avr-butterfly/src/bin/avr-butterfly-adc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#![no_std]
#![no_main]

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

// Define core clock in the root crate
type CoreClock = atmega_hal::clock::MHz8;
// Use it as follows in the rest of the project
type Adc = atmega_hal::adc::Adc<crate::CoreClock>;

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

// double set in 4 cycles to disable JTAG and make shared ADC pins usable
dp.JTAG.mcucr.write(|w| w.jtd().set_bit());
dp.JTAG.mcucr.write(|w| w.jtd().set_bit());

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 adc = Adc::new(dp.ADC, Default::default());

// To store multiple channels in an array, we use the `into_channel()` method.
let channels: [atmega_hal::adc::Channel; 5] = [
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(),
pins.pf4.into_analog_input(&mut adc).into_channel(),
];

loop {
for (i, ch) in channels.iter().enumerate() {
let v = adc.read_blocking(ch);
ufmt::uwrite!(&mut serial, "PF{}: {} ", i, v).unwrap();
}

ufmt::uwriteln!(&mut serial, "\r").unwrap();
delay.delay_ms(1000);
}
}
47 changes: 47 additions & 0 deletions examples/avr-butterfly/src/bin/avr-butterfly-blink.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*!
# A led blink application

This is a simple circuit with a blinking led.

# The hardware

* a led
* a 330 Ohm resistor
* some patch cables/connections
* The ports on the board (GND (JTAG/J402 Pin 2), PF4 (JTAG/J402 Pin 1), 3.3V (JTAG/J402 Pin 4))

# The board layout

Connect the resistor to Pin 1 of J402 socket. Connect led anode to other end of resistor, led cathode to Pin 2 of J402.
Connect your 3.3V power source to Pin 4 (+) and Pin 2 (-) of J402 socket.
So yes, led cathode and negative part of powersource connects both at Pin 2 of J402, maybe use a breadboard.
*/
#![no_std]
#![no_main]

use atmega_hal::delay::Delay;
use embedded_hal::delay::DelayNs;
use panic_halt as _;

// Define core clock. This can be used in the rest of the project.
type CoreClock = atmega_hal::clock::MHz8;

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

// double set in 4 cycles to disable JTAG and make shared ADC pins usable
dp.JTAG.mcucr.write(|w| w.jtd().set_bit());
dp.JTAG.mcucr.write(|w| w.jtd().set_bit());

let pins = atmega_hal::pins!(dp);

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

let mut led = pins.pf4.into_output();

loop {
led.toggle();
delay.delay_ms(1000);
}
}
44 changes: 44 additions & 0 deletions examples/avr-butterfly/src/bin/avr-butterfly-eeprom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#![no_std]
#![no_main]

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

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

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 eeprom_capacity = eeprom.capacity();
ufmt::uwriteln!(&mut serial, "eeprom capacity is: {}", eeprom_capacity).unwrap();

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, "\rBoot count: {}\r", boot_count).unwrap();

loop {
delay.delay_ms(1000);
}
}
165 changes: 165 additions & 0 deletions examples/avr-butterfly/src/bin/avr-butterfly-joystick.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
//! Joystick test code for AVR Butterfly
//!
//! Modifies display counter with up-right and down-left.
//! Pressing button displays PRESS.
//!

#![no_std]
#![no_main]
#![feature(abi_avr_interrupt)]

use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};

use atmega_hal::delay::Delay;
use embedded_hal::delay::DelayNs;
use panic_halt as _;

use avr_butterfly_lcd::Lcd;

#[allow(dead_code)]
mod avr_butterfly_lcd;

// Define core clock. This can be used in the rest of the project.
type CoreClock = atmega_hal::clock::MHz8;

const PRESS: &[u8] = b"PRESS";
const PRESS_OFFSET: usize = 6 - PRESS.len();

static CTRL_CHANGE: AtomicBool = AtomicBool::new(false);
static CTRL_PRESS: AtomicBool = AtomicBool::new(false);
static CTRL_VALUE: AtomicU8 = AtomicU8::new(127);

/// Convert u8 number to ascii digits
fn u8_to_ascii(mut n: u8, buf: &mut [u8; 3]) -> usize {
if n < 10 {
buf[0] = b'0' + n;
return 1;
}

let mut i = 0;
let mut tmp = [0u8; 3];
while n > 0 {
let digit = n % 10;
tmp[i] = b'0' + digit;
n /= 10;
i += 1;
}
for j in 0..i {
buf[j] = tmp[i - 1 - j];
}

i
}

/// Update LCD with current `CTRL_VALUE`
fn update_lcd(lcd: &mut Lcd) {
let val = CTRL_VALUE.load(Ordering::Relaxed);
let mut ascii_buf = [0u8; 3];
let n = u8_to_ascii(val, &mut ascii_buf);
let display_val = &ascii_buf[..n];
lcd.lcd_sync_enable(false);
// 1 for len of PRESS
for i in 1..(6 - n) {
lcd.lcd_write(0, i as u8);
}
for (i, &char_val) in display_val.iter().enumerate() {
lcd.lcd_write(char_val, (6 - n + i) as u8);
}
lcd.lcd_sync_enable(true);
}

#[avr_device::entry]
fn main() -> ! {
let mut delay = Delay::<crate::CoreClock>::new();

let dp = atmega_hal::Peripherals::take().unwrap();

let pins = atmega_hal::pins!(dp);
let _bt_down = pins.pb7.into_pull_up_input();
let _bt_left = pins.pe2.into_pull_up_input();
let _bt_press = pins.pb4.into_pull_up_input();
let _bt_right = pins.pe3.into_pull_up_input();
let _bt_up = pins.pb6.into_pull_up_input();

// enable irq for pe2 (pcint2), pe3 (pcint3)
dp.EXINT.pcmsk0.write(|w| w.pcint().bits(0b0000_1100));
// enable irq for pb4 (pcint12), pb6 (pcint14), pb7 (pcint15)
dp.EXINT.pcmsk1.write(|w| w.pcint().bits(0b1101_0000));
// enable pcie0 and pcie1 for above pin irqs
dp.EXINT
.eimsk
.write(|w| w.pcie0().set_bit().pcie1().set_bit());

let lcd = Lcd::lcd_init(dp.LCD, Some(dp.TC2)).unwrap();
unsafe {
avr_device::interrupt::enable(); // LCD uses start of frame interrupt
}

update_lcd(lcd);

loop {
if CTRL_CHANGE.load(Ordering::Relaxed) {
update_lcd(lcd);
CTRL_CHANGE.store(false, Ordering::Relaxed);
} else if CTRL_PRESS.load(Ordering::Relaxed) {
lcd.lcd_sync_enable(false);
for (i, &char_val) in PRESS.iter().enumerate() {
lcd.lcd_write(char_val, (PRESS_OFFSET + i) as u8);
}
lcd.lcd_sync_enable(true);
CTRL_PRESS.store(false, Ordering::Relaxed);
}

delay.delay_ms(10); // should be ok for demo
}
}

/// Irq handler for pcint2/3, so left/right
#[avr_device::interrupt(atmega169pa)]
fn PCINT0() {
// safety: in irq it is ok to use like this to check pin state
let dp = unsafe { atmega_hal::Peripherals::steal() };

let pe = dp.PORTE.pine.read();
if pe.pe2().bit_is_clear() {
let val = CTRL_VALUE.load(Ordering::Relaxed);
if val > u8::MIN {
CTRL_VALUE.store(val - 1, Ordering::Relaxed);
CTRL_CHANGE.store(true, Ordering::Relaxed);
}
} else if pe.pe3().bit_is_clear() {
let val = CTRL_VALUE.load(Ordering::Relaxed);
if val < u8::MAX {
CTRL_VALUE.store(val + 1, Ordering::Relaxed);
CTRL_CHANGE.store(true, Ordering::Relaxed);
}
}
}

/// Irq handler for pb4,6,7, so button/up/down
#[avr_device::interrupt(atmega169pa)]
fn PCINT1() {
// safety: in irq it is ok to use like this to check pin state
let dp = unsafe { atmega_hal::Peripherals::steal() };

let pb = dp.PORTB.pinb.read();
if pb.pb7().bit_is_clear() {
let val = CTRL_VALUE.load(Ordering::Relaxed);
if val > u8::MIN {
CTRL_VALUE.store(val - 1, Ordering::Relaxed);
CTRL_CHANGE.store(true, Ordering::Relaxed);
}
} else if pb.pb6().bit_is_clear() {
let val = CTRL_VALUE.load(Ordering::Relaxed);
if val < u8::MAX {
CTRL_VALUE.store(val + 1, Ordering::Relaxed);
CTRL_CHANGE.store(true, Ordering::Relaxed);
}
}

if pb.pb4().bit_is_clear() {
CTRL_PRESS.store(true, Ordering::Relaxed);
} else if pb.pb4().bit_is_set() {
CTRL_CHANGE.store(true, Ordering::Relaxed);
}
}
Loading
Loading