Rust embedded store Peripherals - rust

I'm trying to work on a nrf52-dk board and trying to blink a light and get SPI working at the same time.
I could have one or the other working at a time, but not both at once.
I'm pretty sure that nrf52810_hal::pac::Peripherals::take() shouldn't be called more than once, as its data would change all references, but it gets moved when I specify pins.
I'm unsure how I'd be able to make it work without passing in without using up the variable.
In the following example, the "PERIPHERALS are null" is always written out and the code panics because of the statements that are following it.
I do need to have the PERIPHERALS being "static mut", as I need them in another "thread", because it's an interrupt-called function, to which I'm unable to pass data.
#![no_main]
#![no_std]
#[allow(unused_extern_crates)]
use panic_halt as _;
use asm_delay::AsmDelay;
use cortex_m_rt::entry;
use cortex_m_semihosting::hprint;
use hal::gpio::Level;
use hal::pac::interrupt;
use nrf52810_hal as hal;
use nrf52810_hal::prelude::*;
use nrf52810_pac as nrf;
use nrf52810_pac::{Interrupt, NVIC};
static mut PERIPHERALS: Option<nrf::Peripherals> = None;
#[entry]
unsafe fn main() -> ! {
let p = hal::pac::Peripherals::take().unwrap();
let port0 = hal::gpio::p0::Parts::new(p.P0);
let spiclk = port0.p0_25.into_push_pull_output(Level::Low).degrade();
let spimosi = port0.p0_24.into_push_pull_output(Level::Low).degrade();
let spimiso = port0.p0_23.into_floating_input().degrade();
let pins = hal::spim::Pins {
sck: spiclk,
miso: Some(spimiso),
mosi: Some(spimosi),
};
let spi = hal::Spim::new(
p.SPIM0,
pins,
hal::spim::Frequency::K500,
hal::spim::MODE_0,
0,
);
let reference_data = "Hello World!".as_bytes();
let mut eh_spi = embedded_hal_spy::new(spi, |_| {});
use embedded_hal::blocking::spi::Write;
match eh_spi.write(reference_data) {
Ok(_) => {}
Err(_) => {}
}
PERIPHERALS = nrf::Peripherals::take();
if PERIPHERALS.is_none() {
hprint!("PERIPHERALS are null!").unwrap();
}
NVIC::unmask(Interrupt::SWI0_EGU0);
let mut d = AsmDelay::new(asm_delay::bitrate::U32BitrateExt::mhz(74));
PERIPHERALS
.as_ref()
.unwrap()
.P0
.dir
.write(|w| w.pin20().output());
PERIPHERALS
.as_ref()
.unwrap()
.P0
.out
.write(|w| w.pin20().low());
loop {
NVIC::pend(Interrupt::SWI0_EGU0);
d.delay_ms(100u32);
}
}
#[interrupt]
fn SWI0_EGU0() {
static mut LED_STATE: bool = false;
flip_led(LED_STATE);
*LED_STATE = !*LED_STATE;
}
fn flip_led(led_state: &mut bool) {
match led_state {
true => unsafe {
PERIPHERALS
.as_ref()
.unwrap()
.P0
.out
.write(|w| w.pin20().low());
},
false => unsafe {
PERIPHERALS
.as_ref()
.unwrap()
.P0
.out
.write(|w| w.pin20().high());
},
}
}

Do you actually need to access all of Peripherals from your interrupt context? Probably not, you probably need to access only specific peripherals there. You can move those out of the Peripherals struct and into a static. Then you'll have the peripherals you need in your main as local variables there, and everything else in a static.
But there's an even better solution, in my opinion: Use RTIC. It's designed to handle that exact use case. It allows you to specify exactly which resources you need in which context, and will make those resources available there. You can even safely share resources between different contexts. It will automatically protect them with mutexes, as required.
I can't recommend RTIC highly enough. For me, the only reason not to use it would be, if my program doesn't have any interrupt handlers.

Related

How to store one of two constants in a value, where the constants share traits?

Depending on configuration I need to select either stdout or sink once, and pass the results as an output destination for subsequent output call.s
My Java and C++ experience tell me that abstracting away from the concrete type is wise and makes room for future design changes. This code however won't compile:
let out = if std::env::var("LOG").is_ok() {
std::io::stdout()
} else {
std::io::sink()
};
Stating...
`if` and `else` have incompatible types
What is the Rust-o-matic way of solving this?
Dynamic dispatch using trait objects is probably what you need:
use std::io::{self, Write};
use std::env;
fn get_output() -> Box<dyn Write> {
if env::var("LOG").is_ok() {
Box::new(io::stdout())
} else {
Box::new(io::sink())
}
}
let out = get_output();
The approach from Peter's answer is probably what you need, but it does require an extra allocation. (Which probably doesn't matter in the least in this case, but could matter in other scenarios.) If you are only passing out downward, i.e. as argument to functions, you can avoid the allocation by using two variables to store the different outputs:
let (mut stdout, mut sink);
let out: &mut dyn Write = if std::env::var("LOG").is_ok() {
stdout = std::io::stdout();
&mut stdout
} else {
sink = std::io::sink();
&mut sink
};
// ...proceed to use out...

What's the best way to register a function to run during an unexpected exit of a Rust program?

I'm creating a terminal text editor in Rust. The editor puts the terminal into raw mode, disabling character echoing and the like, and then restores the original terminal function upon exit.
However, the editor has some bugs, and crashes unexpectedly every now and again due to issues like unsigned variable underflow. When this happens, the cleanup code which would restore the terminal to its original state never runs.
The cleanup function I'd like to run is the following:
fn restore_orig_mode(editor_config: &EditorConfig) -> io::Result<()> {
termios::tcsetattr(STDIN, termios::TCSAFLUSH, &editor_config.orig_termios)
}
In the latest stable Rust, #for1096's answer is the best. In your case, it might be quite simple to apply because your clean-up does not need to use state that is shared with the application code:
use std::panic::catch_unwind;
fn run_editor(){
panic!("Error!");
println!("Running!");
}
fn clean_up(){
println!("Cleaning up!");
}
fn main(){
match catch_unwind(|| run_editor()) {
Ok(_) => println!("Exited successfully"),
Err(_) => clean_up()
}
}
If your clean-up requires accessing shared state with your application, then you will need some additional machinery to convince the compiler that it is safe. For example, if your application looks like this:
// The shared state of your application
struct Editor { /* ... */ }
impl Editor {
fn run(&mut self){
println!("running!");
// panic!("Error!");
}
fn clean_up(&mut self){
println!("cleaning up!");
}
fn new() -> Editor {
Editor { }
}
}
Then, in order to call clean_up, you would have to manage access to the data, something like this:
use std::panic::catch_unwind;
use std::sync::{Arc, Mutex};
fn main() {
let editor = Arc::new(Mutex::new(Editor::new()));
match catch_unwind(|| editor.lock().unwrap().run()) {
Ok(_) => println!("Exited successfully"),
Err(_) => {
println!("Application panicked.");
let mut editor = match editor.lock() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
};
editor.clean_up();
}
}
}
Prior to Rust 1.9, you can only handle panics that occur in a child thread. This isn't much different except that you need to clone the Arc because the original one will need to be moved into the thread closure.
use std::thread;
use std::sync::{Arc, Mutex};
fn main() {
let editor = Arc::new(Mutex::new(Editor::new()));
// clone before the original is moved into the thread closure
let editor_recovery = editor.clone();
let child = thread::spawn(move || {
editor.lock().unwrap().run();
});
match child.join() {
Ok(_) => println!("Exited successfully"),
Err(_) => {
println!("Application panicked.");
let mut editor = match editor_recovery.lock() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
};
editor.clean_up();
}
}
}
Try catch_unwind. I haven't used it, so I cannot guarantee it works.
A common solution to this problem in Unix applications and using other languages such as C is to fork() and have your parent wait for the child. On an error exit by the child, clean up.
This is really the only reliable way to clean up if the clean up is important. For example, your program might be killed by the Linux OOM kill. It will never be able to run a language specific panic, exception, at_exit or anything like that because the operating system simply destroys it.
By having a separate process watching it, that process can handle any special cleanup of files or shared memory.
This solution does not really require using fork(). The parent could be a shell script or a separate executable.

How am I supposed to apply a match or Option after an unwrap?

I'm trying to get into Rust from a Python background and I'm having an issue with a PoC I'm messing around with. I've read through a bunch of blogs and documentation on how to handle errors in Rust, but I can't figure out how to implement it when I use unwrap and get a panic. Here is part of the code:
fn main() {
let listener = TcpListener::bind("127.0.0.1:5432").unwrap();
// The .0 at the end is indexing a tuple, FYI
loop {
let stream = listener.accept().unwrap().0;
stream.set_read_timeout(Some(Duration::from_millis(100)));
handle_request(stream);
}
}
// Things change a bit in here
fn handle_request(stream: TcpStream) {
let address = stream.peer_addr().unwrap();
let mut reader = BufReader::new(stream);
let mut payload = "".to_string();
for line in reader.by_ref().lines() {
let brap = line.unwrap();
payload.push_str(&*brap);
if brap == "" {
break;
}
}
println!("{0} -> {1}", address, payload);
send_response(reader.into_inner());
}
It is handling the socket not receiving anything with set_read_timeout on the stream as expected, but when that triggers my unwrap on line in the loop it is causing a panic. Can someone help me understand how I'm properly supposed to apply a match or Option to this code?
There seems to be a large disconnect here. unwrap or expect handle errors by panicking the thread. You aren't really supposed to "handle" a panic in 99.9% of Rust programs; you just let things die.
If you don't want a panic, don't use unwrap or expect. Instead, pass back the error via a Result or an Option, as described in the Error Handling section of The Rust Programming Language.
You can match (or any other pattern matching technique) on the Result or Option and handle an error appropriately for your case. One example of handling the error in your outer loop:
use std::net::{TcpStream, TcpListener};
use std::time::Duration;
use std::io::prelude::*;
use std::io::BufReader;
fn main() {
let listener = TcpListener::bind("127.0.0.1:5432")
.expect("Unable to bind to the port");
loop {
if let Ok((stream, _)) = listener.accept() {
stream
.set_read_timeout(Some(Duration::from_millis(100)))
.expect("Unable to set timeout");
handle_request(stream);
}
}
}
Note that I highly recommend using expect instead of unwrap in just about every case.

Cannot move data out of a Mutex

Consider the following code example, I have a vector of JoinHandlers in which I need it iterate over to join back to the main thread, however, upon doing so I am getting the error error: cannot move out of borrowed content.
let threads = Arc::new(Mutex::new(Vec::new()));
for _x in 0..100 {
let handle = thread::spawn(move || {
//do some work
}
threads.lock().unwrap().push((handle));
}
for t in threads.lock().unwrap().iter() {
t.join();
}
Unfortunately, you can't do this directly. When Mutex consumes the data structure you fed to it, you can't get it back by value again. You can only get &mut reference to it, which won't allow moving out of it. So even into_iter() won't work - it needs self argument which it can't get from MutexGuard.
There is a workaround, however. You can use Arc<Mutex<Option<Vec<_>>>> instead of Arc<Mutex<Vec<_>>> and then just take() the value out of the mutex:
for t in threads.lock().unwrap().take().unwrap().into_iter() {
}
Then into_iter() will work just fine as the value is moved into the calling thread.
Of course, you will need to construct the vector and push to it appropriately:
let threads = Arc::new(Mutex::new(Some(Vec::new())));
...
threads.lock().unwrap().as_mut().unwrap().push(handle);
However, the best way is to just drop the Arc<Mutex<..>> layer altogether (of course, if this value is not used from other threads).
As referenced in How to take ownership of T from Arc<Mutex<T>>? this is now possible to do without any trickery in Rust using Arc::try_unwrap and Mutex.into_inner()
let threads = Arc::new(Mutex::new(Vec::new()));
for _x in 0..100 {
let handle = thread::spawn(move || {
println!("{}", _x);
});
threads.lock().unwrap().push(handle);
}
let threads_unwrapped: Vec<JoinHandle<_>> = Arc::try_unwrap(threads).unwrap().into_inner().unwrap();
for t in threads_unwrapped.into_iter() {
t.join().unwrap();
}
Play around with it in this playground to verify.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9d5635e7f778bc744d1fb855b92db178
while the drain is a good solution, you can also do the following thing
// with a copy
let built_words: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
let result: Vec<String> = built_words.lock().unwrap().clone();
// using drain
let mut locked_result = built_words.lock().unwrap();
let mut result: Vec<String> = vec![];
result.extend(locked_result.drain(..));
I would prefer to clone the data to get the original value. Not sure if it has any performance overhead.

Can't capture dynamic environment in a fn item

In this code everything works except task_id. I want this script to count requests in task_id:
use std::thread;
use std::thread::sleep_ms;
use std::sync::mpsc;
#[macro_use] extern crate nickel;
use nickel::Nickel;
fn main() {
let mut server = Nickel::new();
let mut task_id: i64 = 0;
server.utilize(router! {
get "**" => |_req, _res| {
task_id += 1;
run_heavy_task(task_id);
"Yo!"
}
});
server.listen("127.0.0.1:6767");
}
fn run_heavy_task(task_id: i64) {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
println!("heavy task {} started!", task_id);
sleep_ms(3000);
println!("heavy task {} completed", task_id);
let result = tx.send(());
});
//rx.recv();
//println!("Task {} completed", task_id);
}
error:
can't capture dynamic environment in a fn item; use the || { ... }
closure form instead main.rs:13 task_id += 1;
Please help me solve this issue - how can I pass task_id into closure?
To expand on Chris Morgan's answer, this is a self-contained example with the same error:
fn main() {
let mut a = 0;
fn router() {
a += 1;
}
router();
println!("{}", a)
}
The problem is that fn items are not allowed to capture their environment, period. Capturing environment is non-trivial, and there are multiple ways to get variables into a closure. Closures are actually structs with the appropriate member variables for each captured variable.
Review Chris Morgan's statement:
Bear in mind how it may be multi-threaded; at the very least you will need to use some form of mutex to get it to work.
(Emphasis mine). You are creating a function, but you don't get to control how or when it is called. For all you know, Nickel may choose to call it from multiple threads - that's up to the library. As it is, you do not ever call the code task_id += 1!
I'm no expert on Nickel, but it appears that having dynamic routing is not possible with the code you've posted. However, it should be possible for you to avoid using the macro and construct a handler yourself, you "just" need to implement Middleware. That handler may be able to contain state, like your task_id.

Resources