Skip to content

Commit 70733b3

Browse files
committed
WIP: sdio: add raw command/response functions
Adds `Sdio::read_command_raw` + `Sdio::write_response_raw` functions to read and write raw data on the `CMD` line. TODO: add SDIO mode implementations NOTE: these functions may be unecessary, the controller seems to handle command/response in hardware (at least in SPI mode).
1 parent 3a78dba commit 70733b3

File tree

2 files changed

+184
-0
lines changed

2 files changed

+184
-0
lines changed

esp-hal/src/sdio.rs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ pub use slchost::{AnySlchost, SlchostInfo, SlchostInstance};
4747
pub use state::State;
4848
pub use timing::Timing;
4949

50+
/// Represents the direction of a SPI transmission.
51+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
52+
pub enum SpiDirection {
53+
/// Indicates a read transmission (host-to-device).
54+
Read,
55+
/// Indicates a write transmission (device-to-host).
56+
Write,
57+
}
58+
5059
/// Represents the transmission modes for the SDIO peripheral.
5160
#[repr(u8)]
5261
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -374,6 +383,167 @@ impl<'d> Sdio<'d> {
374383

375384
Ok(())
376385
}
386+
387+
/// Waits for the CLK/SCLK line to be idle.
388+
pub(crate) fn wait_for_idle(&self) -> Result<(), Error> {
389+
match self.pins.mode() {
390+
Mode::Spi => {
391+
// Mode 0 + 1 idles low, mode 2 + 3 idles high
392+
let idle = matches!(self.config.spi_mode(), SpiMode::_2 | SpiMode::_3);
393+
394+
while self.pins.sclk().map(|p| p.is_set_high() == idle)? {
395+
core::hint::spin_loop();
396+
}
397+
398+
Ok(())
399+
}
400+
_ => {
401+
while self.pins.clk().map(|p| !p.is_set_high())? {
402+
core::hint::spin_loop();
403+
}
404+
405+
Ok(())
406+
}
407+
}
408+
}
409+
410+
/// Waits for a clock edge transition to indicate when to read/write data.
411+
// TODO: configure SPI modes
412+
pub(crate) fn wait_for_clock_edge(&self, direction: SpiDirection) -> Result<(), Error> {
413+
match self.pins.mode() {
414+
Mode::Spi => {
415+
let mode = self.config.spi_mode();
416+
417+
// Mode 0 + 1 idles low, mode 2 + 3 idles high
418+
let idle = matches!(mode, SpiMode::_2 | SpiMode::_3);
419+
420+
// Checks if we are waiting for a first transition from the IDLE state
421+
let first_transition = matches!(
422+
(mode, direction),
423+
(SpiMode::_0, SpiDirection::Read)
424+
| (SpiMode::_2, SpiDirection::Read)
425+
| (SpiMode::_1, SpiDirection::Write)
426+
| (SpiMode::_3, SpiDirection::Write)
427+
);
428+
429+
// Check if we need to wait for a second edge transition
430+
let second_transition = matches!(
431+
(mode, direction),
432+
(SpiMode::_0, SpiDirection::Write)
433+
| (SpiMode::_2, SpiDirection::Write)
434+
| (SpiMode::_1, SpiDirection::Read)
435+
| (SpiMode::_3, SpiDirection::Read)
436+
);
437+
438+
let edge = second_transition ^ idle;
439+
440+
// wait for first SCLK edge change from IDLE
441+
if first_transition {
442+
while self.pins.sclk().map(|p| p.is_set_high() == idle)? {
443+
core::hint::spin_loop();
444+
}
445+
}
446+
447+
// wait for second edge transition for the appropriate mode + direction combinations
448+
if second_transition {
449+
while self.pins.sclk().map(|p| p.is_set_high() == edge)? {
450+
core::hint::spin_loop();
451+
}
452+
}
453+
454+
Ok(())
455+
}
456+
_ => Err(Error::Unimplemented),
457+
}
458+
}
459+
460+
/// Waits for the CS pin to be asserted in SPI mode.
461+
///
462+
/// Returns an error if not in SPI mode.
463+
// TODO: add a timeout parameter
464+
pub fn wait_for_cs(&self) -> Result<(), Error> {
465+
match self.pins.mode() {
466+
Mode::Spi => {
467+
// block until CS pin is asserted (driven low)
468+
while self.pins.cs().map(|p| p.is_set_high())? {
469+
core::hint::spin_loop();
470+
}
471+
472+
Ok(())
473+
}
474+
_ => Err(Error::General),
475+
}
476+
}
477+
478+
/// Asserts the CS pin to indicate starting of data transmission.
479+
pub(crate) fn assert_cs(&self) -> Result<(), Error> {
480+
// assert the CS pin
481+
self.pins
482+
.cs()
483+
.map(Flex::new)
484+
.map(|mut p| p.set_level(false.into()))
485+
}
486+
487+
/// Deasserts the CS pin to indicate ending of data transmission.
488+
pub(crate) fn deassert_cs(&self) -> Result<(), Error> {
489+
// deassert the CS pin
490+
self.pins
491+
.cs()
492+
.map(Flex::new)
493+
.map(|mut p| p.set_level(true.into()))
494+
}
495+
496+
/// Reads the raw command bytes from the wire.
497+
pub fn read_command_raw(&mut self) -> Result<[u8; command::AnyCmd::LEN], Error> {
498+
self.wait_for_idle()?;
499+
500+
match self.pins.mode() {
501+
Mode::Spi => {
502+
self.wait_for_cs()?;
503+
504+
let mut buf = [0u8; command::AnyCmd::LEN];
505+
506+
for b in buf.iter_mut() {
507+
for i in 0..8 {
508+
self.wait_for_clock_edge(SpiDirection::Read)?;
509+
510+
let shift = 7 - i;
511+
*b |= self.pins.mosi().map(|p| (p.is_set_high() as u8) << shift)?;
512+
}
513+
}
514+
515+
Ok(buf)
516+
}
517+
_ => Err(Error::Unimplemented),
518+
}
519+
}
520+
521+
/// Writes the raw response bytes to the wire.
522+
pub fn write_response_raw(&mut self, res: &[u8]) -> Result<(), Error> {
523+
match self.pins.mode() {
524+
Mode::Spi => {
525+
self.assert_cs()?;
526+
527+
let mut miso = self.pins.miso().map(Flex::new)?;
528+
529+
for b in res.iter() {
530+
for i in 0..8 {
531+
self.wait_for_clock_edge(SpiDirection::Write)?;
532+
533+
let shift = 7 - i;
534+
let level = ((b >> shift) & 0x1) != 0;
535+
536+
miso.set_level(level.into());
537+
}
538+
}
539+
540+
self.deassert_cs()?;
541+
542+
Ok(())
543+
}
544+
_ => Err(Error::Unimplemented),
545+
}
546+
}
377547
}
378548

379549
/// Represents the error variants for SDIO peripherals.

esp-hal/src/sdio/command.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,17 @@ pub use crc::Crc;
1919
pub use flag::{RawFlag, RwFlag};
2020
pub use fn_number::FunctionNumber;
2121
pub use index::CommandIndex;
22+
23+
/// Represents any SDIO command structure.
24+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
25+
pub enum AnyCmd {
26+
/// Represents the `IO_RW_DIRECT` command.
27+
Cmd52(Cmd52),
28+
/// Represents the `IO_RW_EXTENDED` command.
29+
Cmd53(Cmd53),
30+
}
31+
32+
impl AnyCmd {
33+
/// Represents the byte length of the command.
34+
pub const LEN: usize = 6;
35+
}

0 commit comments

Comments
 (0)