Compare commits

..

9 Commits

33 changed files with 168 additions and 28131 deletions

View File

@ -18,9 +18,9 @@ rustflags = [
"-C", "link-arg=-Tlink.x", "-C", "link-arg=-Tlink.x",
# knurling-rs tooling. If you want to use flip-link, ensure it is installed first. # knurling-rs tooling. If you want to use flip-link, ensure it is installed first.
# "-C", "linker=flip-link", "-C", "linker=flip-link",
# Unfortunately, defmt is clunky to use without probe-rs.. # Unfortunately, defmt is clunky to use without probe-rs..
# "-C", "link-arg=-Tdefmt.x", "-C", "link-arg=-Tdefmt.x",
# Can be useful for debugging. # Can be useful for debugging.
# "-Clink-args=-Map=app.map" # "-Clink-args=-Map=app.map"

View File

@ -13,7 +13,6 @@ members = [
"flashloader", "flashloader",
] ]
exclude = [ exclude = [
"defmt-testapp",
"flashloader/slot-a-blinky", "flashloader/slot-a-blinky",
"flashloader/slot-b-blinky", "flashloader/slot-b-blinky",
] ]

View File

@ -77,7 +77,7 @@ to flash and run the blinky program on the RAM. There is also a `VA108xx` chip t
available for persistent flashing. available for persistent flashing.
Runner configuration avilable in the `.cargo/def-config.toml` file to use `probe-rs` for Runner configuration avilable in the `.cargo/def-config.toml` file to use `probe-rs` for
convenience. convenience. `probe-rs` is also able to process and display `defmt` strings directly.
### Using VS Code ### Using VS Code
@ -123,13 +123,13 @@ is also run when running the `jlink-gdb.sh` script)
```sh ```sh
JLinkGDBServer -select USB -device Cortex-M0 -endian little -if JTAG-speed auto \ JLinkGDBServer -select USB -device Cortex-M0 -endian little -if JTAG-speed auto \
-LocalhostOnly -LocalhostOnly -jtagconf -1,-1
``` ```
After this, you can flash and debug the application with the following command After this, you can flash and debug the application with the following command
```sh ```sh
gdb-mutliarch -q -x jlink/jlink.gdb target/thumbv6m-none-eabihf/debug/examples/blinky gdb-mutliarch -q -x jlink/jlink.gdb target/thumbv6m-none-eabihf/debug/examples/blinky -tui
``` ```
Please note that you can automate all steps except starting the GDB server by using a cargo Please note that you can automate all steps except starting the GDB server by using a cargo
@ -148,3 +148,23 @@ example.
The Segger RTT viewer can be used to display log messages received from the target. The base The Segger RTT viewer can be used to display log messages received from the target. The base
address for the RTT block placement is 0x10000000. It is recommended to use a search range of address for the RTT block placement is 0x10000000. It is recommended to use a search range of
0x1000 around that base address when using the RTT viewer. 0x1000 around that base address when using the RTT viewer.
The RTT viewer will not be able to process `defmt` printouts. However, you can view the defmt
logs by [installing defmt-print](https://crates.io/crates/defmt-print) first and then piping
the output on telnet port 19021 into `defmt-print`, for example by running
```sh
telnet localhost 19021 | defmt-print -e <pathToElfFile>
```
The path of the ELF file which is being debugged needs to be specified for this to work.
## Learning (Embedded) Rust
If you are unfamiliar with Rust on Embedded Systems or Rust in general, the following resources
are recommended:
- [Rust Book](https://doc.rust-lang.org/book/)
- [Embedded Rust Book](https://docs.rust-embedded.org/book/)
- [Embedded Rust Discovery](https://docs.rust-embedded.org/discovery/microbit/)
- [Awesome Embedded Rust](https://github.com/rust-embedded/awesome-embedded-rust)

View File

@ -14,6 +14,6 @@ embedded-hal-nb = "1"
embedded-io = "0.6" embedded-io = "0.6"
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = "0.10.0" version = "0.11"
features = ["rt"] features = ["rt"]

View File

@ -15,7 +15,7 @@ num_enum = { version = "0.7", default-features = false }
static_assertions = "1" static_assertions = "1"
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = "0.10" version = "0.11"
[dependencies.vorago-reb1] [dependencies.vorago-reb1]
version = "0.8" version = "0.8"

View File

@ -1,48 +0,0 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# uncomment ONE of these three option to make `cargo run` start a GDB session
# which option to pick depends on your system
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
# runner = "gdb-multiarch -q -x openocd.gdb"
# runner = "gdb -q -x openocd.gdb"
runner = "gdb-multiarch -q -x jlink.gdb"
# Probe-rs is currently problematic: https://github.com/probe-rs/probe-rs/issues/2567
# runner = "probe-rs run --chip VA108xx --chip-description-path ./scripts/VA108xx_Series.yaml"
# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
rustflags = [
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
"-C", "link-arg=--nmagic",
# LLD (shipped with the Rust toolchain) is used as the default linker
"-C", "link-arg=-Tlink.x",
# knurling-rs tooling. If you want to use flip-link, ensure it is installed first.
"-C", "linker=flip-link",
# Unfortunately, defmt is clunky to use without probe-rs..
"-C", "link-arg=-Tdefmt.x",
# Can be useful for debugging.
"-Clink-args=-Map=app.map"
]
[build]
# Pick ONE of these compilation targets
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
# target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
# target = "thumbv8m.base-none-eabi" # Cortex-M23
# target = "thumbv8m.main-none-eabi" # Cortex-M33 (no FPU)
# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU)
[alias]
re = "run --example"
rb = "run --bin"
rrb = "run --release --bin"
ut = "test --target x86_64-unknown-linux-gnu"
[env]
DEFMT_LOG = "info"

View File

@ -1 +0,0 @@
/target

View File

@ -1,34 +0,0 @@
[package]
name = "defmt-testapp"
version = "0.1.0"
edition = "2021"
[dependencies]
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
panic-rtt-target = "0.1"
cortex-m-rt = "0.7"
rtt-target = "0.5"
rtic-sync = { version = "1.3", features = ["defmt-03"] }
embedded-hal = "1"
embedded-hal-nb = "1"
embedded-io = "0.6"
cortex-m-semihosting = "0.5.0"
# Tricky without probe-rs.
defmt = "0.3"
defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
panic-probe = { version = "0.3", features = ["print-defmt"] }
[dependencies.rtic]
version = "2"
features = ["thumbv6-backend"]
[dependencies.rtic-monotonics]
version = "1"
features = ["cortex-m-systick"]
[dependencies.va108xx-hal]
version = "0.10"
features = ["rt", "defmt"]
[dependencies.va108xx]
version = "0.5"

View File

@ -1,9 +0,0 @@
defmt Testapp
======
`defmt` is clunky to use without probe-rs and requires special configuration inside the
`.cargo/config.toml` file.
`probe-rs` is currently problematic for usage with the VA108xx , so it is not the default tool
recommended and used for the whole workspace. This project contains an isolated, `defmt` compatible
configuration for testing with `defmt` (and `probe-rs`).

View File

@ -1,53 +0,0 @@
#![no_main]
#![no_std]
use cortex_m_semihosting::debug;
use defmt_brtt as _; // global logger
use va108xx_hal as _; // memory layout
use panic_probe as _;
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
// #[defmt::panic_handler]
/*
fn panic() -> ! {
cortex_m::asm::udf()
}
*/
/// Terminates the application and makes a semihosting-capable debug tool exit
/// with status code 0.
pub fn exit() -> ! {
loop {
debug::exit(debug::EXIT_SUCCESS);
}
}
/// Hardfault handler.
///
/// Terminates the application and makes a semihosting-capable debug tool exit
/// with an error. This seems better than the default, which is to spin in a
/// loop.
#[cortex_m_rt::exception]
unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! {
loop {
debug::exit(debug::EXIT_FAILURE);
}
}
// defmt-test 0.3.0 has the limitation that this `#[tests]` attribute can only be used
// once within a crate. the module can be in any file but there can only be at most
// one `#[tests]` module in this library crate
#[cfg(test)]
#[defmt_test::tests]
mod unit_tests {
use defmt::assert;
#[test]
fn it_works() {
assert!(true)
}
}

View File

@ -1,29 +0,0 @@
//! Empty RTIC project template
#![no_main]
#![no_std]
use defmt_testapp as _;
#[rtic::app(device = pac)]
mod app {
use va108xx_hal::pac;
#[local]
struct Local {}
#[shared]
struct Shared {}
#[init]
fn init(_ctx: init::Context) -> (Shared, Local) {
defmt::println!("-- Vorago RTIC template --");
(Shared {}, Local {})
}
// `shared` cannot be accessed from this context
#[idle]
fn idle(_cx: idle::Context) -> ! {
#[allow(clippy::empty_loop)]
loop {}
}
}

View File

@ -14,8 +14,10 @@ embedded-io-async = "0.6"
heapless = "0.8" heapless = "0.8"
static_cell = "2" static_cell = "2"
rtt-target = "0.6" defmt = "1"
panic-rtt-target = "0.2" defmt-rtt = "0.4"
panic-probe = { version = "0.3", features = ["print-defmt"] }
critical-section = "1" critical-section = "1"
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]} portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
@ -27,8 +29,8 @@ embassy-executor = { version = "0.7", features = [
"executor-interrupt" "executor-interrupt"
]} ]}
va108xx-hal = { version = "0.11", path = "../../va108xx-hal" } va108xx-hal = { version = "0.11", features = ["defmt"] }
va108xx-embassy = { version = "0.2", path = "../../va108xx-embassy" } va108xx-embassy = { version = "0.2" }
[features] [features]
default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"] default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]

View File

@ -4,14 +4,14 @@
//! and then set the `CHECK_PB22_TO_PB23` to true to also test async operations on Port B. //! and then set the `CHECK_PB22_TO_PB23` to true to also test async operations on Port B.
#![no_std] #![no_std]
#![no_main] #![no_main]
// This imports the logger and the panic handler.
use embassy_example as _;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_sync::channel::{Receiver, Sender}; use embassy_sync::channel::{Receiver, Sender};
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel}; use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
use embassy_time::{Duration, Instant, Timer}; use embassy_time::{Duration, Instant, Timer};
use embedded_hal_async::digital::Wait; use embedded_hal_async::digital::Wait;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::gpio::{ use va108xx_hal::gpio::{
on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, Port, on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, Port,
}; };
@ -58,8 +58,7 @@ static CHANNEL_PB22_TO_PB23: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::
#[embassy_executor::main] #[embassy_executor::main]
async fn main(spawner: Spawner) { async fn main(spawner: Spawner) {
rtt_init_print!(); defmt::println!("-- VA108xx Async GPIO Demo --");
rprintln!("-- VA108xx Async GPIO Demo --");
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
@ -102,15 +101,15 @@ async fn main(spawner: Spawner) {
if CHECK_PA0_TO_PA1 { if CHECK_PA0_TO_PA1 {
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), in_pa1_async).await; check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), in_pa1_async).await;
rprintln!("Example PA0 to PA1 done"); defmt::info!("Example PA0 to PA1 done");
} }
if CHECK_PB22_TO_PB23 { if CHECK_PB22_TO_PB23 {
check_pin_to_pin_async_ops("PB22 to PB23", CHANNEL_PB22_TO_PB23.sender(), in_pb23_async) check_pin_to_pin_async_ops("PB22 to PB23", CHANNEL_PB22_TO_PB23.sender(), in_pb23_async)
.await; .await;
rprintln!("Example PB22 to PB23 done"); defmt::info!("Example PB22 to PB23 done");
} }
rprintln!("Example done, toggling LED0"); defmt::info!("Example done, toggling LED0");
loop { loop {
led0.toggle(); led0.toggle();
Timer::after(Duration::from_millis(500)).await; Timer::after(Duration::from_millis(500)).await;
@ -122,46 +121,46 @@ async fn check_pin_to_pin_async_ops(
sender: Sender<'static, ThreadModeRawMutex, GpioCmd, 3>, sender: Sender<'static, ThreadModeRawMutex, GpioCmd, 3>,
mut async_input: impl Wait, mut async_input: impl Wait,
) { ) {
rprintln!( defmt::info!(
"{}: sending SetHigh command ({} ms)", "{}: sending SetHigh command ({} ms)",
ctx, ctx,
Instant::now().as_millis() Instant::now().as_millis()
); );
sender.send(GpioCmd::new(GpioCmdType::SetHigh, 20)).await; sender.send(GpioCmd::new(GpioCmdType::SetHigh, 20)).await;
async_input.wait_for_high().await.unwrap(); async_input.wait_for_high().await.unwrap();
rprintln!( defmt::info!(
"{}: Input pin is high now ({} ms)", "{}: Input pin is high now ({} ms)",
ctx, ctx,
Instant::now().as_millis() Instant::now().as_millis()
); );
rprintln!( defmt::info!(
"{}: sending SetLow command ({} ms)", "{}: sending SetLow command ({} ms)",
ctx, ctx,
Instant::now().as_millis() Instant::now().as_millis()
); );
sender.send(GpioCmd::new(GpioCmdType::SetLow, 20)).await; sender.send(GpioCmd::new(GpioCmdType::SetLow, 20)).await;
async_input.wait_for_low().await.unwrap(); async_input.wait_for_low().await.unwrap();
rprintln!( defmt::info!(
"{}: Input pin is low now ({} ms)", "{}: Input pin is low now ({} ms)",
ctx, ctx,
Instant::now().as_millis() Instant::now().as_millis()
); );
rprintln!( defmt::info!(
"{}: sending RisingEdge command ({} ms)", "{}: sending RisingEdge command ({} ms)",
ctx, ctx,
Instant::now().as_millis() Instant::now().as_millis()
); );
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await; sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
async_input.wait_for_rising_edge().await.unwrap(); async_input.wait_for_rising_edge().await.unwrap();
rprintln!( defmt::info!(
"{}: input pin had rising edge ({} ms)", "{}: input pin had rising edge ({} ms)",
ctx, ctx,
Instant::now().as_millis() Instant::now().as_millis()
); );
rprintln!( defmt::info!(
"{}: sending Falling command ({} ms)", "{}: sending Falling command ({} ms)",
ctx, ctx,
Instant::now().as_millis() Instant::now().as_millis()
@ -170,13 +169,13 @@ async fn check_pin_to_pin_async_ops(
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20)) .send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
.await; .await;
async_input.wait_for_falling_edge().await.unwrap(); async_input.wait_for_falling_edge().await.unwrap();
rprintln!( defmt::info!(
"{}: input pin had a falling edge ({} ms)", "{}: input pin had a falling edge ({} ms)",
ctx, ctx,
Instant::now().as_millis() Instant::now().as_millis()
); );
rprintln!( defmt::info!(
"{}: sending Falling command ({} ms)", "{}: sending Falling command ({} ms)",
ctx, ctx,
Instant::now().as_millis() Instant::now().as_millis()
@ -185,20 +184,20 @@ async fn check_pin_to_pin_async_ops(
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20)) .send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
.await; .await;
async_input.wait_for_any_edge().await.unwrap(); async_input.wait_for_any_edge().await.unwrap();
rprintln!( defmt::info!(
"{}: input pin had a falling (any) edge ({} ms)", "{}: input pin had a falling (any) edge ({} ms)",
ctx, ctx,
Instant::now().as_millis() Instant::now().as_millis()
); );
rprintln!( defmt::info!(
"{}: sending Falling command ({} ms)", "{}: sending Falling command ({} ms)",
ctx, ctx,
Instant::now().as_millis() Instant::now().as_millis()
); );
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await; sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
async_input.wait_for_any_edge().await.unwrap(); async_input.wait_for_any_edge().await.unwrap();
rprintln!( defmt::info!(
"{}: input pin had a rising (any) edge ({} ms)", "{}: input pin had a rising (any) edge ({} ms)",
ctx, ctx,
Instant::now().as_millis() Instant::now().as_millis()
@ -216,22 +215,22 @@ async fn output_task(
Timer::after(Duration::from_millis(next_cmd.after_delay.into())).await; Timer::after(Duration::from_millis(next_cmd.after_delay.into())).await;
match next_cmd.cmd_type { match next_cmd.cmd_type {
GpioCmdType::SetHigh => { GpioCmdType::SetHigh => {
rprintln!("{}: Set output high", ctx); defmt::info!("{}: Set output high", ctx);
out.set_high().unwrap(); out.set_high().unwrap();
} }
GpioCmdType::SetLow => { GpioCmdType::SetLow => {
rprintln!("{}: Set output low", ctx); defmt::info!("{}: Set output low", ctx);
out.set_low().unwrap(); out.set_low().unwrap();
} }
GpioCmdType::RisingEdge => { GpioCmdType::RisingEdge => {
rprintln!("{}: Rising edge", ctx); defmt::info!("{}: Rising edge", ctx);
if !out.is_low().unwrap() { if !out.is_low().unwrap() {
out.set_low().unwrap(); out.set_low().unwrap();
} }
out.set_high().unwrap(); out.set_high().unwrap();
} }
GpioCmdType::FallingEdge => { GpioCmdType::FallingEdge => {
rprintln!("{}: Falling edge", ctx); defmt::info!("{}: Falling edge", ctx);
if !out.is_high().unwrap() { if !out.is_high().unwrap() {
out.set_high().unwrap(); out.set_high().unwrap();
} }

View File

@ -13,16 +13,16 @@
//! RTT logs to see received data. //! RTT logs to see received data.
#![no_std] #![no_std]
#![no_main] #![no_main]
use core::cell::RefCell; // This imports the logger and the panic handler.
use embassy_example as _;
use core::cell::RefCell;
use critical_section::Mutex; use critical_section::Mutex;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_time::Instant; use embassy_time::Instant;
use embedded_io::Write; use embedded_io::Write;
use embedded_io_async::Read; use embedded_io_async::Read;
use heapless::spsc::{Consumer, Producer, Queue}; use heapless::spsc::{Consumer, Producer, Queue};
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::{ use va108xx_hal::{
gpio::PinsA, gpio::PinsA,
pac::{self, interrupt}, pac::{self, interrupt},
@ -49,8 +49,7 @@ static CONSUMER_UART_B: Mutex<RefCell<Option<Consumer<u8, 256>>>> = Mutex::new(R
// main is itself an async function. // main is itself an async function.
#[embassy_executor::main] #[embassy_executor::main]
async fn main(spawner: Spawner) { async fn main(spawner: Spawner) {
rtt_init_print!(); defmt::println!("-- VA108xx Async UART RX Demo --");
rprintln!("-- VA108xx Async UART RX Demo --");
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
@ -108,13 +107,13 @@ async fn main(spawner: Spawner) {
.unwrap(); .unwrap();
let mut buf = [0u8; 256]; let mut buf = [0u8; 256];
loop { loop {
rprintln!("Current time UART A: {}", Instant::now().as_secs()); defmt::info!("Current time UART A: {}", Instant::now().as_secs());
led0.toggle(); led0.toggle();
led1.toggle(); led1.toggle();
led2.toggle(); led2.toggle();
let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap(); let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap();
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap(); let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
rprintln!( defmt::info!(
"Read {} bytes asynchronously on UART A: {:?}", "Read {} bytes asynchronously on UART A: {:?}",
read_bytes, read_bytes,
read_str read_str
@ -127,11 +126,11 @@ async fn main(spawner: Spawner) {
async fn uart_b_task(mut async_rx: RxAsyncOverwriting<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) { async fn uart_b_task(mut async_rx: RxAsyncOverwriting<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
let mut buf = [0u8; 256]; let mut buf = [0u8; 256];
loop { loop {
rprintln!("Current time UART B: {}", Instant::now().as_secs()); defmt::info!("Current time UART B: {}", Instant::now().as_secs());
// Infallible asynchronous operation. // Infallible asynchronous operation.
let read_bytes = async_rx.read(&mut buf).await.unwrap(); let read_bytes = async_rx.read(&mut buf).await.unwrap();
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap(); let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
rprintln!( defmt::info!(
"Read {} bytes asynchronously on UART B: {:?}", "Read {} bytes asynchronously on UART B: {:?}",
read_bytes, read_bytes,
read_str read_str
@ -149,7 +148,7 @@ fn OC2() {
critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod)); critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod));
// In a production app, we could use a channel to send the errors to the main task. // In a production app, we could use a channel to send the errors to the main task.
if let Err(errors) = errors { if let Err(errors) = errors {
rprintln!("UART A errors: {:?}", errors); defmt::info!("UART A errors: {:?}", errors);
} }
} }
@ -162,6 +161,6 @@ fn OC3() {
critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod)); critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod));
// In a production app, we could use a channel to send the errors to the main task. // In a production app, we could use a channel to send the errors to the main task.
if let Err(errors) = errors { if let Err(errors) = errors {
rprintln!("UART B errors: {:?}", errors); defmt::info!("UART B errors: {:?}", errors);
} }
} }

View File

@ -10,11 +10,12 @@
//! can verify the correctness of the sent strings. //! can verify the correctness of the sent strings.
#![no_std] #![no_std]
#![no_main] #![no_main]
// This imports the logger and the panic handler.
use embassy_example as _;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Ticker}; use embassy_time::{Duration, Instant, Ticker};
use embedded_io_async::Write; use embedded_io_async::Write;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::{ use va108xx_hal::{
gpio::PinsA, gpio::PinsA,
pac::{self, interrupt}, pac::{self, interrupt},
@ -35,8 +36,7 @@ const STR_LIST: &[&str] = &[
// main is itself an async function. // main is itself an async function.
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
rtt_init_print!(); defmt::println!("-- VA108xx Async UART TX Demo --");
rprintln!("-- VA108xx Async UART TX Demo --");
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
@ -70,7 +70,7 @@ async fn main(_spawner: Spawner) {
let mut ticker = Ticker::every(Duration::from_secs(1)); let mut ticker = Ticker::every(Duration::from_secs(1));
let mut idx = 0; let mut idx = 0;
loop { loop {
rprintln!("Current time: {}", Instant::now().as_secs()); defmt::info!("Current time: {}", Instant::now().as_secs());
led0.toggle(); led0.toggle();
led1.toggle(); led1.toggle();
led2.toggle(); led2.toggle();

View File

@ -0,0 +1,3 @@
#![no_std]
use defmt_rtt as _;
use panic_probe as _;

View File

@ -1,9 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use embassy_example as _;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Ticker}; use embassy_time::{Duration, Instant, Ticker};
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(feature = "custom-irqs")] { if #[cfg(feature = "custom-irqs")] {
@ -20,8 +19,7 @@ const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
// main is itself an async function. // main is itself an async function.
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
rtt_init_print!(); defmt::println!("-- VA108xx Embassy Demo --");
rprintln!("-- VA108xx Embassy Demo --");
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
@ -55,7 +53,7 @@ async fn main(_spawner: Spawner) {
let mut ticker = Ticker::every(Duration::from_secs(1)); let mut ticker = Ticker::every(Duration::from_secs(1));
loop { loop {
ticker.next().await; ticker.next().await;
rprintln!("Current time: {}", Instant::now().as_secs()); defmt::info!("Current time: {}", Instant::now().as_secs());
led0.toggle(); led0.toggle();
led1.toggle(); led1.toggle();
led2.toggle(); led2.toggle();

View File

@ -22,5 +22,5 @@ rtic-sync = { version = "1.3", features = ["defmt-03"] }
once_cell = {version = "1", default-features = false, features = ["critical-section"]} once_cell = {version = "1", default-features = false, features = ["critical-section"]}
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] } ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
va108xx-hal = { version = "0.11", path = "../../va108xx-hal" } va108xx-hal = { version = "0.11" }
vorago-reb1 = { version = "0.8", path = "../../vorago-reb1" } vorago-reb1 = { version = "0.8" }

View File

@ -17,9 +17,7 @@ cortex-m-semihosting = "0.5.0"
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = "0.11" version = "0.11"
path = "../../va108xx-hal" features = ["defmt"]
features = ["rt", "defmt"]
[dependencies.vorago-reb1] [dependencies.vorago-reb1]
version = "0.8" version = "0.8"
path = "../../vorago-reb1"

View File

@ -4,6 +4,7 @@
use cortex_m_rt::entry; use cortex_m_rt::entry;
use panic_rtt_target as _; use panic_rtt_target as _;
use va108xx_hal as _;
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {

View File

@ -9,17 +9,16 @@ cortex-m-rt = "0.7"
embedded-hal = "1" embedded-hal = "1"
embedded-hal-nb = "1" embedded-hal-nb = "1"
embedded-io = "0.6" embedded-io = "0.6"
panic-rtt-target = "0.2" defmt = "1"
rtt-target = "0.6" defmt-rtt = { version = "0.4" }
panic-probe = { version = "0.3", features = ["print-defmt"] }
num_enum = { version = "0.7", default-features = false } num_enum = { version = "0.7", default-features = false }
log = "0.4"
crc = "3" crc = "3"
cobs = { version = "0.3", default-features = false } cobs = { version = "0.3", default-features = false }
satrs = { version = "0.2", default-features = false } satrs = { version = "0.2", default-features = false }
rtt-log = "0.5"
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] } ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
once_cell = { version = "1", default-features = false, features = ["critical-section"] } once_cell = { version = "1", default-features = false, features = ["critical-section"] }
spacepackets = { version = "0.11", default-features = false } spacepackets = { version = "0.11", default-features = false, features = ["defmt"] }
# Even though we do not use this directly, we need to activate this feature explicitely # Even though we do not use this directly, we need to activate this feature explicitely
# so that RTIC compiles because thumv6 does not have CAS operations natively. # so that RTIC compiles because thumv6 does not have CAS operations natively.
portable-atomic = {version = "1", features = ["unsafe-assume-single-core"]} portable-atomic = {version = "1", features = ["unsafe-assume-single-core"]}
@ -29,7 +28,8 @@ rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
rtic-sync = {version = "1", features = ["defmt-03"]} rtic-sync = {version = "1", features = ["defmt-03"]}
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = "0.10" version = "0.11"
features = ["defmt"]
[dependencies.vorago-reb1] [dependencies.vorago-reb1]
version = "0.8" version = "0.8"

View File

@ -1,9 +0,0 @@
#![no_std]
#[cfg(test)]
mod tests {
#[test]
fn simple() {
assert_eq!(1 + 1, 2);
}
}

View File

@ -3,8 +3,9 @@
#![no_main] #![no_main]
#![no_std] #![no_std]
use defmt_rtt as _; // global logger
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
use panic_rtt_target as _; use panic_probe as _;
use ringbuf::{ use ringbuf::{
traits::{Consumer, Observer, Producer}, traits::{Consumer, Observer, Producer},
StaticRb, StaticRb,
@ -29,7 +30,7 @@ pub enum ActionId {
SetBootSlot = 130, SetBootSlot = 130,
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, defmt::Format)]
#[repr(u8)] #[repr(u8)]
enum AppSel { enum AppSel {
A = 0, A = 0,
@ -60,10 +61,8 @@ mod app {
use super::*; use super::*;
use cortex_m::asm; use cortex_m::asm;
use embedded_io::Write; use embedded_io::Write;
use panic_rtt_target as _;
use rtic::Mutex; use rtic::Mutex;
use rtic_monotonics::systick::prelude::*; use rtic_monotonics::systick::prelude::*;
use rtt_target::rprintln;
use satrs::pus::verification::{FailParams, VerificationReportCreator}; use satrs::pus::verification::{FailParams, VerificationReportCreator};
use spacepackets::ecss::PusServiceId; use spacepackets::ecss::PusServiceId;
use spacepackets::ecss::{ use spacepackets::ecss::{
@ -102,8 +101,7 @@ mod app {
#[init] #[init]
fn init(cx: init::Context) -> (Shared, Local) { fn init(cx: init::Context) -> (Shared, Local) {
rtt_log::init(); defmt::println!("-- Vorago flashloader --");
rprintln!("-- Vorago flashloader --");
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw()); Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
@ -181,8 +179,8 @@ mod app {
{ {
Ok(result) => { Ok(result) => {
if RX_DEBUGGING { if RX_DEBUGGING {
log::debug!("RX Info: {:?}", cx.local.rx_context); defmt::debug!("RX Info: {:?}", cx.local.rx_context);
log::debug!("RX Result: {:?}", result); defmt::debug!("RX Result: {:?}", result);
} }
if result.complete() { if result.complete() {
// Check frame validity (must have COBS format) and decode the frame. // Check frame validity (must have COBS format) and decode the frame.
@ -193,7 +191,7 @@ mod app {
let decoded_size = let decoded_size =
cobs::decode_in_place(&mut cx.local.rx_buf[1..result.bytes_read]); cobs::decode_in_place(&mut cx.local.rx_buf[1..result.bytes_read]);
if decoded_size.is_err() { if decoded_size.is_err() {
log::warn!("COBS decoding failed"); defmt::warn!("COBS decoding failed");
} else { } else {
let decoded_size = decoded_size.unwrap(); let decoded_size = decoded_size.unwrap();
let mut tc_rb_full = false; let mut tc_rb_full = false;
@ -207,11 +205,13 @@ mod app {
} }
}); });
if tc_rb_full { if tc_rb_full {
log::warn!("COBS TC queue full"); defmt::warn!("COBS TC queue full");
} }
} }
} else { } else {
log::warn!("COBS frame with invalid format, start and end bytes are not 0"); defmt::warn!(
"COBS frame with invalid format, start and end bytes are not 0"
);
} }
// Initiate next transfer. // Initiate next transfer.
@ -221,11 +221,11 @@ mod app {
.expect("read operation failed"); .expect("read operation failed");
} }
if result.has_errors() { if result.has_errors() {
log::warn!("UART error: {:?}", result.errors.unwrap()); defmt::warn!("UART error: {:?}", result.errors.unwrap());
} }
} }
Err(e) => { Err(e) => {
log::warn!("UART error: {:?}", e); defmt::warn!("UART error: {:?}", e);
} }
} }
} }
@ -252,7 +252,7 @@ mod app {
continue; continue;
} }
let packet_len = packet_len.unwrap(); let packet_len = packet_len.unwrap();
log::info!(target: "TC Handler", "received packet with length {}", packet_len); defmt::info!("received packet with length {}", packet_len);
let popped_packet_len = cx let popped_packet_len = cx
.shared .shared
.tc_rb .tc_rb
@ -266,7 +266,7 @@ mod app {
fn handle_valid_pus_tc(cx: &mut pus_tc_handler::Context) { fn handle_valid_pus_tc(cx: &mut pus_tc_handler::Context) {
let pus_tc = PusTcReader::new(cx.local.tc_buf); let pus_tc = PusTcReader::new(cx.local.tc_buf);
if pus_tc.is_err() { if pus_tc.is_err() {
log::warn!(target: "TC Handler", "PUS TC error: {}", pus_tc.unwrap_err()); defmt::warn!("PUS TC error: {}", pus_tc.unwrap_err());
return; return;
} }
let (pus_tc, _) = pus_tc.unwrap(); let (pus_tc, _) = pus_tc.unwrap();
@ -312,22 +312,25 @@ mod app {
write_and_send(&tm); write_and_send(&tm);
}; };
if pus_tc.subservice() == ActionId::CorruptImageA as u8 { if pus_tc.subservice() == ActionId::CorruptImageA as u8 {
rprintln!("corrupting App Image A"); defmt::info!("corrupting App Image A");
corrupt_image(APP_A_START_ADDR); corrupt_image(APP_A_START_ADDR);
} }
if pus_tc.subservice() == ActionId::CorruptImageB as u8 { if pus_tc.subservice() == ActionId::CorruptImageB as u8 {
rprintln!("corrupting App Image B"); defmt::info!("corrupting App Image B");
corrupt_image(APP_B_START_ADDR); corrupt_image(APP_B_START_ADDR);
} }
if pus_tc.subservice() == ActionId::SetBootSlot as u8 { if pus_tc.subservice() == ActionId::SetBootSlot as u8 {
if pus_tc.app_data().is_empty() { if pus_tc.app_data().is_empty() {
log::warn!(target: "TC Handler", "App data for preferred image command too short"); defmt::warn!("App data for preferred image command too short");
} }
let app_sel_result = AppSel::try_from(pus_tc.app_data()[0]); let app_sel_result = AppSel::try_from(pus_tc.app_data()[0]);
if app_sel_result.is_err() { if app_sel_result.is_err() {
log::warn!("Invalid app selection value: {}", pus_tc.app_data()[0]); defmt::warn!("Invalid app selection value: {}", pus_tc.app_data()[0]);
} }
log::info!(target: "TC Handler", "received boot selection command with app select: {:?}", app_sel_result.unwrap()); defmt::info!(
"received boot selection command with app select: {:?}",
app_sel_result.unwrap()
);
cx.local cx.local
.nvm .nvm
.write(PREFERRED_SLOT_OFFSET as usize, &[pus_tc.app_data()[0]]) .write(PREFERRED_SLOT_OFFSET as usize, &[pus_tc.app_data()[0]])
@ -341,7 +344,7 @@ 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"); defmt::info!("received ping TC");
let tm = cx let tm = cx
.local .local
.verif_reporter .verif_reporter
@ -366,23 +369,21 @@ mod app {
if pus_tc.subservice() == 2 { if pus_tc.subservice() == 2 {
let app_data = pus_tc.app_data(); let app_data = pus_tc.app_data();
if app_data.len() < 10 { if app_data.len() < 10 {
log::warn!( defmt::warn!(
target: "TC Handler",
"app data for raw memory write is too short: {}", "app data for raw memory write is too short: {}",
app_data.len() app_data.len()
); );
} }
let memory_id = app_data[0]; let memory_id = app_data[0];
if memory_id != BOOT_NVM_MEMORY_ID { if memory_id != BOOT_NVM_MEMORY_ID {
log::warn!(target: "TC Handler", "memory ID {} not supported", memory_id); defmt::warn!("memory ID {} not supported", memory_id);
// TODO: Error reporting // TODO: Error reporting
return; return;
} }
let offset = u32::from_be_bytes(app_data[2..6].try_into().unwrap()); let offset = u32::from_be_bytes(app_data[2..6].try_into().unwrap());
let data_len = u32::from_be_bytes(app_data[6..10].try_into().unwrap()); let data_len = u32::from_be_bytes(app_data[6..10].try_into().unwrap());
if 10 + data_len as usize > app_data.len() { if 10 + data_len as usize > app_data.len() {
log::warn!( defmt::warn!(
target: "TC Handler",
"invalid data length {} for raw mem write detected", "invalid data length {} for raw mem write detected",
data_len data_len
); );
@ -390,12 +391,7 @@ mod app {
return; return;
} }
let data = &app_data[10..10 + data_len as usize]; let data = &app_data[10..10 + data_len as usize];
log::info!( defmt::info!("writing {} bytes at offset {} to NVM", data_len, offset);
target: "TC Handler",
"writing {} bytes at offset {} to NVM",
data_len,
offset
);
cx.local cx.local
.nvm .nvm
.write(offset as usize, data) .write(offset as usize, data)
@ -406,7 +402,7 @@ mod app {
.verify(offset as usize, data) .verify(offset as usize, data)
.expect("NVM verification failed") .expect("NVM verification failed")
{ {
log::warn!("verification of data written to NVM failed"); defmt::warn!("verification of data written to NVM failed");
cx.local cx.local
.verif_reporter .verif_reporter
.completion_failure( .completion_failure(
@ -424,9 +420,7 @@ mod app {
.expect("completion success failed") .expect("completion success failed")
}; };
write_and_send(&tm); write_and_send(&tm);
log::info!( defmt::info!("NVM operation done");
target: "TC Handler",
"NVM operation done");
} }
} }
} }

View File

@ -1,3 +1,3 @@
#!/bin/bash #!/bin/bash
JLinkGDBServer -select USB -device Cortex-M0 -endian little -if JTAG -speed auto \ JLinkGDBServer -select USB -device VA10820 -endian little -if JTAG -speed auto \
-LocalhostOnly -LocalhostOnly -jtagconf -1,-1

18
scripts/defmt-telnet.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
# Check if binary path was provided
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <path-to-binary>"
exit 1
fi
BINARY="$1"
# Check if file exists
if [ ! -f "$BINARY" ]; then
echo "Error: File '$BINARY' not found."
exit 1
fi
# Run the command
telnet localhost 19021 | defmt-print -e "$BINARY"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package] [package]
name = "va108xx-embassy" name = "va108xx-embassy"
version = "0.2.0" version = "0.2.1"
edition = "2021" edition = "2021"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
description = "Embassy-rs support for the Vorago VA108xx family of microcontrollers" description = "Embassy-rs support for the Vorago VA108xx family of microcontrollers"

View File

@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [unreleased]
## [v0.11.1] 2025-03-10
## Fixed
- Fix `embedded_io` UART implementation to implement the documented contract properly.
The implementation will now block until at least one byte is available or can be written, unless
the send or receive buffer is empty.
## [v0.11.0] 2025-03-07 ## [v0.11.0] 2025-03-07
## Changed ## Changed
@ -253,6 +261,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- README with basic instructions how to set up own binary crate - README with basic instructions how to set up own binary crate
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.11.0...HEAD [unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.11.0...HEAD
[v0.11.1]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.11.0...va108xx-hal-v0.11.1
[v0.11.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.10.0...va108xx-hal-v0.11.0 [v0.11.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.10.0...va108xx-hal-v0.11.0
[v0.10.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.9.0...va108xx-hal-v0.10.0 [v0.10.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.9.0...va108xx-hal-v0.10.0
[v0.9.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.8.0...va108xx-hal-v0.9.0 [v0.9.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.8.0...va108xx-hal-v0.9.0

View File

@ -1,6 +1,6 @@
[package] [package]
name = "va108xx-hal" name = "va108xx-hal"
version = "0.11.0" version = "0.11.1"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021" edition = "2021"
description = "HAL for the Vorago VA108xx family of microcontrollers" description = "HAL for the Vorago VA108xx family of microcontrollers"

View File

@ -1004,7 +1004,7 @@ where
/// * `sys_clk` - System clock /// * `sys_clk` - System clock
/// * `spi` - SPI bus to use /// * `spi` - SPI bus to use
/// * `pins` - Pins to be used for SPI transactions. These pins are consumed /// * `pins` - Pins to be used for SPI transactions. These pins are consumed
/// to ensure the pins can not be used for other purposes anymore /// to ensure the pins can not be used for other purposes anymore
/// * `spi_cfg` - Configuration specific to the SPI bus /// * `spi_cfg` - Configuration specific to the SPI bus
pub fn new( pub fn new(
syscfg: &mut pac::Sysconfig, syscfg: &mut pac::Sysconfig,

View File

@ -631,8 +631,8 @@ where
/// - `pins`: UART TX and RX pin tuple. /// - `pins`: UART TX and RX pin tuple.
/// - `config`: UART specific configuration parameters like baudrate. /// - `config`: UART specific configuration parameters like baudrate.
/// - `irq_cfg`: Optional interrupt configuration. This should be a valid value if the plan /// - `irq_cfg`: Optional interrupt configuration. This should be a valid value if the plan
/// is to use TX or RX functionality relying on interrupts. If only the blocking API without /// is to use TX or RX functionality relying on interrupts. If only the blocking API without
/// any interrupt support is used, this can be [None]. /// any interrupt support is used, this can be [None].
pub fn new( pub fn new(
syscfg: &mut va108xx::Sysconfig, syscfg: &mut va108xx::Sysconfig,
sys_clk: impl Into<Hertz>, sys_clk: impl Into<Hertz>,
@ -892,7 +892,15 @@ impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for Rx<Uart> {
impl<Uart: Instance> embedded_io::Read for Rx<Uart> { impl<Uart: Instance> embedded_io::Read for Rx<Uart> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}
let mut read = 0; let mut read = 0;
loop {
if self.0.rxstatus().read().rdavl().bit_is_set() {
break;
}
}
for byte in buf.iter_mut() { for byte in buf.iter_mut() {
match <Self as embedded_hal_nb::serial::Read<u8>>::read(self) { match <Self as embedded_hal_nb::serial::Read<u8>>::read(self) {
Ok(w) => { Ok(w) => {
@ -1058,6 +1066,14 @@ impl<Uart: Instance> embedded_hal_nb::serial::Write<u8> for Tx<Uart> {
impl<Uart: Instance> embedded_io::Write for Tx<Uart> { impl<Uart: Instance> embedded_io::Write for Tx<Uart> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}
loop {
if self.0.txstatus().read().wrrdy().bit_is_set() {
break;
}
}
let mut written = 0; let mut written = 0;
for byte in buf.iter() { for byte in buf.iter() {
match <Self as embedded_hal_nb::serial::Write<u8>>::write(self, *byte) { match <Self as embedded_hal_nb::serial::Write<u8>>::write(self, *byte) {
@ -1066,7 +1082,7 @@ impl<Uart: Instance> embedded_io::Write for Tx<Uart> {
} }
} }
Ok(buf.len()) Ok(written)
} }
fn flush(&mut self) -> Result<(), Self::Error> { fn flush(&mut self) -> Result<(), Self::Error> {

View File

@ -13,7 +13,7 @@ categories = ["embedded", "no-std", "hardware-support"]
[dependencies] [dependencies]
cortex-m = "0.7" cortex-m = "0.7"
vcell = "0.1.3" vcell = "0.1.3"
defmt = { version = "0.3", optional = true } defmt = { version = "1", optional = true }
critical-section = { version = "1", optional = true } critical-section = { version = "1", optional = true }
[dependencies.cortex-m-rt] [dependencies.cortex-m-rt]