Make flashload COM more reliable
Some checks are pending
Rust/va416xx-rs/pipeline/pr-main Build queued...
Some checks are pending
Rust/va416xx-rs/pipeline/pr-main Build queued...
Fixes for UART RX with IRQ implementation
This commit is contained in:
parent
cad968342a
commit
78dd7ee5c3
@ -1,12 +1,10 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from spacepackets.ecss import RequestId
|
|
||||||
from spacepackets.ecss.defs import PusService
|
from spacepackets.ecss.defs import PusService
|
||||||
from spacepackets.ecss.tm import PusTm
|
from spacepackets.ecss.tm import PusTm
|
||||||
import toml
|
import toml
|
||||||
import struct
|
import struct
|
||||||
import logging
|
import logging
|
||||||
import argparse
|
import argparse
|
||||||
import threading
|
|
||||||
import time
|
import time
|
||||||
import enum
|
import enum
|
||||||
from tmtccmd.com.serial_base import SerialCfg
|
from tmtccmd.com.serial_base import SerialCfg
|
||||||
@ -45,6 +43,7 @@ ACTION_SERVICE = 8
|
|||||||
|
|
||||||
RAW_MEMORY_WRITE_SUBSERVICE = 2
|
RAW_MEMORY_WRITE_SUBSERVICE = 2
|
||||||
BOOT_NVM_MEMORY_ID = 1
|
BOOT_NVM_MEMORY_ID = 1
|
||||||
|
PING_PAYLOAD_SIZE = 0
|
||||||
|
|
||||||
|
|
||||||
class ActionId(enum.IntEnum):
|
class ActionId(enum.IntEnum):
|
||||||
@ -104,6 +103,29 @@ def main() -> int:
|
|||||||
com_if = SerialCobsComIF(serial_cfg)
|
com_if = SerialCobsComIF(serial_cfg)
|
||||||
com_if.open()
|
com_if.open()
|
||||||
file_path = None
|
file_path = None
|
||||||
|
if args.ping:
|
||||||
|
_LOGGER.info("Sending ping command")
|
||||||
|
ping_tc = PusTc(
|
||||||
|
apid=0x00,
|
||||||
|
service=PusService.S17_TEST,
|
||||||
|
subservice=1,
|
||||||
|
seq_count=SEQ_PROVIDER.get_and_increment(),
|
||||||
|
app_data=bytes(PING_PAYLOAD_SIZE),
|
||||||
|
)
|
||||||
|
verificator.add_tc(ping_tc)
|
||||||
|
com_if.send(ping_tc.pack())
|
||||||
|
|
||||||
|
data_available = com_if.data_available(0.4)
|
||||||
|
if not data_available:
|
||||||
|
_LOGGER.warning("no ping reply received")
|
||||||
|
for reply in com_if.receive():
|
||||||
|
result = verificator.add_tm(
|
||||||
|
Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0))
|
||||||
|
)
|
||||||
|
if result is not None and result.completed:
|
||||||
|
_LOGGER.info("received ping completion reply")
|
||||||
|
if not args.target:
|
||||||
|
return 0
|
||||||
if args.target:
|
if args.target:
|
||||||
if not args.corrupt:
|
if not args.corrupt:
|
||||||
if not args.path:
|
if not args.path:
|
||||||
@ -113,15 +135,6 @@ def main() -> int:
|
|||||||
if not file_path.exists():
|
if not file_path.exists():
|
||||||
_LOGGER.error("File does not exist")
|
_LOGGER.error("File does not exist")
|
||||||
return -1
|
return -1
|
||||||
if args.ping:
|
|
||||||
_LOGGER.info("Sending ping command")
|
|
||||||
ping_tc = PusTc(
|
|
||||||
apid=0x00,
|
|
||||||
service=PusService.S17_TEST,
|
|
||||||
subservice=1,
|
|
||||||
seq_count=SEQ_PROVIDER.get_and_increment(),
|
|
||||||
)
|
|
||||||
com_if.send(ping_tc.pack())
|
|
||||||
if args.corrupt:
|
if args.corrupt:
|
||||||
if not args.target:
|
if not args.target:
|
||||||
_LOGGER.error("target for corruption command required")
|
_LOGGER.error("target for corruption command required")
|
||||||
@ -254,7 +267,7 @@ def main() -> int:
|
|||||||
):
|
):
|
||||||
done = True
|
done = True
|
||||||
# Still keep a small delay
|
# Still keep a small delay
|
||||||
time.sleep(0.01)
|
# time.sleep(0.05)
|
||||||
verificator.remove_completed_entries()
|
verificator.remove_completed_entries()
|
||||||
if done:
|
if done:
|
||||||
break
|
break
|
||||||
|
@ -18,13 +18,11 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use embedded_hal_nb::serial::Read;
|
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use va416xx_hal::{clock::Clocks, edac, pac, time::Hertz, wdt::Wdt};
|
use va416xx_hal::{clock::Clocks, edac, pac, time::Hertz, wdt::Wdt};
|
||||||
|
|
||||||
const EXTCLK_FREQ: u32 = 40_000_000;
|
const EXTCLK_FREQ: u32 = 40_000_000;
|
||||||
const COBS_FRAME_SEPARATOR: u8 = 0x0;
|
|
||||||
|
|
||||||
const MAX_TC_SIZE: usize = 1024;
|
const MAX_TC_SIZE: usize = 1024;
|
||||||
const MAX_TC_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TC_SIZE);
|
const MAX_TC_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TC_SIZE);
|
||||||
@ -33,10 +31,8 @@ const MAX_TM_SIZE: usize = 128;
|
|||||||
const MAX_TM_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TM_SIZE);
|
const MAX_TM_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TM_SIZE);
|
||||||
|
|
||||||
const UART_BAUDRATE: u32 = 115200;
|
const UART_BAUDRATE: u32 = 115200;
|
||||||
const SERIAL_RX_WIRETAPPING: bool = false;
|
|
||||||
const COBS_RX_DEBUGGING: bool = false;
|
|
||||||
|
|
||||||
const BOOT_NVM_MEMORY_ID: u8 = 1;
|
const BOOT_NVM_MEMORY_ID: u8 = 1;
|
||||||
|
const RX_DEBUGGING: bool = false;
|
||||||
|
|
||||||
pub enum ActionId {
|
pub enum ActionId {
|
||||||
CorruptImageA = 128,
|
CorruptImageA = 128,
|
||||||
@ -62,13 +58,24 @@ use ringbuf::{
|
|||||||
CachingCons, StaticProd, StaticRb,
|
CachingCons, StaticProd, StaticRb,
|
||||||
};
|
};
|
||||||
|
|
||||||
const BUF_RB_SIZE_TX: usize = 1024;
|
// Larger buffer for TC to be able to hold the possibly large memory write packets.
|
||||||
const SIZES_RB_SIZE_TX: usize = 16;
|
const BUF_RB_SIZE_TC: usize = 2048;
|
||||||
|
const SIZES_RB_SIZE_TC: usize = 16;
|
||||||
|
|
||||||
static mut BUF_RB_TX: Lazy<StaticRb<u8, BUF_RB_SIZE_TX>> =
|
const BUF_RB_SIZE_TM: usize = 512;
|
||||||
Lazy::new(StaticRb::<u8, BUF_RB_SIZE_TX>::default);
|
const SIZES_RB_SIZE_TM: usize = 16;
|
||||||
static mut SIZES_RB_TX: Lazy<StaticRb<usize, SIZES_RB_SIZE_TX>> =
|
|
||||||
Lazy::new(StaticRb::<usize, SIZES_RB_SIZE_TX>::default);
|
// Ring buffers to handling variable sized telemetry
|
||||||
|
static mut BUF_RB_TM: Lazy<StaticRb<u8, BUF_RB_SIZE_TM>> =
|
||||||
|
Lazy::new(StaticRb::<u8, BUF_RB_SIZE_TM>::default);
|
||||||
|
static mut SIZES_RB_TM: Lazy<StaticRb<usize, SIZES_RB_SIZE_TM>> =
|
||||||
|
Lazy::new(StaticRb::<usize, SIZES_RB_SIZE_TM>::default);
|
||||||
|
|
||||||
|
// Ring buffers to handling variable sized telecommands
|
||||||
|
static mut BUF_RB_TC: Lazy<StaticRb<u8, BUF_RB_SIZE_TC>> =
|
||||||
|
Lazy::new(StaticRb::<u8, BUF_RB_SIZE_TC>::default);
|
||||||
|
static mut SIZES_RB_TC: Lazy<StaticRb<usize, SIZES_RB_SIZE_TC>> =
|
||||||
|
Lazy::new(StaticRb::<usize, SIZES_RB_SIZE_TC>::default);
|
||||||
|
|
||||||
pub struct DataProducer<const BUF_SIZE: usize, const SIZES_LEN: usize> {
|
pub struct DataProducer<const BUF_SIZE: usize, const SIZES_LEN: usize> {
|
||||||
pub buf_prod: StaticProd<'static, u8, BUF_SIZE>,
|
pub buf_prod: StaticProd<'static, u8, BUF_SIZE>,
|
||||||
@ -91,15 +98,10 @@ pub const APP_B_END_ADDR: u32 = 0x40000;
|
|||||||
mod app {
|
mod app {
|
||||||
use super::*;
|
use super::*;
|
||||||
use cortex_m::asm;
|
use cortex_m::asm;
|
||||||
use embedded_hal_nb::nb;
|
|
||||||
use embedded_io::Write;
|
use embedded_io::Write;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtic::Mutex;
|
use rtic::Mutex;
|
||||||
use rtic_monotonics::systick::prelude::*;
|
use rtic_monotonics::systick::prelude::*;
|
||||||
use rtic_sync::{
|
|
||||||
channel::{Receiver, Sender},
|
|
||||||
make_channel,
|
|
||||||
};
|
|
||||||
use rtt_target::rprintln;
|
use rtt_target::rprintln;
|
||||||
use satrs::pus::verification::VerificationReportCreator;
|
use satrs::pus::verification::VerificationReportCreator;
|
||||||
use spacepackets::ecss::PusServiceId;
|
use spacepackets::ecss::PusServiceId;
|
||||||
@ -127,26 +129,24 @@ mod app {
|
|||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {
|
struct Local {
|
||||||
uart_rx: uart::Rx<pac::Uart0>,
|
uart_rx: uart::RxWithIrq<pac::Uart0>,
|
||||||
uart_tx: uart::Tx<pac::Uart0>,
|
uart_tx: uart::Tx<pac::Uart0>,
|
||||||
cobs_reader_state: CobsReaderStates,
|
|
||||||
tc_tx: TcTx,
|
|
||||||
tc_rx: TcRx,
|
|
||||||
rom_spi: Option<pac::Spi3>,
|
rom_spi: Option<pac::Spi3>,
|
||||||
tx_cons: DataConsumer<BUF_RB_SIZE_TX, SIZES_RB_SIZE_TX>,
|
// We handle all TM in one task.
|
||||||
|
tm_cons: DataConsumer<BUF_RB_SIZE_TM, SIZES_RB_SIZE_TM>,
|
||||||
|
// We consume all TC in one task.
|
||||||
|
tc_cons: DataConsumer<BUF_RB_SIZE_TC, SIZES_RB_SIZE_TC>,
|
||||||
|
// We produce all TC in one task.
|
||||||
|
tc_prod: DataProducer<BUF_RB_SIZE_TC, SIZES_RB_SIZE_TC>,
|
||||||
verif_reporter: VerificationReportCreator,
|
verif_reporter: VerificationReportCreator,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[shared]
|
#[shared]
|
||||||
struct Shared {
|
struct Shared {
|
||||||
decode_buffer_busy: bool,
|
// Having this shared allows multiple tasks to generate telemetry.
|
||||||
decode_buf: [u8; MAX_TC_SIZE],
|
tm_prod: DataProducer<BUF_RB_SIZE_TM, SIZES_RB_SIZE_TM>,
|
||||||
tx_prod: DataProducer<BUF_RB_SIZE_TX, SIZES_RB_SIZE_TX>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type TcTx = Sender<'static, usize, 2>;
|
|
||||||
pub type TcRx = Receiver<'static, usize, 2>;
|
|
||||||
|
|
||||||
rtic_monotonics::systick_monotonic!(Mono, 10_000);
|
rtic_monotonics::systick_monotonic!(Mono, 10_000);
|
||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
@ -176,38 +176,45 @@ mod app {
|
|||||||
&mut cx.device.sysconfig,
|
&mut cx.device.sysconfig,
|
||||||
&clocks,
|
&clocks,
|
||||||
);
|
);
|
||||||
let (tx, rx) = uart0.split();
|
let (tx, mut rx, _) = uart0.split_with_irq();
|
||||||
let (tc_tx, tc_rx) = make_channel!(usize, 2);
|
|
||||||
|
|
||||||
let verif_reporter = VerificationReportCreator::new(0).unwrap();
|
let verif_reporter = VerificationReportCreator::new(0).unwrap();
|
||||||
|
|
||||||
let (buf_prod, buf_cons) = unsafe { BUF_RB_TX.split_ref() };
|
let (buf_prod_tm, buf_cons_tm) = unsafe { BUF_RB_TM.split_ref() };
|
||||||
let (sizes_prod, sizes_cons) = unsafe { SIZES_RB_TX.split_ref() };
|
let (sizes_prod_tm, sizes_cons_tm) = unsafe { SIZES_RB_TM.split_ref() };
|
||||||
|
|
||||||
|
let (buf_prod_tc, buf_cons_tc) = unsafe { BUF_RB_TC.split_ref() };
|
||||||
|
let (sizes_prod_tc, sizes_cons_tc) = unsafe { SIZES_RB_TC.split_ref() };
|
||||||
|
|
||||||
Mono::start(cx.core.SYST, clocks.sysclk().raw());
|
Mono::start(cx.core.SYST, clocks.sysclk().raw());
|
||||||
CLOCKS.set(clocks).unwrap();
|
CLOCKS.set(clocks).unwrap();
|
||||||
|
|
||||||
|
rx.read_fixed_len_using_irq(MAX_TC_FRAME_SIZE, true)
|
||||||
|
.expect("initiating UART RX failed");
|
||||||
pus_tc_handler::spawn().unwrap();
|
pus_tc_handler::spawn().unwrap();
|
||||||
uart_reader_task::spawn().unwrap();
|
|
||||||
pus_tm_tx_handler::spawn().unwrap();
|
pus_tm_tx_handler::spawn().unwrap();
|
||||||
(
|
(
|
||||||
Shared {
|
Shared {
|
||||||
decode_buffer_busy: false,
|
tm_prod: DataProducer {
|
||||||
decode_buf: [0; MAX_TC_SIZE],
|
buf_prod: buf_prod_tm,
|
||||||
tx_prod: DataProducer {
|
sizes_prod: sizes_prod_tm,
|
||||||
buf_prod,
|
|
||||||
sizes_prod,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Local {
|
Local {
|
||||||
uart_rx: rx,
|
uart_rx: rx,
|
||||||
uart_tx: tx,
|
uart_tx: tx,
|
||||||
cobs_reader_state: CobsReaderStates::default(),
|
|
||||||
tc_tx,
|
|
||||||
tc_rx,
|
|
||||||
rom_spi: Some(cx.device.spi3),
|
rom_spi: Some(cx.device.spi3),
|
||||||
tx_cons: DataConsumer {
|
tm_cons: DataConsumer {
|
||||||
buf_cons,
|
buf_cons: buf_cons_tm,
|
||||||
sizes_cons,
|
sizes_cons: sizes_cons_tm,
|
||||||
|
},
|
||||||
|
tc_cons: DataConsumer {
|
||||||
|
buf_cons: buf_cons_tc,
|
||||||
|
sizes_cons: sizes_cons_tc,
|
||||||
|
},
|
||||||
|
tc_prod: DataProducer {
|
||||||
|
buf_prod: buf_prod_tc,
|
||||||
|
sizes_prod: sizes_prod_tc,
|
||||||
},
|
},
|
||||||
verif_reporter,
|
verif_reporter,
|
||||||
},
|
},
|
||||||
@ -223,120 +230,62 @@ mod app {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[task(
|
#[task(
|
||||||
priority = 4,
|
binds = UART0_RX,
|
||||||
local=[
|
local = [
|
||||||
read_buf: [u8;MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
cnt: u32 = 0,
|
||||||
|
rx_buf: [u8; MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
||||||
uart_rx,
|
uart_rx,
|
||||||
cobs_reader_state,
|
tc_prod
|
||||||
tc_tx
|
|
||||||
],
|
],
|
||||||
shared=[decode_buffer_busy, decode_buf]
|
|
||||||
)]
|
)]
|
||||||
async fn uart_reader_task(mut cx: uart_reader_task::Context) {
|
fn uart_rx_irq(cx: uart_rx_irq::Context) {
|
||||||
let mut current_idx = 0;
|
match cx.local.uart_rx.irq_handler(cx.local.rx_buf) {
|
||||||
loop {
|
Ok(result) => {
|
||||||
match cx.local.uart_rx.read() {
|
if RX_DEBUGGING {
|
||||||
Ok(byte) => {
|
log::debug!("RX Info: {:?}", cx.local.uart_rx.irq_info());
|
||||||
if SERIAL_RX_WIRETAPPING {
|
log::debug!("RX Result: {:?}", result);
|
||||||
log::debug!("RX Byte: 0x{:x?}", byte);
|
|
||||||
}
|
|
||||||
handle_single_rx_byte(&mut cx, byte, &mut current_idx)
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
match e {
|
|
||||||
nb::Error::Other(e) => {
|
|
||||||
log::warn!("UART error: {:?}", e);
|
|
||||||
match e {
|
|
||||||
uart::Error::Overrun => {
|
|
||||||
cx.local.uart_rx.clear_fifo();
|
|
||||||
}
|
|
||||||
uart::Error::FramingError => (),
|
|
||||||
uart::Error::ParityError => (),
|
|
||||||
uart::Error::BreakCondition => (),
|
|
||||||
uart::Error::TransferPending => (),
|
|
||||||
uart::Error::BufferTooShort => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nb::Error::WouldBlock => {
|
|
||||||
// Delay for a short period before polling again.
|
|
||||||
Mono::delay(400.micros()).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if result.complete() {
|
||||||
|
// Check frame validity (must have COBS format) and decode the frame.
|
||||||
|
// Currently, we expect a full frame or a frame received through a timeout
|
||||||
|
// to be one COBS frame. We could parse for multiple COBS packets in one
|
||||||
|
// frame, but the additional complexity is not necessary here..
|
||||||
|
if cx.local.rx_buf[0] == 0 && cx.local.rx_buf[result.bytes_read - 1] == 0 {
|
||||||
|
let decoded_size =
|
||||||
|
cobs::decode_in_place(&mut cx.local.rx_buf[1..result.bytes_read]);
|
||||||
|
if decoded_size.is_err() {
|
||||||
|
log::warn!("COBS decoding failed");
|
||||||
|
} else {
|
||||||
|
let decoded_size = decoded_size.unwrap();
|
||||||
|
if cx.local.tc_prod.sizes_prod.vacant_len() >= 1
|
||||||
|
&& cx.local.tc_prod.buf_prod.vacant_len() >= decoded_size
|
||||||
|
{
|
||||||
|
// Should never fail, we checked there is enough space.
|
||||||
|
cx.local.tc_prod.sizes_prod.try_push(decoded_size).unwrap();
|
||||||
|
cx.local
|
||||||
|
.tc_prod
|
||||||
|
.buf_prod
|
||||||
|
.push_slice(&cx.local.rx_buf[1..1 + decoded_size]);
|
||||||
|
} else {
|
||||||
|
log::warn!("COBS TC queue full");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log::warn!("COBS frame with invalid format, start and end bytes are not 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_single_rx_byte(
|
// Initiate next transfer.
|
||||||
cx: &mut uart_reader_task::Context,
|
cx.local
|
||||||
byte: u8,
|
.uart_rx
|
||||||
current_idx: &mut usize,
|
.read_fixed_len_using_irq(MAX_TC_FRAME_SIZE, true)
|
||||||
) {
|
.expect("read operation failed");
|
||||||
match cx.local.cobs_reader_state {
|
|
||||||
CobsReaderStates::WaitingForStart => {
|
|
||||||
if byte == COBS_FRAME_SEPARATOR {
|
|
||||||
if COBS_RX_DEBUGGING {
|
|
||||||
log::debug!("COBS start marker detected");
|
|
||||||
}
|
}
|
||||||
*cx.local.cobs_reader_state = CobsReaderStates::WatingForEnd;
|
if result.error() {
|
||||||
*current_idx = 0;
|
log::warn!("UART error: {:?}", result.error());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CobsReaderStates::WatingForEnd => {
|
Err(e) => {
|
||||||
if byte == COBS_FRAME_SEPARATOR {
|
log::warn!("UART error: {:?}", e);
|
||||||
if COBS_RX_DEBUGGING {
|
|
||||||
log::debug!("COBS end marker detected");
|
|
||||||
}
|
|
||||||
let mut sending_failed = false;
|
|
||||||
let mut decoding_error = false;
|
|
||||||
let mut decode_buffer_busy = false;
|
|
||||||
cx.shared.decode_buffer_busy.lock(|busy| {
|
|
||||||
if *busy {
|
|
||||||
decode_buffer_busy = true;
|
|
||||||
} else {
|
|
||||||
cx.shared.decode_buf.lock(|buf| {
|
|
||||||
match cobs::decode(&cx.local.read_buf[..*current_idx], buf) {
|
|
||||||
Ok(packet_len) => {
|
|
||||||
if COBS_RX_DEBUGGING {
|
|
||||||
log::debug!(
|
|
||||||
"COBS decoded packet with length {}",
|
|
||||||
packet_len
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if cx.local.tc_tx.try_send(packet_len).is_err() {
|
|
||||||
sending_failed = true;
|
|
||||||
}
|
|
||||||
*busy = true;
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
decoding_error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if sending_failed {
|
|
||||||
log::warn!("sending TC packet failed, queue full");
|
|
||||||
}
|
|
||||||
if decoding_error {
|
|
||||||
log::warn!("decoding error");
|
|
||||||
}
|
|
||||||
if decode_buffer_busy {
|
|
||||||
log::warn!("decode buffer busy. data arriving too fast");
|
|
||||||
}
|
|
||||||
*cx.local.cobs_reader_state = CobsReaderStates::WaitingForStart;
|
|
||||||
} else if *current_idx >= cx.local.read_buf.len() {
|
|
||||||
*cx.local.cobs_reader_state = CobsReaderStates::FrameOverflow;
|
|
||||||
} else {
|
|
||||||
cx.local.read_buf[*current_idx] = byte;
|
|
||||||
*current_idx += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CobsReaderStates::FrameOverflow => {
|
|
||||||
if byte == COBS_FRAME_SEPARATOR {
|
|
||||||
*cx.local.cobs_reader_state = CobsReaderStates::WaitingForStart;
|
|
||||||
*current_idx = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,30 +293,48 @@ mod app {
|
|||||||
#[task(
|
#[task(
|
||||||
priority = 2,
|
priority = 2,
|
||||||
local=[
|
local=[
|
||||||
read_buf: [u8;MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
tc_buf: [u8; MAX_TC_SIZE] = [0; MAX_TC_SIZE],
|
||||||
src_data_buf: [u8; 16] = [0; 16],
|
src_data_buf: [u8; 16] = [0; 16],
|
||||||
verif_buf: [u8; 32] = [0; 32],
|
verif_buf: [u8; 32] = [0; 32],
|
||||||
tc_rx,
|
tc_cons,
|
||||||
rom_spi,
|
rom_spi,
|
||||||
verif_reporter
|
verif_reporter
|
||||||
],
|
],
|
||||||
shared=[decode_buffer_busy, decode_buf, tx_prod]
|
shared=[tm_prod]
|
||||||
)]
|
)]
|
||||||
async fn pus_tc_handler(mut cx: pus_tc_handler::Context) {
|
async fn pus_tc_handler(mut cx: pus_tc_handler::Context) {
|
||||||
loop {
|
loop {
|
||||||
let packet_len = cx.local.tc_rx.recv().await.expect("all senders down");
|
// Try to read a TC from the ring buffer.
|
||||||
|
let packet_len = cx.local.tc_cons.sizes_cons.try_pop();
|
||||||
|
if packet_len.is_none() {
|
||||||
|
// Small delay, TCs might arrive very quickly.
|
||||||
|
Mono::delay(20.millis()).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let packet_len = packet_len.unwrap();
|
||||||
log::info!(target: "TC Handler", "received packet with length {}", packet_len);
|
log::info!(target: "TC Handler", "received packet with length {}", packet_len);
|
||||||
// We still copy the data to a local buffer, so the exchange buffer can already be used
|
assert_eq!(
|
||||||
// for the next packet / decode process.
|
cx.local
|
||||||
cx.shared
|
.tc_cons
|
||||||
.decode_buf
|
.buf_cons
|
||||||
.lock(|buf| cx.local.read_buf[0..buf.len()].copy_from_slice(buf));
|
.pop_slice(&mut cx.local.tc_buf[0..packet_len]),
|
||||||
cx.shared.decode_buffer_busy.lock(|busy| *busy = false);
|
packet_len
|
||||||
match PusTcReader::new(cx.local.read_buf) {
|
);
|
||||||
Ok((pus_tc, _)) => {
|
// Read a telecommand, now handle it.
|
||||||
|
handle_valid_pus_tc(&mut cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_valid_pus_tc(cx: &mut pus_tc_handler::Context) {
|
||||||
|
let pus_tc = PusTcReader::new(cx.local.tc_buf);
|
||||||
|
if pus_tc.is_err() {
|
||||||
|
log::warn!("PUS TC error: {}", pus_tc.unwrap_err());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let (pus_tc, _) = pus_tc.unwrap();
|
||||||
let mut write_and_send = |tm: &PusTmCreator| {
|
let mut write_and_send = |tm: &PusTmCreator| {
|
||||||
let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap();
|
let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap();
|
||||||
cx.shared.tx_prod.lock(|prod| {
|
cx.shared.tm_prod.lock(|prod| {
|
||||||
prod.sizes_prod.try_push(tm.len_written()).unwrap();
|
prod.sizes_prod.try_push(tm.len_written()).unwrap();
|
||||||
prod.buf_prod
|
prod.buf_prod
|
||||||
.push_slice(&cx.local.verif_buf[0..written_size]);
|
.push_slice(&cx.local.verif_buf[0..written_size]);
|
||||||
@ -421,6 +388,12 @@ mod app {
|
|||||||
}
|
}
|
||||||
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
||||||
log::info!(target: "TC Handler", "received ping TC");
|
log::info!(target: "TC Handler", "received ping TC");
|
||||||
|
let tm = cx
|
||||||
|
.local
|
||||||
|
.verif_reporter
|
||||||
|
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||||
|
.expect("completion success failed");
|
||||||
|
write_and_send(&tm);
|
||||||
} else if pus_tc.service() == PusServiceId::MemoryManagement as u8 {
|
} else if pus_tc.service() == PusServiceId::MemoryManagement as u8 {
|
||||||
let tm = cx
|
let tm = cx
|
||||||
.local
|
.local
|
||||||
@ -484,12 +457,6 @@ mod app {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
|
||||||
log::warn!("PUS TC error: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(
|
#[task(
|
||||||
priority = 1,
|
priority = 1,
|
||||||
@ -497,16 +464,16 @@ mod app {
|
|||||||
read_buf: [u8;MAX_TM_SIZE] = [0; MAX_TM_SIZE],
|
read_buf: [u8;MAX_TM_SIZE] = [0; MAX_TM_SIZE],
|
||||||
encoded_buf: [u8;MAX_TM_FRAME_SIZE] = [0; MAX_TM_FRAME_SIZE],
|
encoded_buf: [u8;MAX_TM_FRAME_SIZE] = [0; MAX_TM_FRAME_SIZE],
|
||||||
uart_tx,
|
uart_tx,
|
||||||
tx_cons,
|
tm_cons
|
||||||
],
|
],
|
||||||
shared=[]
|
shared=[]
|
||||||
)]
|
)]
|
||||||
async fn pus_tm_tx_handler(cx: pus_tm_tx_handler::Context) {
|
async fn pus_tm_tx_handler(cx: pus_tm_tx_handler::Context) {
|
||||||
loop {
|
loop {
|
||||||
while cx.local.tx_cons.sizes_cons.occupied_len() > 0 {
|
while cx.local.tm_cons.sizes_cons.occupied_len() > 0 {
|
||||||
let next_size = cx.local.tx_cons.sizes_cons.try_pop().unwrap();
|
let next_size = cx.local.tm_cons.sizes_cons.try_pop().unwrap();
|
||||||
cx.local
|
cx.local
|
||||||
.tx_cons
|
.tm_cons
|
||||||
.buf_cons
|
.buf_cons
|
||||||
.pop_slice(&mut cx.local.read_buf[0..next_size]);
|
.pop_slice(&mut cx.local.read_buf[0..next_size]);
|
||||||
cx.local.encoded_buf[0] = 0;
|
cx.local.encoded_buf[0] = 0;
|
||||||
|
@ -17,12 +17,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
- Added `va41620`, `va41630`, `va41628` and `va41629` device features. A device now has to be
|
- Added `va41620`, `va41630`, `va41628` and `va41629` device features. A device now has to be
|
||||||
selected for HAL compilation to work properly
|
selected for HAL compilation to work properly
|
||||||
|
- Adaptions for the UART IRQ feature which are now only implemented for the RX part of the UART.
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
- Small fixes and improvements for ADC drivers
|
- Small fixes and improvements for ADC drivers
|
||||||
- Fixes for the SPI implementation where the clock divider values were not calculated
|
- Fixes for the SPI implementation where the clock divider values were not calculated
|
||||||
correctly
|
correctly
|
||||||
|
- Fixes for UART IRQ handler implementation
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ embedded-io = "0.6"
|
|||||||
num_enum = { version = "0.7", default-features = false }
|
num_enum = { version = "0.7", default-features = false }
|
||||||
typenum = "1"
|
typenum = "1"
|
||||||
bitflags = "2"
|
bitflags = "2"
|
||||||
bitfield = "0.15"
|
bitfield = "0.17"
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
fugit = "0.3"
|
fugit = "0.3"
|
||||||
delegate = "0.12"
|
delegate = "0.12"
|
||||||
|
@ -197,66 +197,36 @@ impl From<Hertz> for Config {
|
|||||||
// IRQ Definitions
|
// IRQ Definitions
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
struct IrqInfo {
|
#[derive(Debug)]
|
||||||
|
pub struct IrqInfo {
|
||||||
rx_len: usize,
|
rx_len: usize,
|
||||||
rx_idx: usize,
|
rx_idx: usize,
|
||||||
mode: IrqReceptionMode,
|
mode: IrqReceptionMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum IrqResultMask {
|
|
||||||
Complete = 0,
|
|
||||||
Overflow = 1,
|
|
||||||
FramingError = 2,
|
|
||||||
ParityError = 3,
|
|
||||||
Break = 4,
|
|
||||||
Timeout = 5,
|
|
||||||
Addr9 = 6,
|
|
||||||
/// Should not happen
|
|
||||||
Unknown = 7,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This struct is used to return the default IRQ handler result to the user
|
/// This struct is used to return the default IRQ handler result to the user
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct IrqResult {
|
pub struct IrqResult {
|
||||||
raw_res: u32,
|
complete: bool,
|
||||||
|
timeout: bool,
|
||||||
|
pub errors: IrqUartError,
|
||||||
pub bytes_read: usize,
|
pub bytes_read: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrqResult {
|
impl IrqResult {
|
||||||
pub const fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
IrqResult {
|
IrqResult {
|
||||||
raw_res: 0,
|
complete: false,
|
||||||
|
timeout: false,
|
||||||
|
errors: IrqUartError::default(),
|
||||||
bytes_read: 0,
|
bytes_read: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrqResult {
|
impl IrqResult {
|
||||||
#[inline]
|
|
||||||
pub fn raw_result(&self) -> u32 {
|
|
||||||
self.raw_res
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn clear_result(&mut self) {
|
|
||||||
self.raw_res = 0;
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn set_result(&mut self, flag: IrqResultMask) {
|
|
||||||
self.raw_res |= 1 << flag as u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn complete(&self) -> bool {
|
|
||||||
if ((self.raw_res >> IrqResultMask::Complete as u32) & 0x01) == 0x01 {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn error(&self) -> bool {
|
pub fn error(&self) -> bool {
|
||||||
if self.overflow_error() || self.framing_error() || self.parity_error() {
|
if self.errors.overflow || self.errors.parity || self.errors.framing {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
@ -264,34 +234,27 @@ impl IrqResult {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn overflow_error(&self) -> bool {
|
pub fn overflow_error(&self) -> bool {
|
||||||
if ((self.raw_res >> IrqResultMask::Overflow as u32) & 0x01) == 0x01 {
|
self.errors.overflow
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn framing_error(&self) -> bool {
|
pub fn framing_error(&self) -> bool {
|
||||||
if ((self.raw_res >> IrqResultMask::FramingError as u32) & 0x01) == 0x01 {
|
self.errors.framing
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parity_error(&self) -> bool {
|
pub fn parity_error(&self) -> bool {
|
||||||
if ((self.raw_res >> IrqResultMask::ParityError as u32) & 0x01) == 0x01 {
|
self.errors.parity
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn timeout(&self) -> bool {
|
pub fn timeout(&self) -> bool {
|
||||||
if ((self.raw_res >> IrqResultMask::Timeout as u32) & 0x01) == 0x01 {
|
self.timeout
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
false
|
|
||||||
|
#[inline]
|
||||||
|
pub fn complete(&self) -> bool {
|
||||||
|
self.complete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,41 +280,27 @@ pub struct Uart<UartInstance, Pins> {
|
|||||||
pins: Pins,
|
pins: Pins,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// UART using the IRQ capabilities of the peripheral. Can be created with the
|
/// Serial receiver
|
||||||
/// [`Uart::into_uart_with_irq`] function. Currently, only the RX side for IRQ based reception
|
pub struct Rx<Uart>(Uart);
|
||||||
/// is implemented.
|
|
||||||
pub struct UartWithIrq<Uart, Pins> {
|
|
||||||
base: UartWithIrqBase<Uart>,
|
|
||||||
pins: Pins,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type-erased UART using the IRQ capabilities of the peripheral. Can be created with the
|
// Serial receiver, using interrupts to offload reading to the hardware.
|
||||||
/// [`UartWithIrq::downgrade`] function. Currently, only the RX side for IRQ based reception
|
pub struct RxWithIrq<Uart> {
|
||||||
/// is implemented.
|
inner: Rx<Uart>,
|
||||||
pub struct UartWithIrqBase<UART> {
|
|
||||||
pub inner: UartBase<UART>,
|
|
||||||
irq_info: IrqInfo,
|
irq_info: IrqInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serial receiver
|
|
||||||
pub struct Rx<Uart> {
|
|
||||||
uart: Uart,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serial transmitter
|
/// Serial transmitter
|
||||||
pub struct Tx<Uart> {
|
pub struct Tx<Uart>(Uart);
|
||||||
uart: Uart,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance> Rx<Uart> {
|
impl<Uart: Instance> Rx<Uart> {
|
||||||
fn new(uart: Uart) -> Self {
|
fn new(uart: Uart) -> Self {
|
||||||
Self { uart }
|
Self(uart)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Uart> Tx<Uart> {
|
impl<Uart> Tx<Uart> {
|
||||||
fn new(uart: Uart) -> Self {
|
fn new(uart: Uart) -> Self {
|
||||||
Self { uart }
|
Self(uart)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,20 +551,30 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// If the IRQ capabilities of the peripheral are used, the UART needs to be converted
|
/// If the IRQ capabilities of the peripheral are used, the UART needs to be converted
|
||||||
/// with this function
|
/// with this function. Currently, IRQ abstractions are only implemented for the RX part
|
||||||
pub fn into_uart_with_irq(self) -> UartWithIrq<UartInstance, (TxPinInst, RxPinInst)> {
|
/// of the UART, so this function will release a TX and RX handle as well as the pin
|
||||||
|
/// instances.
|
||||||
|
pub fn split_with_irq(
|
||||||
|
self,
|
||||||
|
) -> (
|
||||||
|
Tx<UartInstance>,
|
||||||
|
RxWithIrq<UartInstance>,
|
||||||
|
(TxPinInst, RxPinInst),
|
||||||
|
) {
|
||||||
let (inner, pins) = self.downgrade_internal();
|
let (inner, pins) = self.downgrade_internal();
|
||||||
UartWithIrq {
|
let (tx, rx) = inner.split();
|
||||||
pins,
|
(
|
||||||
base: UartWithIrqBase {
|
tx,
|
||||||
inner,
|
RxWithIrq {
|
||||||
|
inner: rx,
|
||||||
irq_info: IrqInfo {
|
irq_info: IrqInfo {
|
||||||
rx_len: 0,
|
rx_len: 0,
|
||||||
rx_idx: 0,
|
rx_idx: 0,
|
||||||
mode: IrqReceptionMode::Idle,
|
mode: IrqReceptionMode::Idle,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
pins,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate::delegate! {
|
delegate::delegate! {
|
||||||
@ -673,11 +632,26 @@ impl<Uart: Instance> Rx<Uart> {
|
|||||||
///
|
///
|
||||||
/// You must ensure that only registers related to the operation of the RX side are used.
|
/// You must ensure that only registers related to the operation of the RX side are used.
|
||||||
pub unsafe fn uart(&self) -> &Uart {
|
pub unsafe fn uart(&self) -> &Uart {
|
||||||
&self.uart
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn clear_fifo(&self) {
|
pub fn clear_fifo(&self) {
|
||||||
self.uart.fifo_clr().write(|w| w.rxfifo().set_bit());
|
self.0.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
self.0.enable().modify(|_, w| w.rxenable().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
self.0.enable().modify(|_, w| w.rxenable().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(self) -> Uart {
|
||||||
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -688,11 +662,22 @@ impl<Uart: Instance> Tx<Uart> {
|
|||||||
///
|
///
|
||||||
/// You must ensure that only registers related to the operation of the TX side are used.
|
/// You must ensure that only registers related to the operation of the TX side are used.
|
||||||
pub unsafe fn uart(&self) -> &Uart {
|
pub unsafe fn uart(&self) -> &Uart {
|
||||||
&self.uart
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn clear_fifo(&self) {
|
pub fn clear_fifo(&self) {
|
||||||
self.uart.fifo_clr().write(|w| w.txfifo().set_bit());
|
self.0.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
self.0.enable().modify(|_, w| w.txenable().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
self.0.enable().modify(|_, w| w.txenable().clear_bit());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -701,6 +686,7 @@ pub struct IrqUartError {
|
|||||||
overflow: bool,
|
overflow: bool,
|
||||||
framing: bool,
|
framing: bool,
|
||||||
parity: bool,
|
parity: bool,
|
||||||
|
other: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrqUartError {
|
impl IrqUartError {
|
||||||
@ -715,7 +701,7 @@ pub enum IrqError {
|
|||||||
Uart(IrqUartError),
|
Uart(IrqUartError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Uart: Instance> UartWithIrqBase<Uart> {
|
impl<Uart: Instance> RxWithIrq<Uart> {
|
||||||
/// This initializes a non-blocking read transfer using the IRQ capabilities of the UART
|
/// This initializes a non-blocking read transfer using the IRQ capabilities of the UART
|
||||||
/// peripheral.
|
/// peripheral.
|
||||||
///
|
///
|
||||||
@ -735,8 +721,7 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
|||||||
self.irq_info.mode = IrqReceptionMode::Pending;
|
self.irq_info.mode = IrqReceptionMode::Pending;
|
||||||
self.irq_info.rx_idx = 0;
|
self.irq_info.rx_idx = 0;
|
||||||
self.irq_info.rx_len = max_len;
|
self.irq_info.rx_len = max_len;
|
||||||
self.inner.enable_rx();
|
self.inner.enable();
|
||||||
self.inner.enable_tx();
|
|
||||||
self.enable_rx_irq_sources(enb_timeout_irq);
|
self.enable_rx_irq_sources(enb_timeout_irq);
|
||||||
unsafe { enable_interrupt(Uart::IRQ_RX) };
|
unsafe { enable_interrupt(Uart::IRQ_RX) };
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -744,7 +729,7 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn enable_rx_irq_sources(&mut self, timeout: bool) {
|
fn enable_rx_irq_sources(&mut self, timeout: bool) {
|
||||||
self.inner.uart.irq_enb().modify(|_, w| {
|
self.inner.0.irq_enb().modify(|_, w| {
|
||||||
if timeout {
|
if timeout {
|
||||||
w.irq_rx_to().set_bit();
|
w.irq_rx_to().set_bit();
|
||||||
}
|
}
|
||||||
@ -755,30 +740,24 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn disable_rx_irq_sources(&mut self) {
|
fn disable_rx_irq_sources(&mut self) {
|
||||||
self.inner.uart.irq_enb().modify(|_, w| {
|
self.inner.0.irq_enb().modify(|_, w| {
|
||||||
w.irq_rx_to().clear_bit();
|
w.irq_rx_to().clear_bit();
|
||||||
w.irq_rx_status().clear_bit();
|
w.irq_rx_status().clear_bit();
|
||||||
w.irq_rx().clear_bit()
|
w.irq_rx().clear_bit()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn enable_tx(&mut self) {
|
|
||||||
self.inner.enable_tx()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn disable_tx(&mut self) {
|
|
||||||
self.inner.disable_tx()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cancel_transfer(&mut self) {
|
pub fn cancel_transfer(&mut self) {
|
||||||
self.disable_rx_irq_sources();
|
self.disable_rx_irq_sources();
|
||||||
self.inner.clear_tx_fifo();
|
self.inner.clear_fifo();
|
||||||
self.irq_info.rx_idx = 0;
|
self.irq_info.rx_idx = 0;
|
||||||
self.irq_info.rx_len = 0;
|
self.irq_info.rx_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uart(&self) -> &Uart {
|
||||||
|
&self.inner.0
|
||||||
|
}
|
||||||
|
|
||||||
/// Default IRQ handler which can be used to read the packets arriving on the UART peripheral.
|
/// Default IRQ handler which can be used to read the packets arriving on the UART peripheral.
|
||||||
///
|
///
|
||||||
/// If passed buffer is equal to or larger than the specified maximum length, an
|
/// If passed buffer is equal to or larger than the specified maximum length, an
|
||||||
@ -791,18 +770,38 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
let mut res = IrqResult::default();
|
let mut res = IrqResult::default();
|
||||||
let mut possible_error = IrqUartError::default();
|
|
||||||
|
|
||||||
let rx_status = self.inner.uart.rxstatus().read();
|
let irq_end = self.inner.0.irq_end().read();
|
||||||
res.raw_res = rx_status.bits();
|
let enb_status = self.inner.0.enable().read();
|
||||||
let irq_end = self.inner.uart.irq_end().read();
|
|
||||||
let enb_status = self.inner.uart.enable().read();
|
|
||||||
let rx_enabled = enb_status.rxenable().bit_is_set();
|
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||||
let _tx_enabled = enb_status.txenable().bit_is_set();
|
|
||||||
let read_handler = |res: &mut IrqResult,
|
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
||||||
possible_error: &mut IrqUartError,
|
if irq_end.irq_rx().bit_is_set() {
|
||||||
read_res: nb::Result<u8, Error>|
|
// Determine the number of bytes to read, ensuring we leave 1 byte in the FIFO.
|
||||||
-> Option<u8> {
|
// We use this trick/hack because the timeout feature of the peripheral relies on data
|
||||||
|
// being in the RX FIFO. If data continues arriving, another half-full IRQ will fire.
|
||||||
|
// If not, the last byte(s) is/are emptied by the timeout interrupt.
|
||||||
|
let available_bytes = self.inner.0.rxfifoirqtrg().read().bits() as usize;
|
||||||
|
|
||||||
|
let bytes_to_read = core::cmp::min(
|
||||||
|
available_bytes.saturating_sub(1),
|
||||||
|
self.irq_info.rx_len - self.irq_info.rx_idx,
|
||||||
|
);
|
||||||
|
|
||||||
|
// If this interrupt bit is set, the trigger level is available at the very least.
|
||||||
|
// Read everything as fast as possible
|
||||||
|
for _ in 0..bytes_to_read {
|
||||||
|
buf[self.irq_info.rx_idx] = (self.inner.0.data().read().bits() & 0xff) as u8;
|
||||||
|
self.irq_info.rx_idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On high-baudrates, data might be available immediately, and we possible have to
|
||||||
|
// read continuosly? Then again, the CPU should always be faster than that. I'd rather
|
||||||
|
// rely on the hardware firing another IRQ. I have not tried baudrates higher than
|
||||||
|
// 115200 so far.
|
||||||
|
}
|
||||||
|
let read_handler =
|
||||||
|
|possible_error: &mut IrqUartError, read_res: nb::Result<u8, Error>| -> Option<u8> {
|
||||||
match read_res {
|
match read_res {
|
||||||
Ok(byte) => Some(byte),
|
Ok(byte) => Some(byte),
|
||||||
Err(nb::Error::WouldBlock) => None,
|
Err(nb::Error::WouldBlock) => None,
|
||||||
@ -818,77 +817,55 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
|||||||
possible_error.parity = true;
|
possible_error.parity = true;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
res.set_result(IrqResultMask::Unknown);
|
possible_error.other = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if irq_end.irq_rx().bit_is_set() {
|
// Timeout, empty the FIFO completely.
|
||||||
// If this interrupt bit is set, the trigger level is available at the very least.
|
if irq_end.irq_rx_to().bit_is_set() {
|
||||||
// Read everything as fast as possible
|
|
||||||
for _ in 0..core::cmp::min(
|
|
||||||
self.inner.uart.rxfifoirqtrg().read().bits() as usize,
|
|
||||||
self.irq_info.rx_len,
|
|
||||||
) {
|
|
||||||
buf[self.irq_info.rx_idx] = (self.inner.uart.data().read().bits() & 0xff) as u8;
|
|
||||||
self.irq_info.rx_idx += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// While there is data in the FIFO, write it into the reception buffer
|
// While there is data in the FIFO, write it into the reception buffer
|
||||||
loop {
|
loop {
|
||||||
if self.irq_info.rx_idx == self.irq_info.rx_len {
|
if self.irq_info.rx_idx == self.irq_info.rx_len {
|
||||||
self.irq_completion_handler(&mut res);
|
break;
|
||||||
return Ok(res);
|
|
||||||
}
|
}
|
||||||
if let Some(byte) = read_handler(&mut res, &mut possible_error, self.inner.read()) {
|
if let Some(byte) = read_handler(&mut res.errors, self.inner.read()) {
|
||||||
buf[self.irq_info.rx_idx] = byte;
|
buf[self.irq_info.rx_idx] = byte;
|
||||||
self.irq_info.rx_idx += 1;
|
self.irq_info.rx_idx += 1;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.irq_completion_handler(&mut res);
|
||||||
|
return Ok(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
// RX transfer not complete, check for RX errors
|
// RX transfer not complete, check for RX errors
|
||||||
if (self.irq_info.rx_idx < self.irq_info.rx_len) && rx_enabled {
|
if (self.irq_info.rx_idx < self.irq_info.rx_len) && rx_enabled {
|
||||||
// Read status register again, might have changed since reading received data
|
// Read status register again, might have changed since reading received data
|
||||||
let rx_status = self.inner.uart.rxstatus().read();
|
let rx_status = self.inner.0.rxstatus().read();
|
||||||
res.raw_res = rx_status.bits();
|
|
||||||
if rx_status.rxovr().bit_is_set() {
|
if rx_status.rxovr().bit_is_set() {
|
||||||
possible_error.overflow = true;
|
res.errors.overflow = true;
|
||||||
}
|
}
|
||||||
if rx_status.rxfrm().bit_is_set() {
|
if rx_status.rxfrm().bit_is_set() {
|
||||||
possible_error.framing = true;
|
res.errors.framing = true;
|
||||||
}
|
}
|
||||||
if rx_status.rxpar().bit_is_set() {
|
if rx_status.rxpar().bit_is_set() {
|
||||||
possible_error.parity = true;
|
res.errors.parity = true;
|
||||||
}
|
|
||||||
if rx_status.rxto().bit_is_set() {
|
|
||||||
// A timeout has occured but there might be some leftover data in the FIFO,
|
|
||||||
// so read that data as well
|
|
||||||
while let Some(byte) =
|
|
||||||
read_handler(&mut res, &mut possible_error, self.inner.read())
|
|
||||||
{
|
|
||||||
buf[self.irq_info.rx_idx] = byte;
|
|
||||||
self.irq_info.rx_idx += 1;
|
|
||||||
}
|
|
||||||
self.irq_completion_handler(&mut res);
|
|
||||||
res.set_result(IrqResultMask::Timeout);
|
|
||||||
return Ok(res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it is not a timeout, it's an error
|
// If it is not a timeout, it's an error
|
||||||
if possible_error.error() {
|
if res.error() {
|
||||||
self.disable_rx_irq_sources();
|
self.disable_rx_irq_sources();
|
||||||
return Err(IrqError::Uart(possible_error));
|
return Err(IrqError::Uart(res.errors));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the interrupt status bits
|
// Clear the interrupt status bits
|
||||||
self.inner
|
self.inner
|
||||||
.uart
|
.0
|
||||||
.irq_clr()
|
.irq_clr()
|
||||||
.write(|w| unsafe { w.bits(irq_end.bits()) });
|
.write(|w| unsafe { w.bits(irq_end.bits()) });
|
||||||
Ok(res)
|
Ok(res)
|
||||||
@ -896,48 +873,23 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
|||||||
|
|
||||||
fn irq_completion_handler(&mut self, res: &mut IrqResult) {
|
fn irq_completion_handler(&mut self, res: &mut IrqResult) {
|
||||||
self.disable_rx_irq_sources();
|
self.disable_rx_irq_sources();
|
||||||
self.inner.disable_rx();
|
self.inner.disable();
|
||||||
res.bytes_read = self.irq_info.rx_idx;
|
res.bytes_read = self.irq_info.rx_idx;
|
||||||
res.clear_result();
|
res.complete = true;
|
||||||
res.set_result(IrqResultMask::Complete);
|
|
||||||
self.irq_info.mode = IrqReceptionMode::Idle;
|
self.irq_info.mode = IrqReceptionMode::Idle;
|
||||||
self.irq_info.rx_idx = 0;
|
self.irq_info.rx_idx = 0;
|
||||||
self.irq_info.rx_len = 0;
|
self.irq_info.rx_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn irq_info(&self) -> &IrqInfo {
|
||||||
|
&self.irq_info
|
||||||
|
}
|
||||||
|
|
||||||
pub fn release(self) -> Uart {
|
pub fn release(self) -> Uart {
|
||||||
self.inner.release()
|
self.inner.release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Uart: Instance, Pins> UartWithIrq<Uart, Pins> {
|
|
||||||
/// See [`UartWithIrqBase::read_fixed_len_using_irq`] doc
|
|
||||||
pub fn read_fixed_len_using_irq(
|
|
||||||
&mut self,
|
|
||||||
max_len: usize,
|
|
||||||
enb_timeout_irq: bool,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.base.read_fixed_len_using_irq(max_len, enb_timeout_irq)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cancel_transfer(&mut self) {
|
|
||||||
self.base.cancel_transfer()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`UartWithIrqBase::irq_handler`] doc
|
|
||||||
pub fn irq_handler(&mut self, buf: &mut [u8]) -> Result<IrqResult, IrqError> {
|
|
||||||
self.base.irq_handler(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn release(self) -> (Uart, Pins) {
|
|
||||||
(self.base.release(), self.pins)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn downgrade(self) -> (UartWithIrqBase<Uart>, Pins) {
|
|
||||||
(self.base, self.pins)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl embedded_io::Error for Error {
|
impl embedded_io::Error for Error {
|
||||||
fn kind(&self) -> embedded_io::ErrorKind {
|
fn kind(&self) -> embedded_io::ErrorKind {
|
||||||
embedded_io::ErrorKind::Other
|
embedded_io::ErrorKind::Other
|
||||||
|
Loading…
Reference in New Issue
Block a user