Add a minimal HAL crate for the ESP32 with a serial example
This commit is contained in:
parent
64d9430d84
commit
2bc97768b6
12
esp32-hal/.cargo/config.toml
Normal file
12
esp32-hal/.cargo/config.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[target.xtensa-esp32-none-elf]
|
||||
runner = "xtensa-esp32-elf-gdb -q -x xtensa.gdb"
|
||||
|
||||
[build]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-nostartfiles",
|
||||
"-C", "link-arg=-Wl,-Tlink.x",
|
||||
]
|
||||
target = "xtensa-esp32-none-elf"
|
||||
|
||||
[unstable]
|
||||
build-std = ["core"]
|
||||
22
esp32-hal/Cargo.toml
Normal file
22
esp32-hal/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "esp32-hal"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bare-metal = "1.0"
|
||||
embedded-hal = { version = "0.2", features = ["unproven"] }
|
||||
nb = "1.0"
|
||||
void = { version = "1.0", default-features = false }
|
||||
xtensa-lx = { version = "0.4", features = ["lx6"] }
|
||||
xtensa-lx-rt = { version = "0.7", optional = true, features = ["lx6"] }
|
||||
|
||||
[dependencies.esp32]
|
||||
path = "../../esp-pacs/esp32"
|
||||
|
||||
[dev-dependencies]
|
||||
panic-halt = "0.2"
|
||||
|
||||
[features]
|
||||
default = ["rt"]
|
||||
rt = ["esp32/rt", "xtensa-lx-rt"]
|
||||
1
esp32-hal/README.md
Normal file
1
esp32-hal/README.md
Normal file
@ -0,0 +1 @@
|
||||
# esp32-hal
|
||||
21
esp32-hal/build.rs
Normal file
21
esp32-hal/build.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use std::{env, fs::File, io::Write, path::PathBuf};
|
||||
|
||||
fn main() {
|
||||
// Put the linker script somewhere the linker can find it
|
||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
File::create(out.join("memory.x"))
|
||||
.unwrap()
|
||||
.write_all(include_bytes!("memory.x"))
|
||||
.unwrap();
|
||||
|
||||
File::create(out.join("alias.x"))
|
||||
.unwrap()
|
||||
.write_all(include_bytes!("rom.x"))
|
||||
.unwrap();
|
||||
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
// Only re-run the build script when memory.x is changed,
|
||||
// instead of when any part of the source code changes.
|
||||
println!("cargo:rerun-if-changed=memory.x");
|
||||
}
|
||||
25
esp32-hal/examples/serial.rs
Normal file
25
esp32-hal/examples/serial.rs
Normal file
@ -0,0 +1,25 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::fmt::Write;
|
||||
|
||||
use esp32_hal::{pac, prelude::*, Serial, Timer};
|
||||
use nb::block;
|
||||
use panic_halt as _;
|
||||
use xtensa_lx_rt::entry;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = pac::Peripherals::take().unwrap();
|
||||
|
||||
let mut timer0 = Timer::new(peripherals.TIMG0);
|
||||
let mut serial0 = Serial::new(peripherals.UART0).unwrap();
|
||||
|
||||
timer0.disable();
|
||||
timer0.start(10_000_000u64);
|
||||
|
||||
loop {
|
||||
writeln!(serial0, "Hello world!").unwrap();
|
||||
block!(timer0.wait()).unwrap();
|
||||
}
|
||||
}
|
||||
193
esp32-hal/memory.x
Normal file
193
esp32-hal/memory.x
Normal file
@ -0,0 +1,193 @@
|
||||
/* This memory map assumes the flash cache is on;
|
||||
the blocks used are excluded from the various memory ranges
|
||||
|
||||
see: https://github.com/espressif/esp-idf/blob/master/components/soc/src/esp32/soc_memory_layout.c
|
||||
for details
|
||||
*/
|
||||
|
||||
/* override entry point */
|
||||
ENTRY(ESP32Reset)
|
||||
|
||||
/* reserved at the start of DRAM for e.g. teh BT stack */
|
||||
RESERVE_DRAM = 0;
|
||||
|
||||
/* reserved at the start of the RTC memories for use by the ULP processor */
|
||||
RESERVE_RTC_FAST = 0;
|
||||
RESERVE_RTC_SLOW = 0;
|
||||
|
||||
/* define stack size for both cores */
|
||||
STACK_SIZE = 8k;
|
||||
|
||||
/* Specify main memory areas */
|
||||
MEMORY
|
||||
{
|
||||
reserved_cache_seg : ORIGIN = 0x40070000, len = 64k /* SRAM0; reserved for usage as flash cache*/
|
||||
vectors_seg ( RX ) : ORIGIN = 0x40080000, len = 1k /* SRAM0 */
|
||||
iram_seg ( RX ) : ORIGIN = 0x40080400, len = 128k-0x400 /* SRAM0 */
|
||||
|
||||
reserved_for_rom_seg : ORIGIN = 0x3FFAE000, len = 8k /* SRAM2; reserved for usage by the ROM */
|
||||
dram_seg ( RW ) : ORIGIN = 0x3FFB0000 + RESERVE_DRAM, len = 176k - RESERVE_DRAM /* SRAM2+1; first 64kB used by BT if enable */
|
||||
reserved_for_boot_seg : ORIGIN = 0x3FFDC200, len = 144k /* SRAM1; reserved for static ROM usage; can be used for heap */
|
||||
|
||||
/* external flash
|
||||
The 0x20 offset is a convenience for the app binary image generation.
|
||||
Flash cache has 64KB pages. The .bin file which is flashed to the chip
|
||||
has a 0x18 byte file header, and each segment has a 0x08 byte segment
|
||||
header. Setting this offset makes it simple to meet the flash cache MMU's
|
||||
constraint that (paddr % 64KB == vaddr % 64KB).)
|
||||
*/
|
||||
irom_seg ( RX ) : ORIGIN = 0x400D0020, len = 3M - 0x20
|
||||
drom_seg ( R ) : ORIGIN = 0x3F400020, len = 4M - 0x20
|
||||
|
||||
|
||||
/* RTC fast memory (executable). Persists over deep sleep. Only for core 0 (PRO_CPU) */
|
||||
rtc_fast_iram_seg(RWX) : ORIGIN = 0x400C0000, len = 8k
|
||||
|
||||
/* RTC fast memory (same block as above), viewed from data bus. Only for core 0 (PRO_CPU) */
|
||||
rtc_fast_dram_seg(RW) : ORIGIN = 0x3FF80000 + RESERVE_RTC_FAST, len = 8k - RESERVE_RTC_FAST
|
||||
|
||||
/* RTC slow memory (data accessible). Persists over deep sleep. */
|
||||
rtc_slow_seg(RW) : ORIGIN = 0x50000000 + RESERVE_RTC_SLOW, len = 8k - RESERVE_RTC_SLOW
|
||||
|
||||
/* external memory, including data and text,
|
||||
4MB is the maximum, if external psram is bigger, paging is required */
|
||||
psram_seg(RWX) : ORIGIN = 0x3F800000, len = 4M
|
||||
}
|
||||
|
||||
/* map generic regions to output sections */
|
||||
INCLUDE "alias.x"
|
||||
|
||||
/* esp32 specific regions */
|
||||
SECTIONS {
|
||||
.rtc_fast.text : {
|
||||
. = ALIGN(4);
|
||||
*(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*)
|
||||
} > rtc_fast_iram_seg AT > RODATA
|
||||
|
||||
/*
|
||||
This section is required to skip rtc.text area because rtc_iram_seg and
|
||||
rtc_data_seg are reflect the same address space on different buses.
|
||||
*/
|
||||
.rtc_fast.dummy (NOLOAD) :
|
||||
{
|
||||
_rtc_dummy_start = ABSOLUTE(.); /* needed to make section proper size */
|
||||
. = SIZEOF(.rtc_fast.text);
|
||||
_rtc_dummy_end = ABSOLUTE(.); /* needed to make section proper size */
|
||||
} > rtc_fast_dram_seg
|
||||
|
||||
|
||||
.rtc_fast.data :
|
||||
{
|
||||
_rtc_fast_data_start = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
*(.rtc_fast.data .rtc_fast.data.*)
|
||||
_rtc_fast_data_end = ABSOLUTE(.);
|
||||
} > rtc_fast_dram_seg AT > RODATA
|
||||
|
||||
.rtc_fast.bss (NOLOAD) :
|
||||
{
|
||||
_rtc_fast_bss_start = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
*(.rtc_fast.bss .rtc_fast.bss.*)
|
||||
_rtc_fast_bss_end = ABSOLUTE(.);
|
||||
} > rtc_fast_dram_seg
|
||||
|
||||
.rtc_fast.noinit (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.rtc_fast.noinit .rtc_fast.noinit.*)
|
||||
} > rtc_fast_dram_seg
|
||||
|
||||
|
||||
.rtc_slow.text : {
|
||||
. = ALIGN(4);
|
||||
*(.rtc_slow.literal .rtc_slow.text .rtc_slow.literal.* .rtc_slow.text.*)
|
||||
} > rtc_slow_seg AT > RODATA
|
||||
|
||||
.rtc_slow.data :
|
||||
{
|
||||
_rtc_slow_data_start = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
*(.rtc_slow.data .rtc_slow.data.*)
|
||||
_rtc_slow_data_end = ABSOLUTE(.);
|
||||
} > rtc_slow_seg AT > RODATA
|
||||
|
||||
.rtc_slow.bss (NOLOAD) :
|
||||
{
|
||||
_rtc_slow_bss_start = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
*(.rtc_slow.bss .rtc_slow.bss.*)
|
||||
_rtc_slow_bss_end = ABSOLUTE(.);
|
||||
} > rtc_slow_seg
|
||||
|
||||
.rtc_slow.noinit (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.rtc_slow.noinit .rtc_slow.noinit.*)
|
||||
} > rtc_slow_seg
|
||||
|
||||
.external.data :
|
||||
{
|
||||
_external_data_start = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
*(.external.data .external.data.*)
|
||||
_external_data_end = ABSOLUTE(.);
|
||||
} > psram_seg AT > RODATA
|
||||
|
||||
.external.bss (NOLOAD) :
|
||||
{
|
||||
_external_bss_start = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
*(.external.bss .external.bss.*)
|
||||
_external_bss_end = ABSOLUTE(.);
|
||||
} > psram_seg
|
||||
|
||||
.external.noinit (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.external.noinit .external.noinit.*)
|
||||
} > psram_seg
|
||||
|
||||
/* must be last segment using psram_seg */
|
||||
.external_heap_start (NOLOAD) :
|
||||
{
|
||||
. = ALIGN (4);
|
||||
_external_heap_start = ABSOLUTE(.);
|
||||
} > psram_seg
|
||||
|
||||
|
||||
/* wifi data */
|
||||
|
||||
.rwtext.wifi :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*( .wifi0iram .wifi0iram.*)
|
||||
*( .wifirxiram .wifirxiram.*)
|
||||
*( .iram1 .iram1.*)
|
||||
} > RWTEXT AT > RODATA
|
||||
|
||||
.data.wifi :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*( .dram1 .dram1.*)
|
||||
} > RWDATA AT > RODATA
|
||||
}
|
||||
|
||||
_external_ram_start = ABSOLUTE(ORIGIN(psram_seg));
|
||||
_external_ram_end = ABSOLUTE(ORIGIN(psram_seg)+LENGTH(psram_seg));
|
||||
|
||||
_heap_end = ABSOLUTE(ORIGIN(dram_seg))+LENGTH(dram_seg)+LENGTH(reserved_for_boot_seg) - 2*STACK_SIZE;
|
||||
_text_heap_end = ABSOLUTE(ORIGIN(iram_seg)+LENGTH(iram_seg));
|
||||
_external_heap_end = ABSOLUTE(ORIGIN(psram_seg)+LENGTH(psram_seg));
|
||||
|
||||
_stack_start_cpu1 = _heap_end;
|
||||
_stack_end_cpu1 = _stack_start_cpu1 + STACK_SIZE;
|
||||
_stack_start_cpu0 = _stack_end_cpu1;
|
||||
_stack_end_cpu0 = _stack_start_cpu0 + STACK_SIZE;
|
||||
|
||||
EXTERN(DefaultHandler);
|
||||
|
||||
EXTERN(WIFI_EVENT); /* Force inclusion of WiFi libraries */
|
||||
|
||||
INCLUDE "device.x"
|
||||
|
||||
4
esp32-hal/rom.x
Normal file
4
esp32-hal/rom.x
Normal file
@ -0,0 +1,4 @@
|
||||
REGION_ALIAS("ROTEXT", irom_seg);
|
||||
REGION_ALIAS("RWTEXT", iram_seg);
|
||||
REGION_ALIAS("RODATA", drom_seg);
|
||||
REGION_ALIAS("RWDATA", dram_seg);
|
||||
4
esp32-hal/rust-toolchain.toml
Normal file
4
esp32-hal/rust-toolchain.toml
Normal file
@ -0,0 +1,4 @@
|
||||
[toolchain]
|
||||
channel = "esp"
|
||||
components = ["rustfmt", "rustc-dev"]
|
||||
targets = ["xtensa-esp32-none-elf"]
|
||||
57
esp32-hal/src/lib.rs
Normal file
57
esp32-hal/src/lib.rs
Normal file
@ -0,0 +1,57 @@
|
||||
#![no_std]
|
||||
|
||||
pub use embedded_hal as ehal;
|
||||
pub use esp32 as pac;
|
||||
|
||||
pub mod prelude;
|
||||
pub mod serial;
|
||||
pub mod timer;
|
||||
|
||||
pub use serial::Serial;
|
||||
pub use timer::Timer;
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn DefaultHandler(_level: u32, _interrupt: pac::Interrupt) {}
|
||||
|
||||
/// Function initializes ESP32 specific memories (RTC slow and fast) and
|
||||
/// then calls original Reset function
|
||||
///
|
||||
/// ENTRY point is defined in memory.x
|
||||
/// *Note: the pre_init function is called in the original reset handler
|
||||
/// after the initializations done in this function*
|
||||
#[cfg(feature = "rt")]
|
||||
#[doc(hidden)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ESP32Reset() -> ! {
|
||||
// These symbols come from `memory.x`
|
||||
extern "C" {
|
||||
static mut _rtc_fast_bss_start: u32;
|
||||
static mut _rtc_fast_bss_end: u32;
|
||||
|
||||
static mut _rtc_slow_bss_start: u32;
|
||||
static mut _rtc_slow_bss_end: u32;
|
||||
|
||||
static mut _stack_end_cpu0: u32;
|
||||
}
|
||||
|
||||
// copying data from flash to various data segments is done by the bootloader
|
||||
// initialization to zero needs to be done by the application
|
||||
|
||||
// Initialize RTC RAM
|
||||
xtensa_lx_rt::zero_bss(&mut _rtc_fast_bss_start, &mut _rtc_fast_bss_end);
|
||||
xtensa_lx_rt::zero_bss(&mut _rtc_slow_bss_start, &mut _rtc_slow_bss_end);
|
||||
|
||||
// set stack pointer to end of memory: no need to retain stack up to this point
|
||||
xtensa_lx::set_stack_pointer(&mut _stack_end_cpu0);
|
||||
|
||||
// continue with default reset handler
|
||||
xtensa_lx_rt::Reset();
|
||||
}
|
||||
|
||||
/// The esp32 has a first stage bootloader that handles loading program data into the right place
|
||||
/// therefore we skip loading it again.
|
||||
#[no_mangle]
|
||||
#[rustfmt::skip]
|
||||
pub extern "Rust" fn __init_data() -> bool {
|
||||
false
|
||||
}
|
||||
9
esp32-hal/src/prelude.rs
Normal file
9
esp32-hal/src/prelude.rs
Normal file
@ -0,0 +1,9 @@
|
||||
pub use embedded_hal::{
|
||||
digital::v2::{
|
||||
InputPin as _,
|
||||
OutputPin as _,
|
||||
StatefulOutputPin as _,
|
||||
ToggleableOutputPin as _,
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
180
esp32-hal/src/serial.rs
Normal file
180
esp32-hal/src/serial.rs
Normal file
@ -0,0 +1,180 @@
|
||||
//! UART Functionality (UART)
|
||||
//!
|
||||
//! Reference: ESP32-C3 TRM v0.3 Section 20 (as TRM)
|
||||
//!
|
||||
//! The ESP32-C3 has two identical UART groups UART0 and UART1.
|
||||
//!
|
||||
//! TODO:
|
||||
//! - Interrupt support
|
||||
|
||||
use embedded_hal::serial::{Read, Write};
|
||||
|
||||
use crate::pac::{uart0::RegisterBlock, UART0, UART1, UART2};
|
||||
|
||||
const UART_FIFO_SIZE: u16 = 128;
|
||||
|
||||
/// UART-specific errors
|
||||
#[derive(Debug)]
|
||||
pub enum Error {}
|
||||
|
||||
/// UART peripheral (UART)
|
||||
pub struct Serial<T> {
|
||||
uart: T,
|
||||
}
|
||||
|
||||
impl<T: Instance> Serial<T> {
|
||||
pub fn new(uart: T) -> Result<Self, Error> {
|
||||
let mut serial = Serial { uart };
|
||||
serial.uart.disable_rx_interrupts();
|
||||
serial.uart.disable_tx_interrupts();
|
||||
|
||||
Ok(serial)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance {
|
||||
/// Return registerblock of uart instance as if it were UART0
|
||||
fn as_uart0(&self) -> &RegisterBlock;
|
||||
|
||||
/// Clear and disable all tx-related interrupts
|
||||
fn disable_tx_interrupts(&mut self) {
|
||||
// Disable UART TX interrupts
|
||||
self.as_uart0().int_clr.write(|w| {
|
||||
w.txfifo_empty_int_clr()
|
||||
.set_bit()
|
||||
.tx_brk_done_int_clr()
|
||||
.set_bit()
|
||||
.tx_brk_idle_done_int_clr()
|
||||
.set_bit()
|
||||
.tx_done_int_clr()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
self.as_uart0().int_ena.write(|w| {
|
||||
w.txfifo_empty_int_ena()
|
||||
.clear_bit()
|
||||
.tx_brk_done_int_ena()
|
||||
.clear_bit()
|
||||
.tx_brk_idle_done_int_ena()
|
||||
.clear_bit()
|
||||
.tx_done_int_ena()
|
||||
.clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
/// Clear and disable all rx-related interrupts
|
||||
fn disable_rx_interrupts(&mut self) {
|
||||
// Disable UART RX interrupts
|
||||
self.as_uart0().int_clr.write(|w| {
|
||||
w.rxfifo_full_int_clr()
|
||||
.set_bit()
|
||||
.rxfifo_ovf_int_clr()
|
||||
.set_bit()
|
||||
.rxfifo_tout_int_clr()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
self.as_uart0().int_ena.write(|w| {
|
||||
w.rxfifo_full_int_ena()
|
||||
.clear_bit()
|
||||
.rxfifo_ovf_int_ena()
|
||||
.clear_bit()
|
||||
.rxfifo_tout_int_ena()
|
||||
.clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the number of occupied entries in the tx fifo buffer
|
||||
fn get_tx_fifo_count(&mut self) -> u16 {
|
||||
self.as_uart0().status.read().txfifo_cnt().bits().into()
|
||||
}
|
||||
|
||||
/// Get the number of occupied entries in the rx fifo buffer
|
||||
fn get_rx_fifo_count(&mut self) -> u16 {
|
||||
self.as_uart0().status.read().rxfifo_cnt().bits().into()
|
||||
}
|
||||
|
||||
/// Check if the UART TX statemachine is is idle
|
||||
fn is_tx_idle(&mut self) -> bool {
|
||||
self.as_uart0().status.read().st_utx_out().bits() == 0x0u8
|
||||
}
|
||||
|
||||
/// Check if the UART RX statemachine is is idle
|
||||
fn is_rx_idle(&mut self) -> bool {
|
||||
self.as_uart0().status.read().st_urx_out().bits() == 0x0u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Write half of a serial interface
|
||||
impl<T: Instance> Write<u8> for Serial<T> {
|
||||
type Error = Error;
|
||||
|
||||
/// Writes a single word to the serial interface
|
||||
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||
if self.uart.get_tx_fifo_count() >= UART_FIFO_SIZE {
|
||||
Err(nb::Error::WouldBlock)
|
||||
} else {
|
||||
self.uart
|
||||
.as_uart0()
|
||||
.fifo
|
||||
.write(|w| unsafe { w.rxfifo_rd_byte().bits(word) });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures that none of the previously written words are still buffered
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
if self.uart.is_tx_idle() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(nb::Error::WouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write half of a serial interface
|
||||
impl<T: Instance> Read<u8> for Serial<T> {
|
||||
type Error = Error;
|
||||
|
||||
/// Reads a single word from the serial interface
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
if self.uart.get_rx_fifo_count() > 0 {
|
||||
Ok(self.uart.as_uart0().fifo.read().rxfifo_rd_byte().bits())
|
||||
} else {
|
||||
Err(nb::Error::WouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Instance> core::fmt::Write for Serial<T> {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
s.as_bytes()
|
||||
.iter()
|
||||
.try_for_each(|c| nb::block!(self.write(*c)))
|
||||
.map_err(|_| core::fmt::Error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Specific instance implementation for the UART0 peripheral
|
||||
impl Instance for UART0 {
|
||||
#[inline(always)]
|
||||
fn as_uart0(&self) -> &RegisterBlock {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Specific instance implementation for the UART1 peripheral
|
||||
impl Instance for UART1 {
|
||||
#[inline(always)]
|
||||
fn as_uart0(&self) -> &RegisterBlock {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Specific instance implementation for the UART2 peripheral
|
||||
impl Instance for UART2 {
|
||||
#[inline(always)]
|
||||
fn as_uart0(&self) -> &RegisterBlock {
|
||||
self
|
||||
}
|
||||
}
|
||||
196
esp32-hal/src/timer.rs
Normal file
196
esp32-hal/src/timer.rs
Normal file
@ -0,0 +1,196 @@
|
||||
use embedded_hal::{
|
||||
timer::{Cancel, CountDown, Periodic},
|
||||
watchdog::WatchdogDisable,
|
||||
};
|
||||
use void::Void;
|
||||
|
||||
use crate::pac::{timg0::RegisterBlock, TIMG0, TIMG1};
|
||||
|
||||
pub struct Timer<T> {
|
||||
timg: T,
|
||||
}
|
||||
|
||||
pub enum Error {
|
||||
/// Report that the timer is active and certain management
|
||||
/// operations cannot be performed safely
|
||||
TimerActive,
|
||||
/// Report that the timer is inactive and thus not
|
||||
/// ever reaching any potentially configured alarm value
|
||||
TimerInactive,
|
||||
/// Report that the alarm functionality is disabled
|
||||
AlarmInactive,
|
||||
}
|
||||
|
||||
impl<T> Timer<T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
pub fn new(timg: T) -> Self {
|
||||
Self { timg }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance {
|
||||
fn as_timg0(&self) -> &RegisterBlock;
|
||||
|
||||
fn reset_counter(&mut self) {
|
||||
self.as_timg0()
|
||||
.t0loadhi
|
||||
.write(|w| unsafe { w.t0_load_hi().bits(0) });
|
||||
|
||||
self.as_timg0()
|
||||
.t0load
|
||||
.write(|w| unsafe { w.t0_load().bits(1) });
|
||||
}
|
||||
|
||||
fn set_counter_active(&mut self, state: bool) {
|
||||
self.as_timg0().t0config.modify(|_, w| w.t0_en().bit(state));
|
||||
}
|
||||
|
||||
fn is_counter_active(&mut self) -> bool {
|
||||
self.as_timg0().t0config.read().t0_en().bit_is_set()
|
||||
}
|
||||
|
||||
fn set_counter_decrementing(&mut self, decrementing: bool) {
|
||||
self.as_timg0()
|
||||
.t0config
|
||||
.modify(|_, w| w.t0_increase().bit(!decrementing));
|
||||
}
|
||||
|
||||
fn set_auto_reload(&mut self, auto_reload: bool) {
|
||||
self.as_timg0()
|
||||
.t0config
|
||||
.modify(|_, w| w.t0_autoreload().bit(auto_reload));
|
||||
}
|
||||
|
||||
fn set_alarm_active(&mut self, state: bool) {
|
||||
self.as_timg0()
|
||||
.t0config
|
||||
.modify(|_, w| w.t0_alarm_en().bit(state));
|
||||
}
|
||||
|
||||
fn is_alarm_active(&mut self) -> bool {
|
||||
self.as_timg0().t0config.read().t0_alarm_en().bit_is_set()
|
||||
}
|
||||
|
||||
fn load_alarm_value(&mut self, value: u64) {
|
||||
let value = value & 0x3F_FFFF_FFFF_FFFF;
|
||||
let high = (value >> 32) as u32;
|
||||
let low = (value & 0xFFFF_FFFF) as u32;
|
||||
|
||||
self.as_timg0()
|
||||
.t0alarmlo
|
||||
.write(|w| unsafe { w.t0_alarm_lo().bits(low) });
|
||||
self.as_timg0()
|
||||
.t0alarmhi
|
||||
.write(|w| unsafe { w.t0_alarm_hi().bits(high) });
|
||||
}
|
||||
|
||||
fn set_wdt_enabled(&mut self, enabled: bool) {
|
||||
self.as_timg0()
|
||||
.wdtwprotect
|
||||
.write(|w| unsafe { w.wdt_wkey().bits(0u32) });
|
||||
|
||||
self.as_timg0()
|
||||
.wdtconfig0
|
||||
.write(|w| w.wdt_en().bit(enabled));
|
||||
|
||||
self.as_timg0()
|
||||
.wdtwprotect
|
||||
.write(|w| unsafe { w.wdt_wkey().bits(0x50D8_3AA1u32) });
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> CountDown for Timer<T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
type Time = u64;
|
||||
|
||||
fn start<Time>(&mut self, timeout: Time)
|
||||
where
|
||||
Time: Into<u64>,
|
||||
{
|
||||
self.timg.set_counter_active(false);
|
||||
self.timg.set_alarm_active(false);
|
||||
|
||||
self.timg.reset_counter();
|
||||
self.timg.load_alarm_value(timeout.into());
|
||||
|
||||
self.timg.set_counter_decrementing(false);
|
||||
self.timg.set_auto_reload(true);
|
||||
self.timg.set_counter_active(true);
|
||||
self.timg.set_alarm_active(true);
|
||||
}
|
||||
|
||||
fn wait(&mut self) -> nb::Result<(), Void> {
|
||||
if !self.timg.is_counter_active() {
|
||||
panic!("Called wait on an inactive timer!");
|
||||
}
|
||||
|
||||
let int_raw_is_clear = self
|
||||
.timg
|
||||
.as_timg0()
|
||||
.int_raw_timers
|
||||
.read()
|
||||
.t0_int_raw()
|
||||
.bit_is_clear();
|
||||
|
||||
if int_raw_is_clear {
|
||||
Err(nb::Error::WouldBlock)
|
||||
} else {
|
||||
self.timg
|
||||
.as_timg0()
|
||||
.int_clr_timers
|
||||
.write(|w| w.t0_int_clr().set_bit());
|
||||
|
||||
self.timg.set_alarm_active(true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Periodic for Timer<T> where T: Instance {}
|
||||
|
||||
impl<T> Cancel for Timer<T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn cancel(&mut self) -> Result<(), Error> {
|
||||
if !self.timg.is_counter_active() {
|
||||
return Err(Error::TimerInactive);
|
||||
} else if !self.timg.is_alarm_active() {
|
||||
return Err(Error::AlarmInactive);
|
||||
}
|
||||
|
||||
self.timg.set_counter_active(false);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> WatchdogDisable for Timer<T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
fn disable(&mut self) {
|
||||
self.timg.set_wdt_enabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for TIMG0 {
|
||||
#[inline(always)]
|
||||
fn as_timg0(&self) -> &RegisterBlock {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for TIMG1 {
|
||||
#[inline(always)]
|
||||
fn as_timg0(&self) -> &RegisterBlock {
|
||||
self
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user