Comments (12)
Before divining into this, a fair warning: Dealing with interrupts is hard. Even in rust, where shooting yourself in the foot is prevented in most cases, you have to be careful. And you will need unsafe in places so you need to understand very well what you are doing.
Okay, that said, first of all the entire code (this one is for Arduino Leonardo, but I'm sure you can adapt it to whatever board you're using):
Click to expand entire listing
#![no_std]
#![no_main]
#![feature(abi_avr_interrupt)]
use arduino_leonardo::hal::port;
use arduino_leonardo::prelude::*;
use core::mem;
use panic_halt as _;
static mut LED: mem::MaybeUninit<port::portc::PC7<port::mode::Output>> = mem::MaybeUninit::uninit();
#[arduino_leonardo::entry]
fn main() -> ! {
let dp = arduino_leonardo::Peripherals::take().unwrap();
let mut pins = arduino_leonardo::Pins::new(dp.PORTB, dp.PORTC, dp.PORTD, dp.PORTE, dp.PORTF);
let led = pins.d13.into_output(&mut pins.ddr);
unsafe {
// SAFETY: Interrupts are not enabled at this point so we can safely write the global
// variable here. A memory barrier afterwards ensures the compiler won't reorder this
// after any operation that enables interrupts.
LED = mem::MaybeUninit::new(led);
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
// Timer Configuration:
// - WGM = 4: CTC mode (Clear Timer on Compare Match)
// - Prescaler 256
// - OCR1A = 15624
//
// => F = 16 MHz / (256 * (1 + 15624)) = 4 Hz
// (^ this formula I deduced from reading the datasheet)
//
// => The LED will toggle at 4 Hz, thus it blinks at 2 Hz
let tmr1 = dp.TC1;
tmr1.tccr1a.write(|w| w.wgm1().bits(0b00));
tmr1.tccr1b.write(|w| w.cs1().prescale_256().wgm1().bits(0b01));
tmr1.ocr1a.write(|w| unsafe { w.bits(15624) });
// Enable the timer interrupt
tmr1.timsk1.write(|w| w.ocie1a().set_bit());
// In theory this should not be necessary ... But if you previously had
// a sketch from Arduino loaded, the USB device will not have been reset.
// Because of this we will be spammed with interrupts which will never
// stop because they are never handled.
//
// (only for ATmega32U4)
dp.USB_DEVICE.usbcon.reset();
// Enable interrupts globally
unsafe {
// SAFETY: Not inside a critical section and any non-atomic operations have been completed
// at this point.
avr_device::interrupt::enable();
}
loop {
avr_device::asm::sleep();
}
}
#[avr_device::interrupt(atmega32u4)]
fn TIMER1_COMPA() {
let led = unsafe {
// SAFETY: We _know_ that interrupts will only be enabled after the LED global was
// initialized so this ISR will never run when LED is uninitialized.
&mut *LED.as_mut_ptr()
};
led.toggle().void_unwrap();
}
Let's take it apart:
#![feature(abi_avr_interrupt)]
this is needed because ISRs on AVR are an unstable feature at this point.
static mut LED: mem::MaybeUninit<port::portc::PC7<port::mode::Output>> = mem::MaybeUninit::uninit();
To move data between an ISR and the main program, you have to use global variables. This is where things get ugly because using globals is very unsafe for the most part (not just in Rust!). Here we get away without using any synchronization because there is no sharing happening. The LED is solely moved into the global after initialization and from then on only used in the ISR.
If you need to access a variable from both the main program and the ISR, this gets complicated: You'll have to ensure it is only accessed inside critical sections and the relevant compiler barriers are in place. Be careful, it is super easy to do this wrong.
// Timer Configuration:
// - WGM = 4: CTC mode (Clear Timer on Compare Match)
// - Prescaler 256
// - OCR1A = 15624
//
// => F = 16 MHz / (256 * (1 + 15624)) = 4 Hz
// (^ this formula I deduced from reading the datasheet)
//
// => The LED will toggle at 4 Hz, thus it blinks at 2 Hz
let tmr1 = dp.TC1;
tmr1.tccr1a.write(|w| w.wgm1().bits(0b00));
tmr1.tccr1b.write(|w| w.cs1().prescale_256().wgm1().bits(0b01));
tmr1.ocr1a.write(|w| unsafe { w.bits(15624) });
// Enable the timer interrupt
tmr1.timsk1.write(|w| w.ocie1a().set_bit());
The comment pretty much explains what I'm doing, for more details there are tons of guides online and the datasheet also explains all the details. The most important part is actually enabling the relevant timer interrupt here.
// Enable interrupts globally
avr_device::interrupt::enable();
When the CPU resets, interrupts are globally disabled. Here we enable them after all setup is done. From this point onwards, at any point in the program an interrupt could fire so any shared data access needs to be done very carefully.
loop {
avr_device::asm::sleep();
}
Because the main program has nothing to do in this case, I just let it sleep until any interrupt happens (and then make it immediately go back to sleep again).
#[avr_device::interrupt(atmega32u4)]
fn TIMER1_COMPA() {
// ...
}
This is how to actually define an ISR, similar to the ISR()
C macro. The function name must be the name of a know interrupt. You can look them up here: for ATmega32U4 (Arduino Leonardo) or for ATmega328P (Arduino Uno).
The avr_device::interrupt
macro also needs the name of the CPU and its important to use the right one here. Otherwise you'll get strange results ...
There are a few important things to remember when writing ISRs:
- As long as the ISR is running, nothing else can happen. The main program is stopped and no other interrupt can trigger. Thus, do as little as possible; try to offload any actual work into the main program and just set a signal flag in the ISR.
- The ISR can run at any time when interrupts are enabled. You absolutely must synchronize access to shared data (using critical sections).
- An interrupt firing can usually mean that multiple hardware events might have occured. You should be prepared to handle them all instead of assuming you'll get one interrupt for one event. For example, if interrupts were disabled for a long time, a timer might have overflown multiple times.
from avr-hal.
I have gotten multiple timers working by manually doing the compare within the loop like so:
// enter loop
loop {
// manual compare timer1 : 1Hz
if timer1.tcnt1.read().bits() >= 62499 {
// --------------------------------------------------
// DO SOMETHING AS IF THIS WERE AN ISR FOR TIMER1
// --------------------------------------------------
hardware = handle_timer1_1hz(hardware);
// reset timer
timer1.tcnt1.write(|w| unsafe { w.bits(0) });
}
// manual compare timer0 : 1kHz
if timer0.tcnt0.read().bits() >= 249 {
// --------------------------------------------------
// DO SOMETHING AS IF THIS WERE AN ISR FOR TIMER0
// --------------------------------------------------
hardware = handle_timer0_1k_hz(hardware);
// reset timer
timer0.tcnt0.write(|w| unsafe { w.bits(0) });
}
}
}
This strategy works fine, though I still wonder if there isn't a more elegant way to configure the ISRs.
from avr-hal.
This is a quite new RUST HAL-layer for AVR and all features is not yet implemented.
Help is always wanted.
For delays using timers the issue #9 is planned.
from avr-hal.
Thank you for the detailed write up. I'm beginning to see what you mean about this being complicated. If nothing else, this is useful to force me to learn various aspects of Rust and embedded development.
I have extended my example to follow your advice w/respect to guarding state, etc. See full example for context, but here's the snippet that is currently blocking me. Basically, I want to encapsulate the board/device pins in a global struct that guards access with a critical section. However, I am having a tough time understanding the macro expansion of the Pins
.
I'm following a pattern of encapsulating shared state in an UnsafeCell and then guarding access with critical sections. I'm doing this for: such as LedToStrobe, DisplayCounter, and MultiplexedLEDArray.
Here are my specific comments from this code
/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
This does not work. The problem is...how do I create a default Output Pin
to populate this global/static variable? I can set it in the entrypoint,
but I need to allocate the space here, as I do for the other globals.
Specifically:
Pin::<mode::Output>::new()
I don't see how to create a new generic output pin.
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
struct WrappedHardware(UnsafeCell<Hardware>);
unsafe impl Sync for WrappedHardware {}
static WRAPPED_HARDWARE: WrappedHardware =
WrappedHardware(
UnsafeCell::new(
Hardware{
onboard_led_pin: Pin::<mode::Output>::new(),
pins: arr![Pin::<mode::Output>::new(); 10] }));
*/
The example readme shows the path and the resources that got me here.
from avr-hal.
I'm following a pattern of encapsulating shared state in an UnsafeCell and then guarding access with critical sections. I'm doing this for: such as LedToStrobe, DisplayCounter, and MultiplexedLEDArray.
First of all, you're missing compiler fences here. You should add a
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
to the end of each critical section to ensure that operations won't get reordered.
I don't see how to create a new generic output pin.
You can't. The pins must be taken from the Pins
struct and moved into your global. If there was some kind of Pin::new()
function, this would be unsound, as it would allow creating multiple variables for the same pin.
To create generic pins from the concrete pin types you find in the Pins
struct, you should use the .downgrade()
method. This will give you a variable of the generic Pin
type. Read more about this in the Digital I/O Documentation. As an example, leonardo-blink.rs
uses this to store three pins in an array.
from avr-hal.
@Rahix Thank you for the additional comments.
I spent 2 full days trying to get timers to work. For anyone following, let me just state that this will drive you insane. The timers are incredibly fragile. I have lost count of the ways that I have broken them... and debugging by just looking at a few blinking leds is pretty draining as well.
For anyone interested, I'll paste in my code but I'm not going to spend anymore time on this. Keeping the code in the main thread and just checking the timer values worked well enough.
main.rs
#![no_std]
#![no_main]
#![feature(abi_avr_interrupt)]
use arduino_uno::hal::port;
use arduino_uno::prelude::*;
use arr_macro::arr;
use core::cell::UnsafeCell;
use core::mem;
use panic_halt as _;
use avr_device::interrupt;
use mseg_lib::{cmap, bits};
/// ------------------------------------------------------------------------
/// Consts
/// Modify these values based on the number of LEDs:
/// ------------------------------------------------------------------------
/// MAX_VALUE : if 1 LED, then this would be 10, 2 leds then 100, 3 leds then 1000.
/// This is obviously pow(10, NUM_LEDS).
const MAX_VALUE: usize = 100;
/// The number of LEDS in the display. Common numbers are 1, 2, 6, etc. Whatever
/// you have wired up.
const NUM_LEDS: usize = 2;
/// Always gonna be 10
const BASE: usize = 10usize;
/// This is where you wire up the LED segments to the GPIO pins.
const LEDS_COMMON_CATHODES: [usize; 8] = [2, 3, 4, 5, 6, 7, 8, 9];
const LED_ANODES: [usize; 2] = [10, 11];
/// ------------------------------------------------------------------------
/// State
/// ------------------------------------------------------------------------
#[derive(Debug, Clone, Copy)]
struct State {
_led_to_strobe_index: usize,
_display_counter: usize,
}
impl State {
pub fn increment_led_to_strobe(&self) -> usize {
(self._led_to_strobe_index + 1) % NUM_LEDS
}
pub fn increment_counter(&self) -> usize {
(self._display_counter + 1) % MAX_VALUE
}
pub fn led_to_strobe(&self) -> usize {
self._led_to_strobe_index
}
pub fn counter(&self) -> usize {
self._display_counter
}
pub fn digits(&self) -> [u8; NUM_LEDS] {
let mut num = self._display_counter;
let mut result = [0; NUM_LEDS];
// extract each digit from the count up counter and update the display value accordingly
for i in 0..NUM_LEDS {
result[i] = (num % BASE) as u8;
num /= BASE
}
result
}
}
struct GlobalState(UnsafeCell<State>);
impl GlobalState {
pub fn get(&self, _cs: &avr_device::interrupt::CriticalSection) -> State {
unsafe {
let state = *self.0.get();
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
state
}
}
pub fn update(&self, new_state: State, _cs: &avr_device::interrupt::CriticalSection) {
unsafe {
*self.0.get() = new_state;
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
};
}
}
const GLOBAL_STATE_INIT: GlobalState = GlobalState(
UnsafeCell::new(
State {
_led_to_strobe_index: 0,
_display_counter: 0,
}));
unsafe impl Sync for GlobalState {}
/// ------------------------------------------------------------------------
/// Statics
/// ------------------------------------------------------------------------
static mut LED: mem::MaybeUninit<port::Pin<port::mode::Output>> = mem::MaybeUninit::uninit();
static mut PINS: mem::MaybeUninit<[port::Pin<port::mode::Output>; 10]> = mem::MaybeUninit::uninit();
static GLOBAL_STATE: GlobalState = GLOBAL_STATE_INIT;
#[arduino_uno::entry]
fn main() -> ! {
configure_hardware();
unsafe {
// !!!COMPILER FENCE!!!
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
// Enable interrupts globally
avr_device::interrupt::enable();
}
loop {
avr_device::asm::sleep();
}
}
fn configure_hardware() {
let peripherals = arduino_uno::Peripherals::take().unwrap();
let mut pins = arduino_uno::Pins::new(
peripherals.PORTB,
peripherals.PORTC,
peripherals.PORTD,
);
let led = pins.d13.into_output(&mut pins.ddr).downgrade();
// let mut pd0 = pins.d0.into_output(&mut pins.ddr); // rx
// let mut pd1 = pins.d1.into_output(&mut pins.ddr); // tx
let pd02 = pins.d2.into_output(&mut pins.ddr).downgrade();
let pd03 = pins.d3.into_output(&mut pins.ddr).downgrade();
let pd04 = pins.d4.into_output(&mut pins.ddr).downgrade();
let pd05 = pins.d5.into_output(&mut pins.ddr).downgrade();
let pd06 = pins.d6.into_output(&mut pins.ddr).downgrade();
let pd07 = pins.d7.into_output(&mut pins.ddr).downgrade();
let pd08 = pins.d8.into_output(&mut pins.ddr).downgrade();
let pd09 = pins.d9.into_output(&mut pins.ddr).downgrade();
let pd10 = pins.d10.into_output(&mut pins.ddr).downgrade();
let pd11 = pins.d11.into_output(&mut pins.ddr).downgrade();
unsafe {
// SAFETY: Interrupts are not enabled at this point so we can safely write the global (static)
// variable here. A memory barrier afterwards ensures the compiler won't reorder this
// after any operation that enables interrupts.
LED = mem::MaybeUninit::new(led);
PINS = mem::MaybeUninit::new([pd02, pd03, pd04, pd05, pd06, pd07, pd08, pd09, pd10, pd11]);
// !!!COMPILER FENCE!!!
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
// // TIMER0 : 1kHz
// // # ------------------------------------
// // # interrupt frequency: f
// // # prescaler: p
// // # compare match register value: cmr
// // # timers: t
// // # ------------------------------------
// // "f: 1000, p: 64, cmr: 249.0, t: [0, 2]"
let timer0 = peripherals.TC0;
timer0.tccr0a.write(|w| unsafe { w.bits(0) });
timer0.tccr0b.write(|w| w.cs0().prescale_64());
timer0.tcnt0.write(|w| unsafe { w.bits(0) });
// Enable the timer interrupt
timer0.timsk0.write(|w| w.ocie0a().set_bit());
// TIMER1 : 1Hz
// # ------------------------------------
// # interrupt frequency: f
// # prescaler: p
// # compare match register value: cmr
// # timers: t
// # ------------------------------------
// "f: 1, p: 256, cmr: 62499.0, t: [1]"
let timer1 = peripherals.TC1;
timer1.tccr1a.write(|w| unsafe { w.bits(0) });
timer1.tccr1b.write(|w| w.cs1().prescale_256());
timer1.ocr1a.write(|w| unsafe { w.bits(62499) });
timer1.tcnt1.write(|w| unsafe { w.bits(0) });
// Enable the timer interrupt
timer1.timsk1.write(|w| w.ocie1a().set_bit());
}
#[avr_device::interrupt(atmega328p)]
fn TIMER0_COMPA() {
let pins = unsafe {
&mut *PINS.as_mut_ptr()
};
let state = interrupt::free(|cs| GLOBAL_STATE.get(cs));
let strobe_idx = state.led_to_strobe();
// set COM HIGH for the led to strobe
let target_led_com = LED_ANODES[strobe_idx];
for com in LED_ANODES.iter() {
let xcom = *com - 2;
if xcom == target_led_com {
if pins[xcom].is_set_low().void_unwrap() {
pins[xcom].set_high().void_unwrap();
}
} else {
if pins[xcom].is_set_high().void_unwrap() {
pins[xcom].set_low().void_unwrap();
}
}
}
// set the segments to high and low for this digit
let digit = state.digits()[strobe_idx];
let segments = cmap::segments(digit);
for x in 0..8{
let segment = bits::get(segments, x);
let cathode = LEDS_COMMON_CATHODES[x];
let xcat = cathode - 2;
if segment && pins[xcat].is_set_low().void_unwrap(){
pins[xcat].set_high().void_unwrap();
}else if !segment && pins[xcat].is_set_high().void_unwrap() {
pins[xcat].set_low().void_unwrap();
}
}
let next_state = State { _led_to_strobe_index: state.increment_led_to_strobe(), ..state };
interrupt::free(|cs| GLOBAL_STATE.update(next_state, cs));
}
#[avr_device::interrupt(atmega328p)]
fn TIMER1_COMPA() {
let state = interrupt::free(|cs| GLOBAL_STATE.get(cs));
let next_state = State { _display_counter: state.increment_counter(), ..state };
interrupt::free(|cs| GLOBAL_STATE.update(next_state, cs));
let led = unsafe {
&mut *LED.as_mut_ptr()
};
led.toggle().void_unwrap();
}
bits.rs
///! TODO: should I put in bounds checking? Right now the index could
///! TODO: rollover if we leftshift more than the number bits in T.
///!
///! Notes/Links:
///! * https://immunant.com/blog/2020/01/bitfields/
///! * https://doc.rust-lang.org/reference/type-layout.html
///! * https://stackoverflow.com/questions/36061560/can-i-take-a-byte-array-and-deserialize-it-into-a-struct
/// Set a single bit in a bit array.
///
/// Examples:
///
/// ```
/// use mseg_lib::bits::set;
///
/// let b1 = set(0b00000000, 0, true);
/// assert_eq!(0b00000001, b1);
///
/// let b2 = set(b1, 7, true);
/// assert_eq!(0b10000001, b2);
///
/// let b3 = set(b2, 7, true);
/// assert_eq!(0b10000001, b3);
///
/// let b4 = set(b2, 7, false);
/// assert_eq!(0b00000001, b4);
/// ```
pub fn set(input: u8, index: usize, value: bool) -> u8 {
let mask: u8 = 1 << index;
return if value {
input | mask
} else {
input & !(mask)
};
}
/// Retrieve a single bit from a bit array.
///
/// Examples:
///
/// ```
/// use mseg_lib::bits::get;
///
/// let b1 = get(0b00000001, 0);
/// assert_eq!(b1, true);
///
/// let b2 = get(0b00000001, 1);
/// assert_eq!(b2, false);
/// ```
pub fn get(input: u8, index: usize) -> bool {
let mask: u8 = 1 << index;
let mut result = input & mask;
result >>= index;
return if result == 1 { true } else { false };
}
#[cfg(test)]
mod tests {
#[test]
/// set each bit on, one at a time, and verify that all the other bits are off.
fn test_bits_set() {
let b: u8 = 0b00000000;
use super::*;
for i in 0..8 {
for j in 0..8 {
let actual = get(set(b, i, true), j);
if i == j {
assert_eq!(true, actual);
} else {
assert_eq!(false, actual);
}
}
}
}
}
cmap.rs
///! `cmap` module handles character maps.
///!
///!
///! Generic 8 Segment LED
///!
///! +--------------------------------------------------------------+
///! | |
///! | |
///! | A |
///! | |----------------------------| |
///! | |----------------------------| |
///! | +--+ +--+ |
///! | | | | | |
///! | | | | | |
///! | | | | | |
///! | | | | | B |
///! | F | | | | |
///! | | | | | |
///! | | | | | |
///! | +--+ G +--+ |
///! | |----------------------------| |
///! | |----------------------------| |
///! | +--+ +--+ |
///! | | | | | |
///! | | | | | |
///! | | | | | |
///! | E | | | | C |
///! | | | | | |
///! | | | | | |
///! | | | | | |
///! | +--+ +--+ |
///! | |----------------------------| +-+ |
///! | |----------------------------| +-+ DP |
///! | D |
///! | |
///! +--------------------------------------------------------------+
///!
// segment indexes
pub const SEGMENT_INDEX_A: u8 = 0;
pub const SEGMENT_INDEX_B: u8 = 1;
pub const SEGMENT_INDEX_C: u8 = 2;
pub const SEGMENT_INDEX_D: u8 = 3;
pub const SEGMENT_INDEX_E: u8 = 4;
pub const SEGMENT_INDEX_F: u8 = 5;
pub const SEGMENT_INDEX_G: u8 = 6;
pub const SEGMENT_INDEX_DP: u8 = 7;
/// led segments
///
/// remember these are pulled in reverse order, so
/// the mapping is:
///
/// DP G F E D C B A
pub fn segments(n: u8) -> u8 {
return match n {
0 => 0b00111111,
1 => 0b00000110,
2 => 0b01011011,
3 => 0b01001111,
4 => 0b01100110,
5 => 0b01101101,
6 => 0b01111101,
7 => 0b00000111,
8 => 0b01111111,
9 => 0b01100111,
_ => 0b11111111,
}
}
led.rs
///! led module
///!
///! # Links:
///! * https://doc.rust-lang.org/book/ch10-02-traits.html
///! * https://doc.rust-lang.org/rust-by-example/trait.html
///! * https://doc.rust-lang.org/stable/rust-by-example/trait/impl_trait.html
///! * https://doc.rust-lang.org/std/keyword.impl.html
/// SM4105W6 Eight Segment LED
/// https://www.velleman.eu/downloads/29/infosheets/vmp502_sma42056etc.pdf
///
/// Hardware Pins
/// A B C D E F G DP COM
/// 7 6 4 2 1 9 10 5 3/8
///
/// In the array, the pins are indexed as:
/// A B C D E F G DP COM
/// 0 1 2 3 4 5 6 7 8
#[derive(Clone, Copy, Debug)]
pub struct EightSegmentLEDCommonAnode {
// pins (0-7 are A-DP, 8 is com)
pub pins: [usize; 9],
}
impl EightSegmentLEDCommonAnode {
pub fn pins(&self) -> [usize;9] {
self.pins
}
pub fn com(&self) -> usize {
self.pins[8]
}
}
Links
- https://docs.rs/rust-libcore/0.0.3/core/sync/atomic/fn.compiler_fence.html
- https://doc.rust-lang.org/core/sync/atomic/fn.compiler_fence.html
from avr-hal.
I spent 2 full days trying to get timers to work. For anyone following, let me just state that this will drive you insane. The timers are incredibly fragile. I have lost count of the ways that I have broken them... and debugging by just looking at a few blinking leds is pretty draining as well.
Oh, I know this all too well! Props to you for going this far though!
For anyone interested, I'll paste in my code but I'm not going to spend anymore time on this. Keeping the code in the main thread and just checking the timer values worked well enough.
Just a few comments about your code and regarding this: Doing all the work in ISRs is really not ideal. AVR does not have interrupt nesting so during an ISR absolutely nothing can happen in the system. Ideally, ISRs should be as short as possible and any work should happen in the main thread. This also helps avoid all the nasty synchronization issues ... A common pattern is a global flag marker that is set by the ISR and reset by the main thread:
(not tested)
use core::sync::atomic;
static TMR_OVERFLOW: atomic::AtomicBool = atomic::AtomicBool::new(false);
#[avr_device::interrupt(atmega328p)]
fn TIMER1_COMPA() {
TMR_OVERFLOW.store(true, atomic::Ordering::SeqCst);
}
#[arduino_uno::entry]
fn main() -> ! {
// ...
loop {
let tmr_overflow = avr_device::interrupt::free(|_cs| {
// With interrupts disabled, check and reset the flag. If interrupts
// were enabled, the ISR could run in between the check and reset, leading to
// us loosing a timer overflow.
if TMR_OVERFLOW.load(atomic::Ordering::SeqCst) {
TMR_OVERFLOW.store(false, atomic::Ordering::SeqCst);
true
} else {
false
}
});
if tmr_overflow {
// the code you currently have in the ISR goes here.
}
}
}
from avr-hal.
Related question, in that using I2C is going to use interrupts under the covers (I think).
Q1: How to write the code for a slave
that listens on an address?
Both the sample code and the I2C API docs seem to be from the perspective of the master:
For context, I am attempting to implement a simple master/slave example from here: http://gammon.com.au/i2c
from avr-hal.
Related question, in that using I2C is going to use interrupts under the covers (I think).
No, the I2C (also called TWI an AVR MCUs) master driver currently does not enable any interrupts at all. Slave operation should also be possible without interrupts. Though in practice, usually you'd want the core to go to sleep while waiting for a bus transaction, which of course will need to make use of an interrupt.
Both the sample code and the I2C API docs seem to be from the perspective of the master:
There is no I2C slave driver in avr-hal
as of now. You'd need to manually do this with direct register accesses (or write your own I2C slave driver).
from avr-hal.
There is no I2C slave driver in
avr-hal
as of now. You'd need to manually do this with direct register accesses (or write your own I2C slave driver).
If you are really interested in this we could work together on adding an I2C slave driver to avr-hal
though. Just let me know!
from avr-hal.
Absolutely, let's do this! Ok, so I'll start brainstorming:
- Are there other platforms that we could look at and model after?
- What sections of the AVR spec should I look at closely?
- What's the overall game plan?
My sense of this is that the slave needs to:
- set an interrupt on the SDA pin being pulled low
- the interrupt wakes the CPU and sets a flag
- main thread loop checks flag, if set, invokes reader function with user defined callback
- callback is given an iterator over a stream
Conceptually, it looks simple enough when looking at Arduino code: Wire.onReceive (receiveEvent);
from avr-hal.
Right, let's move this to a separate issue though: #90
from avr-hal.
Related Issues (20)
- Panic while trying to write to serial using `ufmt::uwriteln` HOT 4
- Add configurable board options to ravedude HOT 3
- Get pin number HOT 6
- Program freezes if compiled in debug mode.
- ravedude should check rust target against selected MCU HOT 3
- Document clock type design in more detail HOT 1
- cargo build - error[E0658] - proc_macro::Literal::byte_character(byte) HOT 9
- can't find crate for `core` HOT 12
- cargo build - error[E0658] - proc_macro::Literal::byte_character(byte) HOT 4
- Very weird error when compiling a very basic program: __addsf3 multiple defenitions HOT 9
- Ravedude freezes on programmer uploading HOT 5
- Arduino Nano: avrdude error: programmer is not responding HOT 11
- Watchdog intterupt mode HOT 1
- [Solved] Cannot find `pwm` in `embedded_hal` HOT 2
- peripherals type alias HOT 2
- Enhance `avr_hal_generic::renamed_pins! {}` to also generate type aliases for each pin
- tier 3 target and broken HOT 4
- Utilising the `atmega2560`'s "USART in SPI Mode" HOT 10
- Issue with math calculations with floats to serial port output HOT 5
- Incorrect ADC readings after toolchain bump HOT 14
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from avr-hal.