Compare commits
2 commits
bccf6456c5
...
55d0dace03
| Author | SHA1 | Date | |
|---|---|---|---|
| 55d0dace03 | |||
| 305ec83621 |
7 changed files with 365 additions and 176 deletions
48
firmware/Cargo.lock
generated
48
firmware/Cargo.lock
generated
|
|
@ -74,9 +74,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.0"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "block-device-driver"
|
||||
|
|
@ -245,7 +245,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "embassy-embedded-hal"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
|
||||
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
|
||||
dependencies = [
|
||||
"defmt 0.3.100",
|
||||
"embassy-futures",
|
||||
|
|
@ -263,7 +263,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "embassy-executor"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
|
||||
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
|
||||
dependencies = [
|
||||
"cortex-m",
|
||||
"critical-section",
|
||||
|
|
@ -275,7 +275,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "embassy-executor-macros"
|
||||
version = "0.6.2"
|
||||
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
|
||||
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
|
|
@ -286,7 +286,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "embassy-futures"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
|
||||
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
|
||||
dependencies = [
|
||||
"defmt 0.3.100",
|
||||
]
|
||||
|
|
@ -294,7 +294,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "embassy-hal-internal"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
|
||||
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
|
||||
dependencies = [
|
||||
"cortex-m",
|
||||
"critical-section",
|
||||
|
|
@ -305,7 +305,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "embassy-net-driver"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
|
||||
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
|
||||
dependencies = [
|
||||
"defmt 0.3.100",
|
||||
]
|
||||
|
|
@ -313,11 +313,11 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "embassy-stm32"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
|
||||
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
|
||||
dependencies = [
|
||||
"aligned",
|
||||
"bit_field",
|
||||
"bitflags 2.9.0",
|
||||
"bitflags 2.9.1",
|
||||
"block-device-driver",
|
||||
"cfg-if",
|
||||
"cortex-m",
|
||||
|
|
@ -360,7 +360,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "embassy-sync"
|
||||
version = "0.6.2"
|
||||
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
|
||||
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"critical-section",
|
||||
|
|
@ -374,7 +374,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "embassy-time"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
|
||||
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"critical-section",
|
||||
|
|
@ -390,7 +390,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "embassy-time-driver"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
|
||||
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
|
||||
dependencies = [
|
||||
"document-features",
|
||||
]
|
||||
|
|
@ -398,7 +398,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "embassy-time-queue-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
|
||||
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
|
||||
dependencies = [
|
||||
"embassy-executor",
|
||||
"heapless",
|
||||
|
|
@ -407,7 +407,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "embassy-usb-driver"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
|
||||
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
|
||||
dependencies = [
|
||||
"defmt 0.3.100",
|
||||
"embedded-io-async",
|
||||
|
|
@ -416,7 +416,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "embassy-usb-synopsys-otg"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
|
||||
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"defmt 0.3.100",
|
||||
|
|
@ -516,9 +516,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.11"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
|
||||
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
|
|
@ -635,9 +635,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
|||
|
||||
[[package]]
|
||||
name = "micropb"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fba9d45f70365e5ecfe23bc1dbe523ce5c3f2e81aa762880961b61ab25c99a7"
|
||||
checksum = "bff8c16c9fb4b03e9b23e0bc17ce3b67465c3f0f75a004a65d604d382c13b7bd"
|
||||
dependencies = [
|
||||
"heapless",
|
||||
"never",
|
||||
|
|
@ -646,9 +646,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "micropb-gen"
|
||||
version = "0.1.0"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21d0a9d827869dd3d834e6992c6a8fdb4229a82b44afe0679e82efe14c50643e"
|
||||
checksum = "dd8b5b73c6511641cc191a1c7ba95cbe86abb9801dcb6f2a6d8d23b79836ee31"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"micropb",
|
||||
|
|
@ -862,7 +862,7 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"bitflags 2.9.1",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
|
|
@ -1100,5 +1100,5 @@ version = "0.39.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ embedded-resources = { version = "0.2.1", features = ["stm32"] }
|
|||
portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] }
|
||||
static_cell = "2.1.0"
|
||||
|
||||
thiserror = { version = "2.0.12", default-features = false }
|
||||
micropb = { version = "0.1.1", default-features = false, features = [ "encode", "decode", "container-heapless" ] }
|
||||
|
||||
defmt = { version = "1.0.0", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
panic-probe = { version = "1.0.0", optional = true }
|
||||
panic-halt = "1.0.0"
|
||||
thiserror = { version = "2.0.12", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
micropb-gen = "0.1.0"
|
||||
|
|
@ -35,7 +35,7 @@ micropb-gen = "0.1.0"
|
|||
default = ["board-nucleo64", "status-led", "trace"]
|
||||
board-nucleo64 = []
|
||||
status-led = []
|
||||
use-copy-within = []
|
||||
copy-within = []
|
||||
trace = [
|
||||
"dep:defmt",
|
||||
"dep:defmt-rtt",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ extern crate core;
|
|||
|
||||
mod crc_engine;
|
||||
mod tasks;
|
||||
mod uarts;
|
||||
mod uart;
|
||||
|
||||
use core::mem;
|
||||
use embassy_executor::Spawner;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use crate::uarts::{UartReadError, read_frame_fragment};
|
||||
use crate::{crc_engine, println};
|
||||
use crate::{crc_engine, println, uart};
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use core::str::FromStr;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::{gpio, usart};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use embedded_io_async::{Read, Write};
|
||||
use embedded_io_async::Write;
|
||||
use static_cell::StaticCell;
|
||||
|
||||
use micropb::{
|
||||
|
|
@ -132,104 +131,67 @@ async fn tx_task(mut tx: Tx, crc: crc_engine::CrcHandle) {
|
|||
|
||||
#[embassy_executor::task]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
async fn rx_task(mut rx: Rx, crc: crc_engine::CrcHandle) {
|
||||
loop {
|
||||
// look for a sync byte in the buffer; the buffer may have
|
||||
// leftover bytes from a previous cycle through this loop
|
||||
// (due to an incomplete message, or a message which failed
|
||||
// validation)
|
||||
if let Some(sync) = rx.message_buf.iter().position(|&x| x == SYNC_BYTE) {
|
||||
// if a sync byte was found, reset the buffer to the data
|
||||
// following it
|
||||
//
|
||||
let post_bytes = rx.message_buf.len() - sync - 1;
|
||||
#[cfg(not(feature = "use-copy-within"))]
|
||||
for i in 0..post_bytes {
|
||||
rx.message_buf[i] = rx.message_buf[sync + 1 + i];
|
||||
}
|
||||
#[cfg(feature = "use-copy-within")]
|
||||
rx.message_buf.copy_within(sync + 1.., 0);
|
||||
rx.message_buf.truncate(post_bytes);
|
||||
} else {
|
||||
// wait for a sync byte to arrive
|
||||
let mut buf = [0_u8; 1];
|
||||
loop {
|
||||
if (rx.uart.read_exact(&mut buf).await).is_ok() && buf[0] == SYNC_BYTE {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
async fn rx_task(rx: Rx, crc: crc_engine::CrcHandle) {
|
||||
let mut frx = uart::framed_rx::FramedUartRx::new(
|
||||
rx.message_buf,
|
||||
rx.uart,
|
||||
SYNC_BYTE,
|
||||
Duration::from_millis(250),
|
||||
Duration::from_millis(50),
|
||||
);
|
||||
|
||||
loop {
|
||||
frx.wait_for_frame_sync().await;
|
||||
println!("received sync byte from host");
|
||||
|
||||
// sync byte was seen, read the length of the message; if
|
||||
// there aren't enough bytes in the buffer, wait for them,
|
||||
// with timeouts
|
||||
match read_frame_fragment(
|
||||
rx.message_buf,
|
||||
0,
|
||||
MESSAGE_LENGTH_SIZE,
|
||||
&mut rx.uart,
|
||||
Duration::from_millis(250),
|
||||
Duration::from_millis(50),
|
||||
)
|
||||
.await
|
||||
{
|
||||
match frx.read_frame_fragment(0, MESSAGE_LENGTH_SIZE).await {
|
||||
Ok(()) => {}
|
||||
Err(UartReadError::Ready) => {
|
||||
println!("uart ready error reading message length");
|
||||
rx.message_buf.clear();
|
||||
Err(uart::ReadError::ReadyCheck) => {
|
||||
println!("uart ready-check error reading message length");
|
||||
frx.buf.clear();
|
||||
continue;
|
||||
}
|
||||
Err(UartReadError::BufferCapacity) => {
|
||||
Err(uart::ReadError::BufferCapacity) => {
|
||||
println!("insufficient space to read message length");
|
||||
rx.message_buf.clear();
|
||||
frx.buf.clear();
|
||||
continue;
|
||||
}
|
||||
Err(UartReadError::Timeout) => {
|
||||
Err(uart::ReadError::Timeout) => {
|
||||
// restart loop, since the sync byte seen wasn't
|
||||
// the beginning of a message, or the sender
|
||||
// stopped sending
|
||||
println!("timeout waiting for message message length");
|
||||
println!("timeout waiting for message length");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// get the message length from the buffer
|
||||
let message_len = LittleEndian::read_u16(&rx.message_buf[..MESSAGE_LENGTH_SIZE]) as usize;
|
||||
|
||||
let message_len = LittleEndian::read_u16(&frx.buf[..MESSAGE_LENGTH_SIZE]) as usize;
|
||||
println!("receiving message of {=u16} bytes", message_len as u16);
|
||||
|
||||
// read the message; if there aren't enough bytes in the
|
||||
// buffer for the message, wait for them to arrive, with
|
||||
// timeouts
|
||||
// buffer for the message, wait for them, with timeouts
|
||||
let message_pos = MESSAGE_LENGTH_SIZE;
|
||||
match read_frame_fragment(
|
||||
rx.message_buf,
|
||||
message_pos,
|
||||
message_len,
|
||||
&mut rx.uart,
|
||||
Duration::from_millis(250),
|
||||
Duration::from_millis(50),
|
||||
)
|
||||
.await
|
||||
{
|
||||
match frx.read_frame_fragment(message_pos, message_len).await {
|
||||
Ok(()) => {}
|
||||
Err(UartReadError::Ready) => {
|
||||
println!("uart ready error reading message");
|
||||
rx.message_buf.clear();
|
||||
Err(uart::ReadError::ReadyCheck) => {
|
||||
println!("uart ready-check error reading message body");
|
||||
frx.buf.clear();
|
||||
continue;
|
||||
}
|
||||
Err(UartReadError::BufferCapacity) => {
|
||||
println!("insufficient space to read message");
|
||||
rx.message_buf.clear();
|
||||
Err(uart::ReadError::BufferCapacity) => {
|
||||
println!("insufficient space to read message body");
|
||||
frx.buf.clear();
|
||||
continue;
|
||||
}
|
||||
Err(UartReadError::Timeout) => {
|
||||
Err(uart::ReadError::Timeout) => {
|
||||
// restart loop, since the sync byte seen wasn't
|
||||
// the beginning of a message, or the sender
|
||||
// stopped sending
|
||||
println!("timeout waiting for message message");
|
||||
println!("timeout waiting for message body");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
@ -237,28 +199,19 @@ async fn rx_task(mut rx: Rx, crc: crc_engine::CrcHandle) {
|
|||
// read the CRC-16 of the message; if there aren't enough
|
||||
// bytes in the buffer, wait for them, with timeouts
|
||||
let crc_pos = message_pos + message_len;
|
||||
match read_frame_fragment(
|
||||
rx.message_buf,
|
||||
crc_pos,
|
||||
MESSAGE_CRC_SIZE,
|
||||
&mut rx.uart,
|
||||
Duration::from_millis(250),
|
||||
Duration::from_millis(50),
|
||||
)
|
||||
.await
|
||||
{
|
||||
match frx.read_frame_fragment(crc_pos, MESSAGE_CRC_SIZE).await {
|
||||
Ok(()) => {}
|
||||
Err(UartReadError::Ready) => {
|
||||
println!("uart ready error reading CRC-16");
|
||||
rx.message_buf.clear();
|
||||
Err(uart::ReadError::ReadyCheck) => {
|
||||
println!("uart ready-check error reading CRC-16");
|
||||
frx.buf.clear();
|
||||
continue;
|
||||
}
|
||||
Err(UartReadError::BufferCapacity) => {
|
||||
Err(uart::ReadError::BufferCapacity) => {
|
||||
println!("insufficient space to read CRC-16");
|
||||
rx.message_buf.clear();
|
||||
frx.buf.clear();
|
||||
continue;
|
||||
}
|
||||
Err(UartReadError::Timeout) => {
|
||||
Err(uart::ReadError::Timeout) => {
|
||||
// restart loop, since the sync byte seen wasn't
|
||||
// the beginning of a message, or the sender
|
||||
// stopped sending
|
||||
|
|
@ -268,9 +221,9 @@ async fn rx_task(mut rx: Rx, crc: crc_engine::CrcHandle) {
|
|||
}
|
||||
|
||||
// get the expected CRC-16 from the buffer
|
||||
let expected_crc = LittleEndian::read_u16(&rx.message_buf[crc_pos..MESSAGE_CRC_SIZE]);
|
||||
let expected_crc = LittleEndian::read_u16(&frx.buf[crc_pos..MESSAGE_CRC_SIZE]);
|
||||
// compute the actual CRC-16
|
||||
let computed_crc = crc.compute(&rx.message_buf[message_pos..message_len]).await;
|
||||
let computed_crc = crc.compute(&frx.buf[message_pos..message_len]).await;
|
||||
|
||||
if computed_crc != expected_crc {
|
||||
println!(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
pub mod echo_ {
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EchoRequest {
|
||||
pub r#data: u32,
|
||||
}
|
||||
|
|
@ -10,7 +10,37 @@ pub mod echo_ {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl EchoRequest {}
|
||||
impl ::core::cmp::PartialEq for EchoRequest {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let mut ret = true;
|
||||
ret &= (self.r#data == other.r#data);
|
||||
ret
|
||||
}
|
||||
}
|
||||
impl EchoRequest {
|
||||
///Return a reference to `data`
|
||||
#[inline]
|
||||
pub fn r#data(&self) -> &u32 {
|
||||
&self.r#data
|
||||
}
|
||||
///Return a mutable reference to `data`
|
||||
#[inline]
|
||||
pub fn mut_data(&mut self) -> &mut u32 {
|
||||
&mut self.r#data
|
||||
}
|
||||
///Set the value of `data`
|
||||
#[inline]
|
||||
pub fn set_data(&mut self, value: u32) -> &mut Self {
|
||||
self.r#data = value.into();
|
||||
self
|
||||
}
|
||||
///Builder method that sets the value of `data`. Useful for initializing the message.
|
||||
#[inline]
|
||||
pub fn init_data(mut self, value: u32) -> Self {
|
||||
self.r#data = value.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
impl ::micropb::MessageDecode for EchoRequest {
|
||||
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
|
||||
&mut self,
|
||||
|
|
@ -68,7 +98,7 @@ pub mod echo_ {
|
|||
size
|
||||
}
|
||||
}
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EchoResponse {
|
||||
pub r#data: u32,
|
||||
}
|
||||
|
|
@ -79,7 +109,37 @@ pub mod echo_ {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl EchoResponse {}
|
||||
impl ::core::cmp::PartialEq for EchoResponse {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let mut ret = true;
|
||||
ret &= (self.r#data == other.r#data);
|
||||
ret
|
||||
}
|
||||
}
|
||||
impl EchoResponse {
|
||||
///Return a reference to `data`
|
||||
#[inline]
|
||||
pub fn r#data(&self) -> &u32 {
|
||||
&self.r#data
|
||||
}
|
||||
///Return a mutable reference to `data`
|
||||
#[inline]
|
||||
pub fn mut_data(&mut self) -> &mut u32 {
|
||||
&mut self.r#data
|
||||
}
|
||||
///Set the value of `data`
|
||||
#[inline]
|
||||
pub fn set_data(&mut self, value: u32) -> &mut Self {
|
||||
self.r#data = value.into();
|
||||
self
|
||||
}
|
||||
///Builder method that sets the value of `data`. Useful for initializing the message.
|
||||
#[inline]
|
||||
pub fn init_data(mut self, value: u32) -> Self {
|
||||
self.r#data = value.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
impl ::micropb::MessageDecode for EchoResponse {
|
||||
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
|
||||
&mut self,
|
||||
|
|
@ -139,7 +199,7 @@ pub mod echo_ {
|
|||
}
|
||||
}
|
||||
pub mod test_ {
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TestResponse {
|
||||
pub r#f1: u32,
|
||||
pub r#f2: ::micropb::heapless::String<16>,
|
||||
|
|
@ -156,7 +216,106 @@ pub mod test_ {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl TestResponse {}
|
||||
impl ::core::cmp::PartialEq for TestResponse {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let mut ret = true;
|
||||
ret &= (self.r#f1 == other.r#f1);
|
||||
ret &= (self.r#f2 == other.r#f2);
|
||||
ret &= (self.r#f3 == other.r#f3);
|
||||
ret &= (self.r#f4 == other.r#f4);
|
||||
ret
|
||||
}
|
||||
}
|
||||
impl TestResponse {
|
||||
///Return a reference to `f1`
|
||||
#[inline]
|
||||
pub fn r#f1(&self) -> &u32 {
|
||||
&self.r#f1
|
||||
}
|
||||
///Return a mutable reference to `f1`
|
||||
#[inline]
|
||||
pub fn mut_f1(&mut self) -> &mut u32 {
|
||||
&mut self.r#f1
|
||||
}
|
||||
///Set the value of `f1`
|
||||
#[inline]
|
||||
pub fn set_f1(&mut self, value: u32) -> &mut Self {
|
||||
self.r#f1 = value.into();
|
||||
self
|
||||
}
|
||||
///Builder method that sets the value of `f1`. Useful for initializing the message.
|
||||
#[inline]
|
||||
pub fn init_f1(mut self, value: u32) -> Self {
|
||||
self.r#f1 = value.into();
|
||||
self
|
||||
}
|
||||
///Return a reference to `f2`
|
||||
#[inline]
|
||||
pub fn r#f2(&self) -> &::micropb::heapless::String<16> {
|
||||
&self.r#f2
|
||||
}
|
||||
///Return a mutable reference to `f2`
|
||||
#[inline]
|
||||
pub fn mut_f2(&mut self) -> &mut ::micropb::heapless::String<16> {
|
||||
&mut self.r#f2
|
||||
}
|
||||
///Set the value of `f2`
|
||||
#[inline]
|
||||
pub fn set_f2(&mut self, value: ::micropb::heapless::String<16>) -> &mut Self {
|
||||
self.r#f2 = value.into();
|
||||
self
|
||||
}
|
||||
///Builder method that sets the value of `f2`. Useful for initializing the message.
|
||||
#[inline]
|
||||
pub fn init_f2(mut self, value: ::micropb::heapless::String<16>) -> Self {
|
||||
self.r#f2 = value.into();
|
||||
self
|
||||
}
|
||||
///Return a reference to `f3`
|
||||
#[inline]
|
||||
pub fn r#f3(&self) -> &bool {
|
||||
&self.r#f3
|
||||
}
|
||||
///Return a mutable reference to `f3`
|
||||
#[inline]
|
||||
pub fn mut_f3(&mut self) -> &mut bool {
|
||||
&mut self.r#f3
|
||||
}
|
||||
///Set the value of `f3`
|
||||
#[inline]
|
||||
pub fn set_f3(&mut self, value: bool) -> &mut Self {
|
||||
self.r#f3 = value.into();
|
||||
self
|
||||
}
|
||||
///Builder method that sets the value of `f3`. Useful for initializing the message.
|
||||
#[inline]
|
||||
pub fn init_f3(mut self, value: bool) -> Self {
|
||||
self.r#f3 = value.into();
|
||||
self
|
||||
}
|
||||
///Return a reference to `f4`
|
||||
#[inline]
|
||||
pub fn r#f4(&self) -> &::micropb::heapless::Vec<u8, 8> {
|
||||
&self.r#f4
|
||||
}
|
||||
///Return a mutable reference to `f4`
|
||||
#[inline]
|
||||
pub fn mut_f4(&mut self) -> &mut ::micropb::heapless::Vec<u8, 8> {
|
||||
&mut self.r#f4
|
||||
}
|
||||
///Set the value of `f4`
|
||||
#[inline]
|
||||
pub fn set_f4(&mut self, value: ::micropb::heapless::Vec<u8, 8>) -> &mut Self {
|
||||
self.r#f4 = value.into();
|
||||
self
|
||||
}
|
||||
///Builder method that sets the value of `f4`. Useful for initializing the message.
|
||||
#[inline]
|
||||
pub fn init_f4(mut self, value: ::micropb::heapless::Vec<u8, 8>) -> Self {
|
||||
self.r#f4 = value.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
impl ::micropb::MessageDecode for TestResponse {
|
||||
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
|
||||
&mut self,
|
||||
|
|
@ -286,7 +445,7 @@ pub mod api_ {
|
|||
Resp(super::Response),
|
||||
}
|
||||
}
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Message {
|
||||
pub r#inner: ::core::option::Option<Message_::Inner>,
|
||||
}
|
||||
|
|
@ -297,6 +456,13 @@ pub mod api_ {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl ::core::cmp::PartialEq for Message {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let mut ret = true;
|
||||
ret &= (self.r#inner == other.r#inner);
|
||||
ret
|
||||
}
|
||||
}
|
||||
impl Message {}
|
||||
impl ::micropb::MessageDecode for Message {
|
||||
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
|
||||
|
|
@ -402,7 +568,7 @@ pub mod api_ {
|
|||
Echo(super::super::echo_::EchoRequest),
|
||||
}
|
||||
}
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Request {
|
||||
pub r#msg: ::core::option::Option<Request_::Msg>,
|
||||
}
|
||||
|
|
@ -413,6 +579,13 @@ pub mod api_ {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl ::core::cmp::PartialEq for Request {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let mut ret = true;
|
||||
ret &= (self.r#msg == other.r#msg);
|
||||
ret
|
||||
}
|
||||
}
|
||||
impl Request {}
|
||||
impl ::micropb::MessageDecode for Request {
|
||||
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
|
||||
|
|
@ -491,7 +664,7 @@ pub mod api_ {
|
|||
Test(super::super::test_::TestResponse),
|
||||
}
|
||||
}
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Response {
|
||||
pub r#msg: ::core::option::Option<Response_::Msg>,
|
||||
}
|
||||
|
|
@ -502,6 +675,13 @@ pub mod api_ {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl ::core::cmp::PartialEq for Response {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let mut ret = true;
|
||||
ret &= (self.r#msg == other.r#msg);
|
||||
ret
|
||||
}
|
||||
}
|
||||
impl Response {}
|
||||
impl ::micropb::MessageDecode for Response {
|
||||
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
|
||||
|
|
|
|||
100
firmware/src/uart/framed_rx.rs
Normal file
100
firmware/src/uart/framed_rx.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
use crate::uart;
|
||||
use embassy_stm32::usart;
|
||||
use embassy_time::Duration;
|
||||
use embedded_io_async::Read;
|
||||
use micropb::heapless::Vec;
|
||||
|
||||
pub struct FramedUartRx<const N: usize> {
|
||||
pub buf: &'static mut Vec<u8, N>,
|
||||
uart: usart::BufferedUartRx<'static>,
|
||||
sync: u8,
|
||||
first_byte_timeout: Duration,
|
||||
between_bytes_timeout: Duration,
|
||||
}
|
||||
|
||||
impl<const N: usize> FramedUartRx<N> {
|
||||
pub fn new(
|
||||
buf: &'static mut Vec<u8, N>,
|
||||
uart: usart::BufferedUartRx<'static>,
|
||||
sync: u8,
|
||||
first_byte_timeout: Duration,
|
||||
between_bytes_timeout: Duration,
|
||||
) -> Self {
|
||||
Self {
|
||||
buf,
|
||||
uart,
|
||||
sync,
|
||||
first_byte_timeout,
|
||||
between_bytes_timeout,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn wait_for_frame_sync(&mut self) {
|
||||
// look for a sync byte in the buffer; the buffer may have
|
||||
// leftover bytes from a previous cycle (due to an incomplete
|
||||
// message, or a message which failed validation)
|
||||
if let Some(sync_pos) = self.buf.iter().position(|&x| x == self.sync) {
|
||||
// if a sync byte was found, reset the buffer to the data
|
||||
// following it
|
||||
//
|
||||
let post_bytes = self.buf.len() - sync_pos - 1;
|
||||
#[cfg(not(feature = "copy-within"))]
|
||||
for i in 0..post_bytes {
|
||||
self.buf[i] = self.buf[sync_pos + 1 + i];
|
||||
}
|
||||
#[cfg(feature = "copy-within")]
|
||||
self.buf.copy_within(sync_pos + 1.., 0);
|
||||
self.buf.truncate(post_bytes);
|
||||
} else {
|
||||
// wait for a sync byte to arrive
|
||||
let mut buf = [0_u8; 1];
|
||||
loop {
|
||||
if (self.uart.read_exact(&mut buf).await).is_ok() && buf[0] == self.sync {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read_frame_fragment(
|
||||
&mut self,
|
||||
fragment_pos: usize,
|
||||
fragment_len: usize,
|
||||
) -> Result<(), uart::ReadError> {
|
||||
// compute the number of bytes available in the buffer, and the
|
||||
// number of bytes to read
|
||||
let bytes_available = self.buf[fragment_pos..].len();
|
||||
let bytes_to_read = fragment_len.saturating_sub(bytes_available);
|
||||
if bytes_to_read == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// if the fragment will not fit in the buffer's available space,
|
||||
// return an error
|
||||
if bytes_to_read > (self.buf.capacity() - self.buf.len()) {
|
||||
return Err(uart::ReadError::BufferCapacity);
|
||||
}
|
||||
|
||||
// if there aren't enough bytes in the buffer for the
|
||||
// fragment, wait for them to arrive, with a timeout
|
||||
if let Ok(r) = uart::read_with_timeouts(
|
||||
&mut self.uart,
|
||||
&mut self.buf[fragment_pos + bytes_available..bytes_to_read],
|
||||
self.first_byte_timeout,
|
||||
self.between_bytes_timeout,
|
||||
)
|
||||
.await
|
||||
{
|
||||
unsafe {
|
||||
self.buf.set_len(fragment_pos + bytes_available + r);
|
||||
}
|
||||
if r < bytes_to_read {
|
||||
return Err(uart::ReadError::Timeout);
|
||||
}
|
||||
} else {
|
||||
return Err(uart::ReadError::Timeout);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -2,15 +2,16 @@ use embassy_stm32::usart;
|
|||
use embassy_time::{Duration, WithTimeout};
|
||||
use embedded_io::ReadReady;
|
||||
use embedded_io_async::Read;
|
||||
use micropb::heapless::Vec;
|
||||
use thiserror::Error;
|
||||
|
||||
pub mod framed_rx;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum UartReadError {
|
||||
pub enum ReadError {
|
||||
#[error("timeout expired waiting for data")]
|
||||
Timeout,
|
||||
#[error("ready check on uart failed")]
|
||||
Ready,
|
||||
ReadyCheck,
|
||||
#[error("buffer is too small for message fragment")]
|
||||
BufferCapacity,
|
||||
}
|
||||
|
|
@ -23,10 +24,10 @@ pub enum UartReadError {
|
|||
/// `between_bytes`: if data is available immediately, or after the
|
||||
/// first byte has been read, will wait this long between bytes
|
||||
///
|
||||
/// Returns `UartReadError::Ready` if the initial check for data returns an
|
||||
/// Returns `ReadError::ReadyCheck` if the initial check for data returns an
|
||||
/// error
|
||||
///
|
||||
/// Returns `UartReadError::Timeout` if the first-byte timeout expires
|
||||
/// Returns `ReadError::Timeout` if the first-byte timeout expires
|
||||
///
|
||||
/// Returns `Ok()` with the number of bytes read in all other cases,
|
||||
/// including a between-bytes timeout; in that case the data read
|
||||
|
|
@ -36,7 +37,7 @@ async fn read_with_timeouts(
|
|||
buf: &mut [u8],
|
||||
first_byte: Duration,
|
||||
between_bytes: Duration,
|
||||
) -> Result<usize, UartReadError> {
|
||||
) -> Result<usize, ReadError> {
|
||||
// if the requested read size is zero bytes, return immediately
|
||||
if buf.is_empty() {
|
||||
return Ok(0);
|
||||
|
|
@ -47,7 +48,7 @@ async fn read_with_timeouts(
|
|||
if let Ok(ready) = rx.read_ready() {
|
||||
timeout = if ready { between_bytes } else { first_byte };
|
||||
} else {
|
||||
return Err(UartReadError::Ready);
|
||||
return Err(ReadError::ReadyCheck);
|
||||
}
|
||||
|
||||
while bytes_read < buf.len() {
|
||||
|
|
@ -58,54 +59,9 @@ async fn read_with_timeouts(
|
|||
if bytes_read > 0 {
|
||||
return Ok(bytes_read);
|
||||
}
|
||||
return Err(UartReadError::Timeout);
|
||||
return Err(ReadError::Timeout);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(bytes_read)
|
||||
}
|
||||
|
||||
pub async fn read_frame_fragment<const N: usize>(
|
||||
buf: &mut Vec<u8, N>,
|
||||
fragment_pos: usize,
|
||||
fragment_len: usize,
|
||||
uart: &mut usart::BufferedUartRx<'_>,
|
||||
first_byte_timeout: Duration,
|
||||
between_bytes_timeout: Duration,
|
||||
) -> Result<(), UartReadError> {
|
||||
// compute the number of bytes available in the buffer, and the
|
||||
// number of bytes to read
|
||||
let bytes_available = buf[fragment_pos..].len();
|
||||
let bytes_to_read = fragment_len.saturating_sub(bytes_available);
|
||||
if bytes_to_read == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// if the fragment will not fit in the buffer's available space,
|
||||
// return an error
|
||||
if bytes_to_read > (buf.capacity() - buf.len()) {
|
||||
return Err(UartReadError::BufferCapacity);
|
||||
}
|
||||
|
||||
// if there aren't enough bytes in the buffer for the
|
||||
// fragment, wait for them to arrive, with a timeout
|
||||
if let Ok(r) = read_with_timeouts(
|
||||
uart,
|
||||
&mut buf[fragment_pos + bytes_available..bytes_to_read],
|
||||
first_byte_timeout,
|
||||
between_bytes_timeout,
|
||||
)
|
||||
.await
|
||||
{
|
||||
unsafe {
|
||||
buf.set_len(fragment_pos + bytes_available + r);
|
||||
}
|
||||
if r < bytes_to_read {
|
||||
return Err(UartReadError::Timeout);
|
||||
}
|
||||
} else {
|
||||
return Err(UartReadError::Timeout);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue