Skip to content

Commit 03f08e5

Browse files
authored
Merge pull request #232 from robamu/argument-response-module
start adding argument and response module
2 parents c7231dd + 0609a8b commit 03f08e5

6 files changed

Lines changed: 858 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ env_logger = "0.11.8"
3232
flate2 = "1.0"
3333
hex-literal = "1.0.0"
3434
sha2 = "0.10"
35+
anyhow = "1"
3536

3637
[features]
3738
default = ["log"]

examples/sd_card_init.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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+
}

src/sdcard/argument.rs

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
//! # Argument module
2+
//!
3+
//! Arguments are additional command parameters. You can use the `sd_card_init` example to see
4+
//! how these data structures can be used during a SD card initialization process.
5+
6+
use arbitrary_int::u24;
7+
8+
/// Voltage settings supplied in CMD8.
9+
#[bitbybit::bitenum(u4, exhaustive = false)]
10+
#[derive(Debug, Default, PartialEq, Eq)]
11+
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
12+
pub enum VoltageSuppliedSelect {
13+
/// Regular voltage range.
14+
#[default]
15+
_2_7To3_6V = 0b0001,
16+
/// Reserved for low voltage.
17+
LowVoltageRange = 0b0010,
18+
}
19+
20+
/// CMD8 argument.
21+
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_fields(feature = "defmt-log"))]
22+
pub struct Cmd8 {
23+
/// PCIe v1.2 support.
24+
#[bit(13, rw)]
25+
pcie_1_2v_support: bool,
26+
/// PCIe available.
27+
#[bit(12, rw)]
28+
pcie_availability: bool,
29+
/// Voltage settings.
30+
#[bits(8..=11, rw)]
31+
voltage_supplied: Option<VoltageSuppliedSelect>,
32+
/// Check pattern which is echoed.
33+
#[bits(0..=7, rw)]
34+
check_pattern: u8,
35+
}
36+
37+
/// Power control (XPC) settings.
38+
#[bitbybit::bitenum(u1, exhaustive = true)]
39+
#[derive(Debug, PartialEq, Eq)]
40+
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
41+
pub enum PowerControl {
42+
/// Power saving.
43+
PowerSaving = 0,
44+
/// Maximum performance.
45+
MaximumPerformance = 1,
46+
}
47+
48+
/// Host Capacity Support (HCS).
49+
#[bitbybit::bitenum(u1, exhaustive = true)]
50+
#[derive(Debug, PartialEq, Eq)]
51+
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
52+
pub enum HostCapacitySupport {
53+
/// SDSC only.
54+
SdscOnly = 0,
55+
/// Extended capacity - SDHC or SDXC.
56+
SdhcOrSdxc = 1,
57+
}
58+
59+
/// Lower OCR bits used to negotiate voltage capabilities.
60+
#[bitbybit::bitfield(u24, default = 0x0, debug, defmt_fields(feature = "defmt-log"))]
61+
pub struct OcrLower {
62+
/// 3.5 to 3.6V
63+
#[bit(23, rw)]
64+
_3_5_to_3_6v: bool,
65+
/// 3.4 to 3.5V
66+
#[bit(22, rw)]
67+
_3_4_to_3_5v: bool,
68+
/// 3.3 to 3.4V
69+
#[bit(21, rw)]
70+
_3_3_to_3_4v: bool,
71+
/// 3.2 to 3.3V
72+
#[bit(20, rw)]
73+
_3_2_to_3_3v: bool,
74+
/// 3.1 to 3.2V
75+
#[bit(19, rw)]
76+
_3_1_to_3_2v: bool,
77+
/// 3.0 to 3.1V
78+
#[bit(18, rw)]
79+
_3_0_to_3_1v: bool,
80+
/// 2.9 to 3.0V
81+
#[bit(17, rw)]
82+
_2_9_to_3_0v: bool,
83+
/// 2.8 to 2.9V
84+
#[bit(16, rw)]
85+
_2_8_to_2_9v: bool,
86+
/// 2.7 to 2.8V
87+
#[bit(15, rw)]
88+
_2_7_to_2_8v: bool,
89+
/// Reserved for low voltage
90+
#[bit(7, rw)]
91+
reserved_low_voltage: bool,
92+
}
93+
94+
/// Bus width setting.
95+
#[bitbybit::bitenum(u2, exhaustive = false)]
96+
#[derive(Debug, PartialEq, Eq)]
97+
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
98+
pub enum BusWidth {
99+
/// 1 bit bus.
100+
_1bit = 0b00,
101+
/// 4 bit bus.
102+
_4bits = 0b10,
103+
}
104+
105+
/// ACMD6 - Set bus width.
106+
#[bitbybit::bitfield(
107+
u32,
108+
default = 0x0,
109+
debug,
110+
defmt_fields(feature = "defmt-log"),
111+
forbid_overlaps
112+
)]
113+
pub struct Acmd6 {
114+
/// Bus width.
115+
#[bits(0..=1, rw)]
116+
bus_width: Option<BusWidth>,
117+
}
118+
119+
/// ACMD41 argument - Capability negotiation.
120+
#[bitbybit::bitfield(
121+
u32,
122+
default = 0x0,
123+
debug,
124+
defmt_fields(feature = "defmt-log"),
125+
forbid_overlaps
126+
)]
127+
pub struct Acmd41 {
128+
/// HCS.
129+
#[bit(30, rw)]
130+
host_capacity_support: HostCapacitySupport,
131+
/// FB.
132+
#[bit(29, rw)]
133+
fast_boot: bool,
134+
/// XPC.
135+
#[bit(28, rw)]
136+
xpc: PowerControl,
137+
/// Switch to 1.8V signal voltage request.
138+
#[bit(24, rw)]
139+
s18r: bool,
140+
/// If this is set to 0. ACMD41 is called "inquire CMD41" that does not start
141+
/// initialization and is used for getting OCR. Bits 24 to 31 are ignored.
142+
#[bits(0..=23, rw)]
143+
ocr: OcrLower,
144+
}
145+
146+
/// Relative Card Address (RCA) select.
147+
#[bitbybit::bitfield(
148+
u32,
149+
default = 0x0,
150+
debug,
151+
defmt_bitfields(feature = "defmt-log"),
152+
forbid_overlaps
153+
)]
154+
pub struct RcaSelect {
155+
/// RCA.
156+
#[bits(16..=31, rw)]
157+
rca: u16,
158+
}
159+
160+
/// CMD9 argument.
161+
pub type Cmd9 = RcaSelect;
162+
/// CMD7 argument.
163+
pub type Cmd7 = RcaSelect;
164+
165+
/// CMD13 argument.
166+
#[bitbybit::bitfield(
167+
u32,
168+
default = 0x0,
169+
debug,
170+
defmt_bitfields(feature = "defmt-log"),
171+
forbid_overlaps
172+
)]
173+
pub struct Cmd13 {
174+
/// RCA.
175+
#[bits(16..=31, rw)]
176+
rca: u16,
177+
/// Send task status instead of regular card status.
178+
#[bit(15, rw)]
179+
send_task_status: bool,
180+
}

0 commit comments

Comments
 (0)