diff --git a/examples/usart2-dma.rs b/examples/usart2-dma.rs index 7d1e2c89131aa7d6e7f4da056c05401ddd186357..305a595809220d31af1cc8bb350658a3f1c035bf 100644 --- a/examples/usart2-dma.rs +++ b/examples/usart2-dma.rs @@ -7,13 +7,14 @@ #![no_std] extern crate cortex_m_rtfm as rtfm; +#[macro_use] extern crate f4; extern crate heapless; use core::fmt::Write; use core::ops::Deref; use f4::Serial; -use f4::Writer as w; +use f4::U8Writer; use f4::prelude::*; use f4::dma::{Buffer, Dma1Channel5, Dma1Channel6}; use f4::time::Hertz; @@ -30,6 +31,12 @@ const MAX_RX_LEN: usize = 1; const BAUD_RATE: Hertz = Hertz(115_200); +enum CmdType { + Greeting, + Unknown, + None, +} + app! { device: f4::stm32f40x, @@ -37,7 +44,7 @@ app! { 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 TX_BUFFER: Buffer<[u8; MAX_TX_LEN], Dma1Channel6> = Buffer::new([0; MAX_TX_LEN]); - static CNT: u8 = 1; + static CNT: u8 = 0; }, tasks: { @@ -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 fn rx_done(t: &mut Threshold, mut r: DMA1_STREAM5::Resources) { use rtfm::Resource; let mut byte: u8 = 0; - let mut say_hello = false; + let mut cmd_type: CmdType = CmdType::None; r.RX_BUFFER.claim(t, |rx, t| { // 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) { r.CMD_BUFFER.claim_mut(t, |cmd, _| { if byte == b'\r' { // End of command - match &***cmd { - b"hi" | b"Hi" => { - say_hello = true; + cmd_type = match &***cmd { + b"hi" | b"Hi" => CmdType::Greeting, + _ => { + if cmd.len() == 0 { + CmdType::None // Empty string + } else { + CmdType::Unknown // Unknown string + } } - _ => {} - } + }; cmd.clear(); } else { if cmd.push(byte).is_err() { - // Error: buffer full - // KISS: we just clear the buffer when it gets full + // Error: command buffer is full cmd.clear(); } } }); // If user wrote 'hi' and pressed enter, respond appropriately - if say_hello { - // Increment 'hi' counter - **r.CNT = **r.CNT + 1; - let cnt: u8 = **r.CNT; - // Claim the transmit buffer and write a formatted string into it - r.TX_BUFFER.claim_mut(t, |tx, _| { - write!( - w::out(&mut (*tx).deref().borrow_mut()[..MAX_TX_LEN]), - "Hello, there! {}\r\n", - cnt - ).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)); + match cmd_type { + CmdType::Greeting => { + // Increment 'hi' counter + **r.CNT = (**r.CNT).wrapping_add(1); + let cnt: u8 = **r.CNT; + // Print a response using DMA + uprint!(t, r.USART2, r.DMA1, r.TX_BUFFER, "Hi counter {}!\r\n", cnt); + } + CmdType::Unknown => { + // Unknown command + uprint!(t, r.USART2, r.DMA1, r.TX_BUFFER, "That's no greeting.\r\n"); + } + _ => {} } } @@ -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]); }); } + +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(); + } +} diff --git a/src/lib.rs b/src/lib.rs index 25060109bf0bf1dd19f2fb9bdc4f2a8b8a549281..96938c9b75293e3f4bc896c279534ebf1093063e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,7 @@ use frequency::*; pub use hal::prelude; pub use serial::Serial; -pub use serial::Writer; +pub use serial::U8Writer; pub use timer::{Channel, Timer}; pub use pwm::Pwm; pub use capture::Capture; diff --git a/src/serial.rs b/src/serial.rs index 35f336b69343f472a1a1913812683ed2c83d3638..be071b51465c94f49e03ef9266872294bb0235d3 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -23,37 +23,6 @@ use dma::{self, Buffer, Dma1Channel5, Dma1Channel6}; 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 pub type Result<T> = ::core::result::Result<T, nb::Error<Error>>; @@ -153,9 +122,13 @@ where if usart.get_type_id() == TypeId::of::<USART2>() { // we don't care about the speed register atm // DM00102166 - // AF7, Table 9 // 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)); + // Highest output speed + gpio.ospeedr + .modify(|_, w| w.ospeedr2().bits(0b11).ospeedr3().bits(0b11)); + // RM0368 8.3 Table 23 gpio.moder .modify(|_, w| w.moder2().bits(2).moder3().bits(2)); } @@ -322,7 +295,9 @@ where } else if sr.rxne().bit_is_set() { // NOTE(read_volatile) the register is 9 bits big but we'll only // 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 { Err(nb::Error::WouldBlock) } @@ -421,3 +396,76 @@ impl<'a> Serial<'a, USART2> { 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(); + }); + }); + }); + } +}