update asynch logger, simplify it #68

Merged
muellerr merged 2 commits from update-asynch-logger into main 2026-05-04 18:01:30 +02:00
6 changed files with 89 additions and 70 deletions
+2
View File
@@ -17,6 +17,8 @@ zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
zynq7000-embassy = { path = "../../zynq7000-embassy" }
dht-sensor = { git = "https://github.com/michaelbeaumont/dht-sensor.git", rev = "10319bdeae9ace3bb0fc79a15da2869c5bf50f52", features = ["async"] }
embedded-hal-async = "1"
embassy-sync = "0.8"
static_cell = "2"
critical-section = "1"
heapless = "0.9"
@@ -5,8 +5,10 @@
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use embassy_executor::Spawner;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_hal_async::delay::DelayNs as _;
use embedded_io::Write;
use log::{error, info};
use zynq7000::Peripherals;
@@ -66,32 +68,60 @@ async fn main(spawner: Spawner) -> ! {
uart.write_all(b"-- Zynq 7000 Logging example --\n\r")
.unwrap();
uart.flush().unwrap();
// Small delay to avoid logging glitches when creating an async UART.
embassy_time::Delay.delay_us(500).await;
let (tx, _rx) = uart.split();
let mut logger = TxAsync::new(tx);
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
static LOG_PIPE: static_cell::ConstStaticCell<
embassy_sync::pipe::Pipe<CriticalSectionRawMutex, 4096>,
> = static_cell::ConstStaticCell::new(embassy_sync::pipe::Pipe::new());
let (log_reader, log_writer) = LOG_PIPE.take().split();
zynq7000_hal::log::asynch::init(log::LevelFilter::Trace, log_writer);
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
let led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
spawner.spawn(led_task(led).unwrap());
spawner.spawn(hello_task().unwrap());
let mut log_buf: [u8; 2048] = [0; 2048];
let frame_queue = zynq7000_hal::log::rb::get_frame_queue();
loop {
let next_frame_len = frame_queue.receive().await;
zynq7000_hal::log::rb::read_next_frame(next_frame_len, &mut log_buf);
logger.write(&log_buf[0..next_frame_len]).await;
let read_bytes = log_reader.read(&mut log_buf).await;
if read_bytes > 0 {
logger.write(&log_buf[0..read_bytes]).await;
}
}
}
#[embassy_executor::task]
async fn led_task(mut mio_led: Output) {
static ATOMIC_COUNTER: core::sync::atomic::AtomicUsize =
core::sync::atomic::AtomicUsize::new(0);
let mut ticker = Ticker::every(Duration::from_millis(1000));
loop {
mio_led.toggle().unwrap();
info!("Toggling LED");
info!(
"Toggling LED ({})",
ATOMIC_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
);
ticker.next().await;
}
}
#[embassy_executor::task]
async fn hello_task() {
static ATOMIC_COUNTER: core::sync::atomic::AtomicUsize =
core::sync::atomic::AtomicUsize::new(0);
let mut ticker = Ticker::every(Duration::from_millis(1000));
loop {
info!(
"Hello from another task ({})",
ATOMIC_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
);
ticker.next().await;
}
}
@@ -12,6 +12,7 @@
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use embassy_executor::Spawner;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time::{Delay, Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_hal_async::delay::DelayNs;
@@ -90,7 +91,13 @@ async fn main(spawner: Spawner) -> ! {
.unwrap();
uart.write_all(b"-- Zynq 7000 Zedboard SPI L3GD20H example --\n\r")
.unwrap();
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
static LOG_PIPE: static_cell::ConstStaticCell<
embassy_sync::pipe::Pipe<CriticalSectionRawMutex, 4096>,
> = static_cell::ConstStaticCell::new(embassy_sync::pipe::Pipe::new());
let (log_reader, log_writer) = LOG_PIPE.take().split();
zynq7000_hal::log::asynch::init(log::LevelFilter::Trace, log_writer);
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
@@ -155,7 +162,7 @@ async fn main(spawner: Spawner) -> ! {
}
}
spawner.spawn(logger_task(uart).unwrap());
spawner.spawn(logger_task(uart, log_reader).unwrap());
if BLOCKING {
blocking_application(mio_led, emio_leds, spi).await;
} else {
@@ -164,15 +171,16 @@ async fn main(spawner: Spawner) -> ! {
}
#[embassy_executor::task]
pub async fn logger_task(uart: uart::Uart) {
pub async fn logger_task(
uart: uart::Uart,
reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>,
) -> ! {
let (tx, _) = uart.split();
let mut tx_async = TxAsync::new(tx);
let frame_queue = zynq7000_hal::log::rb::get_frame_queue();
let mut log_buf: [u8; 2048] = [0; 2048];
loop {
let next_frame_len = frame_queue.receive().await;
zynq7000_hal::log::rb::read_next_frame(next_frame_len, &mut log_buf);
tx_async.write(&log_buf[0..next_frame_len]).await;
let read_bytes = reader.read(&mut log_buf).await;
tx_async.write(&log_buf[0..read_bytes]).await;
}
}
+2
View File
@@ -23,6 +23,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added division by zero check in gtc frequency_to_ticks to avoid runtime panic
- Increased UART type safety by providing dedicated MIO constructors for UART 0 and UART 1
respectively.
- `log::rb` module replaced by `log::asynch` module which uses an asynchronous embassy pipe
for logging.
## Added
-1
View File
@@ -20,7 +20,6 @@ arbitrary-int = "2"
thiserror = { version = "2", default-features = false }
num_enum = { version = "0.7", default-features = false }
bitflags = "2"
ringbuf = { version = "0.4.8", default-features = false }
embedded-hal-nb = "1"
embedded-io = "0.7"
embedded-hal = "1"
+34 -56
View File
@@ -165,36 +165,31 @@ pub mod uart_blocking {
}
}
/// Logger module which logs into a ring buffer to allow asynchronous logging handling.
pub mod rb {
use core::cell::RefCell;
/// Logger module which logs into a pipe to allow asynchronous logging handling.
pub mod asynch {
use core::fmt::Write as _;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use log::{LevelFilter, set_logger, set_max_level};
use ringbuf::{
StaticRb,
traits::{Consumer, Producer},
};
/// Logger implementation which logs frames via a ring buffer and sends the frame sizes
/// as messages.
///
/// The logger does not require allocation and reserved a generous amount of 4096 bytes for
/// both data buffer and ring buffer. This should be sufficient for most logging needs.
/// The logger does not require allocation and reserves a generous amount of 4096 bytes for
/// log data. This should be sufficient for most logging needs.
pub struct Logger {
frame_queue: embassy_sync::channel::Channel<CriticalSectionRawMutex, usize, 32>,
data_buf: critical_section::Mutex<RefCell<heapless::String<4096>>>,
ring_buf: critical_section::Mutex<RefCell<Option<StaticRb<u8, 4096>>>>,
pipe: core::cell::RefCell<
Option<embassy_sync::pipe::Writer<'static, CriticalSectionRawMutex, 4096>>,
>,
buf: critical_section::Mutex<core::cell::RefCell<heapless::String<4096>>>,
}
unsafe impl Send for Logger {}
unsafe impl Sync for Logger {}
static LOGGER_RB: Logger = Logger {
frame_queue: embassy_sync::channel::Channel::new(),
data_buf: critical_section::Mutex::new(RefCell::new(heapless::String::new())),
ring_buf: critical_section::Mutex::new(RefCell::new(None)),
static LOGGER_PIPE: Logger = Logger {
pipe: core::cell::RefCell::new(None),
buf: critical_section::Mutex::new(core::cell::RefCell::new(heapless::String::new())),
};
impl log::Log for Logger {
@@ -203,58 +198,41 @@ pub mod rb {
}
fn log(&self, record: &log::Record) {
if self.pipe.borrow().is_none() {
return;
}
critical_section::with(|cs| {
let ref_buf = self.data_buf.borrow(cs);
let mut buf = ref_buf.borrow_mut();
let mut buf = self.buf.borrow(cs).borrow_mut();
buf.clear();
let _ = writeln!(buf, "{} - {}\r", record.level(), record.args());
let rb_ref = self.ring_buf.borrow(cs);
let mut rb_opt = rb_ref.borrow_mut();
if rb_opt.is_none() {
panic!("log call on uninitialized logger");
let mut written = 0;
let pipe_writer_ref = self.pipe.borrow();
let pipe_writer = pipe_writer_ref.as_ref().unwrap();
while let Ok(written_in_this_call) =
pipe_writer.try_write(&buf.as_bytes()[written..])
{
written += written_in_this_call;
if written >= buf.len() {
break;
}
}
rb_opt.as_mut().unwrap().push_slice(buf.as_bytes());
let _ = self.frame_queue.try_send(buf.len());
});
}
fn flush(&self) {
while !self.frame_queue().is_empty() {}
}
fn flush(&self) {}
}
impl Logger {
pub fn frame_queue(
&self,
) -> &embassy_sync::channel::Channel<CriticalSectionRawMutex, usize, 32> {
&self.frame_queue
}
}
pub fn init(level: LevelFilter) {
pub fn init(
level: LevelFilter,
writer: embassy_sync::pipe::Writer<'static, CriticalSectionRawMutex, 4096>,
) {
if super::LOGGER_INIT_DONE.swap(true, core::sync::atomic::Ordering::Relaxed) {
return;
}
critical_section::with(|cs| {
let rb = StaticRb::<u8, 4096>::default();
let rb_ref = LOGGER_RB.ring_buf.borrow(cs);
rb_ref.borrow_mut().replace(rb);
});
set_logger(&LOGGER_RB).unwrap();
LOGGER_PIPE.pipe.borrow_mut().replace(writer);
set_logger(&LOGGER_PIPE).unwrap();
set_max_level(level); // Adjust as needed
}
pub fn read_next_frame(frame_len: usize, buf: &mut [u8]) {
let read_len = core::cmp::min(frame_len, buf.len());
critical_section::with(|cs| {
let rb_ref = LOGGER_RB.ring_buf.borrow(cs);
let mut rb = rb_ref.borrow_mut();
rb.as_mut().unwrap().pop_slice(&mut buf[0..read_len]);
})
}
pub fn get_frame_queue()
-> &'static embassy_sync::channel::Channel<CriticalSectionRawMutex, usize, 32> {
LOGGER_RB.frame_queue()
}
}