Skip to content

Commit 927c589

Browse files
committed
new interrupt driven serial echo example
1 parent b782375 commit 927c589

File tree

1 file changed

+121
-0
lines changed

1 file changed

+121
-0
lines changed

examples/serial_echo_irq.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//! Interrupt Driven Serial Echo Example
2+
3+
#![no_main]
4+
#![no_std]
5+
#![deny(unsafe_code)]
6+
#![allow(non_camel_case_types)]
7+
8+
use panic_halt as _;
9+
use nb::block;
10+
use core::cell::RefCell;
11+
12+
use cortex_m::interrupt::Mutex;
13+
use cortex_m_rt::entry;
14+
15+
use stm32f0xx_hal as hal;
16+
use hal::{
17+
pac::{ self, interrupt, Interrupt, USART1 },
18+
prelude::*,
19+
serial::Serial, gpio::{gpioa::{PA2, PA15}, Alternate, AF1, Output, PushPull, gpiob::PB3},
20+
delay::Delay
21+
};
22+
23+
// setup some types for our globally shared resources
24+
type LED_PIN = PB3<Output<PushPull>>;
25+
type SERIAL = Serial<USART1, PA2<Alternate<AF1>>, PA15<Alternate<AF1>>>;
26+
27+
/*
28+
Create our global variables:
29+
30+
We use a Mutex because Mutexes require a CriticalSection
31+
context in order to be borrowed. Since CriticalSection
32+
contexts cannot overlap (by definition) we can rest assured
33+
that the resource inside the Mutex will not violate
34+
the RefMut's runtime borrowing rules (Given that we do not
35+
try to borrow the RefMute more than once per CriticalSection).
36+
*/
37+
static LED: Mutex<RefCell<Option<LED_PIN>>> = Mutex::new(RefCell::new(None));
38+
static SER_PORT: Mutex<RefCell<Option<SERIAL>>> = Mutex::new(RefCell::new(None));
39+
40+
// some helper macros to ensure safe usage of our globals
41+
macro_rules! init_global {
42+
($CS:ident, $GLOBAL:ident, $VAL:expr) => {
43+
*$GLOBAL.borrow($CS).borrow_mut() = Some($VAL);
44+
};
45+
}
46+
47+
macro_rules! with_global_mut {
48+
($CS:ident, $GLOBAL:ident, $LOCAL:ident, $CODE:block) => {
49+
if let Some($LOCAL) = $GLOBAL.borrow($CS).borrow_mut().as_mut() {
50+
$CODE
51+
} else {
52+
panic!();
53+
}
54+
};
55+
}
56+
57+
// a helper macro to generalize the initialization of a UART
58+
macro_rules! setup_uart {
59+
( $CS:ident, $DP:ident, $RCC:ident, $GPIOx:ident ) => {
60+
let (tx, rx) =
61+
(
62+
$GPIOx.pa2.into_alternate_af1($CS),
63+
$GPIOx.pa15.into_alternate_af1($CS),
64+
);
65+
66+
init_global!($CS, SER_PORT, Serial::usart1($DP.USART1, (tx, rx), 9_600.bps(), &mut $RCC));
67+
with_global_mut!($CS, SER_PORT, ser, {
68+
ser.listen(hal::serial::Event::Rxne);
69+
});
70+
};
71+
}
72+
73+
#[entry]
74+
fn main() -> ! {
75+
let dp = pac::Peripherals::take().unwrap(); // might as well panic if this doesn't work
76+
let cp = cortex_m::peripheral::Peripherals::take().unwrap();
77+
let mut flash = dp.FLASH;
78+
let mut rcc = dp.RCC.configure().sysclk(48.mhz()).freeze(&mut flash);
79+
80+
let gpioa = dp.GPIOA.split(&mut rcc);
81+
let gpiob = dp.GPIOB.split(&mut rcc);
82+
83+
let mut delay = Delay::new(cp.SYST, &rcc);
84+
85+
cortex_m::interrupt::free(|cs| {
86+
setup_uart!(cs, dp, rcc, gpioa);
87+
88+
init_global!(cs, LED, gpiob.pb3.into_push_pull_output(cs));
89+
});
90+
91+
#[allow(unsafe_code)] // just this once ;)
92+
unsafe { cortex_m::peripheral::NVIC::unmask(Interrupt::USART1); }
93+
94+
loop {
95+
cortex_m::interrupt::free(|cs| {
96+
with_global_mut!(cs, LED, led, {
97+
led.toggle().ok();
98+
});
99+
});
100+
101+
delay.delay_ms(1_000u16);
102+
}
103+
}
104+
105+
#[interrupt]
106+
fn USART1() {
107+
cortex_m::interrupt::free(|cs| {
108+
with_global_mut!(cs, SER_PORT, ser, {
109+
if let Ok(data) = block!(ser.read()) {
110+
block!(ser.write(data)).ok();
111+
} else {
112+
/*
113+
Failed to read a byte:
114+
115+
There could be some kind of alignment error or the UART
116+
was disconnected or something.
117+
*/
118+
}
119+
});
120+
});
121+
}

0 commit comments

Comments
 (0)