From 01ddba740080bf915099736281d90a4ba1c04e7c Mon Sep 17 00:00:00 2001 From: Andelf Date: Sat, 28 Oct 2023 11:01:40 +0800 Subject: [PATCH] feat(uart): add uart rx --- README.md | 2 +- examples/uart-echo.rs | 90 +++++++++++++++++++ src/uart.rs | 202 +++++++++++++++++++++++++++++++++--------- 3 files changed, 249 insertions(+), 45 deletions(-) create mode 100644 examples/uart-echo.rs diff --git a/README.md b/README.md index 0cc57c4..5971798 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ This should be the reference hal implementation for CH57x, CH58x, CH59x. - Basic: clock init, delay, interrupt, etc. - Dedicated runtime -- Uart Tx only +- Uart - GPIO - RTC, with datetime support - embassy time driver via SysTick diff --git a/examples/uart-echo.rs b/examples/uart-echo.rs new file mode 100644 index 0000000..7cda4bc --- /dev/null +++ b/examples/uart-echo.rs @@ -0,0 +1,90 @@ +#![no_std] +#![no_main] + +use ch32v_rt::highcode; +use ch58x_hal as hal; +use embedded_hal_1::delay::DelayUs; +use hal::gpio::{Level, Output, OutputDrive}; +use hal::peripherals; +use hal::rtc::Rtc; +use hal::uart::{UartRx, UartTx}; + +static mut SERIAL: Option> = None; + +macro_rules! println { + ($($arg:tt)*) => { + unsafe { + use core::fmt::Write; + use core::writeln; + + if let Some(uart) = SERIAL.as_mut() { + writeln!(uart, $($arg)*).unwrap(); + } + } + } +} + +macro_rules! print { + ($($arg:tt)*) => { + unsafe { + use core::fmt::Write; + use core::write; + + if let Some(uart) = SERIAL.as_mut() { + write!(uart, $($arg)*).unwrap(); + } + } + } +} + +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + use core::fmt::Write; + + let pa9 = unsafe { peripherals::PA9::steal() }; + let uart1 = unsafe { peripherals::UART1::steal() }; + let mut serial = UartTx::new(uart1, pa9, Default::default()).unwrap(); + + let _ = writeln!(&mut serial, "\n\n\n{}", info); + + loop {} +} + +#[ch32v_rt::entry] +#[highcode] +fn main() -> ! { + let mut config = hal::Config::default(); + config.clock.use_pll_60mhz().enable_lse(); + let p = hal::init(config); + + // GPIO + let mut led = Output::new(p.PB4, Level::Low, OutputDrive::Standard); + // let boot_btn = Input::new(p.PB22, Pull::Up); + // let rst_btn = Input::new(p.PB23, Pull::Up); + + let uart = UartTx::new(p.UART1, p.PA9, Default::default()).unwrap(); + unsafe { + SERIAL.replace(uart); + } + + let mut rx = UartRx::new(p.UART2, p.PA6, Default::default()).unwrap(); + + let rtc = Rtc::new(p.RTC); + + println!("\n\nHello World!"); + println!("System Clocks: {}", hal::sysctl::clocks().hclk); + println!("ChipID: 0x{:02x}", hal::signature::get_chip_id()); + println!("RTC datetime: {}", rtc.now()); + println!("Now, type something to echo:"); + + loop { + let b = nb::block!(rx.nb_read()).unwrap(); + if b == '\r' as u8 { + println!(); + } else { + print!("{}", b as char); + } + + led.toggle(); + } +} diff --git a/src/uart.rs b/src/uart.rs index fadcf01..fe06cb2 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -2,9 +2,11 @@ use core::marker::PhantomData; -use crate::gpio::OutputDrive; +use crate::gpio::{OutputDrive, Pull}; use crate::{into_ref, pac, peripherals, Peripheral}; +const UART_FIFO_SIZE: u8 = 8; + #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum Parity { ParityNone = 0xff, @@ -117,48 +119,8 @@ impl<'d, T: BasicInstance> UartTx<'d, T> { T::set_remap(); } - // set up uart let rb = T::regs(); - - rb.fcr.write(|w| { - w.rx_fifo_clr() - .set_bit() - .tx_fifo_clr() - .set_bit() - .fifo_en() // enable 8 byte FIFO - .set_bit() - .fifo_trig() - .variant(0b00) // 1 bytes to send - }); - rb.lcr.write(|w| w.word_sz().variant(config.data_bits as u8)); - match config.stop_bits { - StopBits::STOP1 => rb.lcr.modify(|_, w| w.stop_bit().clear_bit()), - StopBits::STOP2 => rb.lcr.modify(|_, w| w.stop_bit().set_bit()), - } - match config.parity { - Parity::ParityNone => rb.lcr.modify(|_, w| w.par_en().clear_bit()), - _ => rb - .lcr - .modify(|_, w| w.par_en().set_bit().par_mod().variant(config.parity as u8)), - } - - // baudrate = Fsys * 2 / R8_UARTx_DIV / 16 / R16_UARTx_DL - let (div, dl) = match (crate::sysctl::clocks().hclk.to_Hz(), config.baudrate) { - (60_000_000, 115200) => (13, 5), - (60_000_000, 8600) => (8, 109), - _ => { - let x = 10 * crate::sysctl::clocks().hclk.to_Hz() / 8 / config.baudrate; - let x = ((x + 5) / 10) & 0xffff; - - (1, x as u16) - } - }; - - rb.div.write(|w| unsafe { w.bits(div) }); - rb.dl.write(|w| unsafe { w.bits(dl) }); - - // enable TX - rb.ier.write(|w| w.txd_en().set_bit()); + configure(rb, &config, true, false)?; // create state once! //let _s = T::state(); @@ -174,7 +136,6 @@ impl<'d, T: BasicInstance> UartTx<'d, T> { pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { let rb = T::regs(); - const UART_FIFO_SIZE: u8 = 8; for &c in buffer { // wait @@ -192,7 +153,95 @@ impl<'d, T: BasicInstance> UartTx<'d, T> { } } -// embedded-hal +pub struct UartRx<'d, T: BasicInstance> { + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: BasicInstance> UartRx<'d, T> { + /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. + pub fn new( + peri: impl Peripheral

+ 'd, + // _irq: impl interrupt::typelevel::Binding> + 'd, + rx: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + // T::enable(); + + Self::new_inner(peri, rx, config) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + use crate::interrupt::Interrupt; + into_ref!(peri, rx); + + rx.set_as_input(); + rx.set_pullup(); + if REMAP { + T::set_remap(); + } + + let rb = T::regs(); + configure(rb, &config, false, true)?; + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + // create state once! + //let _s = T::state(); + + Ok(Self { phantom: PhantomData }) + } + + fn check_rx_flags(&self) -> Result { + let rb = T::regs(); + let lsr = rb.lsr.read(); + if lsr.err_rx_fifo().bit() { + if lsr.frame_err().bit() { + return Err(Error::Framing); + } else if lsr.par_err().bit() { + return Err(Error::Parity); + } else if lsr.over_err().bit() { + return Err(Error::Overrun); + } + } + Ok(lsr.data_rdy().bit()) + } + + // fifo disabled + /* pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let rb = T::regs(); + for b in buffer { + while !self.check_rx_flags()? {} + unsafe { *b = rb.rbr().read().bits() as u8 }; + } + Ok(()) + } */ + + // fifo enabled + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let rb = T::regs(); + for b in buffer { + while !self.check_rx_flags()? {} + if rb.rfc.read().bits() > 0 { + *b = rb.rbr().read().bits() as u8; + } + } + Ok(()) + } + + pub fn nb_read(&mut self) -> Result> { + let rb = T::regs(); + if self.check_rx_flags()? { + Ok(unsafe { rb.rbr().read().bits() as u8 }) + } else { + Err(nb::Error::WouldBlock) + } + } +} impl<'d, T: BasicInstance> core::fmt::Write for UartTx<'d, T> { fn write_str(&mut self, s: &str) -> core::fmt::Result { @@ -201,6 +250,63 @@ impl<'d, T: BasicInstance> core::fmt::Write for UartTx<'d, T> { } } +fn configure( + rb: &pac::uart0::RegisterBlock, + config: &Config, + enable_tx: bool, + enable_rx: bool, +) -> Result<(), ConfigError> { + if !enable_rx && !enable_tx { + panic!("USART: At least one of RX or TX should be enabled"); + } + + rb.fcr.write(|w| { + w.rx_fifo_clr() + .set_bit() + .tx_fifo_clr() + .set_bit() + .fifo_en() // enable 8 byte FIFO + .set_bit() + .fifo_trig() + .variant(0b00) // 1 bytes to send + }); + rb.lcr.write(|w| w.word_sz().variant(config.data_bits as u8)); + match config.stop_bits { + StopBits::STOP1 => rb.lcr.modify(|_, w| w.stop_bit().clear_bit()), + StopBits::STOP2 => rb.lcr.modify(|_, w| w.stop_bit().set_bit()), + } + match config.parity { + Parity::ParityNone => rb.lcr.modify(|_, w| w.par_en().clear_bit()), + _ => rb + .lcr + .modify(|_, w| w.par_en().set_bit().par_mod().variant(config.parity as u8)), + } + + // baudrate = Fsys * 2 / R8_UARTx_DIV / 16 / R16_UARTx_DL + // match some common baudrates + let (div, dl) = match (crate::sysctl::clocks().hclk.to_Hz(), config.baudrate) { + (60_000_000, 115200) => (13, 5), + (60_000_000, 8600) => (8, 109), + _ => { + let x = 10 * crate::sysctl::clocks().hclk.to_Hz() / 8 / config.baudrate; + let x = ((x + 5) / 10) & 0xffff; + + (1, x as u16) + } + }; + + rb.div.write(|w| unsafe { w.bits(div) }); + rb.dl.write(|w| unsafe { w.bits(dl) }); + + // enable TX + if enable_tx { + rb.ier.modify(|_, w| w.txd_en().bit(true)); + } + + Ok(()) +} + +// embedded-hal mod eh1 { use super::*; @@ -316,13 +422,21 @@ macro_rules! pin_trait_impl { } pin_trait_impl!(crate::uart::TxPin, UART0, PB7, false); +pin_trait_impl!(crate::uart::RxPin, UART0, PB4, false); pin_trait_impl!(crate::uart::TxPin, UART0, PA14, true); +pin_trait_impl!(crate::uart::RxPin, UART0, PA15, true); pin_trait_impl!(crate::uart::TxPin, UART1, PA9, false); +pin_trait_impl!(crate::uart::RxPin, UART1, PA8, false); pin_trait_impl!(crate::uart::TxPin, UART1, PB13, true); +pin_trait_impl!(crate::uart::RxPin, UART1, PB12, true); pin_trait_impl!(crate::uart::TxPin, UART2, PA7, false); +pin_trait_impl!(crate::uart::RxPin, UART2, PA6, false); pin_trait_impl!(crate::uart::TxPin, UART2, PB23, true); +pin_trait_impl!(crate::uart::RxPin, UART2, PB22, true); pin_trait_impl!(crate::uart::TxPin, UART3, PA5, false); +pin_trait_impl!(crate::uart::RxPin, UART3, PA4, false); pin_trait_impl!(crate::uart::TxPin, UART3, PB21, true); +pin_trait_impl!(crate::uart::RxPin, UART3, PB20, true);