Skip to content
Snippets Groups Projects
Commit d76eda1a authored by Samuel Karlsson's avatar Samuel Karlsson
Browse files

marge update with master

parent 017a17fc
Branches
No related tags found
No related merge requests found
......@@ -128,24 +128,25 @@ fn main() {
// user application
fn idle(rcc: &mut RCC, gpioa: &mut GPIOA) {
let rcc_copy = &rcc;
// power on GPIOA
let r = (*rcc).AHB1ENR.read(); // read
(*rcc).AHB1ENR.write(r | 1 << (0)); // set enable
let r = rcc.AHB1ENR.read(); // read
rcc.AHB1ENR.write(r | 1 << (0)); // set enable
// configure PA5 as output
let r = (*gpioa).MODER.read() & !(0b11 << (5 * 2)); // read and mask
(*gpioa).MODER.write(r | 0b01 << (5 * 2)); // set output mode
let r = gpioa.MODER.read() & !(0b11 << (5 * 2)); // read and mask
gpioa.MODER.write(r | 0b01 << (5 * 2)); // set output mode
// and alter the data output through the BSRR register
// this is more efficient as the read register is not needed.
loop {
// set PA5 high
(*gpioa).BSRRH.write(1 << 5); // set bit, output hight (turn on led)
gpioa.BSRRH.write(1 << 5); // set bit, output hight (turn on led)
wait(10_000);
// set PA5 low
(*gpioa).BSRRL.write(1 << 5); // clear bit, output low (turn off led)
gpioa.BSRRL.write(1 << 5); // clear bit, output low (turn off led)
wait(10_000);
}
}
......@@ -157,8 +158,22 @@ fn idle(rcc: &mut RCC, gpioa: &mut GPIOA) {
// provided by ST (and other companies). Actually, the file presesnted here is mostly a
// cut/paste/replace of the stm32f40x.h, just Rustified.
//
// Here all peripheral access is unsafe, (as we are dereferencing raw pointers). In the code
// we have fairly large unsafe blocks. Your task here is to
// I this case we pass mutable pointer to the `idle`.
//
// Rewrite the accesses in the loop to use the data registerf directly (and make a read/modify/write).
//
// Run and see that the program behaves the same.
//
// commit your answers (bare5_1)
//
// 2.
// Extend the read/write API with a modify, taking the address, field offsbet, field width, and value.
//
// Change the code into using your new API.
//
// Run and see that the program behaves the same.
//
// commit your answers (bare5_2)
// As we are not using interrupts, we just register a dummy catch all handler
#[link_section = ".vector_table.interrupts"]
......
//! 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(block)]
extern crate nb;
#[macro_use]
extern crate cortex_m_debug;
use f4::prelude::*;
use f4::Serial;
use f4::time::Hertz;
use heapless::Vec;
use rtfm::app;
// CONFIGURATION
const BAUD_RATE: Hertz = Hertz(115_200);
// RTFM FRAMEWORK
app! {
device: f4::stm32f40x,
}
// Init executes with interrupts disabled
// Hence its safe to access all peripherals (no race-conditions)
//
// In this case init will never return
// This is not the typical use-case as we will see later
fn init(p: init::Peripherals) {
ipln!("init");
let serial = Serial(p.USART2);
serial.init(BAUD_RATE.invert(), None, p.GPIOA, p.RCC);
let mut vector: Vec<u8, [u8; 4]> = Vec::new();
loop {
match block!(serial.read()) {
Ok(byte) => {
let _ = vector.push(byte);
ipln!("Ok {:?}", vector);
let _ = serial.write(byte);
}
Err(err) => {
ipln!("Error {:?}", err);
p.USART2.dr.read(); // clear the error by reading the data register
}
}
}
}
// We will never reach `idle` since we burn the CPU arduino style :)
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 **
//
// now try sending 'a', 'b', 'c', 'd' character by character
// (just send the characters not the single quotes and commas)
// what did you receive, and what was the output of the ITM trace
// ** your answer here **
//
// why did the transmission fail? (hint, think about timing...)
// ** your answer here **
//
// commit your answers (bare7_1)
//
// 2. now stress the buffer lengt sending a sequence
// 'a', 'b', 'c', 'd', 'e' character by character
// what did you receive, and what was the output of the ITM trace
// ** your answer here **
//
// if done correctly you see an evedince of Rust's memory safety
// the buffer will be saturated (all elements occupied)
// but no buffer owerwrite will occur (outside the buffer)
//
// your job now is to check the API of `heapless`
// https://docs.rs/heapless/0.2.1/heapless/
//
// and catch the case we are trying to write to a full buffer/vector
// and write a suiteble error message
//
// commit your answers (bare7_2)
//
// !!!!! NOTICE !!!!!
// here we are not solving the underlying problem
// we are just mitigating the effects
// in bare8 we will se how to use interrupts for reliable
// high-speed communictation
//! 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::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]);
}
// Here we see the typical use of init INITIALIZING the system
fn init(p: init::Peripherals, _r: init::Resources) {
ipln!("init");
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);
}
// 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...
// now you are on your own.....
//
// look at the examples in the f4 crate
// https://github.com/jsjolund/f4
//
// try out the examples/pwm-control.rs
//
// connect an oschillocope to the pwm output
// check that you can control the pwm over the serial
//
//
// your assingment is to improve the user interface
// to take commands
//
// on // turn on the pwm output
// off // turn off the pwm output
// set p // set the duty cycle to `p` %
//
// and optionally
// freq f // set pww frequency to `f` hz
// ... // your own commands
//
// I would suggest using the bare8 as a starting point
// and adding code from the pwm-control example
// you may look if `parse` can be of use to you here...
// make the command parser work first, and then add the pwm code.
//
//
// commit your solution (bare9)
//
// https://play.rust-lang.org/,
// offers a good way to prototype and share code snippets
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment