use core::iter::once; use embassy_time::{Duration, Timer}; use esp_hal::gpio::{Flex, Level, Output}; async fn spi_delay() { Timer::after(Duration::from_micros(1)).await; } async fn spi_dummy_bit(sck: &mut Output<'_>) { sck.set_low(); spi_delay().await; sck.set_high(); spi_delay().await; } async fn spi_write_bit(bit: bool, mosi: &mut Flex<'_>, sck: &mut Output<'_>) { mosi.set_level(if bit { Level::High } else { Level::Low }); sck.set_low(); spi_delay().await; sck.set_high(); spi_delay().await; } async fn spi_read_bit(mosi: &mut Flex<'_>, sck: &mut Output<'_>) -> bool { sck.set_low(); spi_delay().await; sck.set_high(); spi_delay().await; mosi.is_high() } async fn spi_write_bits( bits: impl Iterator, mosi: &mut Flex<'_>, sck: &mut Output<'_>, ) { for bit in bits { spi_write_bit(bit, mosi, sck).await; } } async fn spi_write_word(is_param: bool, data: u8, mosi: &mut Flex<'_>, sck: &mut Output<'_>) { assert!(sck.is_set_high()); spi_write_bits( once(is_param).chain((0..8).map(|i| (data >> i) & 1 != 0).rev()), mosi, sck, ) .await; } pub async fn spi_write( command: u8, args: impl IntoIterator, mosi: &mut Flex<'_>, sck: &mut Output<'_>, cs: &mut Output<'_>, ) { cs.set_low(); spi_write_word(false, command, mosi, sck).await; for arg in args { spi_write_word(true, arg, mosi, sck).await; } cs.set_high(); } #[expect(unused)] pub async fn spi_read( command: u8, dummy_cycle: bool, mosi: &mut Flex<'_>, sck: &mut Output<'_>, cs: &mut Output<'_>, output_buffer: &mut [u8], ) { output_buffer.fill(0); cs.set_low(); spi_write_word(false, command, mosi, sck).await; mosi.set_output_enable(false); mosi.set_input_enable(true); if dummy_cycle { spi_dummy_bit(sck).await; } for output_byte in output_buffer { for i in (0..8).rev() { if spi_read_bit(mosi, sck).await { *output_byte |= 1 << i; } } } mosi.set_input_enable(false); mosi.set_output_enable(true); cs.set_high(); }