mirror of
https://github.com/esp-rs/esp-idf-hal.git
synced 2025-09-29 05:12:09 +00:00
PeripheralRef support; critical-section support
This commit is contained in:
parent
812e188223
commit
2ace88dc58
216
src/adc.rs
216
src/adc.rs
@ -6,49 +6,47 @@ use esp_idf_sys::*;
|
||||
#[cfg(feature = "riscv-ulp-hal")]
|
||||
use crate::riscv_ulp_hal::sys::*;
|
||||
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
use crate::gpio::ADCPin;
|
||||
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
use crate::peripheral::{Peripheral, PeripheralRef};
|
||||
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub type AdcConfig = config::Config;
|
||||
|
||||
pub trait Adc: Send {
|
||||
fn unit() -> adc_unit_t;
|
||||
}
|
||||
|
||||
pub trait Analog<ADC: Adc>: Send {
|
||||
pub trait Attenuation<ADC: Adc>: Send {
|
||||
fn attenuation() -> adc_atten_t;
|
||||
}
|
||||
|
||||
pub struct Atten0dB<ADC: Adc> {
|
||||
_adc: PhantomData<ADC>,
|
||||
}
|
||||
pub struct Atten0dB<ADC: Adc>(PhantomData<ADC>);
|
||||
pub struct Atten2p5dB<ADC: Adc>(PhantomData<ADC>);
|
||||
pub struct Atten6dB<ADC: Adc>(PhantomData<ADC>);
|
||||
pub struct Atten11dB<ADC: Adc>(PhantomData<ADC>);
|
||||
|
||||
pub struct Atten2p5dB<ADC: Adc> {
|
||||
_adc: PhantomData<ADC>,
|
||||
}
|
||||
|
||||
pub struct Atten6dB<ADC: Adc> {
|
||||
_adc: PhantomData<ADC>,
|
||||
}
|
||||
|
||||
pub struct Atten11dB<ADC: Adc> {
|
||||
_adc: PhantomData<ADC>,
|
||||
}
|
||||
|
||||
impl<ADC: Adc> Analog<ADC> for Atten0dB<ADC> {
|
||||
impl<ADC: Adc> Attenuation<ADC> for Atten0dB<ADC> {
|
||||
fn attenuation() -> adc_atten_t {
|
||||
adc_atten_t_ADC_ATTEN_DB_0
|
||||
}
|
||||
}
|
||||
|
||||
impl<ADC: Adc> Analog<ADC> for Atten2p5dB<ADC> {
|
||||
impl<ADC: Adc> Attenuation<ADC> for Atten2p5dB<ADC> {
|
||||
fn attenuation() -> adc_atten_t {
|
||||
adc_atten_t_ADC_ATTEN_DB_2_5
|
||||
}
|
||||
}
|
||||
|
||||
impl<ADC: Adc> Analog<ADC> for Atten6dB<ADC> {
|
||||
impl<ADC: Adc> Attenuation<ADC> for Atten6dB<ADC> {
|
||||
fn attenuation() -> adc_atten_t {
|
||||
adc_atten_t_ADC_ATTEN_DB_6
|
||||
}
|
||||
}
|
||||
|
||||
impl<ADC: Adc> Analog<ADC> for Atten11dB<ADC> {
|
||||
impl<ADC: Adc> Attenuation<ADC> for Atten11dB<ADC> {
|
||||
fn attenuation() -> adc_atten_t {
|
||||
adc_atten_t_ADC_ATTEN_DB_11
|
||||
}
|
||||
@ -131,8 +129,57 @@ pub mod config {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub struct PoweredAdc<ADC: Adc> {
|
||||
adc: ADC,
|
||||
pub struct AdcChannelDriver<'d, T: ADCPin, ATTEN> {
|
||||
pin: PeripheralRef<'d, T>,
|
||||
_atten: PhantomData<ATTEN>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
impl<'d, T: ADCPin, ATTEN> AdcChannelDriver<'d, T, ATTEN>
|
||||
where
|
||||
ATTEN: Attenuation<T::Adc>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn new(
|
||||
pin: impl Peripheral<P = T> + 'd,
|
||||
) -> Result<AdcChannelDriver<'d, T, ATTEN>, EspError> {
|
||||
crate::into_ref!(pin);
|
||||
|
||||
unsafe {
|
||||
crate::gpio::rtc_reset_pin(pin.pin())?;
|
||||
}
|
||||
|
||||
if T::Adc::unit() == adc_unit_t_ADC_UNIT_1 {
|
||||
esp!(unsafe { adc1_config_channel_atten(pin.adc_channel(), ATTEN::attenuation()) })?;
|
||||
} else {
|
||||
esp!(unsafe { adc2_config_channel_atten(pin.adc_channel(), ATTEN::attenuation()) })?;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
pin,
|
||||
_atten: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn pin(&mut self) -> &mut PeripheralRef<'d, T> {
|
||||
&mut self.pin
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
impl<'d, T: ADCPin, ATTEN> embedded_hal_0_2::adc::Channel<ATTEN>
|
||||
for AdcChannelDriver<'d, T, ATTEN>
|
||||
{
|
||||
type ID = u8;
|
||||
|
||||
fn channel() -> Self::ID {
|
||||
T::CHANNEL as _
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub struct AdcDriver<'d, ADC: Adc> {
|
||||
_adc: PeripheralRef<'d, ADC>,
|
||||
resolution: config::Resolution,
|
||||
#[cfg(any(esp_idf_comp_esp_adc_cal_enabled, esp_idf_comp_esp_adc_enabled))]
|
||||
cal_characteristics:
|
||||
@ -140,10 +187,10 @@ pub struct PoweredAdc<ADC: Adc> {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
unsafe impl<ADC: Adc> Send for PoweredAdc<ADC> {}
|
||||
unsafe impl<'d, ADC: Adc> Send for AdcDriver<'d, ADC> {}
|
||||
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
impl<ADC: Adc> PoweredAdc<ADC> {
|
||||
impl<'d, ADC: Adc> AdcDriver<'d, ADC> {
|
||||
#[cfg(all(
|
||||
esp32,
|
||||
any(esp_idf_comp_esp_adc_cal_enabled, esp_idf_comp_esp_adc_enabled)
|
||||
@ -169,7 +216,12 @@ impl<ADC: Adc> PoweredAdc<ADC> {
|
||||
#[cfg(esp32s2)]
|
||||
const MAX_READING: u32 = 8191;
|
||||
|
||||
pub fn new(adc: ADC, config: config::Config) -> Result<Self, EspError> {
|
||||
pub fn new(
|
||||
adc: impl Peripheral<P = ADC> + 'd,
|
||||
config: &config::Config,
|
||||
) -> Result<Self, EspError> {
|
||||
crate::into_ref!(adc);
|
||||
|
||||
#[cfg(any(esp_idf_comp_esp_adc_cal_enabled, esp_idf_comp_esp_adc_enabled))]
|
||||
if config.calibration {
|
||||
esp!(unsafe { esp_adc_cal_check_efuse(Self::CALIBRATION_SCHEME) })?;
|
||||
@ -180,7 +232,7 @@ impl<ADC: Adc> PoweredAdc<ADC> {
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
adc,
|
||||
_adc: adc,
|
||||
resolution: config.resolution,
|
||||
#[cfg(any(esp_idf_comp_esp_adc_cal_enabled, esp_idf_comp_esp_adc_enabled))]
|
||||
cal_characteristics: if config.calibration {
|
||||
@ -191,8 +243,44 @@ impl<ADC: Adc> PoweredAdc<ADC> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn release(self) -> ADC {
|
||||
self.adc
|
||||
pub fn read<'c, T, ATTEN>(
|
||||
&mut self,
|
||||
pin: &mut AdcChannelDriver<'c, T, ATTEN>,
|
||||
) -> Result<u16, EspError>
|
||||
where
|
||||
T: ADCPin,
|
||||
ATTEN: Attenuation<T::Adc>,
|
||||
{
|
||||
self.read_internal(ADC::unit(), pin.pin().adc_channel(), ATTEN::attenuation())
|
||||
}
|
||||
|
||||
#[cfg(all(esp32, esp_idf_version_major = "4"))]
|
||||
pub fn read_hall(
|
||||
&mut self,
|
||||
_hall_sensor: &mut crate::hall::HallSensor,
|
||||
) -> Result<u16, EspError> {
|
||||
let measurement = unsafe { hall_sensor_read() };
|
||||
|
||||
Ok(self.raw_to_voltage(measurement, adc_atten_t_ADC_ATTEN_DB_0)?)
|
||||
}
|
||||
|
||||
fn read_internal(
|
||||
&mut self,
|
||||
unit: adc_unit_t,
|
||||
channel: adc_channel_t,
|
||||
atten: adc_atten_t,
|
||||
) -> Result<u16, EspError> {
|
||||
let mut measurement = 0_i32;
|
||||
|
||||
if unit == adc_unit_t_ADC_UNIT_1 {
|
||||
measurement = unsafe { adc1_get_raw(channel) };
|
||||
} else {
|
||||
esp!(unsafe {
|
||||
adc2_get_raw(channel, self.resolution.into(), &mut measurement as *mut _)
|
||||
})?;
|
||||
};
|
||||
|
||||
self.raw_to_voltage(measurement, atten)
|
||||
}
|
||||
|
||||
fn raw_to_voltage(
|
||||
@ -275,81 +363,49 @@ impl<ADC: Adc> PoweredAdc<ADC> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
unit: adc_unit_t,
|
||||
channel: adc_channel_t,
|
||||
atten: adc_atten_t,
|
||||
) -> nb::Result<u16, EspError> {
|
||||
let mut measurement = 0_i32;
|
||||
|
||||
if unit == adc_unit_t_ADC_UNIT_1 {
|
||||
measurement = unsafe { adc1_get_raw(channel) };
|
||||
} else {
|
||||
let res = unsafe {
|
||||
adc2_get_raw(channel, self.resolution.into(), &mut measurement as *mut _)
|
||||
};
|
||||
|
||||
if res == ESP_ERR_INVALID_STATE as i32 {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
} else if res < 0 {
|
||||
return Err(nb::Error::Other(EspError::from(res).unwrap()));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(self.raw_to_voltage(measurement, atten)?)
|
||||
}
|
||||
|
||||
#[cfg(all(esp32, esp_idf_version_major = "4"))]
|
||||
fn read_hall(&mut self) -> nb::Result<u16, EspError> {
|
||||
let measurement = unsafe { hall_sensor_read() };
|
||||
|
||||
Ok(self.raw_to_voltage(measurement, adc_atten_t_ADC_ATTEN_DB_0)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
impl<ADC, AN, PIN> embedded_hal_0_2::adc::OneShot<AN, u16, PIN> for PoweredAdc<ADC>
|
||||
impl<'d, ADC, ATTEN, PIN> embedded_hal_0_2::adc::OneShot<ATTEN, u16, PIN> for AdcDriver<'d, ADC>
|
||||
where
|
||||
ADC: Adc,
|
||||
AN: Analog<ADC>,
|
||||
PIN: embedded_hal_0_2::adc::Channel<AN, ID = u8>,
|
||||
ATTEN: Attenuation<ADC>,
|
||||
PIN: embedded_hal_0_2::adc::Channel<ATTEN, ID = u8>,
|
||||
{
|
||||
type Error = EspError;
|
||||
|
||||
fn read(&mut self, _pin: &mut PIN) -> nb::Result<u16, Self::Error> {
|
||||
self.read(
|
||||
self.read_internal(
|
||||
ADC::unit(),
|
||||
PIN::channel() as adc_channel_t,
|
||||
AN::attenuation(),
|
||||
ATTEN::attenuation(),
|
||||
)
|
||||
.map_err(to_nb_err)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(esp32, esp_idf_version_major = "4", not(feature = "riscv-ulp-hal")))]
|
||||
impl embedded_hal_0_2::adc::OneShot<ADC1, u16, crate::hall::HallSensor> for PoweredAdc<ADC1> {
|
||||
impl<'d> embedded_hal_0_2::adc::OneShot<ADC1, u16, crate::hall::HallSensor>
|
||||
for AdcDriver<'d, ADC1>
|
||||
{
|
||||
type Error = EspError;
|
||||
|
||||
fn read(&mut self, _hall_sensor: &mut crate::hall::HallSensor) -> nb::Result<u16, Self::Error> {
|
||||
self.read_hall()
|
||||
fn read(&mut self, hall_sensor: &mut crate::hall::HallSensor) -> nb::Result<u16, Self::Error> {
|
||||
AdcDriver::read_hall(self, hall_sensor).map_err(to_nb_err)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_nb_err(err: EspError) -> nb::Error<EspError> {
|
||||
if err.code() == ESP_ERR_INVALID_STATE as i32 {
|
||||
nb::Error::WouldBlock
|
||||
} else {
|
||||
nb::Error::Other(err)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_adc {
|
||||
($adc:ident: $unit:expr) => {
|
||||
pub struct $adc(::core::marker::PhantomData<*const ()>);
|
||||
|
||||
impl $adc {
|
||||
/// # Safety
|
||||
///
|
||||
/// Care should be taken not to instnatiate this ADC instance, if it is already instantiated and used elsewhere
|
||||
pub unsafe fn new() -> Self {
|
||||
$adc(::core::marker::PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for $adc {}
|
||||
crate::impl_peripheral!($adc);
|
||||
|
||||
impl Adc for $adc {
|
||||
#[inline(always)]
|
||||
|
61
src/can.rs
61
src/can.rs
@ -22,7 +22,7 @@
|
||||
//!
|
||||
//! let timing = can::config::Timing::B500K;
|
||||
//! let config = can::config::Config::new().filter(filter).timing(timing);
|
||||
//! let mut can = can::CanBus::new(peripherals.can, pins.gpio5, pins.gpio4, config).unwrap();
|
||||
//! let mut can = can::CanDriver::new(peripherals.can, pins.gpio5, pins.gpio4, &config).unwrap();
|
||||
//!
|
||||
//! let tx_frame = can::Frame::new(StandardId::new(0x042).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
|
||||
//! nb::block!(can.transmit(&tx_frame)).unwrap();
|
||||
@ -32,12 +32,11 @@
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use esp_idf_sys::*;
|
||||
|
||||
use crate::delay::portMAX_DELAY;
|
||||
use crate::gpio::*;
|
||||
use crate::peripheral::{Peripheral, PeripheralRef};
|
||||
|
||||
crate::embedded_hal_error!(
|
||||
CanError,
|
||||
@ -234,16 +233,19 @@ pub mod config {
|
||||
}
|
||||
|
||||
/// CAN abstraction
|
||||
pub struct CanBus<TX: OutputPin, RX: InputPin> {
|
||||
can: CAN,
|
||||
tx: TX,
|
||||
rx: RX,
|
||||
}
|
||||
pub struct CanDriver<'d>(PeripheralRef<'d, CAN>);
|
||||
|
||||
unsafe impl<TX: OutputPin, RX: InputPin> Send for CanBus<TX, RX> {}
|
||||
unsafe impl<'d> Send for CanDriver<'d> {}
|
||||
|
||||
impl<'d> CanDriver<'d> {
|
||||
pub fn new(
|
||||
can: impl Peripheral<P = CAN> + 'd,
|
||||
tx: impl Peripheral<P = impl OutputPin> + 'd,
|
||||
rx: impl Peripheral<P = impl OutputPin> + 'd,
|
||||
config: &config::Config,
|
||||
) -> Result<Self, EspError> {
|
||||
crate::into_ref!(can, tx, rx);
|
||||
|
||||
impl<TX: OutputPin, RX: InputPin> CanBus<TX, RX> {
|
||||
pub fn new(can: CAN, tx: TX, rx: RX, config: config::Config) -> Result<Self, EspError> {
|
||||
let general_config = twai_general_config_t {
|
||||
mode: twai_mode_t_TWAI_MODE_NORMAL,
|
||||
tx_io: tx.pin(),
|
||||
@ -276,14 +278,7 @@ impl<TX: OutputPin, RX: InputPin> CanBus<TX, RX> {
|
||||
esp!(unsafe { twai_driver_install(&general_config, &timing_config, &filter_config) })?;
|
||||
esp!(unsafe { twai_start() })?;
|
||||
|
||||
Ok(Self { can, tx, rx })
|
||||
}
|
||||
|
||||
pub fn release(self) -> Result<(CAN, TX, RX), EspError> {
|
||||
esp!(unsafe { twai_stop() })?;
|
||||
esp!(unsafe { twai_driver_uninstall() })?;
|
||||
|
||||
Ok((self.can, self.tx, self.rx))
|
||||
Ok(Self(can))
|
||||
}
|
||||
|
||||
fn transmit_internal(&mut self, frame: &Frame, delay: TickType_t) -> Result<(), EspError> {
|
||||
@ -302,7 +297,14 @@ impl<TX: OutputPin, RX: InputPin> CanBus<TX, RX> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<TX: OutputPin, RX: InputPin> embedded_hal_0_2::blocking::can::Can for CanBus<TX, RX> {
|
||||
impl<'d> Drop for CanDriver<'d> {
|
||||
fn drop(&mut self) {
|
||||
esp!(unsafe { twai_stop() }).unwrap();
|
||||
esp!(unsafe { twai_driver_uninstall() }).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> embedded_hal_0_2::blocking::can::Can for CanDriver<'d> {
|
||||
type Frame = Frame;
|
||||
type Error = Can02Error;
|
||||
|
||||
@ -317,7 +319,7 @@ impl<TX: OutputPin, RX: InputPin> embedded_hal_0_2::blocking::can::Can for CanBu
|
||||
}
|
||||
}
|
||||
|
||||
impl<TX: OutputPin, RX: InputPin> embedded_hal::can::blocking::Can for CanBus<TX, RX> {
|
||||
impl<'d> embedded_hal::can::blocking::Can for CanDriver<'d> {
|
||||
type Frame = Frame;
|
||||
type Error = CanError;
|
||||
|
||||
@ -332,7 +334,7 @@ impl<TX: OutputPin, RX: InputPin> embedded_hal::can::blocking::Can for CanBus<TX
|
||||
}
|
||||
}
|
||||
|
||||
impl<TX: OutputPin, RX: InputPin> embedded_hal_0_2::can::nb::Can for CanBus<TX, RX> {
|
||||
impl<'d> embedded_hal_0_2::can::nb::Can for CanDriver<'d> {
|
||||
type Frame = Frame;
|
||||
type Error = Can02Error;
|
||||
|
||||
@ -354,7 +356,7 @@ impl<TX: OutputPin, RX: InputPin> embedded_hal_0_2::can::nb::Can for CanBus<TX,
|
||||
}
|
||||
}
|
||||
|
||||
impl<TX: OutputPin, RX: InputPin> embedded_hal::can::nb::Can for CanBus<TX, RX> {
|
||||
impl<'d> embedded_hal::can::nb::Can for CanDriver<'d> {
|
||||
type Frame = Frame;
|
||||
type Error = CanError;
|
||||
|
||||
@ -581,15 +583,4 @@ impl embedded_hal::can::Frame for Frame {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CAN(PhantomData<*const ()>);
|
||||
|
||||
impl CAN {
|
||||
/// # Safety
|
||||
///
|
||||
/// Care should be taken not to instnatiate this CAN instance, if it is already instantiated and used elsewhere
|
||||
pub unsafe fn new() -> Self {
|
||||
CAN(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for CAN {}
|
||||
crate::impl_peripheral!(CAN);
|
||||
|
1889
src/gpio.rs
1889
src/gpio.rs
File diff suppressed because it is too large
Load Diff
17
src/hall.rs
17
src/hall.rs
@ -1,22 +1,9 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::adc;
|
||||
|
||||
pub struct HallSensor(PhantomData<*const ()>);
|
||||
|
||||
impl HallSensor {
|
||||
/// # Safety
|
||||
///
|
||||
/// Care should be taken not to instnatiate this Hall Sensor instance, if it is already instantiated and used elsewhere
|
||||
pub unsafe fn new() -> Self {
|
||||
HallSensor(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for HallSensor {}
|
||||
|
||||
impl embedded_hal_0_2::adc::Channel<adc::ADC1> for HallSensor {
|
||||
type ID = ();
|
||||
|
||||
fn channel() -> Self::ID {}
|
||||
}
|
||||
|
||||
crate::impl_peripheral!(HallSensor);
|
||||
|
161
src/i2c.rs
161
src/i2c.rs
@ -1,8 +1,13 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
|
||||
|
||||
use esp_idf_sys::*;
|
||||
|
||||
use crate::{delay::*, gpio::*, units::*};
|
||||
use crate::delay::*;
|
||||
use crate::gpio::*;
|
||||
use crate::peripheral::{Peripheral, PeripheralRef};
|
||||
use crate::units::*;
|
||||
|
||||
crate::embedded_hal_error!(
|
||||
I2cError,
|
||||
@ -10,16 +15,6 @@ crate::embedded_hal_error!(
|
||||
embedded_hal::i2c::ErrorKind
|
||||
);
|
||||
|
||||
pub struct MasterPins<SDA: OutputPin + InputPin, SCL: OutputPin + InputPin> {
|
||||
pub sda: SDA,
|
||||
pub scl: SCL,
|
||||
}
|
||||
|
||||
pub struct SlavePins<SDA: OutputPin + InputPin, SCL: OutputPin + InputPin> {
|
||||
pub sda: SDA,
|
||||
pub scl: SCL,
|
||||
}
|
||||
|
||||
/// I2C configuration
|
||||
pub mod config {
|
||||
use crate::units::*;
|
||||
@ -138,59 +133,36 @@ pub trait I2c: Send {
|
||||
fn port() -> i2c_port_t;
|
||||
}
|
||||
|
||||
unsafe impl<I2C: I2c, SDA: OutputPin + InputPin, SCL: OutputPin + InputPin> Send
|
||||
for Master<I2C, SDA, SCL>
|
||||
{
|
||||
}
|
||||
|
||||
pub struct Master<I2C, SDA, SCL>
|
||||
pub struct I2cMasterDriver<'d, I2C>
|
||||
where
|
||||
I2C: I2c,
|
||||
SDA: OutputPin + InputPin,
|
||||
SCL: OutputPin + InputPin,
|
||||
{
|
||||
i2c: I2C,
|
||||
pins: MasterPins<SDA, SCL>,
|
||||
_i2c: PeripheralRef<'d, I2C>,
|
||||
timeout: TickType_t,
|
||||
}
|
||||
|
||||
pub struct Slave<I2C, SDA, SCL>
|
||||
impl<'d, I2C> I2cMasterDriver<'d, I2C>
|
||||
where
|
||||
I2C: I2c,
|
||||
SDA: OutputPin + InputPin,
|
||||
SCL: OutputPin + InputPin,
|
||||
{
|
||||
i2c: I2C,
|
||||
pins: SlavePins<SDA, SCL>,
|
||||
timeout: TickType_t,
|
||||
}
|
||||
|
||||
unsafe impl<I2C: I2c, SDA: OutputPin + InputPin, SCL: OutputPin + InputPin> Send
|
||||
for Slave<I2C, SDA, SCL>
|
||||
{
|
||||
}
|
||||
|
||||
impl<I2C, SDA, SCL> Master<I2C, SDA, SCL>
|
||||
where
|
||||
I2C: I2c,
|
||||
SDA: OutputPin + InputPin,
|
||||
SCL: OutputPin + InputPin,
|
||||
{
|
||||
pub fn new(
|
||||
i2c: I2C,
|
||||
pins: MasterPins<SDA, SCL>,
|
||||
config: config::MasterConfig,
|
||||
) -> Result<Master<I2C, SDA, SCL>, EspError> {
|
||||
i2c: impl Peripheral<P = I2C> + 'd,
|
||||
sda: impl Peripheral<P = impl InputPin + OutputPin> + 'd,
|
||||
scl: impl Peripheral<P = impl InputPin + OutputPin> + 'd,
|
||||
config: &config::MasterConfig,
|
||||
) -> Result<I2cMasterDriver<'d, I2C>, EspError> {
|
||||
// i2c_config_t documentation says that clock speed must be no higher than 1 MHz
|
||||
if config.baudrate > 1.MHz().into() {
|
||||
return Err(EspError::from(ESP_ERR_INVALID_ARG as i32).unwrap());
|
||||
}
|
||||
|
||||
crate::into_ref!(i2c, sda, scl);
|
||||
|
||||
let sys_config = i2c_config_t {
|
||||
mode: i2c_mode_t_I2C_MODE_MASTER,
|
||||
sda_io_num: pins.sda.pin(),
|
||||
sda_io_num: sda.pin(),
|
||||
sda_pullup_en: config.sda_pullup_enabled,
|
||||
scl_io_num: pins.scl.pin(),
|
||||
scl_io_num: scl.pin(),
|
||||
scl_pullup_en: config.scl_pullup_enabled,
|
||||
__bindgen_anon_1: i2c_config_t__bindgen_ty_1 {
|
||||
master: i2c_config_t__bindgen_ty_1__bindgen_ty_1 {
|
||||
@ -212,22 +184,12 @@ where
|
||||
) // TODO: set flags
|
||||
})?;
|
||||
|
||||
Ok(Master {
|
||||
i2c,
|
||||
pins,
|
||||
Ok(I2cMasterDriver {
|
||||
_i2c: i2c,
|
||||
timeout: TickType::from(config.timeout).0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn release(self) -> Result<(I2C, MasterPins<SDA, SCL>), EspError> {
|
||||
esp!(unsafe { i2c_driver_delete(I2C::port()) })?;
|
||||
|
||||
//self.pins.sda.reset()?;
|
||||
//self.pins.scl.reset()?;
|
||||
|
||||
Ok((self.i2c, self.pins))
|
||||
}
|
||||
|
||||
fn cmd_begin(
|
||||
&mut self,
|
||||
command_link: &CommandLink,
|
||||
@ -250,11 +212,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<I2C, SDA, SCL> embedded_hal_0_2::blocking::i2c::Read for Master<I2C, SDA, SCL>
|
||||
impl<'d, I2C: I2c> Drop for I2cMasterDriver<'d, I2C> {
|
||||
fn drop(&mut self) {
|
||||
esp!(unsafe { i2c_driver_delete(I2C::port()) }).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'d, I2C: I2c> Send for I2cMasterDriver<'d, I2C> {}
|
||||
|
||||
impl<'d, I2C> embedded_hal_0_2::blocking::i2c::Read for I2cMasterDriver<'d, I2C>
|
||||
where
|
||||
I2C: I2c,
|
||||
SDA: OutputPin + InputPin,
|
||||
SCL: OutputPin + InputPin,
|
||||
{
|
||||
type Error = I2cError;
|
||||
|
||||
@ -263,11 +231,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<I2C, SDA, SCL> embedded_hal_0_2::blocking::i2c::Write for Master<I2C, SDA, SCL>
|
||||
impl<'d, I2C> embedded_hal_0_2::blocking::i2c::Write for I2cMasterDriver<'d, I2C>
|
||||
where
|
||||
I2C: I2c,
|
||||
SDA: OutputPin + InputPin,
|
||||
SCL: OutputPin + InputPin,
|
||||
{
|
||||
type Error = I2cError;
|
||||
|
||||
@ -276,11 +242,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<I2C, SDA, SCL> embedded_hal_0_2::blocking::i2c::WriteRead for Master<I2C, SDA, SCL>
|
||||
impl<'d, I2C> embedded_hal_0_2::blocking::i2c::WriteRead for I2cMasterDriver<'d, I2C>
|
||||
where
|
||||
I2C: I2c,
|
||||
SDA: OutputPin + InputPin,
|
||||
SCL: OutputPin + InputPin,
|
||||
{
|
||||
type Error = I2cError;
|
||||
|
||||
@ -289,21 +253,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<I2C, SDA, SCL> embedded_hal::i2c::ErrorType for Master<I2C, SDA, SCL>
|
||||
impl<'d, I2C> embedded_hal::i2c::ErrorType for I2cMasterDriver<'d, I2C>
|
||||
where
|
||||
I2C: I2c,
|
||||
SDA: OutputPin + InputPin,
|
||||
SCL: OutputPin + InputPin,
|
||||
{
|
||||
type Error = I2cError;
|
||||
}
|
||||
|
||||
impl<I2C, SDA, SCL> embedded_hal::i2c::blocking::I2c<embedded_hal::i2c::SevenBitAddress>
|
||||
for Master<I2C, SDA, SCL>
|
||||
impl<'d, I2C> embedded_hal::i2c::blocking::I2c<embedded_hal::i2c::SevenBitAddress>
|
||||
for I2cMasterDriver<'d, I2C>
|
||||
where
|
||||
I2C: I2c,
|
||||
SDA: OutputPin + InputPin,
|
||||
SCL: OutputPin + InputPin,
|
||||
{
|
||||
fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
let mut command_link = CommandLink::new().map_err(I2cError::other)?;
|
||||
@ -455,24 +415,35 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<I2C, SDA, SCL> Slave<I2C, SDA, SCL>
|
||||
pub struct I2cSlaveDriver<'d, I2C>
|
||||
where
|
||||
I2C: I2c,
|
||||
{
|
||||
_i2c: PeripheralRef<'d, I2C>,
|
||||
timeout: TickType_t,
|
||||
}
|
||||
|
||||
unsafe impl<'d, I2C: I2c> Send for I2cSlaveDriver<'d, I2C> {}
|
||||
|
||||
impl<'d, I2C> I2cSlaveDriver<'d, I2C>
|
||||
where
|
||||
I2C: I2c,
|
||||
SDA: OutputPin + InputPin,
|
||||
SCL: OutputPin + InputPin,
|
||||
{
|
||||
pub fn new(
|
||||
i2c: I2C,
|
||||
pins: SlavePins<SDA, SCL>,
|
||||
i2c: impl Peripheral<P = I2C> + 'd,
|
||||
sda: impl Peripheral<P = impl InputPin + OutputPin> + 'd,
|
||||
scl: impl Peripheral<P = impl InputPin + OutputPin> + 'd,
|
||||
slave_addr: u8,
|
||||
config: config::SlaveConfig,
|
||||
config: &config::SlaveConfig,
|
||||
) -> Result<Self, EspError> {
|
||||
crate::into_ref!(i2c, sda, scl);
|
||||
|
||||
#[cfg(any(esp_idf_version = "4.4", esp_idf_version_major = "5"))]
|
||||
let sys_config = i2c_config_t {
|
||||
mode: i2c_mode_t_I2C_MODE_SLAVE,
|
||||
sda_io_num: pins.sda.pin(),
|
||||
sda_io_num: sda.pin(),
|
||||
sda_pullup_en: config.sda_pullup_enabled,
|
||||
scl_io_num: pins.scl.pin(),
|
||||
scl_io_num: scl.pin(),
|
||||
scl_pullup_en: config.scl_pullup_enabled,
|
||||
__bindgen_anon_1: i2c_config_t__bindgen_ty_1 {
|
||||
slave: i2c_config_t__bindgen_ty_1__bindgen_ty_2 {
|
||||
@ -513,21 +484,11 @@ where
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
i2c,
|
||||
pins,
|
||||
_i2c: i2c,
|
||||
timeout: TickType::from(config.timeout).0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn release(self) -> Result<(I2C, SlavePins<SDA, SCL>), EspError> {
|
||||
esp!(unsafe { i2c_driver_delete(I2C::port()) })?;
|
||||
|
||||
//self.pins.sda.reset()?;
|
||||
//self.pins.scl.reset()?;
|
||||
|
||||
Ok((self.i2c, self.pins))
|
||||
}
|
||||
|
||||
pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize, EspError> {
|
||||
let n = unsafe {
|
||||
i2c_slave_read_buffer(
|
||||
@ -629,18 +590,7 @@ impl<'buffers> Drop for CommandLink<'buffers> {
|
||||
|
||||
macro_rules! impl_i2c {
|
||||
($i2c:ident: $port:expr) => {
|
||||
pub struct $i2c(::core::marker::PhantomData<*const ()>);
|
||||
|
||||
impl $i2c {
|
||||
/// # Safety
|
||||
///
|
||||
/// Care should be taken not to instnatiate this I2C instance, if it is already instantiated and used elsewhere
|
||||
pub unsafe fn new() -> Self {
|
||||
$i2c(::core::marker::PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for $i2c {}
|
||||
crate::impl_peripheral!($i2c);
|
||||
|
||||
impl I2c for $i2c {
|
||||
#[inline(always)]
|
||||
@ -652,6 +602,5 @@ macro_rules! impl_i2c {
|
||||
}
|
||||
|
||||
impl_i2c!(I2C0: 0);
|
||||
|
||||
#[cfg(not(esp32c3))]
|
||||
impl_i2c!(I2C1: 1);
|
||||
|
188
src/ledc.rs
188
src/ledc.rs
@ -16,24 +16,21 @@
|
||||
//! use esp_idf_hal::prelude::*;
|
||||
//!
|
||||
//! let peripherals = Peripherals::take().unwrap();
|
||||
//! let config = TimerConfig::default().frequency(25.kHz().into());
|
||||
//! let timer = Timer::new(peripherals.ledc.timer0, &config)?;
|
||||
//! let channel = Channel::new(peripherals.ledc.channel0, &timer, peripherals.pins.gpio1)?;
|
||||
//! let driver = LedcDriver::new(peripherals.ledc.channel0, peripherals.ledc.timer0, peripherals.pins.gpio1, &TimerConfig::default().frequency(25.kHz().into()))?;
|
||||
//!
|
||||
//! let max_duty = channel.get_max_duty()?;
|
||||
//! channel.set_duty(max_duty * 3 / 4);
|
||||
//! let max_duty = driver.get_max_duty()?;
|
||||
//! driver.set_duty(max_duty * 3 / 4);
|
||||
//! ```
|
||||
//!
|
||||
//! See the `examples/` folder of this repository for more.
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use esp_idf_sys::*;
|
||||
|
||||
use crate::cs::CriticalSection;
|
||||
use crate::gpio::OutputPin;
|
||||
use crate::peripheral::{Peripheral, PeripheralRef};
|
||||
|
||||
pub use chip::*;
|
||||
|
||||
@ -51,6 +48,7 @@ pub mod config {
|
||||
|
||||
pub use chip::Resolution;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct TimerConfig {
|
||||
pub frequency: Hertz,
|
||||
pub resolution: Resolution,
|
||||
@ -88,16 +86,28 @@ pub mod config {
|
||||
}
|
||||
}
|
||||
|
||||
/// LED Control timer abstraction
|
||||
pub struct Timer<T: HwTimer> {
|
||||
instance: T,
|
||||
/// LED Control driver
|
||||
pub struct LedcDriver<'d, C: LedcChannel, T: LedcTimer> {
|
||||
_channel: PeripheralRef<'d, C>,
|
||||
_timer: PeripheralRef<'d, T>,
|
||||
speed_mode: ledc_mode_t,
|
||||
max_duty: Duty,
|
||||
duty: Duty,
|
||||
}
|
||||
|
||||
impl<T: HwTimer> Timer<T> {
|
||||
/// Creates a new LED Control timer abstraction
|
||||
pub fn new(instance: T, config: &config::TimerConfig) -> Result<Self, EspError> {
|
||||
// TODO: Stop channel when the instance gets dropped. It seems that we can't
|
||||
// have both at the same time: a method for releasing its hardware resources
|
||||
// and implementing Drop.
|
||||
impl<'d, C: LedcChannel, T: LedcTimer> LedcDriver<'d, C, T> {
|
||||
/// Creates a new LED Control driver
|
||||
pub fn new(
|
||||
channel: impl Peripheral<P = C> + 'd,
|
||||
timer: impl Peripheral<P = T> + 'd,
|
||||
pin: impl Peripheral<P = impl OutputPin> + 'd,
|
||||
config: &config::TimerConfig,
|
||||
) -> Result<Self, EspError> {
|
||||
crate::into_ref!(channel, timer, pin);
|
||||
|
||||
let timer_config = ledc_timer_config_t {
|
||||
speed_mode: config.speed_mode,
|
||||
timer_num: T::timer(),
|
||||
@ -114,58 +124,12 @@ impl<T: HwTimer> Timer<T> {
|
||||
// SAFETY: We own the instance and therefor are safe to configure it.
|
||||
esp!(unsafe { ledc_timer_config(&timer_config) })?;
|
||||
|
||||
Ok(Timer {
|
||||
instance,
|
||||
speed_mode: config.speed_mode,
|
||||
max_duty: config.resolution.max_duty(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Pauses the timer. Operation can be resumed with
|
||||
/// [`resume()`](Timer::resume()).
|
||||
pub fn pause(&mut self) -> Result<(), EspError> {
|
||||
esp!(unsafe { ledc_timer_pause(self.speed_mode, T::timer()) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Stops the timer and releases its hardware resource
|
||||
pub fn release(mut self) -> Result<T, EspError> {
|
||||
self.reset()?;
|
||||
Ok(self.instance)
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Result<(), EspError> {
|
||||
esp!(unsafe { ledc_timer_rst(self.speed_mode, T::timer()) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resumes the operation of a previously paused timer
|
||||
pub fn resume(&mut self) -> Result<(), EspError> {
|
||||
esp!(unsafe { ledc_timer_resume(self.speed_mode, T::timer()) })?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// LED Control output channel abstraction
|
||||
pub struct Channel<C: HwChannel, H: HwTimer, T: Borrow<Timer<H>>, P: OutputPin> {
|
||||
instance: C,
|
||||
_hw_timer: PhantomData<H>,
|
||||
timer: T,
|
||||
pin: P,
|
||||
duty: Duty,
|
||||
}
|
||||
|
||||
// TODO: Stop channel when the instance gets dropped. It seems that we can't
|
||||
// have both at the same time: a method for releasing its hardware resources
|
||||
// and implementing Drop.
|
||||
impl<C: HwChannel, H: HwTimer, T: Borrow<Timer<H>>, P: OutputPin> Channel<C, H, T, P> {
|
||||
/// Creates a new LED Control output channel abstraction
|
||||
pub fn new(instance: C, timer: T, pin: P) -> Result<Self, EspError> {
|
||||
let duty = 0;
|
||||
|
||||
let channel_config = ledc_channel_config_t {
|
||||
speed_mode: timer.borrow().speed_mode,
|
||||
speed_mode: config.speed_mode,
|
||||
channel: C::channel(),
|
||||
timer_sel: H::timer(),
|
||||
timer_sel: T::timer(),
|
||||
intr_type: ledc_intr_type_t_LEDC_INTR_DISABLE,
|
||||
gpio_num: pin.pin(),
|
||||
duty: duty as u32,
|
||||
@ -192,28 +156,21 @@ impl<C: HwChannel, H: HwTimer, T: Borrow<Timer<H>>, P: OutputPin> Channel<C, H,
|
||||
// it.
|
||||
esp!(unsafe { ledc_channel_config(&channel_config) })?;
|
||||
|
||||
Ok(Channel {
|
||||
instance,
|
||||
_hw_timer: PhantomData,
|
||||
timer,
|
||||
pin,
|
||||
Ok(LedcDriver {
|
||||
_channel: channel,
|
||||
_timer: timer,
|
||||
duty,
|
||||
speed_mode: config.speed_mode,
|
||||
max_duty: config.resolution.max_duty(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Stops the output channel and releases its hardware resource and GPIO
|
||||
/// pin
|
||||
pub fn release(mut self) -> Result<(C, P), EspError> {
|
||||
self.stop()?;
|
||||
Ok((self.instance, self.pin))
|
||||
}
|
||||
|
||||
pub fn get_duty(&self) -> Duty {
|
||||
self.duty
|
||||
}
|
||||
|
||||
pub fn get_max_duty(&self) -> Duty {
|
||||
self.timer.borrow().max_duty
|
||||
self.max_duty
|
||||
}
|
||||
|
||||
pub fn disable(&mut self) -> Result<(), EspError> {
|
||||
@ -233,21 +190,38 @@ impl<C: HwChannel, H: HwTimer, T: Borrow<Timer<H>>, P: OutputPin> Channel<C, H,
|
||||
// TODO: Why does calling self.get_max_duty() result in the compiler
|
||||
// error 'expected `u32`, found enum `Result`' when our method returns
|
||||
// Duty?
|
||||
let clamped = duty.min(self.timer.borrow().max_duty);
|
||||
let clamped = duty.min(self.max_duty);
|
||||
self.duty = clamped;
|
||||
self.update_duty(clamped)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Pauses the timer. Operation can be resumed with [`resume_timer()`].
|
||||
pub fn pause_timer(&mut self) -> Result<(), EspError> {
|
||||
esp!(unsafe { ledc_timer_pause(self.speed_mode, T::timer()) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resumes the operation of the previously paused timer
|
||||
pub fn resume_timer(&mut self) -> Result<(), EspError> {
|
||||
esp!(unsafe { ledc_timer_resume(self.speed_mode, T::timer()) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset_timer(&mut self) -> Result<(), EspError> {
|
||||
esp!(unsafe { ledc_timer_rst(self.speed_mode, T::timer()) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> Result<(), EspError> {
|
||||
esp!(unsafe { ledc_stop(self.timer.borrow().speed_mode, C::channel(), IDLE_LEVEL) })?;
|
||||
esp!(unsafe { ledc_stop(self.speed_mode, C::channel(), IDLE_LEVEL) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_duty(&mut self, duty: Duty) -> Result<(), EspError> {
|
||||
esp!(unsafe {
|
||||
ledc_set_duty_and_update(
|
||||
self.timer.borrow().speed_mode,
|
||||
self.speed_mode,
|
||||
C::channel(),
|
||||
duty as u32,
|
||||
Default::default(),
|
||||
@ -257,8 +231,15 @@ impl<C: HwChannel, H: HwTimer, T: Borrow<Timer<H>>, P: OutputPin> Channel<C, H,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, C: LedcChannel, T: LedcTimer> Drop for LedcDriver<'d, C, T> {
|
||||
fn drop(&mut self) {
|
||||
self.reset_timer().unwrap();
|
||||
self.stop().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// PwmPin temporarily removed from embedded-hal-1.0.alpha7 in anticipation of e-hal 1.0 release
|
||||
// impl<C: HwChannel, H: HwTimer, T: Borrow<Timer<H>>, P: OutputPin> embedded_hal::pwm::blocking::PwmPin for Channel<C, H, T, P> {
|
||||
// impl<'d, C: LedcChannel, T: LedcTimer> embedded_hal::pwm::blocking::PwmPin for LedcDriver<'d, C, T> {
|
||||
// type Duty = Duty;
|
||||
// type Error = EspError;
|
||||
|
||||
@ -283,9 +264,7 @@ impl<C: HwChannel, H: HwTimer, T: Borrow<Timer<H>>, P: OutputPin> Channel<C, H,
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<C: HwChannel, H: HwTimer, T: Borrow<Timer<H>>, P: OutputPin> embedded_hal_0_2::PwmPin
|
||||
for Channel<C, H, T, P>
|
||||
{
|
||||
impl<'d, C: LedcChannel, T: LedcTimer> embedded_hal_0_2::PwmPin for LedcDriver<'d, C, T> {
|
||||
type Duty = Duty;
|
||||
|
||||
fn disable(&mut self) {
|
||||
@ -316,9 +295,9 @@ impl<C: HwChannel, H: HwTimer, T: Borrow<Timer<H>>, P: OutputPin> embedded_hal_0
|
||||
}
|
||||
|
||||
mod chip {
|
||||
use core::marker::PhantomData;
|
||||
use esp_idf_sys::*;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Resolution {
|
||||
Bits1,
|
||||
Bits2,
|
||||
@ -417,33 +396,20 @@ mod chip {
|
||||
}
|
||||
|
||||
/// LED Control peripheral timer
|
||||
pub trait HwTimer {
|
||||
pub trait LedcTimer {
|
||||
fn timer() -> ledc_timer_t;
|
||||
}
|
||||
|
||||
/// LED Control peripheral output channel
|
||||
pub trait HwChannel {
|
||||
pub trait LedcChannel {
|
||||
fn channel() -> ledc_channel_t;
|
||||
}
|
||||
|
||||
macro_rules! impl_timer {
|
||||
($instance:ident: $timer:expr) => {
|
||||
pub struct $instance {
|
||||
_marker: PhantomData<ledc_timer_t>,
|
||||
}
|
||||
crate::impl_peripheral!($instance);
|
||||
|
||||
impl $instance {
|
||||
/// # Safety
|
||||
///
|
||||
/// It is safe to instantiate this timer exactly one time.
|
||||
pub unsafe fn new() -> Self {
|
||||
$instance {
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HwTimer for $instance {
|
||||
impl LedcTimer for $instance {
|
||||
fn timer() -> ledc_timer_t {
|
||||
$timer
|
||||
}
|
||||
@ -458,23 +424,9 @@ mod chip {
|
||||
|
||||
macro_rules! impl_channel {
|
||||
($instance:ident: $channel:expr) => {
|
||||
pub struct $instance {
|
||||
_marker: PhantomData<ledc_channel_t>,
|
||||
}
|
||||
crate::impl_peripheral!($instance);
|
||||
|
||||
impl $instance {
|
||||
/// # Safety
|
||||
///
|
||||
/// It is safe to instantiate this output channel exactly one
|
||||
/// time.
|
||||
pub unsafe fn new() -> Self {
|
||||
$instance {
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HwChannel for $instance {
|
||||
impl LedcChannel for $instance {
|
||||
fn channel() -> ledc_channel_t {
|
||||
$channel
|
||||
}
|
||||
@ -494,7 +446,7 @@ mod chip {
|
||||
impl_channel!(CHANNEL7: ledc_channel_t_LEDC_CHANNEL_7);
|
||||
|
||||
/// The LED Control device peripheral
|
||||
pub struct Peripheral {
|
||||
pub struct LEDC {
|
||||
pub timer0: TIMER0,
|
||||
pub timer1: TIMER1,
|
||||
pub timer2: TIMER2,
|
||||
@ -511,7 +463,7 @@ mod chip {
|
||||
pub channel7: CHANNEL7,
|
||||
}
|
||||
|
||||
impl Peripheral {
|
||||
impl LEDC {
|
||||
/// Creates a new instance of the LEDC peripheral. Typically one wants
|
||||
/// to use the instance [`ledc`](crate::peripherals::Peripherals::ledc) from
|
||||
/// the device peripherals obtained via
|
||||
|
60
src/lib.rs
60
src/lib.rs
@ -39,20 +39,24 @@ pub mod i2c;
|
||||
pub mod interrupt;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub mod ledc;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
#[cfg(all(
|
||||
any(all(esp32, esp_idf_eth_use_esp32_emac), esp_idf_eth_use_openeth),
|
||||
not(feature = "riscv-ulp-hal")
|
||||
))]
|
||||
pub mod mac;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub mod modem;
|
||||
pub mod peripheral;
|
||||
pub mod peripherals;
|
||||
pub mod prelude;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub mod rmt;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub mod serial;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub mod spi;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub mod task;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub mod uart;
|
||||
#[cfg(all(any(esp32, esp32s2, esp32s3), not(feature = "riscv-ulp-hal")))]
|
||||
pub mod ulp;
|
||||
pub mod units;
|
||||
@ -124,5 +128,55 @@ macro_rules! embedded_hal_error {
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! into_ref {
|
||||
($($name:ident),*) => {
|
||||
$(
|
||||
let $name = $name.into_ref();
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! impl_peripheral_trait {
|
||||
($type:ident) => {
|
||||
unsafe impl Send for $type {}
|
||||
|
||||
impl $crate::peripheral::Peripheral for $type {
|
||||
type P = $type;
|
||||
|
||||
#[inline]
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P {
|
||||
$type { ..*self }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! impl_peripheral {
|
||||
($type:ident) => {
|
||||
pub struct $type(::core::marker::PhantomData<*const ()>);
|
||||
|
||||
impl $type {
|
||||
/// # Safety
|
||||
///
|
||||
/// Care should be taken not to instnatiate this peripheralinstance, if it is already instantiated and used elsewhere
|
||||
#[inline(always)]
|
||||
pub unsafe fn new() -> Self {
|
||||
$type(::core::marker::PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
$crate::impl_peripheral_trait!($type);
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use embedded_hal_error;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use impl_peripheral;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use impl_peripheral_trait;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use into_ref;
|
||||
|
13
src/mac.rs
13
src/mac.rs
@ -1,12 +1 @@
|
||||
pub struct Mac(::core::marker::PhantomData<*const ()>);
|
||||
|
||||
impl Mac {
|
||||
/// # Safety
|
||||
///
|
||||
/// Care should be taken not to instnatiate this Mac instance, if it is already instantiated and used elsewhere
|
||||
pub unsafe fn new() -> Self {
|
||||
Mac(::core::marker::PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Mac {}
|
||||
crate::impl_peripheral!(MAC);
|
||||
|
51
src/modem.rs
51
src/modem.rs
@ -1,18 +1,33 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::peripheral::Peripheral;
|
||||
|
||||
#[cfg(not(esp32s2))]
|
||||
pub use split::*;
|
||||
|
||||
pub trait WifiModemCap {}
|
||||
pub trait WifiModemPeripheral: Peripheral<P = Self> {}
|
||||
|
||||
pub trait BluetoothModemCap {}
|
||||
#[cfg(not(esp32s2))]
|
||||
pub trait BluetoothModemPeripheral: Peripheral<P = Self> {}
|
||||
|
||||
pub struct Modem(::core::marker::PhantomData<*const ()>);
|
||||
#[cfg(not(esp32s2))]
|
||||
pub struct Modem(PhantomData<*const ()>, WifiModem, BluetoothModem);
|
||||
|
||||
#[cfg(esp32s2)]
|
||||
pub struct Modem(PhantomData<*const ()>);
|
||||
|
||||
impl Modem {
|
||||
/// # Safety
|
||||
///
|
||||
/// Care should be taken not to instnatiate this Mac instance, if it is already instantiated and used elsewhere
|
||||
pub unsafe fn new() -> Self {
|
||||
Modem(::core::marker::PhantomData)
|
||||
#[cfg(not(esp32s2))]
|
||||
let this = Modem(PhantomData, WifiModem::new(), BluetoothModem::new());
|
||||
|
||||
#[cfg(esp32s2)]
|
||||
let this = Modem(PhantomData);
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
#[cfg(all(not(esp32s2), esp_idf_esp32_wifi_sw_coexist_enable))]
|
||||
@ -21,29 +36,33 @@ impl Modem {
|
||||
}
|
||||
|
||||
#[cfg(all(not(esp32s2), esp_idf_esp32_wifi_sw_coexist_enable))]
|
||||
pub fn combine(_wifi_modem: WifiModem, _bt_modem: BluetoothModem) -> Self {
|
||||
unsafe { Self::new() }
|
||||
pub fn split_ref(&mut self) -> (&mut WifiModem, &mut BluetoothModem) {
|
||||
unsafe { (WifiModem(PhantomData), BluetoothModem(PhantomData)) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Modem {}
|
||||
|
||||
impl WifiModemCap for Modem {}
|
||||
impl Peripheral for Modem {
|
||||
type P = Self;
|
||||
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl WifiModemPeripheral for Modem {}
|
||||
|
||||
#[cfg(not(esp32s2))]
|
||||
impl BluetoothModemCap for Modem {}
|
||||
impl BluetoothModemPeripheral for Modem {}
|
||||
|
||||
#[cfg(not(esp32s2))]
|
||||
mod split {
|
||||
pub struct WifiModem(::core::marker::PhantomData<*const ()>);
|
||||
crate::impl_peripheral!(WifiModem);
|
||||
|
||||
unsafe impl Send for WifiModem {}
|
||||
impl super::WifiModemPeripheral for WifiModem {}
|
||||
|
||||
impl super::WifiModemCap for WifiModem {}
|
||||
crate::impl_peripheral!(BluetoothModem);
|
||||
|
||||
pub struct BluetoothModem(::core::marker::PhantomData<*const ()>);
|
||||
|
||||
unsafe impl Send for BluetoothModem {}
|
||||
|
||||
impl super::BluetoothModemCap for BluetoothModem {}
|
||||
impl super::BluetoothModemPeripheral for BluetoothModem {}
|
||||
}
|
||||
|
173
src/peripheral.rs
Normal file
173
src/peripheral.rs
Normal file
@ -0,0 +1,173 @@
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
/// An exclusive reference to a peripheral.
|
||||
///
|
||||
/// This is functionally the same as a `&'a mut T`. The reason for having a
|
||||
/// dedicated struct is memory efficiency:
|
||||
///
|
||||
/// Peripheral singletons are typically either zero-sized (for concrete peripehrals
|
||||
/// like `PA9` or `Spi4`) or very small (for example `AnyPin` which is 1 byte).
|
||||
/// However `&mut T` is always 4 bytes for 32-bit targets, even if T is zero-sized.
|
||||
/// PeripheralRef stores a copy of `T` instead, so it's the same size.
|
||||
///
|
||||
/// but it is the size of `T` not the size
|
||||
/// of a pointer. This is useful if T is a zero sized type.
|
||||
pub struct PeripheralRef<'a, T> {
|
||||
inner: T,
|
||||
_lifetime: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
impl<'a, T> PeripheralRef<'a, T> {
|
||||
#[inline]
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
_lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Unsafely clone (duplicate) a peripheral singleton.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This returns an owned clone of the peripheral. You must manually ensure
|
||||
/// only one copy of the peripheral is in use at a time. For example, don't
|
||||
/// create two SPI drivers on `SPI1`, because they will "fight" each other.
|
||||
///
|
||||
/// You should strongly prefer using `reborrow()` instead. It returns a
|
||||
/// `PeripheralRef` that borrows `self`, which allows the borrow checker
|
||||
/// to enforce this at compile time.
|
||||
pub unsafe fn clone_unchecked(&mut self) -> PeripheralRef<'a, T>
|
||||
where
|
||||
T: Peripheral<P = T>,
|
||||
{
|
||||
PeripheralRef::new(self.inner.clone_unchecked())
|
||||
}
|
||||
|
||||
/// Reborrow into a "child" PeripheralRef.
|
||||
///
|
||||
/// `self` will stay borrowed until the child PeripheralRef is dropped.
|
||||
pub fn reborrow(&mut self) -> PeripheralRef<'_, T>
|
||||
where
|
||||
T: Peripheral<P = T>,
|
||||
{
|
||||
// safety: we're returning the clone inside a new PeripheralRef that borrows
|
||||
// self, so user code can't use both at the same time.
|
||||
PeripheralRef::new(unsafe { self.inner.clone_unchecked() })
|
||||
}
|
||||
|
||||
/// Map the inner peripheral using `Into`.
|
||||
///
|
||||
/// This converts from `PeripheralRef<'a, T>` to `PeripheralRef<'a, U>`, using an
|
||||
/// `Into` impl to convert from `T` to `U`.
|
||||
///
|
||||
/// For example, this can be useful to degrade GPIO pins: converting from PeripheralRef<'a, PB11>` to `PeripheralRef<'a, AnyPin>`.
|
||||
#[inline]
|
||||
pub fn map_into<U>(self) -> PeripheralRef<'a, U>
|
||||
where
|
||||
T: Into<U>,
|
||||
{
|
||||
PeripheralRef {
|
||||
inner: self.inner.into(),
|
||||
_lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for PeripheralRef<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for PeripheralRef<'a, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for any type that can be used as a peripheral of type `P`.
|
||||
///
|
||||
/// This is used in driver constructors, to allow passing either owned peripherals (e.g. `TWISPI0`),
|
||||
/// or borrowed peripherals (e.g. `&mut TWISPI0`).
|
||||
///
|
||||
/// For example, if you have a driver with a constructor like this:
|
||||
///
|
||||
/// ```ignore
|
||||
/// impl<'d, T: Instance> Twim<'d, T> {
|
||||
/// pub fn new(
|
||||
/// twim: impl Peripheral<P = T> + 'd,
|
||||
/// irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
/// sda: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
/// scl: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
/// config: Config,
|
||||
/// ) -> Self { .. }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// You may call it with owned peripherals, which yields an instance that can live forever (`'static`):
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut twi: Twim<'static, ...> = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config);
|
||||
/// ```
|
||||
///
|
||||
/// Or you may call it with borrowed peripherals, which yields an instance that can only live for as long
|
||||
/// as the borrows last:
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut twi: Twim<'_, ...> = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config);
|
||||
/// ```
|
||||
///
|
||||
/// # Implementation details, for HAL authors
|
||||
///
|
||||
/// When writing a HAL, the intended way to use this trait is to take `impl Peripheral<P = ..>` in
|
||||
/// the HAL's public API (such as driver constructors), calling `.into_ref()` to obtain a `PeripheralRef`,
|
||||
/// and storing that in the driver struct.
|
||||
///
|
||||
/// `.into_ref()` on an owned `T` yields a `PeripheralRef<'static, T>`.
|
||||
/// `.into_ref()` on an `&'a mut T` yields a `PeripheralRef<'a, T>`.
|
||||
pub trait Peripheral: Sized {
|
||||
/// Peripheral singleton type
|
||||
type P;
|
||||
|
||||
/// Unsafely clone (duplicate) a peripheral singleton.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This returns an owned clone of the peripheral. You must manually ensure
|
||||
/// only one copy of the peripheral is in use at a time. For example, don't
|
||||
/// create two SPI drivers on `SPI1`, because they will "fight" each other.
|
||||
///
|
||||
/// You should strongly prefer using `into_ref()` instead. It returns a
|
||||
/// `PeripheralRef`, which allows the borrow checker to enforce this at compile time.
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P;
|
||||
|
||||
/// Convert a value into a `PeripheralRef`.
|
||||
///
|
||||
/// When called on an owned `T`, yields a `PeripheralRef<'static, T>`.
|
||||
/// When called on an `&'a mut T`, yields a `PeripheralRef<'a, T>`.
|
||||
#[inline]
|
||||
fn into_ref<'a>(mut self) -> PeripheralRef<'a, Self::P>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
PeripheralRef::new(unsafe { self.clone_unchecked() })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, T: DerefMut> Peripheral for T
|
||||
where
|
||||
T::Target: Peripheral,
|
||||
{
|
||||
type P = <T::Target as Peripheral>::P;
|
||||
|
||||
#[inline]
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P {
|
||||
self.deref_mut().clone_unchecked()
|
||||
}
|
||||
}
|
@ -6,16 +6,19 @@ use crate::gpio;
|
||||
use crate::i2c;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
use crate::ledc;
|
||||
#[cfg(all(esp32, not(feature = "riscv-ulp-hal")))]
|
||||
#[cfg(all(
|
||||
any(all(esp32, esp_idf_eth_use_esp32_emac), esp_idf_eth_use_openeth),
|
||||
not(feature = "riscv-ulp-hal")
|
||||
))]
|
||||
use crate::mac;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
use crate::modem;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
use crate::rmt;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
use crate::serial;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
use crate::spi;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
use crate::uart;
|
||||
#[cfg(all(
|
||||
any(esp32, esp32s2, esp32s3),
|
||||
not(feature = "riscv-ulp-hal"),
|
||||
@ -26,11 +29,11 @@ use crate::ulp;
|
||||
pub struct Peripherals {
|
||||
pub pins: gpio::Pins,
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub uart0: serial::UART0,
|
||||
pub uart0: uart::UART0,
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub uart1: serial::UART1,
|
||||
pub uart1: uart::UART1,
|
||||
#[cfg(all(esp32, not(feature = "riscv-ulp-hal")))]
|
||||
pub uart2: serial::UART2,
|
||||
pub uart2: uart::UART2,
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub i2c0: i2c::I2C0,
|
||||
#[cfg(all(not(esp32c3), not(feature = "riscv-ulp-hal")))]
|
||||
@ -48,17 +51,20 @@ pub struct Peripherals {
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub can: can::CAN,
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub ledc: ledc::Peripheral,
|
||||
pub ledc: ledc::LEDC,
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub rmt: rmt::Peripheral,
|
||||
pub rmt: rmt::RMT,
|
||||
#[cfg(all(
|
||||
any(esp32, esp32s2, esp32s3),
|
||||
not(feature = "riscv-ulp-hal"),
|
||||
esp_idf_comp_ulp_enabled
|
||||
))]
|
||||
pub ulp: ulp::ULP,
|
||||
#[cfg(all(esp32, not(feature = "riscv-ulp-hal")))]
|
||||
pub mac: mac::Mac,
|
||||
#[cfg(all(
|
||||
any(all(esp32, esp_idf_eth_use_esp32_emac), esp_idf_eth_use_openeth),
|
||||
not(feature = "riscv-ulp-hal")
|
||||
))]
|
||||
pub mac: mac::MAC,
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub modem: modem::Modem,
|
||||
}
|
||||
@ -109,11 +115,11 @@ impl Peripherals {
|
||||
Self {
|
||||
pins: gpio::Pins::new(),
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
uart0: serial::UART0::new(),
|
||||
uart0: uart::UART0::new(),
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
uart1: serial::UART1::new(),
|
||||
uart1: uart::UART1::new(),
|
||||
#[cfg(all(esp32, not(feature = "riscv-ulp-hal")))]
|
||||
uart2: serial::UART2::new(),
|
||||
uart2: uart::UART2::new(),
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
i2c0: i2c::I2C0::new(),
|
||||
#[cfg(all(not(esp32c3), not(feature = "riscv-ulp-hal")))]
|
||||
@ -131,17 +137,20 @@ impl Peripherals {
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
can: can::CAN::new(),
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
ledc: ledc::Peripheral::new(),
|
||||
ledc: ledc::LEDC::new(),
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
rmt: rmt::Peripheral::new(),
|
||||
rmt: rmt::RMT::new(),
|
||||
#[cfg(all(
|
||||
any(esp32, esp32s2, esp32s3),
|
||||
not(feature = "riscv-ulp-hal"),
|
||||
esp_idf_comp_ulp_enabled
|
||||
))]
|
||||
ulp: ulp::ULP::new(),
|
||||
#[cfg(all(esp32, not(feature = "riscv-ulp-hal")))]
|
||||
mac: mac::Mac::new(),
|
||||
#[cfg(all(
|
||||
any(all(esp32, esp_idf_eth_use_esp32_emac), esp_idf_eth_use_openeth),
|
||||
not(feature = "riscv-ulp-hal")
|
||||
))]
|
||||
mac: mac::MAC::new(),
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
modem: modem::Modem::new(),
|
||||
}
|
||||
|
119
src/rmt.rs
119
src/rmt.rs
@ -26,7 +26,7 @@
|
||||
//! let channel = peripherals.rmt.channel0;
|
||||
//!
|
||||
//! // Create an RMT transmitter.
|
||||
//! let tx = Transmit::new(pin, channel, &config)?;
|
||||
//! let tx = RmtDriver::new(pin, channel, &config)?;
|
||||
//!
|
||||
//! // Prepare signal pulse signal to be sent.
|
||||
//! let low = Pulse::new(PinState::Low, PulseTicks::new(10)?);
|
||||
@ -51,21 +51,26 @@
|
||||
//! [VariableLengthSignal] allows you to use the heap and incrementally add pulse items without knowing the size
|
||||
//! ahead of time.
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::time::Duration;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
use core::cell::UnsafeCell;
|
||||
use core::convert::TryFrom;
|
||||
use core::time::Duration;
|
||||
|
||||
pub use chip::*;
|
||||
use config::TransmitConfig;
|
||||
use esp_idf_sys::*;
|
||||
|
||||
use crate::gpio::OutputPin;
|
||||
use crate::peripheral::{Peripheral, PeripheralRef};
|
||||
use crate::units::Hertz;
|
||||
|
||||
use config::Config;
|
||||
|
||||
pub use chip::*;
|
||||
|
||||
/// A `Low` (0) or `High` (1) state for a pin.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum PinState {
|
||||
@ -217,21 +222,14 @@ pub mod config {
|
||||
pub duty_percent: DutyPercent,
|
||||
}
|
||||
|
||||
impl Default for CarrierConfig {
|
||||
/// Defaults from `<https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/rmt.h#L101>`
|
||||
fn default() -> Self {
|
||||
impl CarrierConfig {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
frequency: 38.kHz().into(),
|
||||
carrier_level: PinState::High,
|
||||
duty_percent: DutyPercent(33),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CarrierConfig {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn frequency(mut self, hz: Hertz) -> Self {
|
||||
self.frequency = hz;
|
||||
@ -249,6 +247,13 @@ pub mod config {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CarrierConfig {
|
||||
/// Defaults from `<https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/rmt.h#L101>`
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration setting for looping a signal.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Loop {
|
||||
@ -259,7 +264,7 @@ pub mod config {
|
||||
}
|
||||
|
||||
/// Used when creating a [`Transmit`][crate::rmt::Transmit] instance.
|
||||
pub struct TransmitConfig {
|
||||
pub struct Config {
|
||||
pub clock_divider: u8,
|
||||
pub mem_block_num: u8,
|
||||
pub carrier: Option<CarrierConfig>,
|
||||
@ -276,9 +281,8 @@ pub mod config {
|
||||
pub aware_dfs: bool,
|
||||
}
|
||||
|
||||
impl Default for TransmitConfig {
|
||||
/// Defaults from `<https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/rmt.h#L101>`
|
||||
fn default() -> Self {
|
||||
impl Config {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
aware_dfs: false,
|
||||
mem_block_num: 1,
|
||||
@ -288,12 +292,6 @@ pub mod config {
|
||||
idle: Some(PinState::Low),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TransmitConfig {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn aware_dfs(mut self, enable: bool) -> Self {
|
||||
self.aware_dfs = enable;
|
||||
@ -325,26 +323,37 @@ pub mod config {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
/// Defaults from `<https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/rmt.h#L101>`
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The RMT transmitter.
|
||||
/// The RMT transmitter driver.
|
||||
///
|
||||
/// Use [`Transmit::start()`] or [`Transmit::start_blocking()`] to transmit pulses.
|
||||
/// Use [`RmtDriver::start()`] or [`RmtDriver::start_blocking()`] to transmit pulses.
|
||||
///
|
||||
/// See the [rmt module][crate::rmt] for more information.
|
||||
pub struct Transmit<P: OutputPin, C: HwChannel> {
|
||||
pin: P,
|
||||
channel: C,
|
||||
pub struct RmtDriver<'d, C: RmtChannel> {
|
||||
_channel: PeripheralRef<'d, C>,
|
||||
}
|
||||
|
||||
impl<P: OutputPin, C: HwChannel> Transmit<P, C> {
|
||||
impl<'d, C: RmtChannel> RmtDriver<'d, C> {
|
||||
/// Initialise the rmt module with the specified pin, channel and configuration.
|
||||
///
|
||||
/// To uninstall the driver and return ownership of the `channel` and `pin` use
|
||||
/// [`Transmit::release()`].
|
||||
/// To uninstall the driver just drop it.
|
||||
///
|
||||
/// Internally this calls `rmt_config()` and `rmt_driver_install()`.
|
||||
pub fn new(pin: P, channel: C, config: &TransmitConfig) -> Result<Self, EspError> {
|
||||
pub fn new(
|
||||
channel: impl Peripheral<P = C> + 'd,
|
||||
pin: impl Peripheral<P = impl OutputPin> + 'd,
|
||||
config: &Config,
|
||||
) -> Result<Self, EspError> {
|
||||
crate::into_ref!(channel, pin);
|
||||
|
||||
let mut flags = 0;
|
||||
if config.aware_dfs {
|
||||
flags |= RMT_CHANNEL_FLAGS_AWARE_DFS;
|
||||
@ -383,7 +392,7 @@ impl<P: OutputPin, C: HwChannel> Transmit<P, C> {
|
||||
esp!(rmt_driver_install(C::channel(), 0, 0))?;
|
||||
}
|
||||
|
||||
Ok(Self { pin, channel })
|
||||
Ok(Self { _channel: channel })
|
||||
}
|
||||
|
||||
/// Get speed of the channel’s internal counter clock.
|
||||
@ -553,15 +562,6 @@ impl<P: OutputPin, C: HwChannel> Transmit<P, C> {
|
||||
esp!(unsafe { rmt_tx_stop(C::channel()) })
|
||||
}
|
||||
|
||||
/// Stop transmitting and release the driver.
|
||||
///
|
||||
/// This will return the pin and channel.
|
||||
pub fn release(mut self) -> Result<(P, C), EspError> {
|
||||
self.stop()?;
|
||||
esp!(unsafe { rmt_driver_uninstall(C::channel()) })?;
|
||||
Ok((self.pin, self.channel))
|
||||
}
|
||||
|
||||
pub fn set_looping(&mut self, looping: config::Loop) -> Result<(), EspError> {
|
||||
esp!(unsafe { rmt_set_tx_loop_mode(C::channel(), looping != config::Loop::None) })?;
|
||||
|
||||
@ -580,6 +580,14 @@ impl<P: OutputPin, C: HwChannel> Transmit<P, C> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, C: RmtChannel> Drop for RmtDriver<'d, C> {
|
||||
/// Stop transmitting and release the driver.
|
||||
fn drop(&mut self) {
|
||||
self.stop().unwrap();
|
||||
esp!(unsafe { rmt_driver_uninstall(C::channel()) }).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Signal storage for [`Transmit`] in a format ready for the RMT driver.
|
||||
pub trait Signal {
|
||||
fn as_slice(&self) -> &[rmt_item32_t];
|
||||
@ -756,33 +764,18 @@ impl Signal for VariableLengthSignal {
|
||||
}
|
||||
|
||||
mod chip {
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use esp_idf_sys::*;
|
||||
|
||||
/// RMT peripheral channel.
|
||||
pub trait HwChannel {
|
||||
pub trait RmtChannel {
|
||||
fn channel() -> rmt_channel_t;
|
||||
}
|
||||
|
||||
macro_rules! impl_channel {
|
||||
($instance:ident: $channel:expr) => {
|
||||
pub struct $instance {
|
||||
_marker: PhantomData<rmt_channel_t>,
|
||||
}
|
||||
crate::impl_peripheral!($instance);
|
||||
|
||||
impl $instance {
|
||||
/// # Safety
|
||||
///
|
||||
/// It is safe to instantiate this channel exactly one time.
|
||||
pub unsafe fn new() -> Self {
|
||||
$instance {
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HwChannel for $instance {
|
||||
impl RmtChannel for $instance {
|
||||
fn channel() -> rmt_channel_t {
|
||||
$channel
|
||||
}
|
||||
@ -805,7 +798,7 @@ mod chip {
|
||||
#[cfg(any(esp32, esp32s3))]
|
||||
impl_channel!(CHANNEL7: rmt_channel_t_RMT_CHANNEL_7);
|
||||
|
||||
pub struct Peripheral {
|
||||
pub struct RMT {
|
||||
pub channel0: CHANNEL0,
|
||||
pub channel1: CHANNEL1,
|
||||
pub channel2: CHANNEL2,
|
||||
@ -820,7 +813,7 @@ mod chip {
|
||||
pub channel7: CHANNEL7,
|
||||
}
|
||||
|
||||
impl Peripheral {
|
||||
impl RMT {
|
||||
/// Creates a new instance of the RMT peripheral. Typically one wants
|
||||
/// to use the instance [`rmt`](crate::peripherals::Peripherals::rmt) from
|
||||
/// the device peripherals obtained via
|
||||
|
781
src/spi.rs
781
src/spi.rs
@ -21,13 +21,16 @@
|
||||
//! - Slave
|
||||
|
||||
use core::cmp::{max, min, Ordering};
|
||||
use core::marker::PhantomData;
|
||||
use core::ptr;
|
||||
|
||||
use embedded_hal::spi::blocking::{SpiBus, SpiBusFlush, SpiBusRead, SpiBusWrite, SpiDevice};
|
||||
|
||||
use esp_idf_sys::*;
|
||||
|
||||
use crate::delay::portMAX_DELAY;
|
||||
use crate::gpio::{self, InputPin, OutputPin};
|
||||
|
||||
use esp_idf_sys::*;
|
||||
use crate::peripheral::{Peripheral, PeripheralRef};
|
||||
|
||||
crate::embedded_hal_error!(
|
||||
SpiError,
|
||||
@ -39,15 +42,7 @@ pub trait Spi: Send {
|
||||
fn device() -> spi_host_device_t;
|
||||
}
|
||||
|
||||
// Limit to 64, as we sometimes allocate a buffer of size TRANS_LEN on the stack, so we have to keep it small
|
||||
// SOC_SPI_MAXIMUM_BUFFER_SIZE equals 64 or 72 (esp32s2) anyway
|
||||
const TRANS_LEN: usize = if SOC_SPI_MAXIMUM_BUFFER_SIZE < 64_u32 {
|
||||
SOC_SPI_MAXIMUM_BUFFER_SIZE as _
|
||||
} else {
|
||||
64_usize
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Dma {
|
||||
Disabled,
|
||||
Channel1(usize),
|
||||
@ -82,20 +77,6 @@ impl Dma {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pins used by the SPI interface
|
||||
pub struct Pins<
|
||||
SCLK: OutputPin,
|
||||
SDO: OutputPin,
|
||||
// default pins to allow type inference
|
||||
SDI: InputPin + OutputPin = crate::gpio::Gpio1<crate::gpio::Input>,
|
||||
CS: OutputPin = crate::gpio::Gpio2<crate::gpio::Output>,
|
||||
> {
|
||||
pub sclk: SCLK,
|
||||
pub sdo: SDO,
|
||||
pub sdi: Option<SDI>,
|
||||
pub cs: Option<CS>,
|
||||
}
|
||||
|
||||
/// SPI configuration
|
||||
pub mod config {
|
||||
use crate::spi::Dma;
|
||||
@ -187,73 +168,397 @@ pub mod config {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SpiBusMasterDriver<'d> {
|
||||
handle: spi_device_handle_t,
|
||||
trans_len: usize,
|
||||
_p: PhantomData<&'d ()>,
|
||||
}
|
||||
|
||||
impl<'d> SpiBusMasterDriver<'d> {
|
||||
fn polling_transmit(
|
||||
&mut self,
|
||||
read: *mut u8,
|
||||
write: *const u8,
|
||||
transaction_length: usize,
|
||||
rx_length: usize,
|
||||
) -> Result<(), SpiError> {
|
||||
polling_transmit(
|
||||
self.handle,
|
||||
read,
|
||||
write,
|
||||
transaction_length,
|
||||
rx_length,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
/// Empty transaction to de-assert CS.
|
||||
fn finish(&mut self) -> Result<(), SpiError> {
|
||||
polling_transmit(self.handle, ptr::null_mut(), ptr::null(), 0, 0, false)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> embedded_hal::spi::ErrorType for SpiBusMasterDriver<'d> {
|
||||
type Error = SpiError;
|
||||
}
|
||||
|
||||
impl<'d> SpiBusFlush for SpiBusMasterDriver<'d> {
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
// Since we use polling transactions, flushing isn't required.
|
||||
// In future, when DMA is available spi_device_get_trans_result
|
||||
// will be called here.
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> SpiBusRead for SpiBusMasterDriver<'d> {
|
||||
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
for chunk in words.chunks_mut(self.trans_len) {
|
||||
self.polling_transmit(chunk.as_mut_ptr(), ptr::null(), chunk.len(), chunk.len())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> SpiBusWrite for SpiBusMasterDriver<'d> {
|
||||
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
for chunk in words.chunks(self.trans_len) {
|
||||
self.polling_transmit(ptr::null_mut(), chunk.as_ptr(), chunk.len(), 0)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> SpiBus for SpiBusMasterDriver<'d> {
|
||||
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||
let common_length = min(read.len(), write.len());
|
||||
let common_read = read[0..common_length].chunks_mut(self.trans_len);
|
||||
let common_write = write[0..common_length].chunks(self.trans_len);
|
||||
|
||||
for (read_chunk, write_chunk) in common_read.zip(common_write) {
|
||||
self.polling_transmit(
|
||||
read_chunk.as_mut_ptr(),
|
||||
write_chunk.as_ptr(),
|
||||
max(read_chunk.len(), write_chunk.len()),
|
||||
read_chunk.len(),
|
||||
)?;
|
||||
}
|
||||
|
||||
match read.len().cmp(&write.len()) {
|
||||
Ordering::Equal => { /* Nothing left to do */ }
|
||||
Ordering::Greater => {
|
||||
// Read remainder
|
||||
self.read(&mut read[write.len()..])?;
|
||||
}
|
||||
Ordering::Less => {
|
||||
// Write remainder
|
||||
self.write(&write[read.len()..])?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
for chunk in words.chunks_mut(self.trans_len) {
|
||||
let ptr = chunk.as_mut_ptr();
|
||||
let len = chunk.len();
|
||||
self.polling_transmit(ptr, ptr, len, len)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Master SPI abstraction
|
||||
pub struct Master<
|
||||
SPI: Spi,
|
||||
SCLK: OutputPin,
|
||||
SDO: OutputPin,
|
||||
// default pins to allow type inference
|
||||
SDI: InputPin + OutputPin = crate::gpio::Gpio1<crate::gpio::Input>,
|
||||
CS: OutputPin = crate::gpio::Gpio2<crate::gpio::Output>,
|
||||
> {
|
||||
spi: SPI,
|
||||
pins: Pins<SCLK, SDO, SDI, CS>,
|
||||
pub struct SpiMasterDriver<'d, SPI: Spi> {
|
||||
_spi: PeripheralRef<'d, SPI>,
|
||||
device: spi_device_handle_t,
|
||||
max_transfer_size: usize,
|
||||
}
|
||||
|
||||
unsafe impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
||||
Send for Master<SPI, SCLK, SDO, SDI, CS>
|
||||
{
|
||||
}
|
||||
|
||||
impl<CS: OutputPin>
|
||||
Master<SPI1, gpio::Gpio6<gpio::Output>, gpio::Gpio7<gpio::Output>, gpio::Gpio8<gpio::Input>, CS>
|
||||
{
|
||||
impl<'d> SpiMasterDriver<'d, SPI1> {
|
||||
/// Create new instance of SPI controller for SPI1
|
||||
///
|
||||
/// SPI1 can only use fixed pin for SCLK, SDO and SDI as they are shared with SPI0.
|
||||
pub fn new(
|
||||
spi: SPI1,
|
||||
pins: Pins<
|
||||
gpio::Gpio6<gpio::Output>,
|
||||
gpio::Gpio7<gpio::Output>,
|
||||
gpio::Gpio8<gpio::Input>,
|
||||
CS,
|
||||
>,
|
||||
spi: impl Peripheral<P = SPI1> + 'd,
|
||||
sclk: impl Peripheral<P = gpio::Gpio6> + 'd,
|
||||
sdo: impl Peripheral<P = gpio::Gpio7> + 'd,
|
||||
sdi: Option<impl Peripheral<P = gpio::Gpio8> + 'd>,
|
||||
cs: Option<impl Peripheral<P = impl OutputPin> + 'd>,
|
||||
config: config::Config,
|
||||
) -> Result<Self, EspError> {
|
||||
Master::new_internal(spi, pins, config)
|
||||
SpiMasterDriver::new_internal(spi, sclk, sdo, sdi, cs, config)
|
||||
}
|
||||
}
|
||||
|
||||
impl<SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
||||
Master<SPI2, SCLK, SDO, SDI, CS>
|
||||
{
|
||||
impl<'d> SpiMasterDriver<'d, SPI2> {
|
||||
/// Create new instance of SPI controller for SPI2
|
||||
pub fn new(
|
||||
spi: SPI2,
|
||||
pins: Pins<SCLK, SDO, SDI, CS>,
|
||||
spi: impl Peripheral<P = SPI2> + 'd,
|
||||
sclk: impl Peripheral<P = impl OutputPin> + 'd,
|
||||
sdo: impl Peripheral<P = impl OutputPin> + 'd,
|
||||
sdi: Option<impl Peripheral<P = impl InputPin + OutputPin> + 'd>,
|
||||
cs: Option<impl Peripheral<P = impl OutputPin> + 'd>,
|
||||
config: config::Config,
|
||||
) -> Result<Self, EspError> {
|
||||
Master::new_internal(spi, pins, config)
|
||||
SpiMasterDriver::new_internal(spi, sclk, sdo, sdi, cs, config)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(esp32c3))]
|
||||
impl<SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
||||
Master<SPI3, SCLK, SDO, SDI, CS>
|
||||
{
|
||||
impl<'d> SpiMasterDriver<'d, SPI3> {
|
||||
/// Create new instance of SPI controller for SPI3
|
||||
pub fn new(
|
||||
spi: SPI3,
|
||||
pins: Pins<SCLK, SDO, SDI, CS>,
|
||||
spi: impl Peripheral<P = SPI3> + 'd,
|
||||
sclk: impl Peripheral<P = impl OutputPin> + 'd,
|
||||
sdo: impl Peripheral<P = impl OutputPin> + 'd,
|
||||
sdi: Option<impl Peripheral<P = impl InputPin + OutputPin> + 'd>,
|
||||
cs: Option<impl Peripheral<P = impl OutputPin> + 'd>,
|
||||
config: config::Config,
|
||||
) -> Result<Self, EspError> {
|
||||
Master::new_internal(spi, pins, config)
|
||||
SpiMasterDriver::new_internal(spi, sclk, sdo, sdi, cs, config)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, SPI: Spi> SpiMasterDriver<'d, SPI> {
|
||||
/// Internal implementation of new shared by all SPI controllers
|
||||
fn new_internal(
|
||||
spi: impl Peripheral<P = SPI> + 'd,
|
||||
sclk: impl Peripheral<P = impl OutputPin> + 'd,
|
||||
sdo: impl Peripheral<P = impl OutputPin> + 'd,
|
||||
sdi: Option<impl Peripheral<P = impl InputPin + OutputPin> + 'd>,
|
||||
cs: Option<impl Peripheral<P = impl OutputPin> + 'd>,
|
||||
config: config::Config,
|
||||
) -> Result<Self, EspError> {
|
||||
crate::into_ref!(spi, sclk, sdo);
|
||||
|
||||
let sdi = sdi.map(|sdi| sdi.into_ref());
|
||||
let cs = cs.map(|cs| cs.into_ref());
|
||||
|
||||
#[cfg(any(esp_idf_version = "4.4", esp_idf_version_major = "5"))]
|
||||
let bus_config = spi_bus_config_t {
|
||||
flags: SPICOMMON_BUSFLAG_MASTER,
|
||||
sclk_io_num: sclk.pin(),
|
||||
|
||||
data4_io_num: -1,
|
||||
data5_io_num: -1,
|
||||
data6_io_num: -1,
|
||||
data7_io_num: -1,
|
||||
__bindgen_anon_1: spi_bus_config_t__bindgen_ty_1 {
|
||||
mosi_io_num: sdo.pin(),
|
||||
//data0_io_num: -1,
|
||||
},
|
||||
__bindgen_anon_2: spi_bus_config_t__bindgen_ty_2 {
|
||||
miso_io_num: sdi.as_ref().map_or(-1, |p| p.pin()),
|
||||
//data1_io_num: -1,
|
||||
},
|
||||
__bindgen_anon_3: spi_bus_config_t__bindgen_ty_3 {
|
||||
quadwp_io_num: -1,
|
||||
//data2_io_num: -1,
|
||||
},
|
||||
__bindgen_anon_4: spi_bus_config_t__bindgen_ty_4 {
|
||||
quadhd_io_num: -1,
|
||||
//data3_io_num: -1,
|
||||
},
|
||||
max_transfer_sz: config.dma.max_transfer_size() as i32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
#[cfg(not(any(esp_idf_version = "4.4", esp_idf_version_major = "5")))]
|
||||
let bus_config = spi_bus_config_t {
|
||||
flags: SPICOMMON_BUSFLAG_MASTER,
|
||||
sclk_io_num: sclk.pin(),
|
||||
|
||||
mosi_io_num: sdo.pin(),
|
||||
miso_io_num: sdi.as_ref().map_or(-1, |p| p.pin()),
|
||||
quadwp_io_num: -1,
|
||||
quadhd_io_num: -1,
|
||||
|
||||
max_transfer_sz: config.dma.max_transfer_size() as i32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
esp!(unsafe { spi_bus_initialize(SPI::device(), &bus_config, config.dma.into()) })?;
|
||||
|
||||
let device_config = spi_device_interface_config_t {
|
||||
spics_io_num: cs.as_ref().map_or(-1, |p| p.pin()),
|
||||
clock_speed_hz: config.baudrate.0 as i32,
|
||||
mode: (((config.data_mode.polarity == embedded_hal::spi::Polarity::IdleHigh) as u8)
|
||||
<< 1)
|
||||
| ((config.data_mode.phase == embedded_hal::spi::Phase::CaptureOnSecondTransition)
|
||||
as u8),
|
||||
queue_size: 64,
|
||||
flags: if config.write_only {
|
||||
SPI_DEVICE_NO_DUMMY
|
||||
} else {
|
||||
0_u32
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut device_handle: spi_device_handle_t = ptr::null_mut();
|
||||
|
||||
esp!(unsafe {
|
||||
spi_bus_add_device(SPI::device(), &device_config, &mut device_handle as *mut _)
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
_spi: spi,
|
||||
device: device_handle,
|
||||
max_transfer_size: config.dma.max_transfer_size(),
|
||||
})
|
||||
}
|
||||
|
||||
fn lock_bus(&mut self) -> Result<Lock, SpiError> {
|
||||
Lock::new(self.device).map_err(SpiError::other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, SPI: Spi> Drop for SpiMasterDriver<'d, SPI> {
|
||||
fn drop(&mut self) {
|
||||
esp!(unsafe { spi_bus_remove_device(self.device) }).unwrap();
|
||||
esp!(unsafe { spi_bus_free(SPI::device()) }).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'d, SPI: Spi> Send for SpiMasterDriver<'d, SPI> {}
|
||||
|
||||
impl<'d, SPI: Spi> embedded_hal::spi::ErrorType for SpiMasterDriver<'d, SPI> {
|
||||
type Error = SpiError;
|
||||
}
|
||||
|
||||
impl<'d, SPI: Spi> SpiDevice for SpiMasterDriver<'d, SPI> {
|
||||
type Bus = SpiBusMasterDriver<'d>;
|
||||
|
||||
fn transaction<R>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut Self::Bus) -> Result<R, <Self::Bus as embedded_hal::spi::ErrorType>::Error>,
|
||||
) -> Result<R, Self::Error> {
|
||||
let mut bus = SpiBusMasterDriver {
|
||||
handle: self.device,
|
||||
trans_len: self.max_transfer_size,
|
||||
_p: PhantomData,
|
||||
};
|
||||
|
||||
let lock = self.lock_bus()?;
|
||||
let trans_result = f(&mut bus);
|
||||
|
||||
let finish_result = bus.finish();
|
||||
|
||||
// Flush whatever is pending.
|
||||
// Note that this is done even when an error is returned from the transaction.
|
||||
let flush_result = bus.flush();
|
||||
|
||||
core::mem::drop(lock);
|
||||
|
||||
let result = trans_result?;
|
||||
finish_result?;
|
||||
flush_result?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, SPI: Spi> embedded_hal_0_2::blocking::spi::Transfer<u8> for SpiMasterDriver<'d, SPI> {
|
||||
type Error = SpiError;
|
||||
|
||||
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
|
||||
let _lock = self.lock_bus();
|
||||
let mut chunks = words.chunks_mut(self.max_transfer_size).peekable();
|
||||
while let Some(chunk) = chunks.next() {
|
||||
let ptr = chunk.as_mut_ptr();
|
||||
let len = chunk.len();
|
||||
polling_transmit(self.device, ptr, ptr, len, len, chunks.peek().is_some())?;
|
||||
}
|
||||
|
||||
Ok(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, SPI: Spi> embedded_hal_0_2::blocking::spi::Write<u8> for SpiMasterDriver<'d, SPI> {
|
||||
type Error = SpiError;
|
||||
|
||||
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
let _lock = self.lock_bus();
|
||||
let mut chunks = words.chunks(self.max_transfer_size).peekable();
|
||||
while let Some(chunk) = chunks.next() {
|
||||
polling_transmit(
|
||||
self.device,
|
||||
ptr::null_mut(),
|
||||
chunk.as_ptr(),
|
||||
chunk.len(),
|
||||
0,
|
||||
chunks.peek().is_some(),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, SPI: Spi> embedded_hal_0_2::blocking::spi::WriteIter<u8> for SpiMasterDriver<'d, SPI> {
|
||||
type Error = SpiError;
|
||||
|
||||
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
|
||||
where
|
||||
WI: IntoIterator<Item = u8>,
|
||||
{
|
||||
let mut words = words.into_iter();
|
||||
|
||||
let mut buf = [0_u8; TRANS_LEN];
|
||||
|
||||
self.transaction(|bus| {
|
||||
loop {
|
||||
let mut offset = 0_usize;
|
||||
|
||||
while offset < buf.len() {
|
||||
if let Some(word) = words.next() {
|
||||
buf[offset] = word;
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if offset == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
bus.write(&buf[..offset])?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, SPI: Spi> embedded_hal_0_2::blocking::spi::Transactional<u8> for SpiMasterDriver<'d, SPI> {
|
||||
type Error = SpiError;
|
||||
|
||||
fn exec<'a>(
|
||||
&mut self,
|
||||
operations: &mut [embedded_hal_0_2::blocking::spi::Operation<'a, u8>],
|
||||
) -> Result<(), Self::Error> {
|
||||
self.transaction(|bus| {
|
||||
for operation in operations {
|
||||
match operation {
|
||||
embedded_hal_0_2::blocking::spi::Operation::Write(write) => bus.write(write),
|
||||
embedded_hal_0_2::blocking::spi::Operation::Transfer(words) => {
|
||||
bus.transfer_in_place(words)
|
||||
}
|
||||
}?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Limit to 64, as we sometimes allocate a buffer of size TRANS_LEN on the stack, so we have to keep it small
|
||||
// SOC_SPI_MAXIMUM_BUFFER_SIZE equals 64 or 72 (esp32s2) anyway
|
||||
const TRANS_LEN: usize = if SOC_SPI_MAXIMUM_BUFFER_SIZE < 64_u32 {
|
||||
SOC_SPI_MAXIMUM_BUFFER_SIZE as _
|
||||
} else {
|
||||
64_usize
|
||||
};
|
||||
|
||||
struct Lock(spi_device_handle_t);
|
||||
|
||||
impl Lock {
|
||||
@ -272,11 +577,6 @@ impl Drop for Lock {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MasterBus {
|
||||
handle: spi_device_handle_t,
|
||||
trans_len: usize,
|
||||
}
|
||||
|
||||
// These parameters assume full duplex.
|
||||
fn polling_transmit(
|
||||
handle: spi_device_handle_t,
|
||||
@ -315,351 +615,9 @@ fn polling_transmit(
|
||||
.map_err(SpiError::other)
|
||||
}
|
||||
|
||||
impl MasterBus {
|
||||
fn polling_transmit(
|
||||
&mut self,
|
||||
read: *mut u8,
|
||||
write: *const u8,
|
||||
transaction_length: usize,
|
||||
rx_length: usize,
|
||||
) -> Result<(), SpiError> {
|
||||
polling_transmit(
|
||||
self.handle,
|
||||
read,
|
||||
write,
|
||||
transaction_length,
|
||||
rx_length,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
/// Empty transaction to de-assert CS.
|
||||
fn finish(&mut self) -> Result<(), SpiError> {
|
||||
polling_transmit(self.handle, ptr::null_mut(), ptr::null(), 0, 0, false)
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::spi::ErrorType for MasterBus {
|
||||
type Error = SpiError;
|
||||
}
|
||||
|
||||
impl SpiBusFlush for MasterBus {
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
// Since we use polling transactions, flushing isn't required.
|
||||
// In future, when DMA is available spi_device_get_trans_result
|
||||
// will be called here.
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SpiBusRead for MasterBus {
|
||||
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
for chunk in words.chunks_mut(self.trans_len) {
|
||||
self.polling_transmit(chunk.as_mut_ptr(), ptr::null(), chunk.len(), chunk.len())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SpiBusWrite for MasterBus {
|
||||
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
for chunk in words.chunks(self.trans_len) {
|
||||
self.polling_transmit(ptr::null_mut(), chunk.as_ptr(), chunk.len(), 0)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SpiBus for MasterBus {
|
||||
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||
let common_length = min(read.len(), write.len());
|
||||
let common_read = read[0..common_length].chunks_mut(self.trans_len);
|
||||
let common_write = write[0..common_length].chunks(self.trans_len);
|
||||
|
||||
for (read_chunk, write_chunk) in common_read.zip(common_write) {
|
||||
self.polling_transmit(
|
||||
read_chunk.as_mut_ptr(),
|
||||
write_chunk.as_ptr(),
|
||||
max(read_chunk.len(), write_chunk.len()),
|
||||
read_chunk.len(),
|
||||
)?;
|
||||
}
|
||||
|
||||
match read.len().cmp(&write.len()) {
|
||||
Ordering::Equal => { /* Nothing left to do */ }
|
||||
Ordering::Greater => {
|
||||
// Read remainder
|
||||
self.read(&mut read[write.len()..])?;
|
||||
}
|
||||
Ordering::Less => {
|
||||
// Write remainder
|
||||
self.write(&write[read.len()..])?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
for chunk in words.chunks_mut(self.trans_len) {
|
||||
let ptr = chunk.as_mut_ptr();
|
||||
let len = chunk.len();
|
||||
self.polling_transmit(ptr, ptr, len, len)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
||||
Master<SPI, SCLK, SDO, SDI, CS>
|
||||
{
|
||||
/// Internal implementation of new shared by all SPI controllers
|
||||
fn new_internal(
|
||||
spi: SPI,
|
||||
pins: Pins<SCLK, SDO, SDI, CS>,
|
||||
config: config::Config,
|
||||
) -> Result<Self, EspError> {
|
||||
#[cfg(any(esp_idf_version = "4.4", esp_idf_version_major = "5"))]
|
||||
let bus_config = spi_bus_config_t {
|
||||
flags: SPICOMMON_BUSFLAG_MASTER,
|
||||
sclk_io_num: pins.sclk.pin(),
|
||||
|
||||
data4_io_num: -1,
|
||||
data5_io_num: -1,
|
||||
data6_io_num: -1,
|
||||
data7_io_num: -1,
|
||||
__bindgen_anon_1: spi_bus_config_t__bindgen_ty_1 {
|
||||
mosi_io_num: pins.sdo.pin(),
|
||||
//data0_io_num: -1,
|
||||
},
|
||||
__bindgen_anon_2: spi_bus_config_t__bindgen_ty_2 {
|
||||
miso_io_num: pins.sdi.as_ref().map_or(-1, |p| p.pin()),
|
||||
//data1_io_num: -1,
|
||||
},
|
||||
__bindgen_anon_3: spi_bus_config_t__bindgen_ty_3 {
|
||||
quadwp_io_num: -1,
|
||||
//data2_io_num: -1,
|
||||
},
|
||||
__bindgen_anon_4: spi_bus_config_t__bindgen_ty_4 {
|
||||
quadhd_io_num: -1,
|
||||
//data3_io_num: -1,
|
||||
},
|
||||
max_transfer_sz: config.dma.max_transfer_size() as i32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
#[cfg(not(any(esp_idf_version = "4.4", esp_idf_version_major = "5")))]
|
||||
let bus_config = spi_bus_config_t {
|
||||
flags: SPICOMMON_BUSFLAG_MASTER,
|
||||
sclk_io_num: pins.sclk.pin(),
|
||||
|
||||
mosi_io_num: pins.sdo.pin(),
|
||||
miso_io_num: pins.sdi.as_ref().map_or(-1, |p| p.pin()),
|
||||
quadwp_io_num: -1,
|
||||
quadhd_io_num: -1,
|
||||
|
||||
max_transfer_sz: config.dma.max_transfer_size() as i32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
esp!(unsafe { spi_bus_initialize(SPI::device(), &bus_config, config.dma.into()) })?;
|
||||
|
||||
let device_config = spi_device_interface_config_t {
|
||||
spics_io_num: pins.cs.as_ref().map_or(-1, |p| p.pin()),
|
||||
clock_speed_hz: config.baudrate.0 as i32,
|
||||
mode: (((config.data_mode.polarity == embedded_hal::spi::Polarity::IdleHigh) as u8)
|
||||
<< 1)
|
||||
| ((config.data_mode.phase == embedded_hal::spi::Phase::CaptureOnSecondTransition)
|
||||
as u8),
|
||||
queue_size: 64,
|
||||
flags: if config.write_only {
|
||||
SPI_DEVICE_NO_DUMMY
|
||||
} else {
|
||||
0_u32
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut device_handle: spi_device_handle_t = ptr::null_mut();
|
||||
|
||||
esp!(unsafe {
|
||||
spi_bus_add_device(SPI::device(), &device_config, &mut device_handle as *mut _)
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
spi,
|
||||
pins,
|
||||
device: device_handle,
|
||||
max_transfer_size: config.dma.max_transfer_size(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Release and return the raw interface to the underlying SPI peripheral
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn release(self) -> Result<(SPI, Pins<SCLK, SDO, SDI, CS>), EspError> {
|
||||
esp!(unsafe { spi_bus_remove_device(self.device) })?;
|
||||
esp!(unsafe { spi_bus_free(SPI::device()) })?;
|
||||
|
||||
Ok((self.spi, self.pins))
|
||||
}
|
||||
|
||||
fn lock_bus(&mut self) -> Result<Lock, SpiError> {
|
||||
Lock::new(self.device).map_err(SpiError::other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
||||
embedded_hal::spi::ErrorType for Master<SPI, SCLK, SDO, SDI, CS>
|
||||
{
|
||||
type Error = SpiError;
|
||||
}
|
||||
|
||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin> SpiDevice
|
||||
for Master<SPI, SCLK, SDO, SDI, CS>
|
||||
{
|
||||
type Bus = MasterBus;
|
||||
|
||||
fn transaction<R>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut Self::Bus) -> Result<R, <Self::Bus as embedded_hal::spi::ErrorType>::Error>,
|
||||
) -> Result<R, Self::Error> {
|
||||
let mut bus = MasterBus {
|
||||
handle: self.device,
|
||||
trans_len: self.max_transfer_size,
|
||||
};
|
||||
|
||||
let lock = self.lock_bus()?;
|
||||
let trans_result = f(&mut bus);
|
||||
|
||||
let finish_result = bus.finish();
|
||||
|
||||
// Flush whatever is pending.
|
||||
// Note that this is done even when an error is returned from the transaction.
|
||||
let flush_result = bus.flush();
|
||||
|
||||
core::mem::drop(lock);
|
||||
|
||||
let result = trans_result?;
|
||||
finish_result?;
|
||||
flush_result?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
||||
embedded_hal_0_2::blocking::spi::Transfer<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
||||
{
|
||||
type Error = SpiError;
|
||||
|
||||
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
|
||||
let _lock = self.lock_bus();
|
||||
let mut chunks = words.chunks_mut(self.max_transfer_size).peekable();
|
||||
while let Some(chunk) = chunks.next() {
|
||||
let ptr = chunk.as_mut_ptr();
|
||||
let len = chunk.len();
|
||||
polling_transmit(self.device, ptr, ptr, len, len, chunks.peek().is_some())?;
|
||||
}
|
||||
|
||||
Ok(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
||||
embedded_hal_0_2::blocking::spi::Write<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
||||
{
|
||||
type Error = SpiError;
|
||||
|
||||
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
let _lock = self.lock_bus();
|
||||
let mut chunks = words.chunks(self.max_transfer_size).peekable();
|
||||
while let Some(chunk) = chunks.next() {
|
||||
polling_transmit(
|
||||
self.device,
|
||||
ptr::null_mut(),
|
||||
chunk.as_ptr(),
|
||||
chunk.len(),
|
||||
0,
|
||||
chunks.peek().is_some(),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
||||
embedded_hal_0_2::blocking::spi::WriteIter<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
||||
{
|
||||
type Error = SpiError;
|
||||
|
||||
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
|
||||
where
|
||||
WI: IntoIterator<Item = u8>,
|
||||
{
|
||||
let mut words = words.into_iter();
|
||||
|
||||
let mut buf = [0_u8; TRANS_LEN];
|
||||
|
||||
self.transaction(|bus| {
|
||||
loop {
|
||||
let mut offset = 0_usize;
|
||||
|
||||
while offset < buf.len() {
|
||||
if let Some(word) = words.next() {
|
||||
buf[offset] = word;
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if offset == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
bus.write(&buf[..offset])?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
||||
embedded_hal_0_2::blocking::spi::Transactional<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
||||
{
|
||||
type Error = SpiError;
|
||||
|
||||
fn exec<'a>(
|
||||
&mut self,
|
||||
operations: &mut [embedded_hal_0_2::blocking::spi::Operation<'a, u8>],
|
||||
) -> Result<(), Self::Error> {
|
||||
self.transaction(|bus| {
|
||||
for operation in operations {
|
||||
match operation {
|
||||
embedded_hal_0_2::blocking::spi::Operation::Write(write) => bus.write(write),
|
||||
embedded_hal_0_2::blocking::spi::Operation::Transfer(words) => {
|
||||
bus.transfer_in_place(words)
|
||||
}
|
||||
}?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_spi {
|
||||
($spi:ident: $device:expr) => {
|
||||
pub struct $spi(::core::marker::PhantomData<*const ()>);
|
||||
|
||||
impl $spi {
|
||||
/// # Safety
|
||||
///
|
||||
/// Care should be taken not to instantiate this SPI instance, if it is already instantiated and used elsewhere
|
||||
pub unsafe fn new() -> Self {
|
||||
$spi(::core::marker::PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for $spi {}
|
||||
crate::impl_peripheral!($spi);
|
||||
|
||||
impl Spi for $spi {
|
||||
#[inline(always)]
|
||||
@ -672,6 +630,5 @@ macro_rules! impl_spi {
|
||||
|
||||
impl_spi!(SPI1: spi_host_device_t_SPI1_HOST);
|
||||
impl_spi!(SPI2: spi_host_device_t_SPI2_HOST);
|
||||
|
||||
#[cfg(not(esp32c3))]
|
||||
impl_spi!(SPI3: spi_host_device_t_SPI3_HOST);
|
||||
|
@ -16,21 +16,19 @@
|
||||
//! let peripherals = Peripherals::take().unwrap();
|
||||
//! let pins = peripherals.pins;
|
||||
//!
|
||||
//! let config = serial::config::Config::default().baudrate(Hertz(115_200));
|
||||
//! let config = uart::config::Config::default().baudrate(Hertz(115_200));
|
||||
//!
|
||||
//! let mut serial: serial::Serial<serial::UART1, _, _> = serial::Serial::new(
|
||||
//! let mut uart: uart::UartDriver = uart::UartDriver::new(
|
||||
//! peripherals.uart1,
|
||||
//! serial::Pins {
|
||||
//! tx: pins.gpio1,
|
||||
//! rx: pins.gpio3,
|
||||
//! cts: None,
|
||||
//! rts: None,
|
||||
//! },
|
||||
//! config
|
||||
//! pins.gpio1,
|
||||
//! pins.gpio3,
|
||||
//! None,
|
||||
//! None,
|
||||
//! &config
|
||||
//! ).unwrap();
|
||||
//!
|
||||
//! for i in 0..10 {
|
||||
//! writeln!(serial, "{:}", format!("count {:}", i)).unwrap();
|
||||
//! writeln!(uart, "{:}", format!("count {:}", i)).unwrap();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
@ -49,17 +47,9 @@ use crate::units::*;
|
||||
|
||||
use esp_idf_sys::*;
|
||||
|
||||
const UART_FIFO_SIZE: i32 = 128;
|
||||
use crate::peripheral::{Peripheral, PeripheralRef};
|
||||
|
||||
// /// Interrupt event
|
||||
// pub enum Event {
|
||||
// /// New data has been received
|
||||
// Rxne,
|
||||
// /// New data can be sent
|
||||
// Txe,
|
||||
// /// Idle line state detected
|
||||
// Idle,
|
||||
// }
|
||||
const UART_FIFO_SIZE: i32 = 128;
|
||||
|
||||
/// UART configuration
|
||||
pub mod config {
|
||||
@ -67,7 +57,7 @@ pub mod config {
|
||||
use esp_idf_sys::*;
|
||||
|
||||
/// Number of data bits
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum DataBits {
|
||||
DataBits5,
|
||||
DataBits6,
|
||||
@ -100,7 +90,7 @@ pub mod config {
|
||||
}
|
||||
|
||||
/// Number of data bits
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum FlowControl {
|
||||
None,
|
||||
RTS,
|
||||
@ -136,7 +126,7 @@ pub mod config {
|
||||
}
|
||||
|
||||
/// Parity check
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum Parity {
|
||||
ParityNone,
|
||||
ParityEven,
|
||||
@ -280,22 +270,6 @@ pub mod config {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pins used by the UART interface
|
||||
///
|
||||
/// Note that any two pins may be used
|
||||
pub struct Pins<
|
||||
TX: OutputPin,
|
||||
RX: InputPin,
|
||||
// default pins to allow type inference
|
||||
CTS: InputPin = crate::gpio::Gpio1<crate::gpio::Input>,
|
||||
RTS: OutputPin = crate::gpio::Gpio2<crate::gpio::Output>,
|
||||
> {
|
||||
pub tx: TX,
|
||||
pub rx: RX,
|
||||
pub cts: Option<CTS>,
|
||||
pub rts: Option<RTS>,
|
||||
}
|
||||
|
||||
pub trait Uart {
|
||||
fn port() -> uart_port_t;
|
||||
}
|
||||
@ -308,39 +282,37 @@ crate::embedded_hal_error!(
|
||||
|
||||
/// Serial abstraction
|
||||
///
|
||||
pub struct Serial<
|
||||
UART: Uart,
|
||||
TX: OutputPin,
|
||||
RX: InputPin,
|
||||
// default pins to allow type inference
|
||||
CTS: InputPin = crate::gpio::Gpio1<crate::gpio::Input>,
|
||||
RTS: OutputPin = crate::gpio::Gpio2<crate::gpio::Output>,
|
||||
> {
|
||||
uart: UART,
|
||||
pins: Pins<TX, RX, CTS, RTS>,
|
||||
rx: Rx<UART>,
|
||||
tx: Tx<UART>,
|
||||
pub struct UartDriver<'d, UART: Uart> {
|
||||
_uart: PeripheralRef<'d, UART>,
|
||||
rx: UartRxDriver<'d, UART>,
|
||||
tx: UartTxDriver<'d, UART>,
|
||||
}
|
||||
|
||||
/// Serial receiver
|
||||
pub struct Rx<UART: Uart> {
|
||||
_uart: PhantomData<UART>,
|
||||
pub struct UartRxDriver<'d, UART: Uart> {
|
||||
_uart: PhantomData<&'d UART>,
|
||||
}
|
||||
|
||||
/// Serial transmitter
|
||||
pub struct Tx<UART: Uart> {
|
||||
_uart: PhantomData<UART>,
|
||||
pub struct UartTxDriver<'d, UART: Uart> {
|
||||
_uart: PhantomData<&'d UART>,
|
||||
}
|
||||
|
||||
impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin>
|
||||
Serial<UART, TX, RX, CTS, RTS>
|
||||
{
|
||||
impl<'d, UART: Uart> UartDriver<'d, UART> {
|
||||
/// Create a new serial driver
|
||||
pub fn new(
|
||||
uart: UART,
|
||||
pins: Pins<TX, RX, CTS, RTS>,
|
||||
config: config::Config,
|
||||
uart: impl Peripheral<P = UART> + 'd,
|
||||
tx: impl Peripheral<P = impl OutputPin> + 'd,
|
||||
rx: impl Peripheral<P = impl InputPin> + 'd,
|
||||
cts: Option<impl Peripheral<P = impl InputPin> + 'd>,
|
||||
rts: Option<impl Peripheral<P = impl OutputPin> + 'd>,
|
||||
config: &config::Config,
|
||||
) -> Result<Self, EspError> {
|
||||
crate::into_ref!(uart, tx, rx);
|
||||
|
||||
let cts = cts.map(|cts| cts.into_ref());
|
||||
let rts = rts.map(|rts| rts.into_ref());
|
||||
|
||||
let uart_config = uart_config_t {
|
||||
baud_rate: config.baudrate.0 as i32,
|
||||
data_bits: config.data_bits.into(),
|
||||
@ -356,10 +328,10 @@ impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin>
|
||||
esp!(unsafe {
|
||||
uart_set_pin(
|
||||
UART::port(),
|
||||
pins.tx.pin(),
|
||||
pins.rx.pin(),
|
||||
pins.rts.as_ref().map_or(-1, |p| p.pin()),
|
||||
pins.cts.as_ref().map_or(-1, |p| p.pin()),
|
||||
tx.pin(),
|
||||
rx.pin(),
|
||||
rts.as_ref().map_or(-1, |p| p.pin()),
|
||||
cts.as_ref().map_or(-1, |p| p.pin()),
|
||||
)
|
||||
})?;
|
||||
|
||||
@ -375,10 +347,9 @@ impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin>
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
uart,
|
||||
pins,
|
||||
rx: Rx { _uart: PhantomData },
|
||||
tx: Tx { _uart: PhantomData },
|
||||
_uart: uart,
|
||||
rx: UartRxDriver { _uart: PhantomData },
|
||||
tx: UartTxDriver { _uart: PhantomData },
|
||||
})
|
||||
}
|
||||
|
||||
@ -490,19 +461,8 @@ impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin>
|
||||
// }
|
||||
|
||||
/// Split the serial driver in separate TX and RX drivers
|
||||
pub fn split(self) -> (Tx<UART>, Rx<UART>) {
|
||||
(self.tx, self.rx)
|
||||
}
|
||||
|
||||
/// Release the UART and GPIO resources
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn release(self) -> Result<(UART, Pins<TX, RX, CTS, RTS>), EspError> {
|
||||
esp!(unsafe { uart_driver_delete(UART::port()) })?;
|
||||
|
||||
// self.pins.tx.reset()?;
|
||||
// self.pins.rx.reset()?;
|
||||
|
||||
Ok((self.uart, self.pins))
|
||||
pub fn split(&mut self) -> (&mut UartTxDriver<'d, UART>, &mut UartRxDriver<'d, UART>) {
|
||||
(&mut self.tx, &mut self.rx)
|
||||
}
|
||||
|
||||
// pub fn reset_rx_fifo(&mut self) {
|
||||
@ -521,15 +481,17 @@ impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin>
|
||||
// }
|
||||
}
|
||||
|
||||
impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin>
|
||||
embedded_hal::serial::ErrorType for Serial<UART, TX, RX, CTS, RTS>
|
||||
{
|
||||
impl<'d, UART: Uart> Drop for UartDriver<'d, UART> {
|
||||
fn drop(&mut self) {
|
||||
esp!(unsafe { uart_driver_delete(UART::port()) }).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, UART: Uart> embedded_hal::serial::ErrorType for UartDriver<'d, UART> {
|
||||
type Error = SerialError;
|
||||
}
|
||||
|
||||
impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin>
|
||||
embedded_hal_0_2::serial::Read<u8> for Serial<UART, TX, RX, CTS, RTS>
|
||||
{
|
||||
impl<'d, UART: Uart> embedded_hal_0_2::serial::Read<u8> for UartDriver<'d, UART> {
|
||||
type Error = SerialError;
|
||||
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
@ -537,17 +499,13 @@ impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin>
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin>
|
||||
embedded_hal::serial::nb::Read<u8> for Serial<UART, TX, RX, CTS, RTS>
|
||||
{
|
||||
impl<'d, UART: Uart> embedded_hal::serial::nb::Read<u8> for UartDriver<'d, UART> {
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
self.rx.read()
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin>
|
||||
embedded_hal_0_2::serial::Write<u8> for Serial<UART, TX, RX, CTS, RTS>
|
||||
{
|
||||
impl<'d, UART: Uart> embedded_hal_0_2::serial::Write<u8> for UartDriver<'d, UART> {
|
||||
type Error = SerialError;
|
||||
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
@ -559,9 +517,7 @@ impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin>
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin>
|
||||
embedded_hal::serial::nb::Write<u8> for Serial<UART, TX, RX, CTS, RTS>
|
||||
{
|
||||
impl<'d, UART: Uart> embedded_hal::serial::nb::Write<u8> for UartDriver<'d, UART> {
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
self.tx.flush()
|
||||
}
|
||||
@ -571,9 +527,7 @@ impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin>
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin> core::fmt::Write
|
||||
for Serial<UART, TX, RX, CTS, RTS>
|
||||
{
|
||||
impl<'d, UART: Uart> core::fmt::Write for UartDriver<'d, UART> {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
use embedded_hal::serial::nb::Write;
|
||||
s.as_bytes()
|
||||
@ -583,11 +537,11 @@ impl<UART: Uart, TX: OutputPin, RX: InputPin, CTS: InputPin, RTS: OutputPin> cor
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Uart> embedded_hal::serial::ErrorType for Rx<UART> {
|
||||
impl<'d, UART: Uart> embedded_hal::serial::ErrorType for UartRxDriver<'d, UART> {
|
||||
type Error = SerialError;
|
||||
}
|
||||
|
||||
impl<UART: Uart> Rx<UART> {
|
||||
impl<'d, UART: Uart> UartRxDriver<'d, UART> {
|
||||
/// Get count of bytes in the receive FIFO
|
||||
pub fn count(&self) -> Result<u8, EspError> {
|
||||
let mut size = 0_u32;
|
||||
@ -638,7 +592,7 @@ impl<UART: Uart> Rx<UART> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Uart> embedded_hal_0_2::serial::Read<u8> for Rx<UART> {
|
||||
impl<'d, UART: Uart> embedded_hal_0_2::serial::Read<u8> for UartRxDriver<'d, UART> {
|
||||
type Error = SerialError;
|
||||
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
@ -656,7 +610,7 @@ impl<UART: Uart> embedded_hal_0_2::serial::Read<u8> for Rx<UART> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Uart> embedded_hal::serial::nb::Read<u8> for Rx<UART> {
|
||||
impl<'d, UART: Uart> embedded_hal::serial::nb::Read<u8> for UartRxDriver<'d, UART> {
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
let mut buf: u8 = 0;
|
||||
|
||||
@ -672,7 +626,7 @@ impl<UART: Uart> embedded_hal::serial::nb::Read<u8> for Rx<UART> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Uart> Tx<UART> {
|
||||
impl<'d, UART: Uart> UartTxDriver<'d, UART> {
|
||||
/// Write multiple bytes from a slice
|
||||
pub fn write_bytes(&mut self, bytes: &[u8]) -> nb::Result<usize, SerialError> {
|
||||
// `uart_write_bytes()` returns error (-1) or how many bytes were written
|
||||
@ -698,11 +652,11 @@ impl<UART: Uart> Tx<UART> {
|
||||
// }
|
||||
}
|
||||
|
||||
impl<UART: Uart> embedded_hal::serial::ErrorType for Tx<UART> {
|
||||
impl<'d, UART: Uart> embedded_hal::serial::ErrorType for UartTxDriver<'d, UART> {
|
||||
type Error = SerialError;
|
||||
}
|
||||
|
||||
impl<UART: Uart> embedded_hal_0_2::serial::Write<u8> for Tx<UART> {
|
||||
impl<'d, UART: Uart> embedded_hal_0_2::serial::Write<u8> for UartTxDriver<'d, UART> {
|
||||
type Error = SerialError;
|
||||
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
@ -726,7 +680,7 @@ impl<UART: Uart> embedded_hal_0_2::serial::Write<u8> for Tx<UART> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Uart> embedded_hal::serial::nb::Write<u8> for Tx<UART> {
|
||||
impl<'d, UART: Uart> embedded_hal::serial::nb::Write<u8> for UartTxDriver<'d, UART> {
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
match unsafe { uart_wait_tx_done(UART::port(), 0) } {
|
||||
ESP_OK => Ok(()),
|
||||
@ -748,9 +702,9 @@ impl<UART: Uart> embedded_hal::serial::nb::Write<u8> for Tx<UART> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Uart> core::fmt::Write for Tx<UART>
|
||||
impl<'d, UART: Uart> core::fmt::Write for UartTxDriver<'d, UART>
|
||||
where
|
||||
Tx<UART>: embedded_hal::serial::nb::Write<u8>,
|
||||
UartTxDriver<'d, UART>: embedded_hal::serial::nb::Write<u8>,
|
||||
{
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
use embedded_hal::serial::nb::Write;
|
||||
@ -763,16 +717,7 @@ where
|
||||
|
||||
macro_rules! impl_uart {
|
||||
($uart:ident: $port:expr) => {
|
||||
pub struct $uart;
|
||||
|
||||
impl $uart {
|
||||
/// # Safety
|
||||
///
|
||||
/// Care should be taken not to instantiate this UART instance, if it is already instantiated and used elsewhere
|
||||
pub unsafe fn new() -> Self {
|
||||
$uart {}
|
||||
}
|
||||
}
|
||||
crate::impl_peripheral!($uart);
|
||||
|
||||
impl Uart for $uart {
|
||||
fn port() -> uart_port_t {
|
||||
@ -784,6 +729,5 @@ macro_rules! impl_uart {
|
||||
|
||||
impl_uart!(UART0: 0);
|
||||
impl_uart!(UART1: 1);
|
||||
|
||||
#[cfg(esp32)]
|
||||
impl_uart!(UART2: 2);
|
138
src/ulp.rs
138
src/ulp.rs
@ -1,4 +1,4 @@
|
||||
use core::marker::PhantomData;
|
||||
use crate::peripheral::{Peripheral, PeripheralRef};
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum SleepTimer {
|
||||
@ -77,18 +77,21 @@ impl Word {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ULP(PhantomData<*const ()>);
|
||||
|
||||
unsafe impl Send for ULP {}
|
||||
|
||||
impl ULP {
|
||||
/// # Safety
|
||||
///
|
||||
/// Care should be taken not to instantiate the ULP instance, if it is already instantiated and used elsewhere
|
||||
pub unsafe fn new() -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
}
|
||||
#[cfg(any(
|
||||
all(not(esp_idf_version_major = "4"), esp_idf_ulp_coproc_enabled),
|
||||
all(esp_idf_version_major = "4", esp32, esp_idf_esp32_ulp_coproc_enabled),
|
||||
all(
|
||||
esp_idf_version_major = "4",
|
||||
esp32s2,
|
||||
esp_idf_esp32s2_ulp_coproc_enabled
|
||||
),
|
||||
all(
|
||||
esp_idf_version_major = "4",
|
||||
esp32s3,
|
||||
esp_idf_esp32s3_ulp_coproc_enabled
|
||||
)
|
||||
))]
|
||||
pub struct UlpDriver<'d>(PeripheralRef<'d, ULP>);
|
||||
|
||||
#[cfg(any(
|
||||
all(not(esp_idf_version_major = "4"), esp_idf_ulp_coproc_enabled),
|
||||
@ -104,41 +107,19 @@ impl ULP {
|
||||
esp_idf_esp32s3_ulp_coproc_enabled
|
||||
)
|
||||
))]
|
||||
impl ULP {
|
||||
const RTC_SLOW_MEM: u32 = 0x5000_0000_u32;
|
||||
impl<'d> UlpDriver<'d> {
|
||||
pub fn new(ulp: impl Peripheral<P = ULP> + 'd) -> Result<Self, esp_idf_sys::EspError> {
|
||||
crate::into_ref!(ulp);
|
||||
|
||||
pub const MEM_START_ULP: *mut core::ffi::c_void = 0_u32 as _;
|
||||
|
||||
pub const MEM_START: *mut core::ffi::c_void = Self::RTC_SLOW_MEM as _;
|
||||
|
||||
#[cfg(all(esp32, esp_idf_version_major = "4"))]
|
||||
pub const MEM_SIZE: usize = esp_idf_sys::CONFIG_ESP32_ULP_COPROC_RESERVE_MEM as _;
|
||||
|
||||
#[cfg(all(esp32s2, esp_idf_version_major = "4"))]
|
||||
pub const MEM_SIZE: usize = esp_idf_sys::CONFIG_ESP32S2_ULP_COPROC_RESERVE_MEM as _;
|
||||
|
||||
#[cfg(all(esp32s3, esp_idf_version_major = "4"))]
|
||||
pub const MEM_SIZE: usize = esp_idf_sys::CONFIG_ESP32S3_ULP_COPROC_RESERVE_MEM as _;
|
||||
|
||||
#[cfg(not(esp_idf_version_major = "4"))]
|
||||
pub const MEM_SIZE: usize = esp_idf_sys::CONFIG_ULP_COPROC_RESERVE_MEM as _;
|
||||
|
||||
#[cfg(esp32)]
|
||||
const TIMER_REG: *mut u32 = esp_idf_sys::RTC_CNTL_STATE0_REG as _;
|
||||
|
||||
#[cfg(any(esp32s2, esp32s3))]
|
||||
const TIMER_REG: *mut u32 = esp_idf_sys::RTC_CNTL_ULP_CP_TIMER_REG as _;
|
||||
|
||||
#[cfg(any(esp32, esp32s2, esp32s3))]
|
||||
const TIMER_EN_BIT: u32 =
|
||||
esp_idf_sys::RTC_CNTL_ULP_CP_SLP_TIMER_EN_V << esp_idf_sys::RTC_CNTL_ULP_CP_SLP_TIMER_EN_S;
|
||||
Ok(Self(ulp))
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) -> Result<(), esp_idf_sys::EspError> {
|
||||
unsafe {
|
||||
// disable ULP timer
|
||||
core::ptr::write_volatile(
|
||||
Self::TIMER_REG,
|
||||
core::ptr::read_volatile(Self::TIMER_REG) & !Self::TIMER_EN_BIT,
|
||||
ULP::TIMER_REG,
|
||||
core::ptr::read_volatile(ULP::TIMER_REG) & !ULP::TIMER_EN_BIT,
|
||||
);
|
||||
|
||||
// wait for at least 1 RTC_SLOW_CLK cycle
|
||||
@ -150,7 +131,7 @@ impl ULP {
|
||||
|
||||
pub fn is_started(&self) -> Result<bool, esp_idf_sys::EspError> {
|
||||
unsafe {
|
||||
let enabled = (core::ptr::read_volatile(Self::TIMER_REG) & Self::TIMER_EN_BIT) != 0;
|
||||
let enabled = (core::ptr::read_volatile(ULP::TIMER_REG) & ULP::TIMER_EN_BIT) != 0;
|
||||
|
||||
Ok(enabled)
|
||||
}
|
||||
@ -180,12 +161,11 @@ impl ULP {
|
||||
|
||||
fn check_boundaries<T>(ptr: *const T) -> Result<(), esp_idf_sys::EspError> {
|
||||
let ptr = ptr as *const u8;
|
||||
let mem_start = Self::MEM_START as *const u8;
|
||||
let mem_start = ULP::MEM_START as *const u8;
|
||||
|
||||
unsafe {
|
||||
if ptr < mem_start
|
||||
|| ptr.offset(core::mem::size_of::<T>() as _)
|
||||
> mem_start.offset(Self::MEM_SIZE as _)
|
||||
|| ptr.offset(core::mem::size_of::<T>() as _) > mem_start.offset(ULP::MEM_SIZE as _)
|
||||
{
|
||||
esp_idf_sys::esp!(esp_idf_sys::ESP_ERR_INVALID_SIZE)?;
|
||||
}
|
||||
@ -215,7 +195,7 @@ impl ULP {
|
||||
not(esp_idf_esp32s3_ulp_coproc_riscv)
|
||||
)
|
||||
))]
|
||||
impl ULP {
|
||||
impl<'d> UlpDriver<'d> {
|
||||
pub unsafe fn load_at_ulp_address(
|
||||
&mut self,
|
||||
address: *mut core::ffi::c_void,
|
||||
@ -240,7 +220,7 @@ impl ULP {
|
||||
}
|
||||
|
||||
pub unsafe fn load(&mut self, program: &[u8]) -> Result<(), esp_idf_sys::EspError> {
|
||||
self.load_at_ulp_address(Self::MEM_START_ULP, program)
|
||||
self.load_at_ulp_address(ULP::MEM_START_ULP, program)
|
||||
}
|
||||
|
||||
pub unsafe fn start(&mut self, address: *const u32) -> Result<(), esp_idf_sys::EspError> {
|
||||
@ -316,7 +296,7 @@ impl ULP {
|
||||
esp_idf_esp32s3_ulp_coproc_riscv
|
||||
)
|
||||
))]
|
||||
impl ULP {
|
||||
impl<'d> UlpDriver<'d> {
|
||||
pub unsafe fn load(&mut self, program: &[u8]) -> Result<(), esp_idf_sys::EspError> {
|
||||
esp_idf_sys::esp!(esp_idf_sys::ulp_riscv_load_binary(
|
||||
program.as_ptr(),
|
||||
@ -358,3 +338,63 @@ impl ULP {
|
||||
Ok(old_value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
all(not(esp_idf_version_major = "4"), esp_idf_ulp_coproc_enabled),
|
||||
all(esp_idf_version_major = "4", esp32, esp_idf_esp32_ulp_coproc_enabled),
|
||||
all(
|
||||
esp_idf_version_major = "4",
|
||||
esp32s2,
|
||||
esp_idf_esp32s2_ulp_coproc_enabled
|
||||
),
|
||||
all(
|
||||
esp_idf_version_major = "4",
|
||||
esp32s3,
|
||||
esp_idf_esp32s3_ulp_coproc_enabled
|
||||
)
|
||||
))]
|
||||
crate::impl_peripheral!(ULP);
|
||||
|
||||
#[cfg(any(
|
||||
all(not(esp_idf_version_major = "4"), esp_idf_ulp_coproc_enabled),
|
||||
all(esp_idf_version_major = "4", esp32, esp_idf_esp32_ulp_coproc_enabled),
|
||||
all(
|
||||
esp_idf_version_major = "4",
|
||||
esp32s2,
|
||||
esp_idf_esp32s2_ulp_coproc_enabled
|
||||
),
|
||||
all(
|
||||
esp_idf_version_major = "4",
|
||||
esp32s3,
|
||||
esp_idf_esp32s3_ulp_coproc_enabled
|
||||
)
|
||||
))]
|
||||
impl ULP {
|
||||
const RTC_SLOW_MEM: u32 = 0x5000_0000_u32;
|
||||
|
||||
pub const MEM_START_ULP: *mut core::ffi::c_void = 0_u32 as _;
|
||||
|
||||
pub const MEM_START: *mut core::ffi::c_void = Self::RTC_SLOW_MEM as _;
|
||||
|
||||
#[cfg(all(esp32, esp_idf_version_major = "4"))]
|
||||
pub const MEM_SIZE: usize = esp_idf_sys::CONFIG_ESP32_ULP_COPROC_RESERVE_MEM as _;
|
||||
|
||||
#[cfg(all(esp32s2, esp_idf_version_major = "4"))]
|
||||
pub const MEM_SIZE: usize = esp_idf_sys::CONFIG_ESP32S2_ULP_COPROC_RESERVE_MEM as _;
|
||||
|
||||
#[cfg(all(esp32s3, esp_idf_version_major = "4"))]
|
||||
pub const MEM_SIZE: usize = esp_idf_sys::CONFIG_ESP32S3_ULP_COPROC_RESERVE_MEM as _;
|
||||
|
||||
#[cfg(not(esp_idf_version_major = "4"))]
|
||||
pub const MEM_SIZE: usize = esp_idf_sys::CONFIG_ULP_COPROC_RESERVE_MEM as _;
|
||||
|
||||
#[cfg(esp32)]
|
||||
const TIMER_REG: *mut u32 = esp_idf_sys::RTC_CNTL_STATE0_REG as _;
|
||||
|
||||
#[cfg(any(esp32s2, esp32s3))]
|
||||
const TIMER_REG: *mut u32 = esp_idf_sys::RTC_CNTL_ULP_CP_TIMER_REG as _;
|
||||
|
||||
#[cfg(any(esp32, esp32s2, esp32s3))]
|
||||
const TIMER_EN_BIT: u32 =
|
||||
esp_idf_sys::RTC_CNTL_ULP_CP_SLP_TIMER_EN_V << esp_idf_sys::RTC_CNTL_ULP_CP_SLP_TIMER_EN_S;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user