1. 项目概述
在嵌入式系统开发中,SD卡存储是一个常见且实用的功能模块。今天我要分享的是基于STM32F407VET6微控制器的SD卡驱动实现方案,配合2.8寸ILI9341 TFT LCD屏幕,构建一个完整的存储和显示系统。
这个项目最核心的部分是通过SPI接口实现SD卡的底层驱动,包括初始化、扇区读写等基本操作。在此基础上,我们开发了应用层功能,如参数存储测试和BMP图片显示。整个系统采用模块化设计,分为底层驱动层(sd_driver.c/h)和应用层(SD.c/h、bmp_reader.c/h),便于维护和扩展。
2. 硬件设计与连接
2.1 硬件选型与原理
我们选择STM32F407VET6作为主控芯片,这是一款基于ARM Cortex-M4内核的高性能微控制器,具有丰富的外设资源。SD卡通信采用SPI模式,相比SDIO模式,SPI接口实现更简单,对硬件要求更低,适合初学者理解和应用。
2.8寸ILI9341 TFT LCD屏幕自带SD卡槽,这是一个非常便利的设计。屏幕通过FSMC接口与MCU连接,而SD卡则通过SPI3接口通信。这种分离设计避免了总线冲突,也简化了硬件连接。
2.2 具体接线方案
根据原理图,SD卡与STM32的连接如下:
- SPI3_SCK (PC10) → SD卡CLK
- SPI3_MISO (PC11) → SD卡DO (Data Out)
- SPI3_MOSI (PC12) → SD卡DI (Data In)
- PC13 (GPIO) → SD卡CS (Chip Select)
这里有几个关键点需要注意:
- MISO线必须上拉,这是SPI总线的基本要求
- CS信号使用普通GPIO控制,便于灵活操作
- 所有SPI信号线都应尽量短,避免信号完整性问题
2.3 电源设计考虑
SD卡对电源质量比较敏感,建议:
- 为SD卡槽单独添加0.1μF去耦电容
- 如果线路较长,可在数据线上串联22-33Ω电阻
- 确保供电电压在2.7-3.6V范围内
3. 软件架构设计
3.1 驱动层实现 (sd_driver.c/h)
驱动层是整个SD卡功能的基础,主要包含以下几个部分:
- SPI硬件初始化:配置SPI3的工作模式、时钟等参数
- 底层字节读写:实现SPI单字节收发函数
- SD卡协议实现:包括命令发送、响应等待等
- 基本功能API:初始化、扇区读写、信息获取等
特别值得注意的是SPI模式的选择。SD卡在SPI模式下使用模式3(CPOL=1, CPHA=1),即时钟空闲时为高电平,在第二个边沿采样数据。
3.2 应用层实现 (SD.c/h)
应用层建立在驱动层之上,提供更友好的接口和功能:
- 用户界面:通过LCD显示测试状态和结果
- 数据测试:实现扇区级的读写验证
- 参数存储:提供结构化的数据存储方案
- 性能测试:测量读写速度(代码中已注释,可按需启用)
应用层的一个典型用例是参数存储。我们定义了一个结构体类型,可以方便地保存设备配置信息:
c复制typedef struct {
u16 header; // 0x5A5A作为魔数
u16 version; // 版本号
u32 write_count; // 写入次数计数
char device_name[20]; // 设备名称
u32 reserved[123]; // 保留区域
} Params_t;
这种设计既保证了数据的可读性,又能通过header字段进行数据有效性验证。
3.3 BMP图片读取模块 (bmp_reader.c/h)
这是一个专门用于显示BMP图片的模块,主要特点包括:
- 支持16位(RGB565)和24位(BGR)格式的BMP文件
- 自动处理BMP的存储方向(从上到下或从下到上)
- 支持跨扇区的行数据读取
- 颜色空间转换(24位BGR转RGB565)
模块的核心是BMP_DrawFromSector函数,它从指定的扇区开始读取BMP文件,解析文件头和信息头,然后将像素数据转换后发送到LCD显示。
4. 关键技术与实现细节
4.1 SD卡初始化流程
SD卡的初始化是一个精细的过程,需要严格按照规范操作:
- 上电后发送至少74个时钟周期(实际发送80个)
- 发送CMD0使卡进入SPI模式
- 发送CMD8检查卡是否支持2.0协议
- 通过ACMD41初始化卡
- 读取OCR寄存器确认卡类型
- 设置适当的块大小(标准容量卡需要设置,高容量卡固定为512字节)
初始化过程中最容易出错的是时序控制。每个命令之间需要有足够的延时,特别是上电后的初始阶段。我们的实现中加入了重试机制,提高了初始化的成功率。
4.2 扇区读写实现
扇区读写是SD卡最基本也是最重要的功能。我们的实现考虑了以下关键点:
- 地址处理:标准容量卡(SDSC)使用字节地址,而高容量卡(SDHC/SDXC)使用块地址
- 错误处理:每次操作后检查响应和状态
- 超时控制:避免因卡无响应导致系统挂起
- 忙状态检测:写操作后必须等待卡准备好
读扇区的典型流程:
c复制1. 发送CMD17(读单个块) + 地址
2. 等待数据令牌(0xFE)
3. 读取512字节数据
4. 读取并丢弃2字节CRC
5. 取消片选
写扇区的典型流程:
c复制1. 发送CMD24(写单个块) + 地址
2. 发送数据前导(0xFF, 0xFE)
3. 发送512字节数据
4. 发送2字节伪CRC
5. 检查数据响应
6. 等待卡完成写入
4.3 BMP图片显示优化
BMP图片显示有几个需要特别注意的技术点:
- 行对齐:BMP每行数据会填充到4字节边界
- 存储方向:BMP高度值为正表示倒向存储(从下到上)
- 颜色转换:24位BGR需要转换为RGB565格式
- 跨扇区处理:一行数据可能跨越两个扇区
我们的实现通过以下方式优化性能:
- 使用1024字节的缓冲区,减少小数据量的读取次数
- 提前计算显示窗口,减少LCD命令发送
- 行数据搬移时使用指针操作而非逐个字节复制
5. 常见问题与解决方案
5.1 初始化失败
现象:SD_Init()返回错误,无法识别卡
可能原因及解决:
- 硬件连接问题:检查接线,特别是MISO是否上拉
- 电源不稳定:测量电压,确保在2.7-3.6V范围内
- 时序问题:尝试降低SPI时钟速度(初始化阶段使用低速)
- 卡格式问题:确保卡已格式化为FAT32/exFAT
5.2 读写数据不一致
现象:写入的数据读取后验证失败
排查步骤:
- 检查扇区地址是否正确(特别是SDSC和SDHC的地址差异)
- 确认SPI时钟相位和极性设置正确(模式3)
- 检查写入后是否等待了足够长的时间
- 尝试不同的SD卡,排除卡本身的问题
5.3 BMP显示异常
现象:图片显示颜色错误或错位
调试方法:
- 确认BMP格式(位深度、压缩方式)
- 检查文件头解析是否正确
- 验证颜色转换代码,特别是RGB顺序
- 检查LCD的像素格式设置
5.4 性能优化建议
如果发现读写速度不理想,可以考虑:
- 提高SPI时钟频率(最高可达SD卡支持的最大速度)
- 使用DMA传输减少CPU开销
- 实现多块读写(CMD18/CMD25)
- 优化缓冲区管理,减少小数据量操作
6. 实际应用案例
6.1 数据记录器
基于这个SD卡驱动,我们可以构建一个简单的数据记录系统:
c复制void LogData(float temperature, float humidity)
{
static uint32_t log_sector = 1000; // 起始扇区
static uint16_t record_count = 0;
typedef struct {
uint32_t timestamp;
float temp;
float humi;
} LogEntry;
LogEntry entry;
entry.timestamp = Get_UnixTime();
entry.temp = temperature;
entry.humi = humidity;
// 每个扇区存储4条记录(512/128)
uint8_t sector_offset = record_count % 4;
SD_ReadSector(sd_buffer, log_sector, 1);
memcpy(sd_buffer + sector_offset*sizeof(LogEntry), &entry, sizeof(LogEntry));
SD_WriteSector(sd_buffer, log_sector, 1);
record_count++;
if(record_count % 4 == 0) log_sector++;
}
6.2 图片幻灯片播放
结合BMP读取模块,可以实现一个简单的图片浏览器:
c复制void SlideShow(uint32_t start_sector, uint16_t image_count)
{
uint16_t x = (lcddev.width - 240) / 2; // 居中显示
uint16_t y = (lcddev.height - 320) / 2;
for(uint16_t i = 0; i < image_count; i++)
{
LCD_Clear(WHITE);
if(BMP_DrawFromSector(start_sector + i*800, x, y) != 0)
{
ShowError("Image load failed");
}
delay_ms(3000); // 显示3秒
if(CheckButton()) break; // 按键中断
}
}
7. 扩展与改进方向
7.1 文件系统支持
当前实现是基于扇区的底层操作,可以进一步集成FatFS等文件系统,提供文件级的操作接口。这将大大增强实用性,支持更复杂的应用场景。
7.2 多卡支持
通过修改硬件设计,可以实现多个SD卡槽的切换控制。软件上需要为每个卡维护独立的状态信息。
7.3 错误恢复机制
增强错误处理能力,例如:
- 写入失败后的重试机制
- 坏块检测和管理
- 断电保护功能
7.4 性能监控
添加详细的性能统计功能,如:
- 平均读写速度
- 最大/最小延迟
- 错误率统计
这些数据可以帮助优化系统设计和参数配置。