mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-10-02 14:44:42 +00:00
C6: Basic LP_UART driver (#1113)
* WIP * Add basic LP_UART C6 driver * Add lp-uart example for C6 * modify entry macro and fmt * final cleanups * revert lp_core_basic example and add lp_core_uart and uart examples * changelog * review changes * second changelog --------- Co-authored-by: Jesse Braham <jesse@beta7.io>
This commit is contained in:
parent
de54a17275
commit
bc96351c17
@ -34,7 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Add `embassy-time-driver` to `esp-hal-common` due to updating `embassy-time` to `v0.3.0` (#1075)
|
- Add `embassy-time-driver` to `esp-hal-common` due to updating `embassy-time` to `v0.3.0` (#1075)
|
||||||
- ESP32-S3: Added support for 80Mhz PSRAM (#1069)
|
- ESP32-S3: Added support for 80Mhz PSRAM (#1069)
|
||||||
- ESP32-C3/S3: Add workaround for USB pin exchange on usb-serial-jtag (#1104).
|
- ESP32-C3/S3: Add workaround for USB pin exchange on usb-serial-jtag (#1104).
|
||||||
|
- ESP32C6: Added LP_UART initialization (#1113)
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Set up interrupts for the DMA and async enabled peripherals only when `async` feature is provided (#1042)
|
- Set up interrupts for the DMA and async enabled peripherals only when `async` feature is provided (#1042)
|
||||||
|
@ -1790,3 +1790,197 @@ mod asynch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(lp_uart)]
|
||||||
|
pub mod lp_uart {
|
||||||
|
use crate::{
|
||||||
|
gpio::{lp_gpio::LowPowerPin, Floating, Input, Output, PushPull},
|
||||||
|
peripherals::{LP_CLKRST, LP_UART},
|
||||||
|
uart::{config, config::Config},
|
||||||
|
};
|
||||||
|
/// UART driver
|
||||||
|
pub struct LpUart {
|
||||||
|
uart: LP_UART,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LpUart {
|
||||||
|
/// Initialize the UART driver using the default configuration
|
||||||
|
// TODO: CTS and RTS pins
|
||||||
|
pub fn new(
|
||||||
|
uart: LP_UART,
|
||||||
|
_tx: LowPowerPin<Output<PushPull>, 5>,
|
||||||
|
_rx: LowPowerPin<Input<Floating>, 4>,
|
||||||
|
) -> Self {
|
||||||
|
let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR };
|
||||||
|
let lp_aon = unsafe { &*crate::peripherals::LP_AON::PTR };
|
||||||
|
|
||||||
|
lp_aon
|
||||||
|
.gpio_mux()
|
||||||
|
.modify(|r, w| w.sel().variant(r.sel().bits() | 1 << 4));
|
||||||
|
lp_aon
|
||||||
|
.gpio_mux()
|
||||||
|
.modify(|r, w| w.sel().variant(r.sel().bits() | 1 << 5));
|
||||||
|
|
||||||
|
lp_io.gpio4().modify(|_, w| w.lp_gpio4_mcu_sel().variant(1));
|
||||||
|
lp_io.gpio5().modify(|_, w| w.lp_gpio5_mcu_sel().variant(1));
|
||||||
|
|
||||||
|
Self::new_with_config(uart, Config::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the UART driver using the provided configuration
|
||||||
|
pub fn new_with_config(uart: LP_UART, config: Config) -> Self {
|
||||||
|
let mut me = Self { uart };
|
||||||
|
|
||||||
|
// Set UART mode - do nothing for LP
|
||||||
|
|
||||||
|
// Disable UART parity
|
||||||
|
// 8-bit world
|
||||||
|
// 1-bit stop bit
|
||||||
|
me.uart.conf0().modify(|_, w| unsafe {
|
||||||
|
w.parity()
|
||||||
|
.clear_bit()
|
||||||
|
.parity_en()
|
||||||
|
.clear_bit()
|
||||||
|
.bit_num()
|
||||||
|
.bits(0x3)
|
||||||
|
.stop_bit_num()
|
||||||
|
.bits(0x1)
|
||||||
|
});
|
||||||
|
// Set tx idle
|
||||||
|
me.uart
|
||||||
|
.idle_conf()
|
||||||
|
.modify(|_, w| unsafe { w.tx_idle_num().bits(0) });
|
||||||
|
// Disable hw-flow control
|
||||||
|
me.uart
|
||||||
|
.hwfc_conf()
|
||||||
|
.modify(|_, w| w.rx_flow_en().clear_bit());
|
||||||
|
|
||||||
|
// Get source clock frequency
|
||||||
|
// default == SOC_MOD_CLK_RTC_FAST == 2
|
||||||
|
|
||||||
|
// LP_CLKRST.lpperi.lp_uart_clk_sel = 0;
|
||||||
|
unsafe { &*LP_CLKRST::PTR }
|
||||||
|
.lpperi()
|
||||||
|
.modify(|_, w| w.lp_uart_clk_sel().clear_bit());
|
||||||
|
|
||||||
|
// Override protocol parameters from the configuration
|
||||||
|
// uart_hal_set_baudrate(&hal, cfg->uart_proto_cfg.baud_rate, sclk_freq);
|
||||||
|
me.change_baud(config.baudrate);
|
||||||
|
// uart_hal_set_parity(&hal, cfg->uart_proto_cfg.parity);
|
||||||
|
me.change_parity(config.parity);
|
||||||
|
// uart_hal_set_data_bit_num(&hal, cfg->uart_proto_cfg.data_bits);
|
||||||
|
me.change_data_bits(config.data_bits);
|
||||||
|
// uart_hal_set_stop_bits(&hal, cfg->uart_proto_cfg.stop_bits);
|
||||||
|
me.change_stop_bits(config.stop_bits);
|
||||||
|
// uart_hal_set_tx_idle_num(&hal, LP_UART_TX_IDLE_NUM_DEFAULT);
|
||||||
|
me.change_tx_idle(0); // LP_UART_TX_IDLE_NUM_DEFAULT == 0
|
||||||
|
|
||||||
|
// Reset Tx/Rx FIFOs
|
||||||
|
me.rxfifo_reset();
|
||||||
|
me.txfifo_reset();
|
||||||
|
|
||||||
|
me
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rxfifo_reset(&mut self) {
|
||||||
|
self.uart.conf0().modify(|_, w| w.rxfifo_rst().set_bit());
|
||||||
|
self.update();
|
||||||
|
|
||||||
|
self.uart.conf0().modify(|_, w| w.rxfifo_rst().clear_bit());
|
||||||
|
self.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn txfifo_reset(&mut self) {
|
||||||
|
self.uart.conf0().modify(|_, w| w.txfifo_rst().set_bit());
|
||||||
|
self.update();
|
||||||
|
|
||||||
|
self.uart.conf0().modify(|_, w| w.txfifo_rst().clear_bit());
|
||||||
|
self.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self) {
|
||||||
|
self.uart
|
||||||
|
.reg_update()
|
||||||
|
.modify(|_, w| w.reg_update().set_bit());
|
||||||
|
while self.uart.reg_update().read().reg_update().bit_is_set() {
|
||||||
|
// wait
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_baud(&mut self, baudrate: u32) {
|
||||||
|
// we force the clock source to be XTAL and don't use the decimal part of
|
||||||
|
// the divider
|
||||||
|
// TODO: Currently it's not possible to use XtalD2Clk
|
||||||
|
let clk = 16_000_000;
|
||||||
|
let max_div = 0b1111_1111_1111 - 1;
|
||||||
|
let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate);
|
||||||
|
|
||||||
|
self.uart.clk_conf().modify(|_, w| unsafe {
|
||||||
|
w.sclk_div_a()
|
||||||
|
.bits(0)
|
||||||
|
.sclk_div_b()
|
||||||
|
.bits(0)
|
||||||
|
.sclk_div_num()
|
||||||
|
.bits(clk_div as u8 - 1)
|
||||||
|
.sclk_sel()
|
||||||
|
.bits(0x3) // TODO: this probably shouldn't be hard-coded
|
||||||
|
.sclk_en()
|
||||||
|
.set_bit()
|
||||||
|
});
|
||||||
|
|
||||||
|
let clk = clk / clk_div;
|
||||||
|
let divider = clk / baudrate;
|
||||||
|
let divider = divider as u16;
|
||||||
|
|
||||||
|
self.uart
|
||||||
|
.clkdiv()
|
||||||
|
.write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) });
|
||||||
|
|
||||||
|
self.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_parity(&mut self, parity: config::Parity) -> &mut Self {
|
||||||
|
if parity != config::Parity::ParityNone {
|
||||||
|
self.uart
|
||||||
|
.conf0()
|
||||||
|
.modify(|_, w| w.parity().bit((parity as u8 & 0x1) != 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.uart.conf0().modify(|_, w| match parity {
|
||||||
|
config::Parity::ParityNone => w.parity_en().clear_bit(),
|
||||||
|
config::Parity::ParityEven => w.parity_en().set_bit().parity().clear_bit(),
|
||||||
|
config::Parity::ParityOdd => w.parity_en().set_bit().parity().set_bit(),
|
||||||
|
});
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_data_bits(&mut self, data_bits: config::DataBits) -> &mut Self {
|
||||||
|
self.uart
|
||||||
|
.conf0()
|
||||||
|
.modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) });
|
||||||
|
|
||||||
|
self.update();
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_stop_bits(&mut self, stop_bits: config::StopBits) -> &mut Self {
|
||||||
|
self.uart
|
||||||
|
.conf0()
|
||||||
|
.modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) });
|
||||||
|
|
||||||
|
self.update();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_tx_idle(&mut self, idle_num: u16) -> &mut Self {
|
||||||
|
self.uart
|
||||||
|
.idle_conf()
|
||||||
|
.modify(|_, w| unsafe { w.tx_idle_num().bits(idle_num) });
|
||||||
|
|
||||||
|
self.update();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
//! - `rtc_slow` - Use RTC slow RAM (not all targets support slow RTC RAM)
|
//! - `rtc_slow` - Use RTC slow RAM (not all targets support slow RTC RAM)
|
||||||
//! - `uninitialized` - Skip initialization of the memory
|
//! - `uninitialized` - Skip initialization of the memory
|
||||||
//! - `zeroed` - Initialize the memory to zero
|
//! - `zeroed` - Initialize the memory to zero
|
||||||
//!
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! #### `interrupt` macro
|
//! #### `interrupt` macro
|
||||||
@ -615,6 +615,7 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
|||||||
use #hal_crate::lp_core::LpCoreWakeupSource;
|
use #hal_crate::lp_core::LpCoreWakeupSource;
|
||||||
use #hal_crate::gpio::lp_gpio::LowPowerPin;
|
use #hal_crate::gpio::lp_gpio::LowPowerPin;
|
||||||
use #hal_crate::gpio::*;
|
use #hal_crate::gpio::*;
|
||||||
|
use #hal_crate::uart::lp_uart::LpUart;
|
||||||
};
|
};
|
||||||
#[cfg(any(feature = "esp32s2", feature = "esp32s3"))]
|
#[cfg(any(feature = "esp32s2", feature = "esp32s3"))]
|
||||||
let imports = quote! {
|
let imports = quote! {
|
||||||
@ -705,9 +706,12 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
let f = parse_macro_input!(input as ItemFn);
|
let f = parse_macro_input!(input as ItemFn);
|
||||||
|
|
||||||
let mut argument_types = Vec::new();
|
let mut argument_types = Vec::new();
|
||||||
|
let mut create_peripheral = Vec::new();
|
||||||
|
|
||||||
let mut used_pins: Vec<u8> = Vec::new();
|
let mut used_pins: Vec<u8> = Vec::new();
|
||||||
for arg in &f.sig.inputs {
|
|
||||||
|
for (num, arg) in f.sig.inputs.iter().enumerate() {
|
||||||
|
let param_name = format_ident!("param{}", num);
|
||||||
match arg {
|
match arg {
|
||||||
FnArg::Receiver(_) => {
|
FnArg::Receiver(_) => {
|
||||||
return parse::Error::new(arg.span(), "invalid argument")
|
return parse::Error::new(arg.span(), "invalid argument")
|
||||||
@ -715,19 +719,30 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
FnArg::Typed(t) => {
|
FnArg::Typed(t) => {
|
||||||
if get_simplename(&t.ty) != "GpioPin" {
|
match get_simplename(&t.ty).as_str() {
|
||||||
return parse::Error::new(arg.span(), "invalid argument to main")
|
"GpioPin" => {
|
||||||
.to_compile_error()
|
let pin = extract_pin(&t.ty);
|
||||||
.into();
|
if used_pins.contains(&pin) {
|
||||||
|
return parse::Error::new(arg.span(), "duplicate pin")
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
used_pins.push(pin);
|
||||||
|
create_peripheral.push(quote!(
|
||||||
|
let mut #param_name = unsafe { the_hal::gpio::conjour().unwrap() };
|
||||||
|
));
|
||||||
|
}
|
||||||
|
"LpUart" => {
|
||||||
|
create_peripheral.push(quote!(
|
||||||
|
let mut #param_name = unsafe { the_hal::uart::conjour().unwrap() };
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return parse::Error::new(arg.span(), "invalid argument to main")
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let pin = extract_pin(&t.ty);
|
|
||||||
if used_pins.contains(&pin) {
|
|
||||||
return parse::Error::new(arg.span(), "duplicate pin")
|
|
||||||
.to_compile_error()
|
|
||||||
.into();
|
|
||||||
}
|
|
||||||
used_pins.push(pin);
|
|
||||||
|
|
||||||
argument_types.push(t);
|
argument_types.push(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -752,7 +767,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
use #hal_crate as the_hal;
|
use #hal_crate as the_hal;
|
||||||
#(
|
#(
|
||||||
let mut #param_names = unsafe { the_hal::gpio::conjour().unwrap() };
|
#create_peripheral;
|
||||||
)*
|
)*
|
||||||
|
|
||||||
main(#(#param_names),*);
|
main(#(#param_names),*);
|
||||||
|
79
esp32c6-hal/examples/lp_core_uart.rs
Normal file
79
esp32c6-hal/examples/lp_core_uart.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
//! This shows a very basic example of running code on the LP core.
|
||||||
|
//!
|
||||||
|
//! Code on LP core uses LP_UART initialized on HP core. For more information
|
||||||
|
//! check `lp_core_uart` example in the `esp32c6-lp-hal.
|
||||||
|
//! Make sure to first compile the `esp32c6-lp-hal/examples/uart.rs` example
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use esp32c6_hal::{
|
||||||
|
clock::ClockControl,
|
||||||
|
gpio::lp_gpio::IntoLowPowerPin,
|
||||||
|
lp_core,
|
||||||
|
peripherals::Peripherals,
|
||||||
|
prelude::*,
|
||||||
|
uart::{
|
||||||
|
config::{Config, DataBits, Parity, StopBits},
|
||||||
|
lp_uart::LpUart,
|
||||||
|
TxRxPins,
|
||||||
|
},
|
||||||
|
Uart,
|
||||||
|
IO,
|
||||||
|
};
|
||||||
|
use esp_backtrace as _;
|
||||||
|
use esp_println::println;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let peripherals = Peripherals::take();
|
||||||
|
let system = peripherals.SYSTEM.split();
|
||||||
|
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||||
|
|
||||||
|
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||||
|
|
||||||
|
// Set up (HP) UART1:
|
||||||
|
|
||||||
|
let config = Config {
|
||||||
|
baudrate: 115_200,
|
||||||
|
data_bits: DataBits::DataBits8,
|
||||||
|
parity: Parity::ParityNone,
|
||||||
|
stop_bits: StopBits::STOP1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let pins = TxRxPins::new_tx_rx(
|
||||||
|
io.pins.gpio6.into_push_pull_output(),
|
||||||
|
io.pins.gpio7.into_floating_input(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut uart1 = Uart::new_with_config(peripherals.UART1, config, Some(pins), &clocks);
|
||||||
|
|
||||||
|
// Set up (LP) UART:
|
||||||
|
|
||||||
|
let lp_tx = io.pins.gpio5.into_low_power().into_push_pull_output();
|
||||||
|
let lp_rx = io.pins.gpio4.into_low_power().into_floating_input();
|
||||||
|
|
||||||
|
let lp_uart = LpUart::new(peripherals.LP_UART, lp_tx, lp_rx);
|
||||||
|
|
||||||
|
let mut lp_core = esp32c6_hal::lp_core::LpCore::new(peripherals.LP_CORE);
|
||||||
|
lp_core.stop();
|
||||||
|
println!("lp core stopped");
|
||||||
|
|
||||||
|
// load code to LP core
|
||||||
|
let lp_core_code = load_lp_code!(
|
||||||
|
"../esp32c6-lp-hal/target/riscv32imac-unknown-none-elf/release/examples/uart"
|
||||||
|
);
|
||||||
|
|
||||||
|
// start LP core
|
||||||
|
lp_core_code.run(&mut lp_core, lp_core::LpCoreWakeupSource::HpCpu, lp_uart);
|
||||||
|
println!("lpcore run");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let read = nb::block!(uart1.read());
|
||||||
|
|
||||||
|
match read {
|
||||||
|
Ok(read) => println!("Read 0x{:02x}", read),
|
||||||
|
Err(err) => println!("Error {:?}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Add the `esp32c6-lp-hal` package (#714)
|
- Add the `esp32c6-lp-hal` package (#714)
|
||||||
- Add GPIO (output) and delay functionality to `esp32c6-lp-hal` (#715)
|
- Add GPIO (output) and delay functionality to `esp32c6-lp-hal` (#715)
|
||||||
- Add GPIO input support and implement additional `embedded-hal` output traits for the C6's LP core [#720]
|
- Add GPIO input support and implement additional `embedded-hal` output traits for the C6's LP core [#720]
|
||||||
|
- Add LP_UART basic driver (#1113)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -24,12 +24,13 @@ categories = [
|
|||||||
critical-section = { version = "1.1.2", features = ["restore-state-u8"] }
|
critical-section = { version = "1.1.2", features = ["restore-state-u8"] }
|
||||||
embedded-hal = { version = "0.2.7", features = ["unproven"] }
|
embedded-hal = { version = "0.2.7", features = ["unproven"] }
|
||||||
esp32c6-lp = { version = "0.1.0", features = ["critical-section"] }
|
esp32c6-lp = { version = "0.1.0", features = ["critical-section"] }
|
||||||
|
nb = "1.1.0"
|
||||||
|
paste = "1.0.14"
|
||||||
procmacros = { package = "esp-hal-procmacros", path = "../esp-hal-procmacros", features = ["esp32c6-lp"] }
|
procmacros = { package = "esp-hal-procmacros", path = "../esp-hal-procmacros", features = ["esp32c6-lp"] }
|
||||||
riscv = "0.10.1"
|
riscv = "0.10.1"
|
||||||
paste = "1.0.14"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
panic-halt = "0.2.0"
|
panic-halt = "0.2.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
23
esp32c6-lp-hal/examples/uart.rs
Normal file
23
esp32c6-lp-hal/examples/uart.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
//! Uses `LP_UART` and logs "Hello World from LP Core".
|
||||||
|
//! Uses GPIO4 for RX and GPIO5 for TX. GPIOs can't be changed.
|
||||||
|
//! It is neccessary to use Serial-Uart bridge connected to TX and RX to see
|
||||||
|
//! logs from LP_UART. Make sure the LP RAM is cleared before loading the code.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
use esp32c6_lp_hal::{delay::Delay, prelude::*, uart::LpUart};
|
||||||
|
use panic_halt as _;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main(mut uart: LpUart) -> ! {
|
||||||
|
let _peripherals = esp32c6_lp::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
let mut delay = Delay::new();
|
||||||
|
loop {
|
||||||
|
writeln!(uart, "Hello World from LP Core").unwrap();
|
||||||
|
delay.delay_ms(1500);
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,8 @@ use core::arch::global_asm;
|
|||||||
|
|
||||||
pub mod delay;
|
pub mod delay;
|
||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
|
pub mod prelude;
|
||||||
|
pub mod uart;
|
||||||
|
|
||||||
pub mod riscv {
|
pub mod riscv {
|
||||||
//! Low level access to RISC-V processors.
|
//! Low level access to RISC-V processors.
|
||||||
@ -13,7 +15,6 @@ pub mod riscv {
|
|||||||
|
|
||||||
pub use riscv::*;
|
pub use riscv::*;
|
||||||
}
|
}
|
||||||
pub mod prelude;
|
|
||||||
|
|
||||||
// LP_FAST_CLK is not very accurate, for now use a rough estimate
|
// LP_FAST_CLK is not very accurate, for now use a rough estimate
|
||||||
const LP_FAST_CLK_HZ: u32 = 16_000_000;
|
const LP_FAST_CLK_HZ: u32 = 16_000_000;
|
||||||
|
175
esp32c6-lp-hal/src/uart.rs
Normal file
175
esp32c6-lp-hal/src/uart.rs
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
//! Low-power UART driver
|
||||||
|
|
||||||
|
use esp32c6_lp::LP_UART;
|
||||||
|
|
||||||
|
const UART_FIFO_SIZE: u16 = 128;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub unsafe fn conjour() -> Option<LpUart> {
|
||||||
|
Some(LpUart {
|
||||||
|
uart: LP_UART::steal(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {}
|
||||||
|
|
||||||
|
/// UART configuration
|
||||||
|
pub mod config {
|
||||||
|
/// Number of data bits
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||||
|
pub enum DataBits {
|
||||||
|
DataBits5 = 0,
|
||||||
|
DataBits6 = 1,
|
||||||
|
DataBits7 = 2,
|
||||||
|
DataBits8 = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parity check
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||||
|
pub enum Parity {
|
||||||
|
ParityNone = 0,
|
||||||
|
ParityEven = 1,
|
||||||
|
ParityOdd = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Number of stop bits
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||||
|
pub enum StopBits {
|
||||||
|
/// 1 stop bit
|
||||||
|
STOP1 = 1,
|
||||||
|
/// 1.5 stop bits
|
||||||
|
STOP1P5 = 2,
|
||||||
|
/// 2 stop bits
|
||||||
|
STOP2 = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UART configuration
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Config {
|
||||||
|
pub baudrate: u32,
|
||||||
|
pub data_bits: DataBits,
|
||||||
|
pub parity: Parity,
|
||||||
|
pub stop_bits: StopBits,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn baudrate(mut self, baudrate: u32) -> Self {
|
||||||
|
self.baudrate = baudrate;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parity_none(mut self) -> Self {
|
||||||
|
self.parity = Parity::ParityNone;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parity_even(mut self) -> Self {
|
||||||
|
self.parity = Parity::ParityEven;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parity_odd(mut self) -> Self {
|
||||||
|
self.parity = Parity::ParityOdd;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data_bits(mut self, data_bits: DataBits) -> Self {
|
||||||
|
self.data_bits = data_bits;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop_bits(mut self, stop_bits: StopBits) -> Self {
|
||||||
|
self.stop_bits = stop_bits;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
baudrate: 115200,
|
||||||
|
data_bits: DataBits::DataBits8,
|
||||||
|
parity: Parity::ParityNone,
|
||||||
|
stop_bits: StopBits::STOP1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// LP-UART driver
|
||||||
|
pub struct LpUart {
|
||||||
|
uart: LP_UART,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LpUart {
|
||||||
|
fn read_byte(&mut self) -> nb::Result<u8, Error> {
|
||||||
|
if self.get_rx_fifo_count() > 0 {
|
||||||
|
let byte = self.uart.fifo().read().rxfifo_rd_byte().bits();
|
||||||
|
Ok(byte)
|
||||||
|
} else {
|
||||||
|
Err(nb::Error::WouldBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_byte(&mut self, byte: u8) -> nb::Result<(), Error> {
|
||||||
|
if self.get_tx_fifo_count() < UART_FIFO_SIZE {
|
||||||
|
self.uart
|
||||||
|
.fifo()
|
||||||
|
.write(|w| unsafe { w.rxfifo_rd_byte().bits(byte) });
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(nb::Error::WouldBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_bytes(&mut self, data: &[u8]) -> nb::Result<(), Error> {
|
||||||
|
data.iter().try_for_each(|c| self.write_byte(*c))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush_tx(&mut self) -> nb::Result<(), Error> {
|
||||||
|
if self.is_tx_idle() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(nb::Error::WouldBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_rx_fifo_count(&mut self) -> u16 {
|
||||||
|
self.uart.status().read().rxfifo_cnt().bits().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tx_fifo_count(&mut self) -> u16 {
|
||||||
|
self.uart.status().read().txfifo_cnt().bits().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_tx_idle(&self) -> bool {
|
||||||
|
self.uart.fsm_status().read().st_utx_out().bits() == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Write for LpUart {
|
||||||
|
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||||
|
self.write_bytes(s.as_bytes()).map_err(|_| core::fmt::Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::serial::Read<u8> for LpUart {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||||
|
self.read_byte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::serial::Write<u8> for LpUart {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||||
|
self.write_byte(word)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||||
|
self.flush_tx()
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user