From 5c0a8ac8acb781d71ee7fdb6197b653d013ea44f Mon Sep 17 00:00:00 2001 From: Per <Per Lindgren> Date: Mon, 19 Feb 2018 23:06:49 +0100 Subject: [PATCH] parser --- examples/parse.rs | 294 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 examples/parse.rs diff --git a/examples/parse.rs b/examples/parse.rs new file mode 100644 index 0000000..4bfccab --- /dev/null +++ b/examples/parse.rs @@ -0,0 +1,294 @@ +//! Serial interface loopback +#![deny(unsafe_code)] +//#![deny(warnings)] +#![feature(proc_macro)] +#![no_std] + +extern crate cortex_m_rtfm as rtfm; +extern crate f4; +extern crate heapless; + +#[macro_use] +extern crate cortex_m_debug; + +use f4::prelude::*; +use f4::{clock, Serial}; +use f4::time::Hertz; +use heapless::Vec; +use rtfm::{app, Resource, Threshold}; + +// CONFIGURATION +const BAUD_RATE: Hertz = Hertz(115_200); + +// RTFM FRAMEWORK +app! { + device: f4::stm32f40x, + + resources: { + static VECTOR: Vec<u8, [u8; 4]> = Vec::new(); + }, + + tasks: { + USART2: { + path: rx, + priority: 2, + resources: [VECTOR, USART2], + }, + EXTI1: { + path: trace, + priority: 1, + resources: [VECTOR], + } + }, +} + +// `rx` task trigger on arrival of a USART2 interrupt +fn rx(t: &mut Threshold, r: USART2::Resources) { + let serial = Serial(&**r.USART2); + + // we don't need to block waiting for data to arrive + // (as we were triggered) by the data arrival (or error) + match serial.read() { + Ok(byte) => { + // received byte correct + r.VECTOR.claim_mut(t, |vector, _| { + // critical section for the shared vector + let _ = vector.push(byte); + // here you could put your error handling for vector full + }); + let _ = serial.write(byte); + } + Err(err) => { + // some transmission error + ipln!("Error {:?}", err); + r.USART2.dr.read(); // clear the error by reading the data register + } + } + + // trigger the `trace` task + rtfm::set_pending(f4::stm32f40x::Interrupt::EXTI1); +} + +// `trace` task triggered by the hight priority `rx` task +// a low priority task for the background processing (like tracing) +fn trace(t: &mut Threshold, r: EXTI1::Resources) { + let mut b = [0; 4]; // local buffer + let mut l = 0; // length of the received vector + + r.VECTOR.claim(t, |vector, _| { + // critical section for the shared vector + // here the task `rx` will be blocked from executing + l = vector.len(); + b[..l].copy_from_slice(&***vector); // efficent copy vector to the local buffer + }); + // since we do the actual tracing (relatively slow) + // OUTSIDE the claim (critical section), there will be no + // additional blocking of `rx` + ipln!("Vec {:?}", &b[..l]); +} + +macro_rules! scan { + ( $string:expr, $sep:expr, $( $x:ty ),+ ) => {{ + let mut iter = $string.split($sep); + ($(iter.next().and_then(|word| word.parse::<$x>().ok()),)*) + }} +} + +// [macro_use] +// use core::fmt; + +#[derive(Debug)] +enum Command { + Start, + Stop, + Freq(u32), +} + +fn parse(s: &str) -> Result<Command, &str> { + let mut iter = s.split(' ').filter(|c| !(c == &"")); + + match iter.next() { + Some("Stop") => Ok(Command::Stop), + Some("Start") => Ok(Command::Start), + Some("Freq") => { + + match iter.next() { + Some(fs) => { + + if let Ok(f) = fs.parse::<u32>() { + Ok(Command::Freq(f)) + } else { + Err("Invalid frequency") + } + + } + None => Err("No frequency") + } + + } + Some(_) => { + Err("Invalid command") + }, + None => Err("No input") + //Err(format!("Invald Command : {:?}", s)), + } +} + +// Here we see the typical use of init INITIALIZING the system +fn init(p: init::Peripherals, _r: init::Resources) { + //clock::set_84_mhz(p.RCC, p.FLASH); + ipln!("init"); + ipln!("{:?}", parse("Start")); + ipln!("{:?}", parse(" Start ")); // works with white spaces + ipln!("{:?}", parse(" Freq 122 ")); // works with white spaces + ipln!("{:?}", parse(" Freq a122 ")); // + ipln!("{:?}", parse(" Freq ")); // + ipln!("{:?}", parse("")); // error + + let serial = Serial(p.USART2); + + serial.init(BAUD_RATE.invert(), None, p.GPIOA, p.RCC); + // in effect telling the USART2 to trigger the `rx` task/interrupt + serial.listen(f4::serial::Event::Rxne); + + // let s = scan!(" 55 123", |c| c == ' ', u32, u32); + // ipln!("{:?}", s); + + // let b = char::is_whitespace; + // //let scanned = scan!(" 55 123 ", core::char::is_whitespace, u32, u32); + // //ipln!("{:?}", scanned); + + // let mut v: Vec<&str, [&str; 4]> = Vec::new(); + + // // collect into a vector of &str + // for i in s.split(" ") { + // v.push(&i); + // } + + // if let Some(command) = v.pop() { + // ipln!("here {:?}", command); + // match command { + // "freq" => { + // if let Some(freq) = v.pop() { + // if let Ok(f) = freq.parse::<i32>() { + // ipln!("freq {:?}", f); + // } + // } + // } + // _ => {} + // } + // } +} + +// We will spend all time sleeping (unless we have work to do) +// reactive programming in RTFM ftw!!! +fn idle() -> ! { + // Sleep + loop { + rtfm::wfi(); + } +} + +// 1. compile and run the project at 16MHz +// make sure its running (not paused) +// start a terminal program, e.g., `moserial` +// connect to the port +// +// Device /dev/ttyACM0 +// Baude Rate 115200 +// Data Bits 8 +// Stop Bits 1 +// Parity None +// Handshake None +// +// (this is also known in short as 15200 8N1) +// +// you should now be able to send data and recive an echo from the MCU +// +// try sending: "abcd" as a single sequence (set the option No end in moserial) +// (don't send the quation marks, just abcd) +// +// what did you receive, and what was the output of the ITM trace +// ** your answer here ** +// +// did you experience any over-run errors? +// ** your answer here ** +// +// what is the key problem and its solution (try to follow the commented code) +// ** your answer here ** +// +// commit your answers (bare8_1) +// +// 2. now catch the case when we are trying to write to a full vector/buffer +// and write a suiteble error message +// +// commit your answers (bare8_2) +// +// as a side note.... +// +// The concurrency model behind RTFM offers +// 1. Race-free resource access +// +// 2. Deadlock-free exection +// +// 3. Shared execution stack (no pre-allocated stack regions) +// +// 4. Bound priority inversion +// +// 5. Theoretical underpinning -> +// + proofs of soundness +// + schedulability analysis +// + response time analysis +// + stack memory analysis +// + ... leverages on 25 years of reseach in the real-time community +// based on the seminal work of Baker in the early 1990s +// (known as the Stack Resource Policy, SRP) +// +// Our implementation in Rust offers +// 1. compile check and analysis of tasks and resources +// + the API implementation together with the Rust compiler will ensure that +// both RTFM (SRP) soundness and the Rust memory model invariants +// are upheld (under all circumpstances). +// +// 2. arguably the worlds fastest real time scheduler * +// + task invocation 0-cycle OH on top of HW interrupt handling +// + 2 cycle OH for locking a shared resource (on claim entry) +// + 1 cycle OH for releasineg a shared resoure (on claim exit) +// +// 3. arguably the worlds most memory efficient scheduler * +// + 1 byte stack memory OH for each (nested) claim +// (no additional book-keeping during run-time) +// +// * applies to static task/resource models with single core +// pre-emptive, static priority scheduling +// +// in comparison "real-time" schedulers for threaded models like FreeRTOS +// - CPU and memory OH magnitudes larger (100s of cycles/kilobytes of memory) +// - ... and what's worse OH is typically unbound (no proofs of worst case) +// - potential race conditions (up to the user to verify) +// - potential dead-locks (up to the implementation) +// - potential unbound priority inversion (up to the implementation) +// +// Rust RTFM (currently) target ONLY STATIC SYSTEMS, there is no notion +// of dynamically creating new executions contexts/threads +// so a direct comparison is not completely fair. +// +// On the other hand, embedded applications are typically static by nature +// so a STATIC model is to that end better suitable. +// +// RTFM is reactive by nature, a task execute to end, triggered +// by an internal or external event, (where an interrupt is an external event +// from the environment, like a HW peripheral such as the USART2). +// +// Threads on the other hand are concurrent and infinte by nature and +// actively blocking/yeilding awaiting stimuli. Hence reactivity needs to be CODED. +// This leads to an anomaly, the underlying HW is reactive (interrupts), +// requiring an interrupt handler, that creates a signal to the scheduler. +// +// The scheduler then needs to keep track of all threads and at some point choose +// to dispatch the awaiting thread. So reactivity is bottlenecked to the point +// of scheduling by que management, context switching and other additional +// book keeping. +// +// In essence, the thread scheduler tries to re-establish the reactivity that +// were there (interrupts), a battle that cannot be won... -- GitLab