Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
R
rtfm-app
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Samuel Karlsson
rtfm-app
Commits
d76eda1a
Commit
d76eda1a
authored
7 years ago
by
Samuel Karlsson
Browse files
Options
Downloads
Patches
Plain Diff
marge update with master
parent
017a17fc
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
examples/bare5.rs
+23
-8
23 additions, 8 deletions
examples/bare5.rs
examples/bare7.rs
+119
-0
119 additions, 0 deletions
examples/bare7.rs
examples/bare8.rs
+211
-0
211 additions, 0 deletions
examples/bare8.rs
examples/bare9.rs
+32
-0
32 additions, 0 deletions
examples/bare9.rs
with
385 additions
and
8 deletions
examples/bare5.rs
+
23
−
8
View file @
d76eda1a
...
...
@@ -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"
]
...
...
This diff is collapsed.
Click to expand it.
examples/bare7.rs
0 → 100644
+
119
−
0
View file @
d76eda1a
//! 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
This diff is collapsed.
Click to expand it.
examples/bare8.rs
0 → 100644
+
211
−
0
View file @
d76eda1a
//! 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...
This diff is collapsed.
Click to expand it.
examples/bare9.rs
0 → 100644
+
32
−
0
View file @
d76eda1a
// 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
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment