|
| 1 | +use anyhow::{Context as _, bail}; |
| 2 | +use embedded_sdmmc::sdcard::argument::{Acmd6, Cmd7, Cmd9, Cmd13, OcrLower, VoltageSuppliedSelect}; |
| 3 | +use embedded_sdmmc::sdcard::mock::SdCardMock; |
| 4 | +use embedded_sdmmc::sdcard::response::{self, R1, R3, R6, R7}; |
| 5 | +use embedded_sdmmc::sdcard::{AcmdId, CmdId, argument}; |
| 6 | +use embedded_sdmmc::sdcard::{cid::Cid, csd::Csd}; |
| 7 | + |
| 8 | +/// Negotiated as part of ACMD41 during SD card initialization. |
| 9 | +pub const VOLTAGE_LEVEL_CAPABILITIES: OcrLower = OcrLower::builder() |
| 10 | + .with__3_5_to_3_6v(false) |
| 11 | + .with__3_4_to_3_5v(false) |
| 12 | + .with__3_3_to_3_4v(false) |
| 13 | + .with__3_2_to_3_3v(true) |
| 14 | + .with__3_1_to_3_2v(false) |
| 15 | + .with__3_0_to_3_1v(false) |
| 16 | + .with__2_9_to_3_0v(false) |
| 17 | + .with__2_8_to_2_9v(false) |
| 18 | + .with__2_7_to_2_8v(false) |
| 19 | + .with_reserved_low_voltage(false) |
| 20 | + .build(); |
| 21 | + |
| 22 | +pub struct SdCardUninitialized(SdCardMock); |
| 23 | + |
| 24 | +impl SdCardUninitialized { |
| 25 | + pub fn new(sd_mock: SdCardMock) -> Self { |
| 26 | + Self(sd_mock) |
| 27 | + } |
| 28 | + |
| 29 | + /// Example initialization sequence for a SD card which can provide |
| 30 | + /// a reference example for a real driver. |
| 31 | + /// |
| 32 | + /// It performs the initialization sequence specified in the SD card |
| 33 | + /// physical layer specification Ver9.10, p.68 and p.70. |
| 34 | + pub fn initialize(mut self) -> Result<SdCard, anyhow::Error> { |
| 35 | + self.0.insert_command(CmdId::CMD0_GoIdleState, 0); |
| 36 | + |
| 37 | + // Voltage level negotiation. Send CMD8 first. |
| 38 | + self.0.insert_command( |
| 39 | + CmdId::CMD8_SendIfCond, |
| 40 | + argument::Cmd8::ZERO |
| 41 | + .with_voltage_supplied( |
| 42 | + embedded_sdmmc::sdcard::argument::VoltageSuppliedSelect::_2_7To3_6V, |
| 43 | + ) |
| 44 | + .with_check_pattern(0xAA) |
| 45 | + .raw_value(), |
| 46 | + ); |
| 47 | + |
| 48 | + let r7 = R7::new_with_raw_value(self.0.read_reply_u32()); |
| 49 | + if r7 |
| 50 | + .voltage_accepted() |
| 51 | + .is_ok_and(|val| val != VoltageSuppliedSelect::_2_7To3_6V) |
| 52 | + { |
| 53 | + bail!("CMD8 reply R7: Voltage not accepted"); |
| 54 | + } |
| 55 | + if r7.echo_check_pattern() != 0xAA { |
| 56 | + bail!("CMD8 reply R7: Check pattern missmatch"); |
| 57 | + } |
| 58 | + |
| 59 | + // Now send ACMD41. |
| 60 | + self.0.insert_acmd( |
| 61 | + AcmdId::ACMD41_SdSendOpCond, |
| 62 | + argument::Acmd41::builder() |
| 63 | + .with_host_capacity_support( |
| 64 | + embedded_sdmmc::sdcard::argument::HostCapacitySupport::SdhcOrSdxc, |
| 65 | + ) |
| 66 | + .with_fast_boot(false) |
| 67 | + .with_xpc(embedded_sdmmc::sdcard::argument::PowerControl::MaximumPerformance) |
| 68 | + .with_s18r(false) |
| 69 | + .with_ocr(VOLTAGE_LEVEL_CAPABILITIES) |
| 70 | + .build() |
| 71 | + .raw_value(), |
| 72 | + ); |
| 73 | + loop { |
| 74 | + // Now poll until the card initialization is complete. In real driver code, timeout |
| 75 | + // handling or an upper polling limit might be a good idea. |
| 76 | + self.0.insert_acmd(AcmdId::ACMD41_SdSendOpCond, 0); |
| 77 | + let r3 = R3::new_with_raw_value(self.0.read_reply_u32()); |
| 78 | + if r3.initialization_complete() { |
| 79 | + break; |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + // Retrieve and cache the CID. This puts it into identification mode. |
| 84 | + self.0.insert_command(CmdId::CMD2_AllSendCid, 0); |
| 85 | + let cid_raw = self.0.read_reply_u128(); |
| 86 | + let cid = embedded_sdmmc::sdcard::cid::Cid::new_with_raw_value(cid_raw); |
| 87 | + |
| 88 | + // Send CMD3 to retrieve RCA required for card addressing, as well as put the card |
| 89 | + // into standby mode. |
| 90 | + self.0.insert_command(CmdId::CMD3_SendRelativeAddr, 0); |
| 91 | + let r6 = R6::new_with_raw_value(self.0.read_reply_u32()); |
| 92 | + let rca = r6.rca(); |
| 93 | + |
| 94 | + // Retrieve and cache CSD, which also contains card specific data. |
| 95 | + self.0 |
| 96 | + .insert_command(CmdId::CMD9_SendCsd, Cmd9::ZERO.with_rca(rca).raw_value()); |
| 97 | + let cid_raw = self.0.read_reply_u128(); |
| 98 | + let csd = Csd::new(&cid_raw.to_be_bytes()).with_context(|| "failed to parse CSD")?; |
| 99 | + |
| 100 | + // CMD7 to put the SD card into transfer state. |
| 101 | + self.0 |
| 102 | + .insert_command(CmdId::CMD7_SelectCard, Cmd7::ZERO.with_rca(rca).raw_value()); |
| 103 | + |
| 104 | + // Check that the card is in transfer mode. |
| 105 | + self.0.insert_command( |
| 106 | + CmdId::CMD13_SendStatus, |
| 107 | + Cmd13::ZERO.with_rca(rca).raw_value(), |
| 108 | + ); |
| 109 | + let r1 = R1::new_with_raw_value(self.0.read_reply_u32()); |
| 110 | + |
| 111 | + if r1.state().is_ok_and(|state| state != response::State::Tran) { |
| 112 | + bail!("CMD8 reply R1: Not in transfer state"); |
| 113 | + } |
| 114 | + |
| 115 | + // In this example, we put the SD card into 4-bit mode. On real hardware, you usually |
| 116 | + // have to configure register bits on the controller side as well. |
| 117 | + self.0.insert_acmd( |
| 118 | + AcmdId::ACMD6_SetBusWidth, |
| 119 | + Acmd6::builder() |
| 120 | + .with_bus_width(argument::BusWidth::_4bits) |
| 121 | + .build() |
| 122 | + .raw_value(), |
| 123 | + ); |
| 124 | + |
| 125 | + Ok(SdCard { |
| 126 | + cid, |
| 127 | + csd, |
| 128 | + rca, |
| 129 | + sd_mock: self.0, |
| 130 | + }) |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +#[derive(Debug)] |
| 135 | +pub struct SdCard { |
| 136 | + cid: Cid, |
| 137 | + csd: Csd, |
| 138 | + rca: u16, |
| 139 | + #[allow(unused)] |
| 140 | + sd_mock: SdCardMock, |
| 141 | +} |
| 142 | + |
| 143 | +const MOCK_SD_RCA: u16 = 1; |
| 144 | + |
| 145 | +fn main() -> Result<(), anyhow::Error> { |
| 146 | + let sd_mock = SdCardMock::new(MOCK_SD_RCA); |
| 147 | + let sd_card_uninit = SdCardUninitialized::new(sd_mock); |
| 148 | + let sd_card = sd_card_uninit |
| 149 | + .initialize() |
| 150 | + .context("failed to initialize SD card")?; |
| 151 | + println!("SD card initialized successfully",); |
| 152 | + println!("--------"); |
| 153 | + println!("Relative Card Address: {}", sd_card.rca); |
| 154 | + println!("--------"); |
| 155 | + println!("CSD: {:?}", sd_card.csd); |
| 156 | + println!("--------"); |
| 157 | + println!("CID: {:?}", sd_card.cid); |
| 158 | + Ok(()) |
| 159 | +} |
0 commit comments