Skip to content

Commit

Permalink
sdh: add example for initializing and reading sdcard (#11)
Browse files Browse the repository at this point in the history
Signed-off-by: Junxing Zhu <zjx1319@hust.edu.cn>
jakezhu9 authored Dec 7, 2024
1 parent 968b949 commit 118b1a7
Showing 7 changed files with 469 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -19,5 +19,6 @@ members = [
"examples/peripherals/sdcard-demo",
"examples/peripherals/sdcard-gpt-demo",
"examples/peripherals/psram-demo",
"examples/peripherals/sdh-demo",
]
resolver = "2"
31 changes: 31 additions & 0 deletions bouffalo-hal/src/gpio.rs
Original file line number Diff line number Diff line change
@@ -472,6 +472,37 @@ impl Alternate for Spi<1> {
const F: v2::Function = v2::Function::Spi1;
}

#[cfg(feature = "glb-v2")]
impl<GLB: Deref<Target = GlbRegisterBlock>, const N: usize, M: Alternate> Pad<GLB, N, M> {
/// Configures the pin to operate as a SDH pin.
#[inline]
pub fn into_sdh(self) -> Pad<GLB, N, Sdh> {
let config = v2::GpioConfig::RESET_VALUE
.enable_input()
.disable_output()
.enable_schmitt()
.set_pull(v2::Pull::Up)
.set_drive(v2::Drive::Drive0)
.set_function(Sdh::F);
unsafe {
self.base.gpio_config[N].write(config);
}

Pad {
base: self.base,
_mode: PhantomData,
}
}
}

/// SD Host mode (type state).
pub struct Sdh;

impl Alternate for Sdh {
#[cfg(feature = "glb-v2")]
const F: v2::Function = v2::Function::Sdh;
}

impl<GLB: Deref<Target = GlbRegisterBlock>, const N: usize, M: Alternate> Pad<GLB, N, M> {
/// Configures the pin to operate as a pull up output pin.
#[inline]
19 changes: 19 additions & 0 deletions examples/peripherals/sdh-demo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "sdh-demo"
version = "0.1.0"
edition = "2021"
publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bouffalo-hal = { path = "../../../bouffalo-hal", features = ["bl808"] }
bouffalo-rt = { path = "../../../bouffalo-rt", features = ["bl808-dsp"] }
panic-halt = "1.0.0"
embedded-time = "0.12.1"
embedded-io = "0.6.1"
embedded-sdmmc = "0.8.1"

[[bin]]
name = "sdh-demo"
test = false
8 changes: 8 additions & 0 deletions examples/peripherals/sdh-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
psram demo

Build this example with:

```
rustup target install riscv64imac-unknown-none-elf
cargo build --target riscv64imac-unknown-none-elf --release -p sdh-demo
```
3 changes: 3 additions & 0 deletions examples/peripherals/sdh-demo/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
println!("cargo:rustc-link-arg=-Tbouffalo-rt.ld");
}
93 changes: 93 additions & 0 deletions examples/peripherals/sdh-demo/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#![no_std]
#![no_main]

use core::{arch::asm, ptr};

use bouffalo_hal::{prelude::*, uart::Config};
use bouffalo_rt::{entry, Clocks, Peripherals};
use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx, VolumeManager};
use embedded_time::rate::*;
use panic_halt as _;
use sdh::*;

mod sdh;

struct MyTimeSource {}

impl embedded_sdmmc::TimeSource for MyTimeSource {
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
// TODO
embedded_sdmmc::Timestamp::from_calendar(2023, 1, 1, 0, 0, 0).unwrap()
}
}

#[entry]
fn main(p: Peripherals, c: Clocks) -> ! {
// light up led
let mut led = p.gpio.io8.into_floating_output();
let mut led_state = PinState::Low;
led.set_state(led_state).ok();

// init serial
let tx = p.gpio.io14.into_uart();
let rx = p.gpio.io15.into_uart();
let sig2 = p.uart_muxes.sig2.into_transmit::<0>();
let sig3 = p.uart_muxes.sig3.into_receive::<0>();

let config = Config::default().set_baudrate(2000000.Bd());
let mut serial = p.uart0.freerun(config, ((tx, sig2), (rx, sig3)), &c);

writeln!(serial, "Welcome to sdh-demo!").ok();

// sdh gpio init
p.gpio.io0.into_sdh();
p.gpio.io1.into_sdh();
p.gpio.io2.into_sdh();
p.gpio.io3.into_sdh();
p.gpio.io4.into_sdh();
p.gpio.io5.into_sdh();

// sdh init
let sdcard = sdh_init(&mut serial);
let time_source = MyTimeSource {};
let mut volume_mgr = VolumeManager::new(sdcard, time_source);
let volume_res = volume_mgr.open_raw_volume(embedded_sdmmc::VolumeIdx(0));
if let Err(e) = volume_res {
writeln!(serial, "Failed to open volume: {:?}", e).ok();
loop {}
}
let volume0 = volume_res.unwrap();
let root_dir = volume_mgr.open_root_dir(volume0).unwrap();

volume_mgr
.iterate_dir(root_dir, |entry| {
writeln!(serial, "Entry: {:?}", entry).ok();
})
.unwrap();

volume_mgr.close_dir(root_dir).unwrap();

loop {
led.set_state(led_state).ok();
led_state = !led_state;
sleep_ms(1000);
}
}

#[inline]
pub(crate) fn set_bits(val: u32, pos: u32, len: u32, val_in: u32) -> u32 {
let mask = ((1 << len) - 1) << pos;
(val & !mask) | ((val_in << pos) & mask)
}

#[inline]
pub(crate) fn is_bit_set(val: u32, pos: u32) -> bool {
(val & (1 << pos)) != 0
}

#[inline]
pub(crate) fn sleep_ms(n: u32) {
for _ in 0..n * 125 {
unsafe { asm!("nop") }
}
}
314 changes: 314 additions & 0 deletions examples/peripherals/sdh-demo/src/sdh.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
//! sdh
use crate::*;
use embedded_io::Write;

const GLB_BASE: u32 = 0x20000000;
const SDH_BASE: u32 = 0x20060000;

pub struct MySdCard {
block_count: u32,
}

impl MySdCard {
fn read_block(&self, block: &mut Block, block_idx: u32) {
let mut tmp_val = read_memory16(SDH_BASE + 0x0C); // SDH_SD_TRANSFER_MODE
tmp_val = set_bits(tmp_val, 4, 1, 1); // SDH_TO_HOST_DIR
tmp_val = set_bits(tmp_val, 2, 2, 0); // SDH_AUTO_CMD_EN
tmp_val = set_bits(tmp_val, 5, 1, 0); // SDH_MULTI_BLK_SEL
write_memory16(SDH_BASE + 0x0C, tmp_val);
write_memory16(SDH_BASE + 0x04, 512); // block_size
write_memory16(SDH_BASE + 0x06, 1); // block_count
write_memory(SDH_BASE + 0x30, 0x00000020); // SDH_ClearIntStatus(SDH_INT_BUFFER_READ_READY)
sdh_send_command(SDHResp::R1, SDHCmdType::Normal, 17, block_idx, true);
tmp_val = read_memory(SDH_BASE + 0x30);
while !is_bit_set(tmp_val, 5) {
tmp_val = read_memory(SDH_BASE + 0x30);
}
for j in 0..Block::LEN / 4 {
let val = read_memory(SDH_BASE + 0x20);
block[j * 4 + 0] = (val >> 0) as u8;
block[j * 4 + 1] = (val >> 8) as u8;
block[j * 4 + 2] = (val >> 16) as u8;
block[j * 4 + 3] = (val >> 24) as u8;
}
}
}

impl BlockDevice for MySdCard {
type Error = core::convert::Infallible;
fn read(
&self,
blocks: &mut [Block],
start_block_idx: BlockIdx,
_reason: &str,
) -> Result<(), Self::Error> {
for (i, block) in blocks.iter_mut().enumerate() {
self.read_block(block, start_block_idx.0 + i as u32);
}
Ok(())
}
fn write(&self, _blocks: &[Block], _start_block_idx: BlockIdx) -> Result<(), Self::Error> {
unimplemented!();
}
fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
Ok(embedded_sdmmc::BlockCount(self.block_count))
}
}

pub fn sdh_init<W: Write>(w: &mut W) -> MySdCard {
let mut tmp_val;

// SDH_RESET
tmp_val = read_memory16(SDH_BASE + 0x2e);
tmp_val = set_bits(tmp_val, 8, 1, 1);
write_memory16(SDH_BASE + 0x2e, tmp_val);
while is_bit_set(tmp_val, 8) {
tmp_val = read_memory16(SDH_BASE + 0x2e);
}

// GLB_Set_SDH_CLK
tmp_val = read_memory(GLB_BASE + 0x430);
tmp_val = set_bits(tmp_val, 13, 1, 0); // GLB_REG_SDH_CLK_EN
write_memory(GLB_BASE + 0x430, tmp_val);
tmp_val = read_memory(GLB_BASE + 0x430);
tmp_val = set_bits(tmp_val, 12, 1, 0); // GLB_REG_SDH_CLK_SEL
tmp_val = set_bits(tmp_val, 9, 3, 7); // GLB_REG_SDH_CLK_DIV
write_memory(GLB_BASE + 0x430, tmp_val);
let mut tmp_val = read_memory(GLB_BASE + 0x430);
tmp_val = set_bits(tmp_val, 13, 1, 1); // GLB_REG_SDH_CLK_EN
write_memory(GLB_BASE + 0x430, tmp_val);

// SDH_Ctrl_Init
tmp_val = read_memory16(SDH_BASE + 0x2c);
tmp_val = set_bits(tmp_val, 8, 8, 0); // SDH_SD_FREQ_SEL_LO
tmp_val = set_bits(tmp_val, 6, 2, 0); // SDH_SD_FREQ_SEL_HI
tmp_val = set_bits(tmp_val, 5, 1, 0); // SDH_CLK_GEN_SEL
tmp_val = set_bits(tmp_val, 0, 1, 1); // SDH_INT_CLK_EN
tmp_val = set_bits(tmp_val, 2, 1, 1); // SDH_SD_CLK_EN
write_memory16(SDH_BASE + 0x2c, tmp_val);
tmp_val = read_memory16(SDH_BASE + 0x2c);
while !is_bit_set(tmp_val, 1) {
tmp_val = read_memory16(SDH_BASE + 0x2c);
}
tmp_val = read_memory16(SDH_BASE + 0xc);
tmp_val = set_bits(tmp_val, 0, 1, 0); // SDH_DMA_EN
write_memory16(SDH_BASE + 0xc, tmp_val);
tmp_val = read_memory16(SDH_BASE + 0x28);
tmp_val = set_bits(tmp_val, 5, 1, 0); // SDH_EX_DATA_WIDTH
tmp_val = set_bits(tmp_val, 1, 1, 0); // SDH_DATA_WIDTH
tmp_val = set_bits(tmp_val, 2, 1, 1); // SDH_HI_SPEED_EN
tmp_val = set_bits(tmp_val, 9, 3, 7); // SDH_SD_BUS_VLT
write_memory16(SDH_BASE + 0x28, tmp_val);
tmp_val = read_memory(SDH_BASE + 0x118);
tmp_val = set_bits(tmp_val, 30, 1, 1); // SDH_TX_INT_CLK_SEL
write_memory(SDH_BASE + 0x118, tmp_val);

// SDH Enable Interrupt
tmp_val = 0;
tmp_val = set_bits(tmp_val, 5, 1, 1); // SDH_INT_BUFFER_READ_READY
write_memory(SDH_BASE + 0x34, tmp_val);

// SDH_Set_Timeout
tmp_val = read_memory16(SDH_BASE + 0x2e);
tmp_val = set_bits(tmp_val, 0, 4, 0x0e); // SDH_TIMEOUT_VALUE
write_memory16(SDH_BASE + 0x2e, tmp_val);

// SDH_Powon
tmp_val = read_memory16(SDH_BASE + 0x28);
tmp_val = set_bits(tmp_val, 8, 1, 1); // SDH_SD_BUS_POWER
write_memory16(SDH_BASE + 0x28, tmp_val);

// sdcard idle
sdh_send_command(SDHResp::None, SDHCmdType::Normal, 0, 0, false);
sleep_ms(100);

// send CMD8
sdh_send_command(SDHResp::R7, SDHCmdType::Normal, 8, 0x1AA, false);
sleep_ms(100);
let data: u128 = sdh_get_resp();
if data != 0x1AA {
writeln!(
*w,
"unexpected response to CMD8: {:#010X}, expected 0x1AA",
data
)
.ok();
loop {}
}

loop {
const OCR_NBUSY: u32 = 0x80000000;
const OCR_VOLTAGE_MASK: u32 = 0x007FFF80;
const OCR_HCS: u32 = 0x40000000;
sdh_send_command(SDHResp::R1, SDHCmdType::Normal, 55, 0, false);
sleep_ms(100);
sdh_send_command(
SDHResp::R3,
SDHCmdType::Normal,
41,
OCR_VOLTAGE_MASK & 0x00ff8000 | OCR_HCS,
false,
);
sleep_ms(100);
let ocr = sdh_get_resp();
if (ocr as u32 & OCR_NBUSY) == OCR_NBUSY {
break;
}
sleep_ms(100);
}

// send CMD2 to get CID
sdh_send_command(SDHResp::R2, SDHCmdType::Normal, 2, 0, false);
sleep_ms(100);
let cid = sdh_get_resp();
writeln!(*w, "cid: {:#034X}", cid).ok();

// send CMD3 to get RCA
sdh_send_command(SDHResp::R6, SDHCmdType::Normal, 3, 0, false);
sleep_ms(100);
let rca = sdh_get_resp() as u32 >> 16;
writeln!(*w, "rca: {:#010X}", rca).ok();

// send CMD9 to get CSD
sdh_send_command(SDHResp::R2, SDHCmdType::Normal, 9, rca << 16, false);
sleep_ms(100);
let csd_raw = sdh_get_resp();
let (csd_structure, c_size) = parse_csd_v2(csd_raw);
if csd_structure != 1 {
writeln!(*w, "unexpected CSD: {:#034X}", csd_raw).ok();
loop {}
}

writeln!(*w, "csd: {:#034X}, c_size: {}", csd_raw, c_size).ok();

let block_size = 512;
let block_count = (c_size + 1) * 1024;

// send CMD7 to select card
sdh_send_command(SDHResp::R1B, SDHCmdType::Normal, 7, rca << 16, false);
sleep_ms(100);

// set 1 data len, CMD55 -> ACMD6
sdh_send_command(SDHResp::R1, SDHCmdType::Normal, 55, rca << 16, false);
sleep_ms(100);
sdh_send_command(SDHResp::R1, SDHCmdType::Normal, 6, 0x0, false);
sleep_ms(100);

writeln!(
*w,
"sdcard init done, size: {} MB",
block_count as u128 * block_size / 1024 / 1024
)
.ok();
MySdCard { block_count }
}

fn sdh_send_command(
resp_type: SDHResp,
cmd_type: SDHCmdType,
cmd_idx: u32,
argument: u32,
has_data: bool,
) {
let mut tmp_val;
let mut flag = SDHTransFlag::None as u32;
if has_data {
flag |= SDHTransFlag::DataPresent as u32;
}
match resp_type {
SDHResp::None => {}
SDHResp::R1 | SDHResp::R5 | SDHResp::R6 | SDHResp::R7 => {
flag |= SDHTransFlag::Resp48Bits as u32
| SDHTransFlag::EnCrcCheck as u32
| SDHTransFlag::EnIndexCheck as u32;
}
SDHResp::R1B | SDHResp::R5B => {
flag |= SDHTransFlag::Resp48BitsWithBusy as u32
| SDHTransFlag::EnCrcCheck as u32
| SDHTransFlag::EnIndexCheck as u32;
}
SDHResp::R2 => {
flag |= SDHTransFlag::Resp136Bits as u32 | SDHTransFlag::EnCrcCheck as u32;
}
SDHResp::R3 | SDHResp::R4 => {
flag |= SDHTransFlag::Resp48Bits as u32;
}
}
tmp_val = flag >> 16;

tmp_val = set_bits(tmp_val, 6, 2, cmd_type as u32);
tmp_val = set_bits(tmp_val, 8, 6, cmd_idx);
write_memory(SDH_BASE + 0x08, argument);
write_memory16(SDH_BASE + 0x0E, tmp_val);
}

fn sdh_get_resp() -> u128 {
let a = read_memory(SDH_BASE + 0x10);
let b = read_memory(SDH_BASE + 0x14);
let c = read_memory(SDH_BASE + 0x18);
let d = read_memory(SDH_BASE + 0x1C);
(a as u128) | ((b as u128) << 32) | ((c as u128) << 64) | ((d as u128) << 96)
}

enum SDHCmdType {
Normal,
Suspend,
Resume,
Abort,
Empty,
}

enum SDHTransFlag {
None = 0x00000000,
EnDma = 0x00000001, // Enable DMA
EnBlkCount = 0x00000002, // Enable block count
EnAutoCmd12 = 0x00000004, // Enable auto CMD12
EnAutoCmd23 = 0x00000008, // Enable auto CMD23
ReadData = 0x00000010, // Enable read data
MultiBlk = 0x00000020, // Enable multi-block data operation
Resp136Bits = 0x00010000, // Response is 136 bits length
Resp48Bits = 0x00020000, // Response is 48 bits length
Resp48BitsWithBusy = 0x00030000, // Response is 48 bits length with busy status
EnCrcCheck = 0x00080000, // Enable CRC check
EnIndexCheck = 0x00100000, // Enable index check
DataPresent = 0x00200000, // Data present
Suspend = 0x00400000, // Suspend command
Resume = 0x00800000, // Resume command
Abort = 0x00C00000, // Abort command
}

enum SDHResp {
None,
R1,
R5,
R6,
R7,
R1B,
R5B,
R2,
R3,
R4,
}

fn write_memory16(addr: u32, val: u32) {
unsafe { ptr::write_volatile(addr as *mut u16, val as u16) }
}

fn read_memory16(addr: u32) -> u32 {
unsafe { ptr::read_volatile(addr as *const u16) as u32 }
}

pub fn read_memory(addr: u32) -> u32 {
unsafe { ptr::read_volatile(addr as *const u32) }
}

pub fn write_memory(addr: u32, val: u32) {
unsafe { ptr::write_volatile(addr as *mut u32, val) }
}

fn parse_csd_v2(csd: u128) -> (u32, u32) {
let csd_structure = (((csd >> (32 * 3)) & 0xC00000) >> 22) as u32;
let c_size = (((csd >> 32) & 0x3FFFFF00) >> 8) as u32;
(csd_structure, c_size)
}

0 comments on commit 118b1a7

Please sign in to comment.