Skip to content
Snippets Groups Projects
Commit 9933bf67 authored by Johannes Sjölund's avatar Johannes Sjölund
Browse files

Add macro for usart dma formatted print

parent 126193ea
No related branches found
No related tags found
No related merge requests found
...@@ -7,13 +7,14 @@ ...@@ -7,13 +7,14 @@
#![no_std] #![no_std]
extern crate cortex_m_rtfm as rtfm; extern crate cortex_m_rtfm as rtfm;
#[macro_use]
extern crate f4; extern crate f4;
extern crate heapless; extern crate heapless;
use core::fmt::Write; use core::fmt::Write;
use core::ops::Deref; use core::ops::Deref;
use f4::Serial; use f4::Serial;
use f4::Writer as w; use f4::U8Writer;
use f4::prelude::*; use f4::prelude::*;
use f4::dma::{Buffer, Dma1Channel5, Dma1Channel6}; use f4::dma::{Buffer, Dma1Channel5, Dma1Channel6};
use f4::time::Hertz; use f4::time::Hertz;
...@@ -30,6 +31,12 @@ const MAX_RX_LEN: usize = 1; ...@@ -30,6 +31,12 @@ const MAX_RX_LEN: usize = 1;
const BAUD_RATE: Hertz = Hertz(115_200); const BAUD_RATE: Hertz = Hertz(115_200);
enum CmdType {
Greeting,
Unknown,
None,
}
app! { app! {
device: f4::stm32f40x, device: f4::stm32f40x,
...@@ -37,7 +44,7 @@ app! { ...@@ -37,7 +44,7 @@ app! {
static CMD_BUFFER: Vec<u8, [u8; MAX_CMD_LEN]> = Vec::new([0; MAX_CMD_LEN]); static CMD_BUFFER: Vec<u8, [u8; MAX_CMD_LEN]> = Vec::new([0; MAX_CMD_LEN]);
static RX_BUFFER: Buffer<[u8; MAX_RX_LEN], Dma1Channel5> = Buffer::new([0; MAX_RX_LEN]); static RX_BUFFER: Buffer<[u8; MAX_RX_LEN], Dma1Channel5> = Buffer::new([0; MAX_RX_LEN]);
static TX_BUFFER: Buffer<[u8; MAX_TX_LEN], Dma1Channel6> = Buffer::new([0; MAX_TX_LEN]); static TX_BUFFER: Buffer<[u8; MAX_TX_LEN], Dma1Channel6> = Buffer::new([0; MAX_TX_LEN]);
static CNT: u8 = 1; static CNT: u8 = 0;
}, },
tasks: { tasks: {
...@@ -54,40 +61,12 @@ app! { ...@@ -54,40 +61,12 @@ app! {
}, },
} }
fn init(p: init::Peripherals, r: init::Resources) {
// Set clock to higher than default in order to test it works
clock::set_84_mhz(&p.RCC, &p.FLASH);
// There is no need to claim() resources in the init.
// Start the serial port
let serial = Serial(p.USART2);
serial.init(BAUD_RATE.invert(), Some(p.DMA1), p.GPIOA, p.RCC);
// Send a welcome message by borrowing transmit buffer and writing a formatted string into it
let x = 1.0;
write!(
w::out(&mut r.TX_BUFFER.borrow_mut()[..MAX_TX_LEN]),
"Hello, world! {}\r\n",
x
).unwrap();
serial.write_all(p.DMA1, r.TX_BUFFER).unwrap();
// Listen to serial input on the receive DMA
serial.read_exact(p.DMA1, r.RX_BUFFER).unwrap();
}
fn idle() -> ! {
loop {
rtfm::wfi();
}
}
// Interrupt for serial receive DMA // Interrupt for serial receive DMA
fn rx_done(t: &mut Threshold, mut r: DMA1_STREAM5::Resources) { fn rx_done(t: &mut Threshold, mut r: DMA1_STREAM5::Resources) {
use rtfm::Resource; use rtfm::Resource;
let mut byte: u8 = 0; let mut byte: u8 = 0;
let mut say_hello = false; let mut cmd_type: CmdType = CmdType::None;
r.RX_BUFFER.claim(t, |rx, t| { r.RX_BUFFER.claim(t, |rx, t| {
// We need to unlock the DMA to use it again in the future // We need to unlock the DMA to use it again in the future
...@@ -117,45 +96,38 @@ fn rx_done(t: &mut Threshold, mut r: DMA1_STREAM5::Resources) { ...@@ -117,45 +96,38 @@ fn rx_done(t: &mut Threshold, mut r: DMA1_STREAM5::Resources) {
r.CMD_BUFFER.claim_mut(t, |cmd, _| { r.CMD_BUFFER.claim_mut(t, |cmd, _| {
if byte == b'\r' { if byte == b'\r' {
// End of command // End of command
match &***cmd { cmd_type = match &***cmd {
b"hi" | b"Hi" => { b"hi" | b"Hi" => CmdType::Greeting,
say_hello = true; _ => {
if cmd.len() == 0 {
CmdType::None // Empty string
} else {
CmdType::Unknown // Unknown string
} }
_ => {}
} }
};
cmd.clear(); cmd.clear();
} else { } else {
if cmd.push(byte).is_err() { if cmd.push(byte).is_err() {
// Error: buffer full // Error: command buffer is full
// KISS: we just clear the buffer when it gets full
cmd.clear(); cmd.clear();
} }
} }
}); });
// If user wrote 'hi' and pressed enter, respond appropriately // If user wrote 'hi' and pressed enter, respond appropriately
if say_hello { match cmd_type {
CmdType::Greeting => {
// Increment 'hi' counter // Increment 'hi' counter
**r.CNT = **r.CNT + 1; **r.CNT = (**r.CNT).wrapping_add(1);
let cnt: u8 = **r.CNT; let cnt: u8 = **r.CNT;
// Claim the transmit buffer and write a formatted string into it // Print a response using DMA
r.TX_BUFFER.claim_mut(t, |tx, _| { uprint!(t, r.USART2, r.DMA1, r.TX_BUFFER, "Hi counter {}!\r\n", cnt);
write!( }
w::out(&mut (*tx).deref().borrow_mut()[..MAX_TX_LEN]), CmdType::Unknown => {
"Hello, there! {}\r\n", // Unknown command
cnt uprint!(t, r.USART2, r.DMA1, r.TX_BUFFER, "That's no greeting.\r\n");
).unwrap(); }
}); _ => {}
// Transmit the response
r.TX_BUFFER.claim(t, |tx, t| {
r.DMA1.claim(t, |dma, t| {
r.USART2.claim(t, |usart, _| {
let serial = Serial(&**usart);
serial.write_all(dma, tx).unwrap();
});
});
});
// r.CNT.claim_mut(t, |cnt,_| cnt.wrapping_add(1));
} }
} }
...@@ -172,3 +144,31 @@ fn tx_done(t: &mut Threshold, r: DMA1_STREAM6::Resources) { ...@@ -172,3 +144,31 @@ fn tx_done(t: &mut Threshold, r: DMA1_STREAM6::Resources) {
array.borrow_mut()[..MAX_TX_LEN].clone_from_slice(&[0; MAX_TX_LEN]); array.borrow_mut()[..MAX_TX_LEN].clone_from_slice(&[0; MAX_TX_LEN]);
}); });
} }
fn init(p: init::Peripherals, r: init::Resources) {
// Set clock to higher than default in order to test that it works
clock::set_84_mhz(&p.RCC, &p.FLASH);
// Start the serial port
let serial = Serial(p.USART2);
serial.init(BAUD_RATE.invert(), Some(p.DMA1), p.GPIOA, p.RCC);
// FIXME: We cannot use the uprint macro in the init since it needs Resources
// and Threshold...
// Send a welcome message by borrowing a slice of the transmit buffer and
// writing a formatted string into it.
write!(
U8Writer::new(&mut r.TX_BUFFER.borrow_mut()[..MAX_TX_LEN]),
"Hello, world! Say hi to me!\r\n",
).unwrap();
serial.write_all(p.DMA1, r.TX_BUFFER).unwrap();
// Listen to serial input on the receive DMA
serial.read_exact(p.DMA1, r.RX_BUFFER).unwrap();
}
fn idle() -> ! {
loop {
rtfm::wfi();
}
}
...@@ -50,7 +50,7 @@ use frequency::*; ...@@ -50,7 +50,7 @@ use frequency::*;
pub use hal::prelude; pub use hal::prelude;
pub use serial::Serial; pub use serial::Serial;
pub use serial::Writer; pub use serial::U8Writer;
pub use timer::{Channel, Timer}; pub use timer::{Channel, Timer};
pub use pwm::Pwm; pub use pwm::Pwm;
pub use capture::Capture; pub use capture::Capture;
......
...@@ -23,37 +23,6 @@ use dma::{self, Buffer, Dma1Channel5, Dma1Channel6}; ...@@ -23,37 +23,6 @@ use dma::{self, Buffer, Dma1Channel5, Dma1Channel6};
use core::fmt; use core::fmt;
///
pub struct Writer<'a> {
buf: &'a mut [u8],
offset: usize,
}
impl<'a> Writer<'a> {
///
pub fn out(buf: &'a mut [u8]) -> Self {
Writer {
buf: buf,
offset: 0,
}
}
}
impl<'a> fmt::Write for Writer<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes = s.as_bytes();
// Skip over already-copied data
let remainder = &mut self.buf[self.offset..];
// Make the two slices the same length
let remainder = &mut remainder[..bytes.len()];
// Copy
remainder.copy_from_slice(bytes);
// Increment offset by number of copied bytes
self.offset += bytes.len();
Ok(())
}
}
/// Specialized `Result` type /// Specialized `Result` type
pub type Result<T> = ::core::result::Result<T, nb::Error<Error>>; pub type Result<T> = ::core::result::Result<T, nb::Error<Error>>;
...@@ -153,9 +122,13 @@ where ...@@ -153,9 +122,13 @@ where
if usart.get_type_id() == TypeId::of::<USART2>() { if usart.get_type_id() == TypeId::of::<USART2>() {
// we don't care about the speed register atm // we don't care about the speed register atm
// DM00102166 // DM00102166
// AF7, Table 9
// PA2 and PA3 is connected to USART2 TX and RX respectively // PA2 and PA3 is connected to USART2 TX and RX respectively
// Alternate function AF7, Table 9
gpio.afrl.modify(|_, w| w.afrl2().bits(7).afrl3().bits(7)); gpio.afrl.modify(|_, w| w.afrl2().bits(7).afrl3().bits(7));
// Highest output speed
gpio.ospeedr
.modify(|_, w| w.ospeedr2().bits(0b11).ospeedr3().bits(0b11));
// RM0368 8.3 Table 23
gpio.moder gpio.moder
.modify(|_, w| w.moder2().bits(2).moder3().bits(2)); .modify(|_, w| w.moder2().bits(2).moder3().bits(2));
} }
...@@ -322,7 +295,9 @@ where ...@@ -322,7 +295,9 @@ where
} else if sr.rxne().bit_is_set() { } else if sr.rxne().bit_is_set() {
// NOTE(read_volatile) the register is 9 bits big but we'll only // NOTE(read_volatile) the register is 9 bits big but we'll only
// work with the first 8 bits // work with the first 8 bits
Ok(unsafe { ptr::read_volatile(&usart2.dr as *const _ as *const u8) }) Ok(unsafe {
ptr::read_volatile(&usart2.dr as *const _ as *const u8)
})
} else { } else {
Err(nb::Error::WouldBlock) Err(nb::Error::WouldBlock)
} }
...@@ -421,3 +396,76 @@ impl<'a> Serial<'a, USART2> { ...@@ -421,3 +396,76 @@ impl<'a> Serial<'a, USART2> {
Ok(()) Ok(())
} }
} }
///
pub struct U8Writer<'a> {
buf: &'a mut [u8],
offset: usize,
}
impl<'a> U8Writer<'a> {
///
pub fn new(buf: &'a mut [u8]) -> Self {
U8Writer {
buf: buf,
offset: 0,
}
}
}
impl<'a> fmt::Write for U8Writer<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes = s.as_bytes();
// Skip over already-copied data
let remainder = &mut self.buf[self.offset..];
// Make the two slices the same length
let remainder = &mut remainder[..bytes.len()];
// Copy
remainder.copy_from_slice(bytes);
// Increment offset by number of copied bytes
self.offset += bytes.len();
Ok(())
}
}
/// Macro for printing formatted strings over serial through DMA.
/// Uses the corted-m-rtfm resource model and can thus not be used
/// outside rtfm tasks.
#[macro_export]
macro_rules! uprint {
($T:ident, $USART:expr, $DMA:expr, $TX_BUFFER:expr, $s:expr) => {
use core::fmt::Write;
// Claim the transmit buffer and write a string literal into it
$TX_BUFFER.claim_mut($T, |tx, _| {
let len = (*tx).deref().borrow().len();
let buf = &mut (*tx).deref().borrow_mut();
write!(U8Writer::new(&mut buf[..len]), $s).unwrap();
});
// Transmit the contents of the buffer using DMA
$TX_BUFFER.claim($T, |tx, t| {
$DMA.claim(t, |dma, t| {
$USART.claim(t, |usart, _| {
let serial = Serial(&**usart);
serial.write_all(dma, tx).unwrap();
});
});
});
};
($T:ident, $USART:expr, $DMA:expr, $TX_BUFFER:expr, $($arg:tt)* ) => {
use core::fmt::Write;
// Claim the transmit buffer and write a formatted string into it
$TX_BUFFER.claim_mut($T, |tx, _| {
let len = (*tx).deref().borrow().len();
let buf = &mut (*tx).deref().borrow_mut();
write!(U8Writer::new(&mut buf[..len]), $($arg)*).unwrap();
});
// Transmit the contents of the buffer using DMA
$TX_BUFFER.claim($T, |tx, t| {
$DMA.claim(t, |dma, t| {
$USART.claim(t, |usart, _| {
let serial = Serial(&**usart);
serial.write_all(dma, tx).unwrap();
});
});
});
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment