From 84b1753b4ed87ef65c5df77b2de73f8d0e6b3642 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Sat, 24 Aug 2024 17:51:33 +0000 Subject: [PATCH 1/4] Port UART updates to rp235x-hal The updates from #798, #837 and #838 were only applied to rp2040-hal. This patch ports them to rp235x-hal. --- rp235x-hal/src/uart/peripheral.rs | 40 ++++++++++++++++++++++++++++++- rp235x-hal/src/uart/reader.rs | 23 +++++++++++++++++- rp235x-hal/src/uart/writer.rs | 10 ++++++-- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/rp235x-hal/src/uart/peripheral.rs b/rp235x-hal/src/uart/peripheral.rs index 89fb74000..b0783084c 100644 --- a/rp235x-hal/src/uart/peripheral.rs +++ b/rp235x-hal/src/uart/peripheral.rs @@ -21,6 +21,7 @@ pub struct UartPeripheral> { device: D, _state: S, pins: P, + read_error: Option, } impl> UartPeripheral { @@ -29,6 +30,7 @@ impl> UartPeripheral { device: self.device, pins: self.pins, _state: state, + read_error: None, } } @@ -48,6 +50,7 @@ impl> UartPeripheral { device, _state: Disabled, pins, + read_error: None, } } @@ -88,6 +91,7 @@ impl> UartPeripheral { device, pins, _state: Enabled, + read_error: None, }) } } @@ -250,6 +254,7 @@ impl> UartPeripheral { device: reader.device, _state: Enabled, pins: reader.pins, + read_error: reader.read_error, } } } @@ -260,6 +265,7 @@ impl> UartPeripheral { let reader = Reader { device: self.device, pins: self.pins, + read_error: self.read_error, }; // Safety: reader and writer will never write to the same address let device_copy = unsafe { Peripherals::steal().UART0 }; @@ -278,6 +284,7 @@ impl> UartPeripheral { let reader = Reader { device: self.device, pins: self.pins, + read_error: self.read_error, }; // Safety: reader and writer will never write to the same address let device_copy = unsafe { Peripherals::steal().UART1 }; @@ -469,9 +476,32 @@ impl> embedded_io::ErrorType } impl> embedded_io::Read for UartPeripheral { fn read(&mut self, buf: &mut [u8]) -> Result { - nb::block!(self.read_raw(buf)).map_err(|e| e.err_type) + // If the last read stored an error, report it now + if let Some(err) = self.read_error.take() { + return Err(err); + } + match nb::block!(self.read_raw(buf)) { + Ok(bytes_read) => Ok(bytes_read), + Err(err) if !err.discarded.is_empty() => { + // If an error was reported but some bytes were already read, + // return the data now and store the error for the next + // invocation. + self.read_error = Some(err.err_type); + Ok(err.discarded.len()) + } + Err(err) => Err(err.err_type), + } + } +} + +impl> embedded_io::ReadReady + for UartPeripheral +{ + fn read_ready(&mut self) -> Result { + Ok(self.uart_is_readable()) } } + impl> embedded_io::Write for UartPeripheral { fn write(&mut self, buf: &[u8]) -> Result { self.write_full_blocking(buf); @@ -482,3 +512,11 @@ impl> embedded_io::Write for UartPeripheral Ok(()) } } + +impl> embedded_io::WriteReady + for UartPeripheral +{ + fn write_ready(&mut self) -> Result { + Ok(self.uart_is_writable()) + } +} diff --git a/rp235x-hal/src/uart/reader.rs b/rp235x-hal/src/uart/reader.rs index 6126a4fcd..8df37e13c 100644 --- a/rp235x-hal/src/uart/reader.rs +++ b/rp235x-hal/src/uart/reader.rs @@ -190,6 +190,7 @@ pub(crate) fn read_full_blocking( pub struct Reader> { pub(super) device: D, pub(super) pins: P, + pub(super) read_error: Option, } impl> Reader { @@ -228,7 +229,27 @@ impl> embedded_io::ErrorType for Reader> embedded_io::Read for Reader { fn read(&mut self, buf: &mut [u8]) -> Result { - nb::block!(self.read_raw(buf)).map_err(|e| e.err_type) + // If the last read stored an error, report it now + if let Some(err) = self.read_error.take() { + return Err(err); + } + match nb::block!(self.read_raw(buf)) { + Ok(bytes_read) => Ok(bytes_read), + Err(err) if !err.discarded.is_empty() => { + // If an error was reported but some bytes were already read, + // return the data now and store the error for the next + // invocation. + self.read_error = Some(err.err_type); + Ok(err.discarded.len()) + } + Err(err) => Err(err.err_type), + } + } +} + +impl> embedded_io::ReadReady for Reader { + fn read_ready(&mut self) -> Result { + Ok(is_readable(&self.device)) } } diff --git a/rp235x-hal/src/uart/writer.rs b/rp235x-hal/src/uart/writer.rs index 941fc9398..ac9c4779c 100644 --- a/rp235x-hal/src/uart/writer.rs +++ b/rp235x-hal/src/uart/writer.rs @@ -215,8 +215,8 @@ impl> embedded_io::ErrorType for Writer> embedded_io::Write for Writer { fn write(&mut self, buf: &[u8]) -> Result { - self.write_full_blocking(buf); - Ok(buf.len()) + let remaining = nb::block!(write_raw(&self.device, buf)).unwrap(); // Infallible + Ok(buf.len() - remaining.len()) } fn flush(&mut self) -> Result<(), Self::Error> { nb::block!(transmit_flushed(&self.device)).unwrap(); // Infallible @@ -224,6 +224,12 @@ impl> embedded_io::Write for Writer { } } +impl> embedded_io::WriteReady for Writer { + fn write_ready(&mut self) -> Result { + Ok(uart_is_writable(&self.device)) + } +} + impl> Write02 for Writer { type Error = Infallible; From 01b9772501eadc21f694349ad3c9018c0576ee38 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 25 Aug 2024 19:50:11 +0100 Subject: [PATCH 2/4] rp235x-hal-examples: fix typo in comment in rom_funcs --- rp235x-hal-examples/src/bin/rom_funcs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rp235x-hal-examples/src/bin/rom_funcs.rs b/rp235x-hal-examples/src/bin/rom_funcs.rs index 79b73f7fc..c572cb59d 100644 --- a/rp235x-hal-examples/src/bin/rom_funcs.rs +++ b/rp235x-hal-examples/src/bin/rom_funcs.rs @@ -183,7 +183,7 @@ fn main() -> ! { } _ = writeln!(uart); - // Do an asynchronous reset in 2000ms time, into the bootloader. + // Do a reset into the bootloader. hal::reboot::reboot( hal::reboot::RebootKind::BootSel { msd_disabled: false, From f84b76497648acb355ca5ca0baa1b760a3f337c0 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 25 Aug 2024 20:00:00 +0100 Subject: [PATCH 3/4] Add uart_loopback example. Whilst testing it I fixed some UART driver bugs I found. I also re-synchronised RP235x and RP2040 UART drivers. --- rp2040-hal-examples/Cargo.toml | 1 + rp2040-hal-examples/src/bin/uart_loopback.rs | 429 +++++++++++++++++++ rp2040-hal/src/uart/peripheral.rs | 9 +- rp2040-hal/src/uart/reader.rs | 37 +- rp235x-hal-examples/Cargo.toml | 1 + rp235x-hal-examples/riscv_examples.txt | 1 + rp235x-hal-examples/src/bin/uart_loopback.rs | 428 ++++++++++++++++++ rp235x-hal/src/uart/peripheral.rs | 2 +- rp235x-hal/src/uart/reader.rs | 37 +- 9 files changed, 931 insertions(+), 14 deletions(-) create mode 100644 rp2040-hal-examples/src/bin/uart_loopback.rs create mode 100644 rp235x-hal-examples/src/bin/uart_loopback.rs diff --git a/rp2040-hal-examples/Cargo.toml b/rp2040-hal-examples/Cargo.toml index 7956ab7ac..852526368 100644 --- a/rp2040-hal-examples/Cargo.toml +++ b/rp2040-hal-examples/Cargo.toml @@ -22,6 +22,7 @@ dht-sensor = "0.2.1" embedded-alloc = "0.5.1" embedded-hal = "1.0.0" embedded-hal-async = "1.0.0" +embedded-io = "0.6.1" embedded_hal_0_2 = {package = "embedded-hal", version = "0.2.5", features = ["unproven"]} fugit = "0.3.6" futures = {version = "0.3.30", default-features = false, features = ["async-await"]} diff --git a/rp2040-hal-examples/src/bin/uart_loopback.rs b/rp2040-hal-examples/src/bin/uart_loopback.rs new file mode 100644 index 000000000..73d7f9dcf --- /dev/null +++ b/rp2040-hal-examples/src/bin/uart_loopback.rs @@ -0,0 +1,429 @@ +//! # UART Loopback Example +//! +//! This application tests handling UART errors. +//! +//! It may need to be adapted to your particular board layout and/or pin +//! assignment. We assume you have connected GP0 to a TTL UART on your host +//! computer at 115200 baud. We assume that GP1 is connected to GP4, which is +//! our UART loopback connection. +//! +//! See the `Cargo.toml` file for Copyright and license details. + +#![no_std] +#![no_main] + +// Alias for our HAL crate +use rp2040_hal as hal; + +// Some traits we need +use core::fmt::Write; +use embedded_hal::delay::DelayNs; +use hal::fugit::RateExtU32; +use rp2040_hal::clocks::Clock; + +// UART related types +use hal::uart::{DataBits, Parity, StopBits, UartConfig}; + +/// The linker will place this boot block at the start of our program image. We +/// need this to help the ROM bootloader get our code up and running. +/// Note: This boot block is not necessary when using a rp-hal based BSP +/// as the BSPs already perform this step. +#[link_section = ".boot2"] +#[used] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H; + +/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust +/// if your board has a different frequency +const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// Some sample text we can send. +/// +/// It's the same length as the UART FIFO, deliberately. +static SAMPLE32: [u8; 32] = *b"abcdefghijklmnopqrstuvwxyz012345"; + +/// Entry point to our bare-metal application. +/// +/// The `#[rp2040_hal::entry]` macro ensures the Cortex-M start-up code calls this function +/// as soon as all global variables and the spinlock are initialised. +/// +/// The function configures the RP2040 peripherals, then writes to the UART in +/// an infinite loop. +#[rp2040_hal::entry] +fn main() -> ! { + // Grab our singleton objects + let mut pac = hal::pac::Peripherals::take().unwrap(); + + // Set up the watchdog driver - needed by the clock setup code + let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); + + // Configure the clocks + let clocks = hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .unwrap(); + + let mut delay = hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); + + // The single-cycle I/O block controls our GPIO pins + let sio = hal::Sio::new(pac.SIO); + + // Set the pins to their default state + let pins = hal::gpio::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + + let uart0_pins = ( + // UART TX (characters sent from RP2350) + pins.gpio0.into_function(), + // UART RX (characters received by RP2350) + pins.gpio1.into_function(), + ); + let mut uart0 = hal::uart::UartPeripheral::new(pac.UART0, uart0_pins, &mut pac.RESETS) + .enable( + UartConfig::new( + 115200.Hz(), + DataBits::Eight, + Some(Parity::Even), + StopBits::One, + ), + clocks.peripheral_clock.freq(), + ) + .unwrap(); + uart0.set_fifos(true); + + let uart1_pins = ( + // UART TX (characters sent from RP2350) + pins.gpio4.into_function(), + // UART RX (characters received by RP2350) + pins.gpio5.into_function(), + ); + let mut uart1 = hal::uart::UartPeripheral::new(pac.UART1, uart1_pins, &mut pac.RESETS) + .enable( + UartConfig::new( + 115200.Hz(), + DataBits::Eight, + Some(Parity::Even), + StopBits::One, + ), + clocks.peripheral_clock.freq(), + ) + .unwrap(); + uart1.set_fifos(true); + + let mut buffer = [0u8; 128]; + + // ====================================================================== + // Single byte read/write + // ====================================================================== + + let sample = &SAMPLE32[0..1]; + _ = writeln!(uart0, "** Testing single byte write/read"); + uart1.write_full_blocking(sample); + delay.delay_ms(100); + uart0 + .read_full_blocking(&mut buffer[0..sample.len()]) + .unwrap(); + if &buffer[0..sample.len()] != sample { + _ = writeln!( + uart0, + "Failed:\n{:02x?} !=\n{:02x?}", + &buffer[0..sample.len()], + sample + ); + panic!("Test failed"); + } + _ = writeln!(uart0, "Tested OK"); + + // ====================================================================== + // FIFO backed read/write + // ====================================================================== + + let sample = &SAMPLE32[..]; + _ = writeln!(uart0, "** Testing FIFO write/read"); + uart1.write_full_blocking(sample); + delay.delay_ms(100); + uart0 + .read_full_blocking(&mut buffer[0..sample.len()]) + .unwrap(); + if &buffer[0..sample.len()] != sample { + _ = writeln!( + uart0, + "Failed:\n{:02x?} !=\n{:02x?}", + &buffer[0..sample.len()], + sample + ); + panic!("Test failed"); + } + _ = writeln!(uart0, "Tested OK"); + + // ====================================================================== + // FIFO overflow read/write + // + // Note: The Arm Primecell PL022 UART that Raspberry Pi uses has a 32-byte + // FIFO. We're about to overflow that FIFO. + // ====================================================================== + + let sample = &SAMPLE32[..]; + _ = writeln!(uart0, "** Testing FIFO overflow write/read"); + _ = writeln!(uart0, "Sending {} bytes...", sample.len() + 1); + // send 32 bytes to the receiving FIFO + uart1.write_full_blocking(sample); + // Now send one more byte to overflow the receiving FIFO. This byte is lost + // to the wind. + // + // NB: It fits into the TX FIFO because this is a 'blocking' call that + // waited for FIFO space. + uart1.write_full_blocking(&[0x00]); + // Let the TX FIFO drain. + delay.delay_ms(100); + // the first 32 bytes should read fine + uart0 + .read_full_blocking(&mut buffer[0..sample.len()]) + .unwrap(); + if &buffer[0..sample.len()] != sample { + _ = writeln!( + uart0, + "Failed:\n{:02x?} !=\n{:02x?}", + &buffer[0..sample.len()], + sample + ); + panic!("Test failed"); + } + _ = writeln!(uart0, "Got first 32 bytes OK..."); + + _ = writeln!( + uart0, + "I now want to see Overrun([]), WouldBlock, WouldBlock" + ); + _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..])); + _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..])); + _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..])); + // Now send two more bytes - the first will also be flagged with an overrun error. + _ = writeln!(uart0, "Sending two more bytes..."); + uart1.write_full_blocking(&[0x01, 0x02]); + // let them transfer over + delay.delay_ms(100); + // annoyingly we see the overrun error again, then our data + _ = writeln!(uart0, "I want to see Overrun([1]), Ok(1), WouldBlock"); + _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..])); + _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..])); + _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..])); + + // ====================================================================== + // FIFO read/write with parity error + // ====================================================================== + + _ = writeln!(uart0, "** Testing FIFO read with parity errors"); + // Send three bytes with correct parity + uart1.write_full_blocking(&[0x00, 0x01, 0x02]); + delay.delay_ms(100); + // send one with bad settings + uart1 = uart1 + .disable() + .enable( + UartConfig::new( + 115200.Hz(), + DataBits::Eight, + Some(Parity::Odd), + StopBits::One, + ), + clocks.peripheral_clock.freq(), + ) + .unwrap(); + uart1.write_full_blocking(&[0x03]); + delay.delay_ms(100); + // send three more with good parity + uart1 = uart1 + .disable() + .enable( + UartConfig::new( + 115200.Hz(), + DataBits::Eight, + Some(Parity::Even), + StopBits::One, + ), + clocks.peripheral_clock.freq(), + ) + .unwrap(); + uart1.write_full_blocking(&[0x04, 0x05, 0x06]); + delay.delay_ms(100); + + _ = writeln!(uart0, "I want to see Parity error [0, 1, 2]"); + match uart0.read_raw(&mut buffer[..]) { + Ok(n) => { + _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + _ = writeln!(uart0, "I want to see RX: [4, 5, 6]"); + match uart0.read_raw(&mut buffer[..]) { + Ok(n) => { + _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + _ = writeln!(uart0, "I want to see WouldBlock"); + match uart0.read_raw(&mut buffer[..]) { + Ok(n) => { + _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + + // ====================================================================== + // FIFO backed read/write with embedded_io traits. + // ====================================================================== + + let sample = &SAMPLE32[..]; + _ = writeln!(uart0, "** Testing FIFO write/read with embedded-io"); + + embedded_io::Write::write_all(&mut uart1, sample).unwrap(); + delay.delay_ms(100); + + embedded_io::Read::read_exact(&mut uart0, &mut buffer[0..sample.len()]).unwrap(); + if &buffer[0..sample.len()] != sample { + _ = writeln!( + uart0, + "Failed:\n{:02x?} !=\n{:02x?}", + &buffer[0..sample.len()], + sample + ); + panic!("Test failed"); + } + _ = writeln!(uart0, "Tested OK"); + + // ====================================================================== + // FIFO read/write with parity error using embedded-io + // ====================================================================== + + _ = writeln!(uart0, "** Testing FIFO read with parity errors"); + // Send three bytes with correct parity + uart1.write_full_blocking(&[0x00, 0x01, 0x02]); + delay.delay_ms(100); + // send one with bad settings + uart1 = uart1 + .disable() + .enable( + UartConfig::new( + 115200.Hz(), + DataBits::Eight, + Some(Parity::Odd), + StopBits::One, + ), + clocks.peripheral_clock.freq(), + ) + .unwrap(); + uart1.write_full_blocking(&[0x03]); + delay.delay_ms(100); + // send three more with good parity + uart1 = uart1 + .disable() + .enable( + UartConfig::new( + 115200.Hz(), + DataBits::Eight, + Some(Parity::Even), + StopBits::One, + ), + clocks.peripheral_clock.freq(), + ) + .unwrap(); + uart1.write_full_blocking(&[0x04, 0x05, 0x06]); + delay.delay_ms(100); + + _ = writeln!(uart0, "I want to see RX: [0, 1, 2]"); + match embedded_io::Read::read(&mut uart0, &mut buffer[..]) { + Ok(n) => { + _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + _ = writeln!(uart0, "I want to see ParityError"); + match embedded_io::Read::read(&mut uart0, &mut buffer[..]) { + Ok(n) => { + _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + + _ = writeln!(uart0, "I want to see RX: [4, 5, 6]"); + match embedded_io::Read::read(&mut uart0, &mut buffer[..]) { + Ok(n) => { + _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + + _ = writeln!(uart0, "I want to see RX ready: false"); + match embedded_io::ReadReady::read_ready(&mut uart0) { + Ok(ready) => { + _ = writeln!(uart0, "RX ready: {}", ready); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + + // ====================================================================== + // Tests complete + // ====================================================================== + + _ = writeln!(uart0, "Tests complete. Review output for correctness."); + + // Reboot back into USB mode (no activity, both interfaces enabled) + rp2040_hal::rom_data::reset_to_usb_boot(0, 0); + + // In case the reboot fails + loop { + cortex_m::asm::wfi(); + } +} + +/// Program metadata for `picotool info` +#[link_section = ".bi_entries"] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"UART Loopback Example"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[panic_handler] +fn panic(_: &core::panic::PanicInfo) -> ! { + // wait about 1s for UART FIFOs to flush + for _ in 0..75_000_000 { + cortex_m::asm::nop(); + } + // Reboot back into USB mode (no activity, both interfaces enabled) + rp2040_hal::rom_data::reset_to_usb_boot(0, 0); + + // In case the reboot fails + loop { + cortex_m::asm::wfi(); + } +} + +// End of file diff --git a/rp2040-hal/src/uart/peripheral.rs b/rp2040-hal/src/uart/peripheral.rs index 0ef341f1a..6435229db 100644 --- a/rp2040-hal/src/uart/peripheral.rs +++ b/rp2040-hal/src/uart/peripheral.rs @@ -195,12 +195,14 @@ impl> UartPeripheral { } /// Writes bytes to the UART. + /// /// This function blocks until the full buffer has been sent. pub fn write_full_blocking(&self, data: &[u8]) { super::writer::write_full_blocking(&self.device, data); } /// Reads bytes from the UART. + /// /// This function blocks until the full buffer has been received. pub fn read_full_blocking(&self, buffer: &mut [u8]) -> Result<(), ReadErrorType> { super::reader::read_full_blocking(&self.device, buffer) @@ -219,15 +221,16 @@ impl> UartPeripheral { /// ```no_run /// # use rp2040_hal::uart::{Pins, ValidUartPinout, Enabled, UartPeripheral}; /// # use rp2040_hal::pac::UART0; + /// # use rp2040_hal::timer::Timer; /// # use rp2040_hal::typelevel::OptionTNone; /// # use embedded_hal_0_2::blocking::delay::DelayUs; /// # type PINS = Pins; - /// # let mut serial: UartPeripheral = unsafe { core::mem::zeroed() }; - /// # let mut timer: rp2040_hal::Timer = unsafe { core::mem::zeroed() }; + /// # fn example(mut serial: UartPeripheral, mut timer: Timer) { /// serial.lowlevel_break_start(); /// // at 115_200Bps on 8N1 configuration, 20bits takes (20*10⁶)/115200 = 173.611…μs. /// timer.delay_us(175); /// serial.lowlevel_break_stop(); + /// } /// ``` pub fn lowlevel_break_start(&mut self) { self.device.uartlcr_h().modify(|_, w| w.brk().set_bit()); @@ -495,7 +498,7 @@ impl> embedded_io::ReadReady for UartPeripheral { fn read_ready(&mut self) -> Result { - Ok(self.uart_is_readable()) + Ok(self.uart_is_readable() || self.read_error.is_some()) } } diff --git a/rp2040-hal/src/uart/reader.rs b/rp2040-hal/src/uart/reader.rs index 6be1e2fac..73acd4ab4 100644 --- a/rp2040-hal/src/uart/reader.rs +++ b/rp2040-hal/src/uart/reader.rs @@ -121,7 +121,29 @@ pub(crate) fn read_raw<'b, D: UartDevice>( Ok(loop { if !is_readable(device) { if bytes_read == 0 { - return Err(WouldBlock); + // The overrun error (OE) bit is checked separately as it + // doesn't really correspond to a specific byte we've read. If + // we don't do this here, the overrun error is hidden until the + // next byte turns up - which may never happen. + if device.uartrsr().read().oe().bit_is_set() { + // We observed a FIFO overrun on an empty FIFO. Clear the + // error otherwise it sticks. + unsafe { + device.uartrsr().write_with_zero(|w| w); + } + // Now report the error. + // + // Note that you will also get an overrun error on the first + // byte that turns up after this error - we can't stop that + // as we have no mutable state to indicate that it's already + // been handled. But two overrun errors is better that none. + return Err(Other(ReadError { + err_type: ReadErrorType::Overrun, + discarded: &buffer[..bytes_read], + })); + } else { + return Err(WouldBlock); + } } else { break bytes_read; } @@ -135,15 +157,20 @@ pub(crate) fn read_raw<'b, D: UartDevice>( // If multiple status bits are set, report // the most serious or most specific condition, // in the following order of precedence: - // overrun > break > parity > framing - if read.oe().bit_is_set() { - error = Some(ReadErrorType::Overrun); - } else if read.be().bit_is_set() { + // break > parity > framing + // + // overrun is last because the byte associated with it is still good. + if read.be().bit_is_set() { error = Some(ReadErrorType::Break); } else if read.pe().bit_is_set() { error = Some(ReadErrorType::Parity); } else if read.fe().bit_is_set() { error = Some(ReadErrorType::Framing); + } else if read.oe().bit_is_set() { + error = Some(ReadErrorType::Overrun); + // if we get an overrun - there's still data there + buffer[bytes_read] = read.data().bits(); + bytes_read += 1; } if let Some(err_type) = error { diff --git a/rp235x-hal-examples/Cargo.toml b/rp235x-hal-examples/Cargo.toml index 4df1b14d1..bb9f25f7e 100644 --- a/rp235x-hal-examples/Cargo.toml +++ b/rp235x-hal-examples/Cargo.toml @@ -22,6 +22,7 @@ dht-sensor = "0.2.1" embedded-alloc = "0.5.1" embedded-hal = "1.0.0" embedded-hal-async = "1.0.0" +embedded-io = "0.6.1" embedded_hal_0_2 = {package = "embedded-hal", version = "0.2.5", features = ["unproven"]} fugit = "0.3.6" futures = {version = "0.3.30", default-features = false, features = ["async-await"]} diff --git a/rp235x-hal-examples/riscv_examples.txt b/rp235x-hal-examples/riscv_examples.txt index 9dc23f7b0..0800802a5 100644 --- a/rp235x-hal-examples/riscv_examples.txt +++ b/rp235x-hal-examples/riscv_examples.txt @@ -25,5 +25,6 @@ spi spi_dma uart uart_dma +uart_loopback usb watchdog diff --git a/rp235x-hal-examples/src/bin/uart_loopback.rs b/rp235x-hal-examples/src/bin/uart_loopback.rs new file mode 100644 index 000000000..2e2be1a93 --- /dev/null +++ b/rp235x-hal-examples/src/bin/uart_loopback.rs @@ -0,0 +1,428 @@ +//! # UART Loopback Example +//! +//! This application tests handling UART errors. +//! +//! It may need to be adapted to your particular board layout and/or pin +//! assignment. We assume you have connected GP0 to a TTL UART on your host +//! computer at 115200 baud. We assume that GP1 is connected to GP4, which is +//! our UART loopback connection. +//! +//! See the `Cargo.toml` file for Copyright and license details. + +#![no_std] +#![no_main] + +// Alias for our HAL crate +use rp235x_hal as hal; + +// Some things we need +use core::fmt::Write; +use embedded_hal::delay::DelayNs; +use hal::clocks::Clock; +use hal::fugit::RateExtU32; + +// UART related types +use hal::uart::{DataBits, Parity, StopBits, UartConfig}; + +/// Tell the Boot ROM about our application +#[link_section = ".start_block"] +#[used] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz. +/// Adjust if your board has a different frequency +const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// Some sample text we can send. +/// +/// It's the same length as the UART FIFO, deliberately. +static SAMPLE32: [u8; 32] = *b"abcdefghijklmnopqrstuvwxyz012345"; + +/// Entry point to our bare-metal application. +/// +/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function +/// as soon as all global variables and the spinlock are initialised. +/// +/// The function configures the rp235x peripherals, then writes to the UART in +/// an infinite loop. +#[hal::entry] +fn main() -> ! { + // Grab our singleton objects + let mut pac = hal::pac::Peripherals::take().unwrap(); + + // Set up the watchdog driver - needed by the clock setup code + let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); + + // Configure the clocks + let clocks = hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .unwrap(); + + let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks); + + // The single-cycle I/O block controls our GPIO pins + let sio = hal::Sio::new(pac.SIO); + + // Set the pins to their default state + let pins = hal::gpio::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + + let uart0_pins = ( + // UART TX (characters sent from RP2350) + pins.gpio0.into_function(), + // UART RX (characters received by RP2350) + pins.gpio1.into_function(), + ); + let mut uart0 = hal::uart::UartPeripheral::new(pac.UART0, uart0_pins, &mut pac.RESETS) + .enable( + UartConfig::new( + 115200.Hz(), + DataBits::Eight, + Some(Parity::Even), + StopBits::One, + ), + clocks.peripheral_clock.freq(), + ) + .unwrap(); + uart0.set_fifos(true); + + let uart1_pins = ( + // UART TX (characters sent from RP2350) + pins.gpio4.into_function(), + // UART RX (characters received by RP2350) + pins.gpio5.into_function(), + ); + let mut uart1 = hal::uart::UartPeripheral::new(pac.UART1, uart1_pins, &mut pac.RESETS) + .enable( + UartConfig::new( + 115200.Hz(), + DataBits::Eight, + Some(Parity::Even), + StopBits::One, + ), + clocks.peripheral_clock.freq(), + ) + .unwrap(); + uart1.set_fifos(true); + + let mut buffer = [0u8; 128]; + + // ====================================================================== + // Single byte read/write + // ====================================================================== + + let sample = &SAMPLE32[0..1]; + _ = writeln!(uart0, "** Testing single byte write/read"); + uart1.write_full_blocking(sample); + delay.delay_ms(100); + uart0 + .read_full_blocking(&mut buffer[0..sample.len()]) + .unwrap(); + if &buffer[0..sample.len()] != sample { + _ = writeln!( + uart0, + "Failed:\n{:02x?} !=\n{:02x?}", + &buffer[0..sample.len()], + sample + ); + panic!("Test failed"); + } + _ = writeln!(uart0, "Tested OK"); + + // ====================================================================== + // FIFO backed read/write + // ====================================================================== + + let sample = &SAMPLE32[..]; + _ = writeln!(uart0, "** Testing FIFO write/read"); + uart1.write_full_blocking(sample); + delay.delay_ms(100); + uart0 + .read_full_blocking(&mut buffer[0..sample.len()]) + .unwrap(); + if &buffer[0..sample.len()] != sample { + _ = writeln!( + uart0, + "Failed:\n{:02x?} !=\n{:02x?}", + &buffer[0..sample.len()], + sample + ); + panic!("Test failed"); + } + _ = writeln!(uart0, "Tested OK"); + + // ====================================================================== + // FIFO overflow read/write + // + // Note: The Arm Primecell PL022 UART that Raspberry Pi uses has a 32-byte + // FIFO. We're about to overflow that FIFO. + // ====================================================================== + + let sample = &SAMPLE32[..]; + _ = writeln!(uart0, "** Testing FIFO overflow write/read"); + _ = writeln!(uart0, "Sending {} bytes...", sample.len() + 1); + // send 32 bytes to the receiving FIFO + uart1.write_full_blocking(sample); + // Now send one more byte to overflow the receiving FIFO. This byte is lost + // to the wind. + // + // NB: It fits into the TX FIFO because this is a 'blocking' call that + // waited for FIFO space. + uart1.write_full_blocking(&[0x00]); + // Let the TX FIFO drain. + delay.delay_ms(100); + // the first 32 bytes should read fine + uart0 + .read_full_blocking(&mut buffer[0..sample.len()]) + .unwrap(); + if &buffer[0..sample.len()] != sample { + _ = writeln!( + uart0, + "Failed:\n{:02x?} !=\n{:02x?}", + &buffer[0..sample.len()], + sample + ); + panic!("Test failed"); + } + _ = writeln!(uart0, "Got first 32 bytes OK..."); + + _ = writeln!( + uart0, + "I now want to see Overrun([]), WouldBlock, WouldBlock" + ); + _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..])); + _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..])); + _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..])); + // Now send two more bytes - the first will also be flagged with an overrun error. + _ = writeln!(uart0, "Sending two more bytes..."); + uart1.write_full_blocking(&[0x01, 0x02]); + // let them transfer over + delay.delay_ms(100); + // annoyingly we see the overrun error again, then our data + _ = writeln!(uart0, "I want to see Overrun([1]), Ok(1), WouldBlock"); + _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..])); + _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..])); + _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..])); + + // ====================================================================== + // FIFO read/write with parity error + // ====================================================================== + + _ = writeln!(uart0, "** Testing FIFO read with parity errors"); + // Send three bytes with correct parity + uart1.write_full_blocking(&[0x00, 0x01, 0x02]); + delay.delay_ms(100); + // send one with bad settings + uart1 = uart1 + .disable() + .enable( + UartConfig::new( + 115200.Hz(), + DataBits::Eight, + Some(Parity::Odd), + StopBits::One, + ), + clocks.peripheral_clock.freq(), + ) + .unwrap(); + uart1.write_full_blocking(&[0x03]); + delay.delay_ms(100); + // send three more with good parity + uart1 = uart1 + .disable() + .enable( + UartConfig::new( + 115200.Hz(), + DataBits::Eight, + Some(Parity::Even), + StopBits::One, + ), + clocks.peripheral_clock.freq(), + ) + .unwrap(); + uart1.write_full_blocking(&[0x04, 0x05, 0x06]); + delay.delay_ms(100); + + _ = writeln!(uart0, "I want to see Parity error [0, 1, 2]"); + match uart0.read_raw(&mut buffer[..]) { + Ok(n) => { + _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + _ = writeln!(uart0, "I want to see RX: [4, 5, 6]"); + match uart0.read_raw(&mut buffer[..]) { + Ok(n) => { + _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + _ = writeln!(uart0, "I want to see WouldBlock"); + match uart0.read_raw(&mut buffer[..]) { + Ok(n) => { + _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + + // ====================================================================== + // FIFO backed read/write with embedded_io traits. + // ====================================================================== + + let sample = &SAMPLE32[..]; + _ = writeln!(uart0, "** Testing FIFO write/read with embedded-io"); + + embedded_io::Write::write_all(&mut uart1, sample).unwrap(); + delay.delay_ms(100); + + embedded_io::Read::read_exact(&mut uart0, &mut buffer[0..sample.len()]).unwrap(); + if &buffer[0..sample.len()] != sample { + _ = writeln!( + uart0, + "Failed:\n{:02x?} !=\n{:02x?}", + &buffer[0..sample.len()], + sample + ); + panic!("Test failed"); + } + _ = writeln!(uart0, "Tested OK"); + + // ====================================================================== + // FIFO read/write with parity error using embedded-io + // ====================================================================== + + _ = writeln!(uart0, "** Testing FIFO read with parity errors"); + // Send three bytes with correct parity + uart1.write_full_blocking(&[0x00, 0x01, 0x02]); + delay.delay_ms(100); + // send one with bad settings + uart1 = uart1 + .disable() + .enable( + UartConfig::new( + 115200.Hz(), + DataBits::Eight, + Some(Parity::Odd), + StopBits::One, + ), + clocks.peripheral_clock.freq(), + ) + .unwrap(); + uart1.write_full_blocking(&[0x03]); + delay.delay_ms(100); + // send three more with good parity + uart1 = uart1 + .disable() + .enable( + UartConfig::new( + 115200.Hz(), + DataBits::Eight, + Some(Parity::Even), + StopBits::One, + ), + clocks.peripheral_clock.freq(), + ) + .unwrap(); + uart1.write_full_blocking(&[0x04, 0x05, 0x06]); + delay.delay_ms(100); + + _ = writeln!(uart0, "I want to see RX: [0, 1, 2]"); + match embedded_io::Read::read(&mut uart0, &mut buffer[..]) { + Ok(n) => { + _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + _ = writeln!(uart0, "I want to see ParityError"); + match embedded_io::Read::read(&mut uart0, &mut buffer[..]) { + Ok(n) => { + _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + + _ = writeln!(uart0, "I want to see RX: [4, 5, 6]"); + match embedded_io::Read::read(&mut uart0, &mut buffer[..]) { + Ok(n) => { + _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + + _ = writeln!(uart0, "I want to see RX ready: false"); + match embedded_io::ReadReady::read_ready(&mut uart0) { + Ok(ready) => { + _ = writeln!(uart0, "RX ready: {}", ready); + } + Err(e) => { + _ = writeln!(uart0, "RXE: {:?}", e); + } + } + + // ====================================================================== + // Tests complete + // ====================================================================== + + _ = writeln!(uart0, "Tests complete. Review output for correctness."); + + // Do a reset into the bootloader. + hal::reboot::reboot( + hal::reboot::RebootKind::BootSel { + msd_disabled: false, + picoboot_disabled: false, + }, + hal::reboot::RebootArch::Normal, + ); +} + +/// Program metadata for `picotool info` +#[link_section = ".bi_entries"] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"UART Loopback Example"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[panic_handler] +fn panic(_: &core::panic::PanicInfo) -> ! { + // wait about 1s for UART FIFOs to flush + for _ in 0..75_000_000 { + hal::arch::nop(); + } + // Do a reset into the bootloader. + hal::reboot::reboot( + hal::reboot::RebootKind::BootSel { + msd_disabled: false, + picoboot_disabled: false, + }, + hal::reboot::RebootArch::Normal, + ); +} + +// End of file diff --git a/rp235x-hal/src/uart/peripheral.rs b/rp235x-hal/src/uart/peripheral.rs index b0783084c..95421d3b8 100644 --- a/rp235x-hal/src/uart/peripheral.rs +++ b/rp235x-hal/src/uart/peripheral.rs @@ -498,7 +498,7 @@ impl> embedded_io::ReadReady for UartPeripheral { fn read_ready(&mut self) -> Result { - Ok(self.uart_is_readable()) + Ok(self.uart_is_readable() || self.read_error.is_some()) } } diff --git a/rp235x-hal/src/uart/reader.rs b/rp235x-hal/src/uart/reader.rs index 8df37e13c..cdef68607 100644 --- a/rp235x-hal/src/uart/reader.rs +++ b/rp235x-hal/src/uart/reader.rs @@ -124,7 +124,29 @@ pub(crate) fn read_raw<'b, D: UartDevice>( Ok(loop { if !is_readable(device) { if bytes_read == 0 { - return Err(WouldBlock); + // The overrun error (OE) bit is checked separately as it + // doesn't really correspond to a specific byte we've read. If + // we don't do this here, the overrun error is hidden until the + // next byte turns up - which may never happen. + if device.uartrsr().read().oe().bit_is_set() { + // We observed a FIFO overrun on an empty FIFO. Clear the + // error otherwise it sticks. + unsafe { + device.uartrsr().write_with_zero(|w| w); + } + // Now report the error. + // + // Note that you will also get an overrun error on the first + // byte that turns up after this error - we can't stop that + // as we have no mutable state to indicate that it's already + // been handled. But two overrun errors is better that none. + return Err(Other(ReadError { + err_type: ReadErrorType::Overrun, + discarded: &buffer[..bytes_read], + })); + } else { + return Err(WouldBlock); + } } else { break bytes_read; } @@ -138,15 +160,20 @@ pub(crate) fn read_raw<'b, D: UartDevice>( // If multiple status bits are set, report // the most serious or most specific condition, // in the following order of precedence: - // overrun > break > parity > framing - if read.oe().bit_is_set() { - error = Some(ReadErrorType::Overrun); - } else if read.be().bit_is_set() { + // break > parity > framing + // + // overrun is last because the byte associated with it is still good. + if read.be().bit_is_set() { error = Some(ReadErrorType::Break); } else if read.pe().bit_is_set() { error = Some(ReadErrorType::Parity); } else if read.fe().bit_is_set() { error = Some(ReadErrorType::Framing); + } else if read.oe().bit_is_set() { + error = Some(ReadErrorType::Overrun); + // if we get an overrun - there's still data there + buffer[bytes_read] = read.data().bits(); + bytes_read += 1; } if let Some(err_type) = error { From 0f41fd9f3fc5449d139a11d13f251dd734eeacb5 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Mon, 26 Aug 2024 21:35:16 +0000 Subject: [PATCH 4/4] Enable pull-ups in uart_loopback examples --- rp2040-hal-examples/src/bin/uart_loopback.rs | 4 ++-- rp235x-hal-examples/src/bin/uart_loopback.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rp2040-hal-examples/src/bin/uart_loopback.rs b/rp2040-hal-examples/src/bin/uart_loopback.rs index 73d7f9dcf..fd41b5e1a 100644 --- a/rp2040-hal-examples/src/bin/uart_loopback.rs +++ b/rp2040-hal-examples/src/bin/uart_loopback.rs @@ -85,7 +85,7 @@ fn main() -> ! { // UART TX (characters sent from RP2350) pins.gpio0.into_function(), // UART RX (characters received by RP2350) - pins.gpio1.into_function(), + pins.gpio1.into_pull_up_input().into_function(), ); let mut uart0 = hal::uart::UartPeripheral::new(pac.UART0, uart0_pins, &mut pac.RESETS) .enable( @@ -104,7 +104,7 @@ fn main() -> ! { // UART TX (characters sent from RP2350) pins.gpio4.into_function(), // UART RX (characters received by RP2350) - pins.gpio5.into_function(), + pins.gpio5.into_pull_up_input().into_function(), ); let mut uart1 = hal::uart::UartPeripheral::new(pac.UART1, uart1_pins, &mut pac.RESETS) .enable( diff --git a/rp235x-hal-examples/src/bin/uart_loopback.rs b/rp235x-hal-examples/src/bin/uart_loopback.rs index 2e2be1a93..8e4a93050 100644 --- a/rp235x-hal-examples/src/bin/uart_loopback.rs +++ b/rp235x-hal-examples/src/bin/uart_loopback.rs @@ -82,7 +82,7 @@ fn main() -> ! { // UART TX (characters sent from RP2350) pins.gpio0.into_function(), // UART RX (characters received by RP2350) - pins.gpio1.into_function(), + pins.gpio1.into_pull_up_input().into_function(), ); let mut uart0 = hal::uart::UartPeripheral::new(pac.UART0, uart0_pins, &mut pac.RESETS) .enable( @@ -101,7 +101,7 @@ fn main() -> ! { // UART TX (characters sent from RP2350) pins.gpio4.into_function(), // UART RX (characters received by RP2350) - pins.gpio5.into_function(), + pins.gpio5.into_pull_up_input().into_function(), ); let mut uart1 = hal::uart::UartPeripheral::new(pac.UART1, uart1_pins, &mut pac.RESETS) .enable(