Update satrs-example for the STM32F3
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
- Update RTIC to v2 - Update Python client version
This commit is contained in:
@ -7,6 +7,8 @@ use rtic::app;
|
||||
use heapless::{mpmc::Q8, Vec};
|
||||
#[allow(unused_imports)]
|
||||
use itm_logger::{debug, info, logger_init, warn};
|
||||
use rtic_monotonics::systick::fugit::TimerInstantU32;
|
||||
use rtic_monotonics::systick::ExtU32;
|
||||
use satrs::seq_count::SequenceCountProviderCore;
|
||||
use satrs::{
|
||||
pool::StoreError,
|
||||
@ -17,19 +19,18 @@ use stm32f3xx_hal::dma::dma1;
|
||||
use stm32f3xx_hal::gpio::{PushPull, AF7, PA2, PA3};
|
||||
use stm32f3xx_hal::pac::USART2;
|
||||
use stm32f3xx_hal::serial::{Rx, RxEvent, Serial, SerialDmaRx, SerialDmaTx, Tx, TxEvent};
|
||||
use systick_monotonic::{fugit::Duration, Systick};
|
||||
|
||||
const UART_BAUD: u32 = 115200;
|
||||
const BLINK_FREQ_MS: u64 = 1000;
|
||||
const TX_HANDLER_FREQ_MS: u64 = 20;
|
||||
const MIN_DELAY_BETWEEN_TX_PACKETS_MS: u16 = 5;
|
||||
const BLINK_FREQ_MS: u32 = 1000;
|
||||
const TX_HANDLER_FREQ_MS: u32 = 20;
|
||||
const MIN_DELAY_BETWEEN_TX_PACKETS_MS: u32 = 5;
|
||||
const MAX_TC_LEN: usize = 128;
|
||||
const MAX_TM_LEN: usize = 128;
|
||||
pub const PUS_APID: u16 = 0x02;
|
||||
|
||||
type TxType = Tx<USART2, PA2<AF7<PushPull>>>;
|
||||
type RxType = Rx<USART2, PA3<AF7<PushPull>>>;
|
||||
type MsDuration = Duration<u64, 1, 1000>;
|
||||
type InstantFugit = TimerInstantU32<1000>;
|
||||
type TxDmaTransferType = SerialDmaTx<&'static [u8], dma1::C7, TxType>;
|
||||
type RxDmaTransferType = SerialDmaRx<&'static mut [u8], dma1::C6, RxType>;
|
||||
|
||||
@ -53,9 +54,6 @@ type TcPacket = Vec<u8, MAX_TC_LEN>;
|
||||
|
||||
static TM_REQUESTS: Q8<TmPacket> = Q8::new();
|
||||
|
||||
const TC_POOL_SLOTS: usize = 8;
|
||||
const TM_POOL_SLOTS: usize = 8;
|
||||
|
||||
use core::cell::RefCell;
|
||||
use core::sync::atomic::{AtomicU16, Ordering};
|
||||
|
||||
@ -151,11 +149,18 @@ pub enum UartTxState {
|
||||
Transmitting(Option<TxDmaTransferType>),
|
||||
}
|
||||
|
||||
#[app(device = stm32f3xx_hal::pac, peripherals = true, dispatchers = [TIM20_BRK, TIM20_UP, TIM20_TRG_COM])]
|
||||
pub struct UartTxShared {
|
||||
last_completed: Option<InstantFugit>,
|
||||
state: UartTxState,
|
||||
}
|
||||
|
||||
#[app(device = stm32f3xx_hal::pac, peripherals = true)]
|
||||
mod app {
|
||||
use super::*;
|
||||
use core::slice::Iter;
|
||||
use cortex_m::iprintln;
|
||||
use rtic_monotonics::systick::Systick;
|
||||
use rtic_monotonics::Monotonic;
|
||||
use satrs::pus::verification::FailParams;
|
||||
use satrs::pus::verification::VerificationReporterCore;
|
||||
use satrs::spacepackets::{
|
||||
@ -166,15 +171,15 @@ mod app {
|
||||
use stm32f3_discovery::leds::Direction;
|
||||
use stm32f3_discovery::leds::Leds;
|
||||
use stm32f3xx_hal::prelude::*;
|
||||
use stm32f3xx_hal::Toggle;
|
||||
|
||||
use stm32f3_discovery::switch_hal::OutputSwitch;
|
||||
use stm32f3xx_hal::Switch;
|
||||
#[allow(dead_code)]
|
||||
type SerialType = Serial<USART2, (PA2<AF7<PushPull>>, PA3<AF7<PushPull>>)>;
|
||||
|
||||
#[shared]
|
||||
struct Shared {
|
||||
tx_transfer: UartTxState,
|
||||
tx_shared: UartTxShared,
|
||||
rx_transfer: Option<RxDmaTransferType>,
|
||||
}
|
||||
|
||||
@ -186,17 +191,14 @@ mod app {
|
||||
curr_dir: Iter<'static, Direction>,
|
||||
}
|
||||
|
||||
#[monotonic(binds = SysTick, default = true)]
|
||||
type MonoTimer = Systick<1000>;
|
||||
|
||||
#[init(local = [
|
||||
tc_pool_mem: [u8; TC_BUF_LEN * TC_POOL_SLOTS] = [0; TC_BUF_LEN * TC_POOL_SLOTS],
|
||||
tm_pool_mem: [u8; MAX_TM_LEN * TM_POOL_SLOTS] = [0; MAX_TM_LEN * TM_POOL_SLOTS]
|
||||
])]
|
||||
fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics) {
|
||||
#[init]
|
||||
fn init(mut cx: init::Context) -> (Shared, Local) {
|
||||
let mut rcc = cx.device.RCC.constrain();
|
||||
|
||||
let mono = Systick::new(cx.core.SYST, 8_000_000);
|
||||
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||
let systick_mono_token = rtic_monotonics::create_systick_token!();
|
||||
Systick::start(cx.core.SYST, 8_000_000, systick_mono_token);
|
||||
|
||||
logger_init();
|
||||
let mut flash = cx.device.FLASH.constrain();
|
||||
let clocks = rcc
|
||||
@ -205,6 +207,10 @@ mod app {
|
||||
.sysclk(8.MHz())
|
||||
.pclk1(8.MHz())
|
||||
.freeze(&mut flash.acr);
|
||||
|
||||
// Set up monotonic timer.
|
||||
//let mono_timer = MonoTimer::new(cx.core.DWT, clocks, &mut cx.core.DCB);
|
||||
|
||||
// setup ITM output
|
||||
iprintln!(
|
||||
&mut cx.core.ITM.stim[0],
|
||||
@ -246,106 +252,138 @@ mod app {
|
||||
clocks,
|
||||
&mut rcc.apb1,
|
||||
);
|
||||
usart2.configure_rx_interrupt(RxEvent::Idle, Toggle::On);
|
||||
usart2.configure_rx_interrupt(RxEvent::Idle, Switch::On);
|
||||
// This interrupt is enabled to re-schedule new transfers in the interrupt handler immediately.
|
||||
usart2.configure_tx_interrupt(TxEvent::TransmissionComplete, Toggle::On);
|
||||
usart2.configure_tx_interrupt(TxEvent::TransmissionComplete, Switch::On);
|
||||
|
||||
let dma1 = cx.device.DMA1.split(&mut rcc.ahb);
|
||||
let (tx_serial, mut rx_serial) = usart2.split();
|
||||
let (mut tx_serial, mut rx_serial) = usart2.split();
|
||||
|
||||
// This interrupt is immediately triggered, clear it. It will only be reset
|
||||
// by the hardware when data is received on RX (RXNE event)
|
||||
rx_serial.clear_event(RxEvent::Idle);
|
||||
// For some reason, this is also immediately triggered..
|
||||
tx_serial.clear_event(TxEvent::TransmissionComplete);
|
||||
let rx_transfer = rx_serial.read_exact(unsafe { DMA_RX_BUF.as_mut_slice() }, dma1.ch6);
|
||||
info!(target: "init", "Spawning tasks");
|
||||
blink::spawn().unwrap();
|
||||
serial_tx_handler::spawn().unwrap();
|
||||
(
|
||||
Shared {
|
||||
tx_transfer: UartTxState::Idle(Some(TxIdle {
|
||||
tx: tx_serial,
|
||||
dma_channel: dma1.ch7,
|
||||
})),
|
||||
tx_shared: UartTxShared {
|
||||
last_completed: None,
|
||||
state: UartTxState::Idle(Some(TxIdle {
|
||||
tx: tx_serial,
|
||||
dma_channel: dma1.ch7,
|
||||
})),
|
||||
},
|
||||
rx_transfer: Some(rx_transfer),
|
||||
},
|
||||
Local {
|
||||
//timer: mono_timer,
|
||||
leds,
|
||||
last_dir: Direction::North,
|
||||
curr_dir: Direction::iter(),
|
||||
verif_reporter,
|
||||
},
|
||||
init::Monotonics(mono),
|
||||
)
|
||||
}
|
||||
|
||||
#[task(local = [leds, curr_dir, last_dir])]
|
||||
fn blink(cx: blink::Context) {
|
||||
let toggle_leds = |dir: &Direction| {
|
||||
let leds = cx.local.leds;
|
||||
let last_led = leds.for_direction(*cx.local.last_dir);
|
||||
async fn blink(cx: blink::Context) {
|
||||
let blink::LocalResources {
|
||||
leds,
|
||||
curr_dir,
|
||||
last_dir,
|
||||
..
|
||||
} = cx.local;
|
||||
let mut toggle_leds = |dir: &Direction| {
|
||||
let last_led = leds.for_direction(*last_dir);
|
||||
last_led.off().ok();
|
||||
let led = leds.for_direction(*dir);
|
||||
led.on().ok();
|
||||
*cx.local.last_dir = *dir;
|
||||
*last_dir = *dir;
|
||||
};
|
||||
|
||||
match cx.local.curr_dir.next() {
|
||||
Some(dir) => {
|
||||
toggle_leds(dir);
|
||||
}
|
||||
None => {
|
||||
*cx.local.curr_dir = Direction::iter();
|
||||
toggle_leds(cx.local.curr_dir.next().unwrap());
|
||||
loop {
|
||||
match curr_dir.next() {
|
||||
Some(dir) => {
|
||||
toggle_leds(dir);
|
||||
}
|
||||
None => {
|
||||
*curr_dir = Direction::iter();
|
||||
toggle_leds(curr_dir.next().unwrap());
|
||||
}
|
||||
}
|
||||
Systick::delay(BLINK_FREQ_MS.millis()).await;
|
||||
}
|
||||
blink::spawn_after(MsDuration::from_ticks(BLINK_FREQ_MS)).unwrap();
|
||||
}
|
||||
|
||||
#[task(
|
||||
shared = [tx_transfer],
|
||||
local = []
|
||||
shared = [tx_shared],
|
||||
)]
|
||||
fn serial_tx_handler(mut cx: serial_tx_handler::Context) {
|
||||
if let Some(vec) = TM_REQUESTS.dequeue() {
|
||||
cx.shared.tx_transfer.lock(|tx_state| match tx_state {
|
||||
UartTxState::Idle(tx) => {
|
||||
//debug!(target: "serial_tx_handler", "bytes: {:x?}", &buf[0..len]);
|
||||
// Safety: We only copy the data into the TX DMA buffer in this task.
|
||||
// If the DMA is active, another branch will be taken.
|
||||
let mut_tx_dma_buf = unsafe { &mut DMA_TX_BUF };
|
||||
// 0 sentinel value as start marker
|
||||
mut_tx_dma_buf[0] = 0;
|
||||
// Should never panic, we accounted for the overhead.
|
||||
// Write into transfer buffer directly, no need for intermediate
|
||||
// encoding buffer.
|
||||
let encoded_len = cobs::encode(&vec[0..vec.len()], &mut mut_tx_dma_buf[1..]);
|
||||
// 0 end marker
|
||||
mut_tx_dma_buf[encoded_len + 1] = 0;
|
||||
//debug!(target: "serial_tx_handler", "Sending {} bytes", encoded_len + 2);
|
||||
//debug!("sent: {:x?}", &mut_tx_dma_buf[0..encoded_len + 2]);
|
||||
let tx_idle = tx.take().unwrap();
|
||||
// Transfer completion and re-scheduling of new TX transfers will be done
|
||||
// by the IRQ handler.
|
||||
let transfer = tx_idle
|
||||
.tx
|
||||
.write_all(&mut_tx_dma_buf[0..encoded_len + 2], tx_idle.dma_channel);
|
||||
*tx_state = UartTxState::Transmitting(Some(transfer));
|
||||
// The memory block is automatically returned to the pool when it is dropped.
|
||||
}
|
||||
UartTxState::Transmitting(_) => {
|
||||
// This is a SW configuration error. Only the ISR which
|
||||
// detects transfer completion should be able to spawn a new
|
||||
// task, and that ISR should set the state to IDLE.
|
||||
panic!("invalid internal tx state detected")
|
||||
}
|
||||
})
|
||||
} else {
|
||||
cx.shared.tx_transfer.lock(|tx_state| {
|
||||
if let UartTxState::Idle(_) = tx_state {
|
||||
serial_tx_handler::spawn_after(MsDuration::from_ticks(TX_HANDLER_FREQ_MS))
|
||||
.unwrap();
|
||||
async fn serial_tx_handler(mut cx: serial_tx_handler::Context) {
|
||||
loop {
|
||||
let is_idle = cx.shared.tx_shared.lock(|tx_shared| {
|
||||
if let UartTxState::Idle(_) = tx_shared.state {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
});
|
||||
if is_idle {
|
||||
let last_completed = cx.shared.tx_shared.lock(|shared| shared.last_completed);
|
||||
if let Some(last_completed) = last_completed {
|
||||
let elapsed_ms = (Systick::now() - last_completed).to_millis();
|
||||
if elapsed_ms < MIN_DELAY_BETWEEN_TX_PACKETS_MS {
|
||||
Systick::delay((MIN_DELAY_BETWEEN_TX_PACKETS_MS - elapsed_ms).millis())
|
||||
.await;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check for completion after 1 ms
|
||||
Systick::delay(1.millis()).await;
|
||||
continue;
|
||||
}
|
||||
if let Some(vec) = TM_REQUESTS.dequeue() {
|
||||
cx.shared
|
||||
.tx_shared
|
||||
.lock(|tx_shared| match &mut tx_shared.state {
|
||||
UartTxState::Idle(tx) => {
|
||||
let encoded_len;
|
||||
//debug!(target: "serial_tx_handler", "bytes: {:x?}", &buf[0..len]);
|
||||
// Safety: We only copy the data into the TX DMA buffer in this task.
|
||||
// If the DMA is active, another branch will be taken.
|
||||
unsafe {
|
||||
// 0 sentinel value as start marker
|
||||
DMA_TX_BUF[0] = 0;
|
||||
encoded_len =
|
||||
cobs::encode(&vec[0..vec.len()], &mut DMA_TX_BUF[1..]);
|
||||
// Should never panic, we accounted for the overhead.
|
||||
// Write into transfer buffer directly, no need for intermediate
|
||||
// encoding buffer.
|
||||
// 0 end marker
|
||||
DMA_TX_BUF[encoded_len + 1] = 0;
|
||||
}
|
||||
//debug!(target: "serial_tx_handler", "Sending {} bytes", encoded_len + 2);
|
||||
//debug!("sent: {:x?}", &mut_tx_dma_buf[0..encoded_len + 2]);
|
||||
let tx_idle = tx.take().unwrap();
|
||||
// Transfer completion and re-scheduling of new TX transfers will be done
|
||||
// by the IRQ handler.
|
||||
// SAFETY: The DMA is the exclusive writer to the DMA buffer now.
|
||||
let transfer = tx_idle.tx.write_all(
|
||||
unsafe { &DMA_TX_BUF[0..encoded_len + 2] },
|
||||
tx_idle.dma_channel,
|
||||
);
|
||||
tx_shared.state = UartTxState::Transmitting(Some(transfer));
|
||||
// The memory block is automatically returned to the pool when it is dropped.
|
||||
}
|
||||
UartTxState::Transmitting(_) => (),
|
||||
});
|
||||
// Check for completion after 1 ms
|
||||
Systick::delay(1.millis()).await;
|
||||
continue;
|
||||
}
|
||||
// Nothing to do, and we are idle.
|
||||
Systick::delay(TX_HANDLER_FREQ_MS.millis()).await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,7 +395,11 @@ mod app {
|
||||
verif_reporter
|
||||
],
|
||||
)]
|
||||
fn serial_rx_handler(cx: serial_rx_handler::Context, received_packet: Vec<u8, MAX_TC_LEN>) {
|
||||
async fn serial_rx_handler(
|
||||
cx: serial_rx_handler::Context,
|
||||
received_packet: Vec<u8, MAX_TC_LEN>,
|
||||
) {
|
||||
info!("running rx handler");
|
||||
let tgt: &'static str = "serial_rx_handler";
|
||||
cx.local.stamp_buf[0] = P_FIELD_BASE;
|
||||
info!(target: tgt, "Received packet with {} bytes", received_packet.len());
|
||||
@ -434,7 +476,6 @@ mod app {
|
||||
FailParams::new(stamp_buf, &EcssEnumU16::new(0), &[]),
|
||||
)
|
||||
.unwrap();
|
||||
// let mem_block = poolmod::TM::alloc().unwrap().init([0u8; MAX_TM_LEN]);
|
||||
let sender = TmSender::new(TmPacket::new(), tgt);
|
||||
if let Err(e) = verif_reporter.send_acceptance_failure(sendable, &sender) {
|
||||
warn!(target: tgt, "Sending acceptance failure failed: {:?}", e.0);
|
||||
@ -445,7 +486,6 @@ mod app {
|
||||
.acceptance_success(src_data_buf, token, SEQ_COUNT_PROVIDER.get(), 0, stamp_buf)
|
||||
.unwrap();
|
||||
|
||||
// let mem_block = poolmod::TM::alloc().unwrap().init([0u8; MAX_TM_LEN]);
|
||||
let sender = TmSender::new(TmPacket::new(), tgt);
|
||||
let accepted_token = match verif_reporter.send_acceptance_success(sendable, &sender) {
|
||||
Ok(token) => token,
|
||||
@ -484,7 +524,8 @@ mod app {
|
||||
let sec_header = PusTmSecondaryHeader::new_simple(17, 2, stamp_buf);
|
||||
let ping_reply = PusTmCreator::new(&mut sp_header, sec_header, &[], true);
|
||||
let mut tm_packet = TmPacket::new();
|
||||
tm_packet.resize(ping_reply.len_written(), 0)
|
||||
tm_packet
|
||||
.resize(ping_reply.len_written(), 0)
|
||||
.expect("vec resize failed");
|
||||
ping_reply.write_to_bytes(&mut tm_packet).unwrap();
|
||||
if TM_REQUESTS.enqueue(tm_packet).is_err() {
|
||||
@ -501,7 +542,6 @@ mod app {
|
||||
stamp_buf,
|
||||
)
|
||||
.unwrap();
|
||||
// let mem_block = poolmod::TM::alloc().unwrap().init([0u8; MAX_TM_LEN]);
|
||||
let sender = TmSender::new(TmPacket::new(), tgt);
|
||||
if let Err(e) = verif_reporter.send_step_or_completion_success(sendable, &sender) {
|
||||
warn!(target: tgt, "Sending completion success failed: {:?}", e.0);
|
||||
@ -514,6 +554,7 @@ mod app {
|
||||
|
||||
#[task(binds = DMA1_CH6, shared = [rx_transfer])]
|
||||
fn rx_dma_isr(mut cx: rx_dma_isr::Context) {
|
||||
let mut tc_packet = TcPacket::new();
|
||||
cx.shared.rx_transfer.lock(|rx_transfer| {
|
||||
let rx_ref = rx_transfer.as_ref().unwrap();
|
||||
if rx_ref.is_complete() {
|
||||
@ -522,7 +563,6 @@ mod app {
|
||||
// The received data is transferred to another task now to avoid any processing overhead
|
||||
// during the interrupt. There are multiple ways to do this, we use a stack allocaed vector here
|
||||
// to do this.
|
||||
let mut tc_packet = TcPacket::new();
|
||||
tc_packet.resize(buf.len(), 0).expect("vec resize failed");
|
||||
tc_packet.copy_from_slice(buf);
|
||||
|
||||
@ -541,23 +581,26 @@ mod app {
|
||||
});
|
||||
}
|
||||
|
||||
#[task(binds = USART2_EXTI26, shared = [rx_transfer, tx_transfer])]
|
||||
#[task(binds = USART2_EXTI26, shared = [rx_transfer, tx_shared])]
|
||||
fn serial_isr(mut cx: serial_isr::Context) {
|
||||
cx.shared.tx_transfer.lock(|tx_state| match tx_state {
|
||||
UartTxState::Idle(_) => (),
|
||||
UartTxState::Transmitting(transfer) => {
|
||||
let transfer_ref = transfer.as_ref().unwrap();
|
||||
if transfer_ref.is_complete() {
|
||||
let transfer = transfer.take().unwrap();
|
||||
let (_, dma_channel, tx) = transfer.stop();
|
||||
*tx_state = UartTxState::Idle(Some(TxIdle { tx, dma_channel }));
|
||||
serial_tx_handler::spawn_after(MsDuration::from_ticks(
|
||||
MIN_DELAY_BETWEEN_TX_PACKETS_MS.into(),
|
||||
))
|
||||
.unwrap();
|
||||
cx.shared
|
||||
.tx_shared
|
||||
.lock(|tx_shared| match &mut tx_shared.state {
|
||||
UartTxState::Idle(_) => (),
|
||||
UartTxState::Transmitting(transfer) => {
|
||||
let transfer_ref = transfer.as_ref().unwrap();
|
||||
if transfer_ref.is_complete() {
|
||||
let transfer = transfer.take().unwrap();
|
||||
let (_, dma_channel, mut tx) = transfer.stop();
|
||||
tx.clear_event(TxEvent::TransmissionComplete);
|
||||
tx_shared.state = UartTxState::Idle(Some(TxIdle { tx, dma_channel }));
|
||||
// We cache the last completed time to ensure that there is a minimum delay between consecutive
|
||||
// transferred packets.
|
||||
tx_shared.last_completed = Some(Systick::now());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
let mut tc_packet = TcPacket::new();
|
||||
cx.shared.rx_transfer.lock(|rx_transfer| {
|
||||
let rx_transfer_ref = rx_transfer.as_ref().unwrap();
|
||||
// Received a partial packet.
|
||||
@ -567,14 +610,13 @@ mod app {
|
||||
// The received data is transferred to another task now to avoid any processing overhead
|
||||
// during the interrupt. There are multiple ways to do this, we use a stack
|
||||
// allocated vector to do this.
|
||||
let mut tc_packet = TcPacket::new();
|
||||
tc_packet
|
||||
.resize(rx_len as usize, 0)
|
||||
.expect("vec resize failed");
|
||||
tc_packet[0..rx_len as usize].copy_from_slice(&buf[0..rx_len as usize]);
|
||||
rx.clear_event(RxEvent::Idle);
|
||||
// Only send owning pointer to pool memory and the received packet length.
|
||||
serial_rx_handler::spawn(tc_packet).expect("spawning rx handler task failed");
|
||||
info!("spawning rx task");
|
||||
serial_rx_handler::spawn(tc_packet).expect("spawning rx handler failed");
|
||||
*rx_transfer = Some(rx.read_exact(buf, ch));
|
||||
}
|
||||
});
|
||||
|
Reference in New Issue
Block a user