Skip to content
Snippets Groups Projects
Commit 68ddbc22 authored by Per's avatar Per
Browse files

parse

parent 5c0a8ac8
No related branches found
No related tags found
No related merge requests found
//! Serial interface loopback
//! Example of parsing an &str
#![deny(unsafe_code)]
//#![deny(warnings)]
#![deny(warnings)]
#![feature(proc_macro)]
#![no_std]
......@@ -11,92 +11,14 @@ 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);
use rtfm::app;
// 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,
......@@ -110,185 +32,34 @@ fn parse(s: &str) -> Result<Command, &str> {
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")
}
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 frequency"),
},
None => Err("No input")
//Err(format!("Invald Command : {:?}", s)),
Some(_) => Err("Invalid command"),
None => Err("No input"),
}
}
// 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);
fn init(_p: init::Peripherals) {
ipln!("init");
ipln!("{:?}", parse("Start"));
ipln!("{:?}", parse("Start")); // works
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);
// }
// }
// }
// _ => {}
// }
// }
ipln!("{:?}", parse(" Freq a122 ")); // Invalid frequency
ipln!("{:?}", parse(" Freq ")); // No frequency
ipln!("{:?}", parse("")); // No input
}
// 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...
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment