基于STM32硬件SPI模拟SPI协议驱动SD卡

一、硬件连接

1. 引脚映射(以STM32F103为例)
STM32引脚功能SD卡引脚
PA4CS片选CS
PA5SCK时钟CLK
PA7MOSI主出从入DI
PA6MISO主入从出DO
3.3V电源VDD
GNDGND
2. 电路设计要点
  • 电平匹配:STM32的GPIO为3.3V电平,需确保SD卡模块支持3.3V逻辑电平
  • 上拉电阻:MOSI和SCK线需接10kΩ上拉电阻
  • 滤波电容:在VDD和GND间并联10μF电解电容+0.1μF陶瓷电容

二、SPI时序模拟实现

1. GPIO初始化
// 硬件SPI引脚配置(HAL库)
void SPI_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 启用GPIO时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    // SCK/MOSI配置(推挽输出)
    GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // MISO配置(浮空输入)
    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // CS引脚配置(推挽输出)
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
2. SPI时序控制函数
// 发送1字节数据(含时钟生成)
uint8_t SPI_WriteByte(uint8_t data) {
    uint8_t rx_data = 0;
    
    for(int i=0; i<8; i++) {
        // 发送位
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, (data & 0x80) ? GPIO_PIN_SET : GPIO_PIN_RESET);
        HAL_Delay(1);  // 保持时间>30ns
        
        // 产生时钟上升沿
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);
        HAL_Delay(1);  // 时钟高电平维持>30ns
        
        // 采样MISO数据
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_SET) {
            rx_data |= (1 << (7 - i));
        }
        
        // 时钟下降沿
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);
        HAL_Delay(1);
        
        data <<= 1;
    }
    return rx_data;
}

三、SD卡驱动核心代码

1. 初始化流程
#define CMD0    0x40  // 复位命令
#define CMD8    0x48  // 电压检测
#define ACMD41  0x60  // 初始化命令

uint8_t SD_Init(void) {
    uint8_t r1;
    uint16_t retry;
    
    // 发送74+时钟脉冲
    for(retry=0; retry<10; retry++) {
        SPI_WriteByte(0xFF);
    }
    
    // 进入SPI模式
    r1 = SD_SendCommand(CMD0, 0, 0x95);
    if(r1 != 0x01) return 1;  // 复位失败
    
    // 检测SD卡版本
    r1 = SD_SendCommand(CMD8, 0x1AA, 0x87);
    if(r1 != 0x01) return 2;  // 不支持CMD8
    
    // 初始化SD卡
    for(retry=0; retry<200; retry++) {
        r1 = SD_SendCommand(ACMD41, 0x40000000, 0x01);
        if(r1 == 0x00) break;
        HAL_Delay(1);
    }
    if(retry >= 200) return 3;  // 初始化超时
    
    return 0;  // 成功
}
2. 命令发送函数
uint8_t SD_SendCommand(uint8_t cmd, uint32_t arg, uint8_t crc) {
    uint8_t r1;
    
    // 取消片选
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
    
    // 发送命令
    SPI_WriteByte(cmd | 0x40);  // 添加起始位
    SPI_WriteByte((arg >> 24) & 0xFF);
    SPI_WriteByte((arg >> 16) & 0xFF);
    SPI_WriteByte((arg >> 8) & 0xFF);
    SPI_WriteByte(arg & 0xFF);
    SPI_WriteByte(crc);
    
    // 等待响应
    for(int i=0; i<1000; i++) {
        r1 = SPI_WriteByte(0xFF);
        if((r1 & 0x80) == 0) break;
    }
    
    return r1;
}

四、数据读写实现

1. 单块读取(CMD17)
uint8_t SD_ReadBlock(uint32_t addr, uint8_t* buffer) {
    uint8_t r1;
    
    // 发送读块命令
    r1 = SD_SendCommand(CMD17, addr, 0xFF);
    if(r1 != 0x00) return 1;
    
    // 等待数据起始令牌
    for(int i=0; i<1000; i++) {
        r1 = SPI_WriteByte(0xFF);
        if(r1 == 0xFE) break;
    }
    
    // 读取512字节数据
    for(int i=0; i<512; i++) {
        buffer[i] = SPI_WriteByte(0xFF);
    }
    
    // 读取CRC
    SPI_WriteByte(0xFF);
    SPI_WriteByte(0xFF);
    
    return 0;
}
2. 单块写入(CMD24)
uint8_t SD_WriteBlock(uint32_t addr, uint8_t* buffer) {
    uint8_t r1;
    
    // 发送写块命令
    r1 = SD_SendCommand(CMD24, addr, 0xFF);
    if(r1 != 0x00) return 1;
    
    // 发送数据起始令牌
    SPI_WriteByte(0xFE);
    
    // 写入512字节数据
    for(int i=0; i<512; i++) {
        SPI_WriteByte(buffer[i]);
    }
    
    // 接收CRC
    SPI_WriteByte(0xFF);
    SPI_WriteByte(0xFF);
    
    return 0;
}

参考代码 stm32单片机,硬件SPI模拟spi协议驱动sd卡 www.youwenfan.com/contentcsj/69803.html

五、调试技巧

1. 逻辑分析仪验证时序
  • 捕获SCK、MOSI、MISO信号,检查: 时钟频率:初始化阶段≤400kHz,正常模式≤25MHz 数据有效性:MOSI在时钟上升沿前稳定,MISO在下降沿后采样
2. 常见问题处理
现象可能原因解决方案
初始化超时电源不稳定检查电源滤波,增加去耦电容
命令无响应片选信号异常检查CS引脚电平,确保低电平有效
数据校验失败时钟频率过高降低SPI时钟频率(≤10MHz)
写入数据错误未等待写入完成增加写操作后的等待时间

六、性能优化

  1. DMA传输

    使用STM32 DMA直接传输数据,减少CPU负载:

    // DMA配置示例(HAL库)
    DMA_HandleTypeDef hdma_spi1_tx;
    
    hdma_spi1_tx.Instance = DMA1_Channel3;
    hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
    HAL_DMA_Init(&hdma_spi1_tx);
    
  2. 硬件CRC加速

    启用STM32硬件CRC模块:

    __HAL_RCC_CRC_CLK_ENABLE();
    CRC_HandleTypeDef hcrc;
    hcrc.Instance = CRC;
    HAL_CRC_Init(&hcrc);
    

七、工程结构

SD_Driver/
├── Src/
│   ├── main.c          # 主程序
│   ├── spi.c           # SPI底层驱动
│   └── sdcard.c        # SD卡协议实现
├── Inc/
│   ├── spi.h           # SPI函数声明
│   └── sdcard.h        # SD卡函数声明
└── Middlewares/
    └── FATFS/          # 可选文件系统

八、扩展功能实现

  1. FATFS文件系统移植

    在标准SPI读写基础上添加文件操作接口:

    FRESULT f_open(FIL* fp, const TCHAR* path, BYTE mode);
    FRESULT f_write(FIL* fp, const void* buff, UINT btw, UINT* bw);
    
  2. 多卡支持 通过GPIO矩阵切换多个SD卡片选信号,实现多卡轮询访问。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值