mirror of
https://github.com/esp-rs/esp-idf-hal.git
synced 2025-10-02 14:44:51 +00:00
Migrate to e-hal 1.0.0-alpha.8 (#61)
* Migrate e-hal 1.0.0-alpha.8 * Implement SpiDevice trait * Fix bugs after real testing * Fix transfer
This commit is contained in:
parent
0c55a52692
commit
b134d3dbc6
@ -26,7 +26,7 @@ experimental = []
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
nb = "0.1.2"
|
nb = "0.1.2"
|
||||||
mutex-trait = { version = "0.2", optional = true, default-features = false }
|
mutex-trait = { version = "0.2", optional = true, default-features = false }
|
||||||
embedded-hal = "=1.0.0-alpha.7"
|
embedded-hal = "=1.0.0-alpha.8"
|
||||||
embedded-hal-0-2 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] }
|
embedded-hal-0-2 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] }
|
||||||
embedded-svc = { version = "0.20", optional = true, default-features = false }
|
embedded-svc = { version = "0.20", optional = true, default-features = false }
|
||||||
esp-idf-sys = { version = "0.31", optional = true, default-features = false, features = ["native"] }
|
esp-idf-sys = { version = "0.31", optional = true, default-features = false, features = ["native"] }
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use embedded_hal::spi::blocking::Transfer;
|
use embedded_hal::spi::blocking::SpiDevice;
|
||||||
|
|
||||||
use esp_idf_hal::peripherals::Peripherals;
|
use esp_idf_hal::peripherals::Peripherals;
|
||||||
use esp_idf_hal::prelude::*;
|
use esp_idf_hal::prelude::*;
|
||||||
|
27
src/adc.rs
27
src/adc.rs
@ -311,24 +311,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
|
||||||
impl<ADC, AN, PIN> embedded_hal::adc::nb::OneShot<AN, u16, PIN> for PoweredAdc<ADC>
|
|
||||||
where
|
|
||||||
ADC: Adc,
|
|
||||||
AN: Analog<ADC>,
|
|
||||||
PIN: embedded_hal::adc::nb::Channel<AN, ID = u8>,
|
|
||||||
{
|
|
||||||
type Error = EspError;
|
|
||||||
|
|
||||||
fn read(&mut self, pin: &mut PIN) -> nb::Result<u16, Self::Error> {
|
|
||||||
self.read(
|
|
||||||
ADC::unit(),
|
|
||||||
pin.channel() as adc_channel_t,
|
|
||||||
AN::attenuation(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(esp32, not(feature = "riscv-ulp-hal")))]
|
#[cfg(all(esp32, not(feature = "riscv-ulp-hal")))]
|
||||||
impl embedded_hal_0_2::adc::OneShot<ADC1, u16, hall::HallSensor> for PoweredAdc<ADC1> {
|
impl embedded_hal_0_2::adc::OneShot<ADC1, u16, hall::HallSensor> for PoweredAdc<ADC1> {
|
||||||
type Error = EspError;
|
type Error = EspError;
|
||||||
@ -338,15 +320,6 @@ impl embedded_hal_0_2::adc::OneShot<ADC1, u16, hall::HallSensor> for PoweredAdc<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(esp32, not(feature = "riscv-ulp-hal")))]
|
|
||||||
impl embedded_hal::adc::nb::OneShot<ADC1, u16, hall::HallSensor> for PoweredAdc<ADC1> {
|
|
||||||
type Error = EspError;
|
|
||||||
|
|
||||||
fn read(&mut self, _hall_sensor: &mut hall::HallSensor) -> nb::Result<u16, Self::Error> {
|
|
||||||
self.read_hall()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_adc {
|
macro_rules! impl_adc {
|
||||||
($adc:ident: $unit:expr) => {
|
($adc:ident: $unit:expr) => {
|
||||||
pub struct $adc(::core::marker::PhantomData<*const ()>);
|
pub struct $adc(::core::marker::PhantomData<*const ()>);
|
||||||
|
22
src/gpio.rs
22
src/gpio.rs
@ -736,17 +736,6 @@ macro_rules! impl_adc {
|
|||||||
$adc
|
$adc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<AN> embedded_hal::adc::nb::Channel<AN> for $pxi<AN>
|
|
||||||
where
|
|
||||||
AN: adc::Analog<adc::ADC1> + Send,
|
|
||||||
{
|
|
||||||
type ID = u8;
|
|
||||||
|
|
||||||
fn channel(&self) -> Self::ID {
|
|
||||||
$adc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
($pxi:ident: $pin:expr, ADC2: $adc:expr) => {
|
($pxi:ident: $pin:expr, ADC2: $adc:expr) => {
|
||||||
@ -815,17 +804,6 @@ macro_rules! impl_adc {
|
|||||||
$adc
|
$adc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<AN> embedded_hal::adc::nb::Channel<AN> for $pxi<AN>
|
|
||||||
where
|
|
||||||
AN: adc::Analog<adc::ADC2> + Send,
|
|
||||||
{
|
|
||||||
type ID = u8;
|
|
||||||
|
|
||||||
fn channel(&self) -> Self::ID {
|
|
||||||
$adc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
($pxi:ident: $pin:expr, NOADC: $adc:expr) => {};
|
($pxi:ident: $pin:expr, NOADC: $adc:expr) => {};
|
||||||
|
@ -22,11 +22,3 @@ impl embedded_hal_0_2::adc::Channel<adc::ADC1> for HallSensor {
|
|||||||
()
|
()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl embedded_hal::adc::nb::Channel<adc::ADC1> for HallSensor {
|
|
||||||
type ID = ();
|
|
||||||
|
|
||||||
fn channel(&self) -> Self::ID {
|
|
||||||
()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
243
src/spi.rs
243
src/spi.rs
@ -20,7 +20,7 @@
|
|||||||
//! - Multiple CS pins
|
//! - Multiple CS pins
|
||||||
//! - Slave
|
//! - Slave
|
||||||
|
|
||||||
use core::cmp::{max, min};
|
use core::cmp::{max, min, Ordering};
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
|
|
||||||
use crate::delay::portMAX_DELAY;
|
use crate::delay::portMAX_DELAY;
|
||||||
@ -216,6 +216,139 @@ impl Drop for Lock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct MasterBus {
|
||||||
|
handle: spi_device_handle_t,
|
||||||
|
trans_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MasterBus {
|
||||||
|
// These parameters assume full duplex.
|
||||||
|
fn polling_transmit(
|
||||||
|
&mut self,
|
||||||
|
read: *mut u8,
|
||||||
|
write: *const u8,
|
||||||
|
transaction_length: usize,
|
||||||
|
rx_length: usize,
|
||||||
|
) -> Result<(), SpiError> {
|
||||||
|
let flags = 0;
|
||||||
|
|
||||||
|
// This unfortunately means that this implementation is incorrect for esp-idf < 4.4.
|
||||||
|
// The CS pin should be kept active through transactions.
|
||||||
|
#[cfg(not(esp_idf_version = "4.3"))]
|
||||||
|
let flags = SPI_TRANS_CS_KEEP_ACTIVE;
|
||||||
|
|
||||||
|
let mut transaction = spi_transaction_t {
|
||||||
|
flags,
|
||||||
|
__bindgen_anon_1: spi_transaction_t__bindgen_ty_1 {
|
||||||
|
tx_buffer: write as *const _,
|
||||||
|
},
|
||||||
|
__bindgen_anon_2: spi_transaction_t__bindgen_ty_2 {
|
||||||
|
rx_buffer: read as *mut _,
|
||||||
|
},
|
||||||
|
length: (transaction_length * 8) as _,
|
||||||
|
rxlength: (rx_length * 8) as _,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
esp!(unsafe { spi_device_polling_transmit(self.handle, &mut transaction as *mut _) })
|
||||||
|
.map_err(SpiError::other)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Empty transaction to de-assert CS.
|
||||||
|
fn finish(&mut self) -> Result<(), SpiError> {
|
||||||
|
let flags = 0;
|
||||||
|
|
||||||
|
let mut transaction = spi_transaction_t {
|
||||||
|
flags,
|
||||||
|
__bindgen_anon_1: spi_transaction_t__bindgen_ty_1 {
|
||||||
|
tx_buffer: ptr::null(),
|
||||||
|
},
|
||||||
|
__bindgen_anon_2: spi_transaction_t__bindgen_ty_2 {
|
||||||
|
rx_buffer: ptr::null_mut(),
|
||||||
|
},
|
||||||
|
length: 0,
|
||||||
|
rxlength: 0,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
esp!(unsafe { spi_device_polling_transmit(self.handle, &mut transaction as *mut _) })
|
||||||
|
.map_err(SpiError::other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::spi::ErrorType for MasterBus {
|
||||||
|
type Error = SpiError;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::spi::blocking::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 embedded_hal::spi::blocking::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 embedded_hal::spi::blocking::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 embedded_hal::spi::blocking::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 => {
|
||||||
|
use embedded_hal::spi::blocking::SpiBusRead;
|
||||||
|
// Read remainder
|
||||||
|
self.read(&mut read[write.len()..])?;
|
||||||
|
}
|
||||||
|
Ordering::Less => {
|
||||||
|
use embedded_hal::spi::blocking::SpiBusWrite;
|
||||||
|
// 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>
|
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
||||||
Master<SPI, SCLK, SDO, SDI, CS>
|
Master<SPI, SCLK, SDO, SDI, CS>
|
||||||
{
|
{
|
||||||
@ -475,6 +608,39 @@ impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: O
|
|||||||
type Error = SpiError;
|
type Error = SpiError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
||||||
|
embedded_hal::spi::blocking::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: TRANS_LEN,
|
||||||
|
};
|
||||||
|
|
||||||
|
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.
|
||||||
|
use embedded_hal::spi::blocking::SpiBusFlush;
|
||||||
|
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>
|
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>
|
embedded_hal_0_2::blocking::spi::Transfer<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
||||||
{
|
{
|
||||||
@ -487,32 +653,6 @@ impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: O
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
|
||||||
embedded_hal::spi::blocking::TransferInplace<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
|
||||||
{
|
|
||||||
fn transfer_inplace(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.transfer_inplace_internal(words, true)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
|
||||||
embedded_hal::spi::blocking::Transfer<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
|
||||||
{
|
|
||||||
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.transfer_internal(read, write, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
|
||||||
embedded_hal::spi::blocking::Read<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
|
||||||
{
|
|
||||||
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.transfer_internal(words, &[], true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
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>
|
embedded_hal_0_2::blocking::spi::Write<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
||||||
{
|
{
|
||||||
@ -523,14 +663,6 @@ impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: O
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
|
||||||
embedded_hal::spi::blocking::Write<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
|
||||||
{
|
|
||||||
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.transfer_internal(&mut [], words, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
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>
|
embedded_hal_0_2::blocking::spi::WriteIter<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
||||||
{
|
{
|
||||||
@ -544,17 +676,6 @@ impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: O
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
|
||||||
embedded_hal::spi::blocking::WriteIter<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
|
||||||
{
|
|
||||||
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
|
|
||||||
where
|
|
||||||
WI: IntoIterator<Item = u8>,
|
|
||||||
{
|
|
||||||
self.write_iter_internal(words)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
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>
|
embedded_hal_0_2::blocking::spi::Transactional<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
||||||
{
|
{
|
||||||
@ -581,36 +702,6 @@ impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: O
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SPI: Spi, SCLK: OutputPin, SDO: OutputPin, SDI: InputPin + OutputPin, CS: OutputPin>
|
|
||||||
embedded_hal::spi::blocking::Transactional<u8> for Master<SPI, SCLK, SDO, SDI, CS>
|
|
||||||
{
|
|
||||||
fn exec<'a>(
|
|
||||||
&mut self,
|
|
||||||
operations: &mut [embedded_hal::spi::blocking::Operation<'a, u8>],
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
let _lock = self.lock_bus()?;
|
|
||||||
|
|
||||||
for operation in operations {
|
|
||||||
match operation {
|
|
||||||
embedded_hal::spi::blocking::Operation::Read(read) => {
|
|
||||||
self.transfer_internal(read, &[], false)?
|
|
||||||
}
|
|
||||||
embedded_hal::spi::blocking::Operation::Write(write) => {
|
|
||||||
self.transfer_internal(&mut [], write, false)?
|
|
||||||
}
|
|
||||||
embedded_hal::spi::blocking::Operation::Transfer(read, write) => {
|
|
||||||
self.transfer_internal(read, write, false)?
|
|
||||||
}
|
|
||||||
embedded_hal::spi::blocking::Operation::TransferInplace(words) => {
|
|
||||||
self.transfer_inplace_internal(words, false)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_spi {
|
macro_rules! impl_spi {
|
||||||
($spi:ident: $device:expr) => {
|
($spi:ident: $device:expr) => {
|
||||||
pub struct $spi(::core::marker::PhantomData<*const ()>);
|
pub struct $spi(::core::marker::PhantomData<*const ()>);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user