Skip to content

Instantly share code, notes, and snippets.

@therealprof
Created January 10, 2019 20:33
Show Gist options
  • Save therealprof/643733e996b1c9eae66c39f6e2ce7173 to your computer and use it in GitHub Desktop.
Save therealprof/643733e996b1c9eae66c39f6e2ce7173 to your computer and use it in GitHub Desktop.
#![no_main]
#![no_std]
use panic_halt;
use stm32f0xx_hal as hal;
use cortex_m_rt::entry;
use crate::hal::gpio::{gpiof::PF0, gpiof::PF1, Alternate, AF1};
use crate::hal::i2c::*;
use crate::hal::prelude::*;
use crate::hal::serial::Serial;
use crate::hal::stm32::{self, interrupt, Interrupt::USART2};
use core::cell::RefCell;
use core::fmt::Write;
use core::ops::{DerefMut, Deref};
use cortex_m::{interrupt::Mutex, peripheral::Peripherals};
// Make some peripherals globally available
struct MyShared {
i2c: hal::i2c::I2c<stm32::I2C1, PF1<Alternate<AF1>>, PF0<Alternate<AF1>>>,
rx: hal::serial::Rx<stm32::USART2>,
tx: hal::serial::Tx<stm32::USART2>,
}
use bare_metal::{CriticalSection};
pub struct Shared<T> {
inner: Mutex<RefCell<Option<T>>>,
}
impl <T> Shared<T> {
/// Creates a new shared value
pub const fn new() -> Self {
Shared {
inner: Mutex::new(RefCell::new(None)),
}
}
pub fn load(&mut self, cs: &CriticalSection, value: T) -> Option<T> {
self.inner.borrow(cs).replace(Some(value))
}
pub fn get<'a>(&'a self, cs: &'a CriticalSection) -> Option<core::cell::Ref<'a, T>> {
match self.inner.borrow(cs).try_borrow().ok() {
Some(inner) => match inner.deref() {
Some(_) => Some(core::cell::Ref::map(inner, |v| v.as_ref().unwrap())),
None => None,
}
None => None,
}
}
pub fn get_mut<'a>(&'a self, cs: &'a CriticalSection) -> Option<core::cell::RefMut<'a, T>> {
match self.inner.borrow(cs).try_borrow_mut().ok() {
Some(mut inner) => match inner.deref_mut() {
Some(_) => Some(core::cell::RefMut::map(inner, |v| v.as_mut().unwrap())),
None => None,
}
None => None,
}
}
}
static SHARED: Shared<MyShared> = Shared::new();
#[entry]
fn main() -> ! {
if let (Some(mut p), Some(cp)) = (stm32::Peripherals::take(), Peripherals::take()) {
cortex_m::interrupt::free(|cs| {
// Configure clock to 8 MHz (i.e. the default) and freeze it
let rcc = p.RCC.configure().freeze(&mut p.FLASH);
let gpioa = p.GPIOA.split(&rcc);
let gpiof = p.GPIOF.split(&rcc);
let mut nvic = cp.NVIC;
let scl = gpiof
.pf1
.into_alternate_af1(cs)
.internal_pull_up(cs, true)
.set_open_drain(cs);
let sda = gpiof
.pf0
.into_alternate_af1(cs)
.internal_pull_up(cs, true)
.set_open_drain(cs);
// Setup I2C1
let i2c = I2c::i2c1(p.I2C1, (scl, sda), 100.khz(), &rcc);
// USART2 at PA2 (TX) and PA15(RX) is connectet to ST-Link
let tx = gpioa.pa2.into_alternate_af1(cs);
let rx = gpioa.pa15.into_alternate_af1(cs);
// Set up our serial port for output
let mut serial = Serial::usart2(p.USART2, (tx, rx), 115_200.bps(), &rcc);
// Enable USART2 interrupt on received input
serial.listen(hal::serial::Event::Rxne);
let (mut tx, rx) = serial.split();
// Enable USART2 interrupt and clear any pending interrupts
nvic.enable(USART2);
cortex_m::peripheral::NVIC::unpend(USART2);
// Print a welcome message
tx.write_str("\r\nWelcome to the I2C scanner. Enter any character to start scan.\r\n")
.ok();
// Move all components under Mutex supervision
SHARED.load(cs, MyShared { i2c, rx, tx });
});
}
loop {
continue;
}
}
// The IRQ handler triggered by a received character in USART buffer, this will conduct our I2C
// scan when we receive anything
#[interrupt]
fn USART2() {
cortex_m::interrupt::free(|cs| {
// Obtain all Mutex protected resources
if let Some(mut shared) = SHARED.get_mut(cs) {
let tx = &mut shared.tx;
let rx = &mut shared.rx;
let i2c = &mut shared.i2c;
/* Read the character that triggered the interrupt from the USART */
while rx.read().is_ok() {}
/* Output address schema for tried addresses */
let _ = tx.write_str("\r\n");
let _ = tx.write_str(
"0 1 2 3 4 5 6 7\r\n",
);
let _ = tx.write_str(
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\r\n",
);
// Execute scanning once for each valid I2C address
for addr in 0..=0x7f {
let res = i2c.write(addr, &[0]);
// If we received a NACK there's no device on the attempted address
let _ = tx.write_str(match res {
Err(Error::NACK) => ".",
_ => "Y",
});
}
let _ = tx.write_str(
"\r\n\r\nScan done.\r\n'Y' means a device was found on the I2C address above.\r\n'.' means no device found on that address.\r\nPlease enter any character to start a new scan.\r\n",
);
}
// Clear interrupt flag
cortex_m::peripheral::NVIC::unpend(USART2);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment