Skip to content

Commit bdc543b

Browse files
committed
add a post on sd-card test
1 parent 83f9379 commit bdc543b

File tree

4 files changed

+461
-0
lines changed

4 files changed

+461
-0
lines changed
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
#+OPTIONS: ^:nil
2+
#+BEGIN_EXPORT html
3+
---
4+
layout: default
5+
title: 一种基于ESP32丰富连接能力的移动存储设备 -- 测试SD卡读写
6+
tags: [Rust on ESP, ESP32, PCB, SD Card]
7+
nav_order: {{ page.date }}
8+
sync_wexin: 1
9+
---
10+
#+END_EXPORT
11+
12+
* 一种基于ESP32丰富连接能力的移动存储设备 -- 测试SD卡读写
13+
14+
** 前言
15+
16+
上一篇文章介绍了我为移动存储设备设计的一块电路板,并且PCB实物也收到了。现在我已经焊接好了电路板,接下来我想先测试SD卡的读写能力是否正常。
17+
[[/images/esp32-storage-assembled-pcb.jpg]]
18+
19+
本系列其他文章
20+
1. [[https://paul356.github.io/2024/10/31/mobile-storage.html][基于ESP32的移动存储设备]]
21+
2. [[https://paul356.github.io/2024/12/12/mobile-storage-pcb.html][一种基于ESP32丰富连接能力的移动存储设备 -- 电路设计]]
22+
23+
** 测试SD卡功能
24+
25+
*** 使用官方例子
26+
要快速测试SD卡的功能,我先找了ESP-IDF 5.3中的一个C语言例子,例子在ESP-IDF源码中的这个位置 ~examples/storage/sd_card/sdmmc~ 。例子中的SD卡槽连接的管脚和我的PCB不一样,需要通过 ~idf.py menuconfig~ 修改CLK,CMD等管脚对应的GPIO口编号。修改之后的状态如下。
27+
[[/images/sdmmc-menuconfig.png]]
28+
29+
然后使用 ~idf.py build~ 命令编译代码,使用 ~idf.py flash~ 上传固件。插入SD卡,测试很顺利。
30+
#+begin_src text
31+
I (897) example: Reading file /sdcard/foo.txt
32+
I (897) example: Read from file: 'Hello SL16G!'
33+
I (897) example: Opening file /sdcard/nihao.txt
34+
I (1117) example: File written
35+
I (1117) example: Reading file /sdcard/nihao.txt
36+
I (1117) example: Read from file: 'Nihao SL16G!'
37+
I (1117) example: Card unmounted
38+
I (1127) main_task: Returned from app_main()
39+
#+end_src
40+
41+
*** 开发Rust代码
42+
通过官方例子验证了我的SD卡硬件设计没有问题,下面就让我们通过Rust来访问SD卡。虽然通过搜索网路我找到一个Rust模块embeded-sdmmc,但是这个项目目前只支持通过SPI协议连接的SD卡。因为我为了获取更好的读写速度,选用了SDMMC接口,所以我无法使用这个模块。之后搜索也没有找到其他支持SDMMC的模块。所以只能直接调用ESP-IDF的C接口这个办法了。
43+
44+
通过查看官方例子的代码,我们了解到关键的接口是 ~esp_vfs_fat_sdmmc_mount~ 函数。
45+
#+begin_src c
46+
/**
47+
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
48+
*
49+
* This is an all-in-one function which does the following:
50+
* - initializes SDMMC driver or SPI driver with configuration in host_config
51+
* - initializes SD card with configuration in slot_config
52+
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
53+
* - registers FATFS library with VFS, with prefix given by base_prefix variable
54+
*
55+
* This function is intended to make example code more compact.
56+
* For real world applications, developers should implement the logic of
57+
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
58+
* with proper error checking and handling of exceptional conditions.
59+
*
60+
* @note Use this API to mount a card through SDSPI is deprecated. Please call
61+
* `esp_vfs_fat_sdspi_mount()` instead for that case.
62+
*
63+
* @param base_path path where partition should be registered (e.g. "/sdcard")
64+
* @param host_config Pointer to structure describing SDMMC host. When using
65+
* SDMMC peripheral, this structure can be initialized using
66+
* SDMMC_HOST_DEFAULT() macro. When using SPI peripheral,
67+
* this structure can be initialized using SDSPI_HOST_DEFAULT()
68+
* macro.
69+
* @param slot_config Pointer to structure with slot configuration.
70+
* For SDMMC peripheral, pass a pointer to sdmmc_slot_config_t
71+
* structure initialized using SDMMC_SLOT_CONFIG_DEFAULT.
72+
* @param mount_config pointer to structure with extra parameters for mounting FATFS
73+
* @param[out] out_card if not NULL, pointer to the card information structure will be returned via this argument
74+
* @return
75+
* - ESP_OK on success
76+
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
77+
* - ESP_ERR_NO_MEM if memory can not be allocated
78+
* - ESP_FAIL if partition can not be mounted
79+
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
80+
*/
81+
esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path,
82+
const sdmmc_host_t* host_config,
83+
const void* slot_config,
84+
const esp_vfs_fat_mount_config_t* mount_config,
85+
sdmmc_card_t** out_card);
86+
#+end_src
87+
这个函数接收一个文件系统路径、SD驱动配置、管脚配置、挂载配置,输出SD卡对象的句柄。我们只要准备这些配置对象,就可以读写SD卡了。由于在官方的C例程中,使用了一些宏来初始化 ~sdmmc_host_t~ 和 ~sdmmc_slot_config_t~ ,这些宏在Rust中访问不到,所以我们需要参照C的源代码来初始化这些结构体。
88+
89+
通过Rust初始化 ~sdmmc_slot_config_t~ 结构体,管脚的赋值需要依据电路设计。
90+
#+begin_src Rust
91+
fn get_slot_config() -> sdmmc_slot_config_t {
92+
sdmmc_slot_config_t {
93+
clk: 7,
94+
cmd: 6,
95+
d0: 15,
96+
d1: 16,
97+
d2: 4,
98+
d3: 5,
99+
d4: -1,
100+
d5: -1,
101+
d6: -1,
102+
d7: -1,
103+
__bindgen_anon_1: sdmmc_slot_config_t__bindgen_ty_1 {
104+
cd: 17,
105+
},
106+
__bindgen_anon_2: sdmmc_slot_config_t__bindgen_ty_2 {
107+
wp: -1,
108+
},
109+
width: 4,
110+
flags: SDMMC_SLOT_FLAG_INTERNAL_PULLUP,
111+
}
112+
}
113+
#+end_src
114+
115+
构造参数mount_config、mount_point、host_config。
116+
#+begin_src Rust
117+
let sdmmc_mount_config = esp_vfs_fat_sdmmc_mount_config_t {
118+
format_if_mount_failed: false,
119+
max_files: 4,
120+
allocation_unit_size: 16 * 1024,
121+
disk_status_check_enable: false,
122+
use_one_fat: false,
123+
};
124+
125+
let mount_point = CString::new("/sdcard").unwrap();
126+
127+
let sd_host = sdmmc_host_t {
128+
flags: SDMMC_HOST_FLAG_1BIT | SDMMC_HOST_FLAG_4BIT | SDMMC_HOST_FLAG_8BIT | SDMMC_HOST_FLAG_DDR,
129+
slot: SDMMC_HOST_SLOT_1,
130+
max_freq_khz: SDMMC_FREQ_DEFAULT,
131+
io_voltage: 3.3,
132+
init: Some(sdmmc_host_init),
133+
set_bus_width: Some(sdmmc_host_set_bus_width),
134+
get_bus_width: Some(sdmmc_host_get_slot_width),
135+
set_bus_ddr_mode: Some(sdmmc_host_set_bus_ddr_mode),
136+
set_card_clk: Some(sdmmc_host_set_card_clk),
137+
set_cclk_always_on: Some(sdmmc_host_set_cclk_always_on),
138+
do_transaction: Some(sdmmc_host_do_transaction),
139+
__bindgen_anon_1: sdmmc_host_t__bindgen_ty_1 {
140+
deinit: Some(sdmmc_host_deinit)
141+
},
142+
io_int_enable: Some(sdmmc_host_io_int_enable),
143+
io_int_wait: Some(sdmmc_host_io_int_wait),
144+
command_timeout_ms: 0,
145+
get_real_freq: Some(sdmmc_host_get_real_freq),
146+
input_delay_phase: SDMMC_DELAY_PHASE_0,
147+
set_input_delay: Some(sdmmc_host_set_input_delay),
148+
dma_aligned_buffer: std::ptr::null_mut(),
149+
pwr_ctrl_handle: std::ptr::null_mut(),
150+
get_dma_info: Some(sdmmc_host_get_dma_info),
151+
};
152+
153+
let slot_config = get_slot_config();
154+
#+end_src
155+
156+
参数都准备好了,最后调用esp_vfs_fat_sdmmc_mount挂载文件系统。
157+
#+begin_src Rust
158+
let mut card_handle: *mut sdmmc_card_t = std::ptr::null_mut();
159+
160+
let ret = unsafe {
161+
esp_vfs_fat_sdmmc_mount(
162+
mount_point.as_ptr(),
163+
&sd_host,
164+
&slot_config as *const sdmmc_slot_config_t as *const c_void,
165+
&sdmmc_mount_config,
166+
&mut card_handle,
167+
)
168+
};
169+
170+
match ret {
171+
ESP_OK => log::info!("SD Card mounted"),
172+
_ => {
173+
log::error!("Failed to mount SD Card");
174+
return;
175+
}
176+
}
177+
#+end_src
178+
179+
再测试SD卡的基本文件读写功能。
180+
#+begin_src Rust
181+
let file_res = std::fs::File::create_new("/sdcard/test.txt");
182+
let mut file = match file_res {
183+
Ok(mut file) => {
184+
file.write_all(b"Hello, world!").unwrap();
185+
186+
std::fs::File::open("/sdcard/test.txt").unwrap()
187+
}
188+
Err(_) => {
189+
std::fs::File::open("/sdcard/test.txt").unwrap()
190+
}
191+
};
192+
193+
let mut content = String::new();
194+
file.read_to_string(&mut content).unwrap();
195+
196+
log::info!("File content: {}", content);
197+
#+end_src
198+
199+
日志如下,初步看来SD卡功能正常。
200+
#+begin_src text
201+
I (417) main_task: Started on CPU0
202+
I (427) main_task: Calling app_main()
203+
I (427) sd_card_test: Hello, world!
204+
I (427) gpio: GPIO[7]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
205+
I (437) gpio: GPIO[6]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
206+
I (447) gpio: GPIO[15]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
207+
I (457) gpio: GPIO[16]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
208+
I (467) gpio: GPIO[4]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
209+
I (477) gpio: GPIO[5]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
210+
I (517) gpio: GPIO[5]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
211+
I (527) sd_card_test: SD Card mounted
212+
I (527) sd_card_test: File content: Hello, world!
213+
I (527) main_task: Returned from app_main()
214+
#+end_src
215+
216+
以上就是Rust中使用SD卡的关键要点,其实还是比较简单的。
217+
218+
** 后记
219+
220+
有兴趣的朋友可以在[[https://github.com/paul356/sd_card_rust][sd_card_rust]]项目中找到完整代码,欢迎大家留言交流。
221+
222+
** 链接列表
223+
224+
1. sd_card_rust -- https://github.com/paul356/sd_card_rust

0 commit comments

Comments
 (0)