Skip to content

Commit

Permalink
feat(uart): add uart rx
Browse files Browse the repository at this point in the history
  • Loading branch information
andelf committed Oct 28, 2023
1 parent e19aca3 commit 01ddba7
Show file tree
Hide file tree
Showing 3 changed files with 249 additions and 45 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
90 changes: 90 additions & 0 deletions examples/uart-echo.rs
Original file line number Diff line number Diff line change
@@ -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<UartTx<peripherals::UART1>> = 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();
}
}
202 changes: 158 additions & 44 deletions src/uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand All @@ -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
Expand All @@ -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<const REMAP: bool>(
peri: impl Peripheral<P = T> + 'd,
// _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
rx: impl Peripheral<P = impl RxPin<T, REMAP>> + 'd,
config: Config,
) -> Result<Self, ConfigError> {
// T::enable();

Self::new_inner(peri, rx, config)
}

fn new_inner<const REMAP: bool>(
peri: impl Peripheral<P = T> + 'd,
rx: impl Peripheral<P = impl RxPin<T, REMAP>> + 'd,
config: Config,
) -> Result<Self, ConfigError> {
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<bool, Error> {
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<u8, nb::Error<Error>> {
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 {
Expand All @@ -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::*;

Expand Down Expand Up @@ -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);

0 comments on commit 01ddba7

Please sign in to comment.