下面是一个基于STM32F103系列微控制器的SD卡读写完整程序,使用标准外设库(StdPeriph)和FatFs文件系统。
硬件准备
- STM32F103C8T6开发板(或其他F103系列)
- SD卡模块(SPI接口)
- 连接线缆
硬件连接
SD卡模块 STM32F103
CS -> PA4 (SPI1_NSS)
SCK -> PA5 (SPI1_SCK)
MISO -> PA6 (SPI1_MISO)
MOSI -> PA7 (SPI1_MOSI)
VCC -> 3.3V
GND -> GND
完整代码
1. 主程序 (main.c)
c
#include "stm32f10x.h"
#include "ff.h"
#include "diskio.h"
#include "spi_sd.h"
#include <stdio.h>
#include <string.h>
FATFS fs; /* FatFs文件系统对象 */
FIL fil; /* 文件对象 */
FRESULT res; /* FatFs函数返回结果 */
UINT bw; /* 读写字节数 */
void RCC_Configuration(void);
void GPIO_Configuration(void);
void USART1_Init(void);
int main(void)
{
char buffer[128];
/* 系统时钟配置 */
RCC_Configuration();
/* GPIO配置 */
GPIO_Configuration();
/* USART1初始化 */
USART1_Init();
printf("STM32F103 SD Card Test\r\n");
/* 挂载文件系统 */
if(f_mount(&fs, "0:", 1) != FR_OK)
{
printf("Mount SD Card Failed!\r\n");
while(1);
}
printf("SD Card Mounted Successfully!\r\n");
/* 打开/创建文件 */
res = f_open(&fil, "0:test.txt", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
if(res != FR_OK)
{
printf("Open File Failed!\r\n");
while(1);
}
/* 写入数据 */
f_lseek(&fil, 0);
f_printf(&fil, "Hello, STM32 SD Card!\r\n");
f_printf(&fil, "This is a test file.\r\n");
/* 读取数据 */
f_lseek(&fil, 0);
while(f_gets(buffer, sizeof(buffer), &fil))
{
printf("%s", buffer);
}
/* 关闭文件 */
f_close(&fil);
/* 卸载文件系统 */
f_mount(NULL, "0:", 1);
while(1)
{
}
}
void RCC_Configuration(void)
{
/* 复位RCC时钟配置 */
RCC_DeInit();
/* 使能外部高速晶振 */
RCC_HSEConfig(RCC_HSE_ON);
/* 等待HSE就绪 */
if(RCC_WaitForHSEStartUp() == SUCCESS)
{
/* 设置HCLK = SYSCLK */
RCC_HCLKConfig(RCC_SYSCLK_Div1);
/* 设置PCLK2 = HCLK */
RCC_PCLK2Config(RCC_HCLK_Div1);
/* 设置PCLK1 = HCLK/2 */
RCC_PCLK1Config(RCC_HCLK_Div2);
/* 设置FLASH延时 */
FLASH_SetLatency(FLASH_Latency_2);
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
/* 设置PLL时钟源为HSE, 9倍频 */
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
/* 使能PLL */
RCC_PLLCmd(ENABLE);
/* 等待PLL就绪 */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
/* 设置PLL为系统时钟源 */
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
/* 等待系统时钟源切换完成 */
while(RCC_GetSYSCLKSource() != 0x08);
}
/* 使能SPI1时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
/* 使能GPIOA时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* 使能USART1时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 配置SPI1引脚: SCK, MISO, MOSI */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 配置SPI1 CS引脚 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_4); // CS高电平
/* 配置USART1 TX引脚 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 配置USART1 RX引脚 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void USART1_Init(void)
{
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
/* 重定向printf到USART1 */
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t)ch);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return ch;
}
2. SPI和SD卡驱动 (spi_sd.c)
c
#include "spi_sd.h"
#include "stm32f10x.h"
#include "delay.h"
/* SPI1读写一个字节 */
uint8_t SPI1_ReadWriteByte(uint8_t TxData)
{
/* 等待发送缓冲区空 */
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
/* 发送数据 */
SPI_I2S_SendData(SPI1, TxData);
/* 等待接收缓冲区非空 */
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
/* 返回接收到的数据 */
return SPI_I2S_ReceiveData(SPI1);
}
/* SPI1初始化 */
void SPI1_Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
/* SPI1配置 */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
/* CS高电平 */
SD_CS_HIGH();
}
/* SD卡初始化 */
uint8_t SD_Initialize(void)
{
uint8_t retry = 0;
uint8_t i;
uint8_t buf[4];
uint16_t count = 0;
/* 初始化SPI */
SPI1_Init();
/* 延时至少74个时钟周期 */
SD_CS_HIGH();
for(i = 0; i < 10; i++)
{
SPI1_ReadWriteByte(0xFF);
}
/* 发送CMD0进入空闲状态 */
while(SD_SendCmd(CMD0, 0, 0x95) != 0x01)
{
retry++;
if(retry > 100)
{
return 1; // 超时
}
}
/* 发送CMD8检查SD卡版本 */
if(SD_SendCmd(CMD8, 0x1AA, 0x87) == 0x01)
{
/* SD卡v2.0 */
for(i = 0; i < 4; i++)
{
buf[i] = SPI1_ReadWriteByte(0xFF);
}
/* 初始化SD卡 */
while(SD_SendCmd(ACMD41, 0x40000000, 0x01) != 0x00)
{
count++;
if(count > 0xFFFE)
{
return 2; // 初始化失败
}
}
/* 读取OCR寄存器 */
if(SD_SendCmd(CMD58, 0, 0x01) != 0x00)
{
return 3;
}
for(i = 0; i < 4; i++)
{
buf[i] = SPI1_ReadWriteByte(0xFF);
}
/* 检查CCS位 */
if(buf[0] & 0x40)
{
CardType = SD_TYPE_V2HC; // SDHC/SDXC
}
else
{
CardType = SD_TYPE_V2; // SDv2
}
}
else
{
/* SD卡v1.x或MMC */
if(SD_SendCmd(ACMD41, 0, 0x01) <= 1)
{
/* SD卡v1.x */
CardType = SD_TYPE_V1;
count = 0;
/* 等待初始化完成 */
while(SD_SendCmd(ACMD41, 0, 0x01) != 0x00)
{
count++;
if(count > 0xFFFE)
{
return 4; // 初始化失败
}
}
}
else
{
/* MMC卡 */
CardType = SD_TYPE_MMC;
count = 0;
/* 等待初始化完成 */
while(SD_SendCmd(CMD1, 0, 0x01) != 0x00)
{
count++;
if(count > 0xFFFE)
{
return 5; // 初始化失败
}
}
}
}
/* 设置SPI时钟为高速模式 */
SPI1_SetSpeed(SPI_BaudRatePrescaler_4);
return 0; // 初始化成功
}
/* 发送SD卡命令 */
uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg, uint8_t crc)
{
uint8_t res;
/* 等待SD卡准备就绪 */
SD_WaitReady();
/* 发送命令 */
SD_CS_LOW();
SPI1_ReadWriteByte(cmd | 0x40);
SPI1_ReadWriteByte(arg >> 24);
SPI1_ReadWriteByte(arg >> 16);
SPI1_ReadWriteByte(arg >> 8);
SPI1_ReadWriteByte(arg);
SPI1_ReadWriteByte(crc);
/* 等待响应 */
if(cmd == CMD12)
{
SPI1_ReadWriteByte(0xFF); // 跳过停止命令后的一个字节
}
/* 等待响应 */
uint8_t retry = 0;
do
{
res = SPI1_ReadWriteByte(0xFF);
retry++;
} while((res & 0x80) && (retry < 200));
return res;
}
/* 等待SD卡准备就绪 */
uint8_t SD_WaitReady(void)
{
uint32_t timeout = 0;
do
{
if(SPI1_ReadWriteByte(0xFF) == 0xFF)
{
return 0;
}
timeout++;
} while(timeout < 0xFFFFFF);
return 1;
}
/* 读取SD卡一个扇区 */
uint8_t SD_ReadDisk(uint8_t *buf, uint32_t sector, uint8_t cnt)
{
uint8_t res;
if(CardType == SD_TYPE_V2HC)
{
sector *= 512; // SDHC/SDXC卡使用字节地址
}
if(cnt == 1)
{
/* 读取单个块 */
res = SD_SendCmd(CMD17, sector, 0x01);
if(res == 0)
{
/* 等待数据开始标记 */
if(!SD_RecvData(buf, 512))
{
res = 1;
}
}
}
else
{
/* 读取多个块 */
res = SD_SendCmd(CMD18, sector, 0x01);
do
{
if(!SD_RecvData(buf, 512))
{
res = 1;
break;
}
buf += 512;
} while(--cnt);
/* 发送停止传输命令 */
SD_SendCmd(CMD12, 0, 0x01);
}
SD_CS_HIGH();
SPI1_ReadWriteByte(0xFF);
return res;
}
/* 接收数据 */
uint8_t SD_RecvData(uint8_t *buf, uint16_t len)
{
uint16_t i;
uint8_t res;
/* 等待数据开始标记 */
uint16_t retry = 0;
do
{
res = SPI1_ReadWriteByte(0xFF);
retry++;
} while((res == 0xFF) && (retry < 0xFFFF));
if(res != 0xFE)
{
return 1; // 数据开始标记错误
}
/* 接收数据 */
for(i = 0; i < len; i++)
{
buf[i] = SPI1_ReadWriteByte(0xFF);
}
/* 接收CRC */
SPI1_ReadWriteByte(0xFF);
SPI1_ReadWriteByte(0xFF);
return 0;
}
/* 写入SD卡一个扇区 */
uint8_t SD_WriteDisk(const uint8_t *buf, uint32_t sector, uint8_t cnt)
{
uint8_t res;
if(CardType == SD_TYPE_V2HC)
{
sector *= 512; // SDHC/SDXC卡使用字节地址
}
if(cnt == 1)
{
/* 写入单个块 */
res = SD_SendCmd(CMD24, sector, 0x01);
if(res == 0)
{
/* 发送数据开始标记 */
if(!SD_SendData(buf, 0xFE))
{
res = 1;
}
}
}
else
{
/* 写入多个块 */
if(CardType != SD_TYPE_MMC)
{
SD_SendCmd(CMD55, 0, 0x01);
SD_SendCmd(CMD23, cnt, 0x01);
}
res = SD_SendCmd(CMD25, sector, 0x01);
if(res == 0)
{
do
{
if(!SD_SendData(buf, 0xFC))
{
res = 1;
break;
}
buf += 512;
} while(--cnt);
/* 发送停止传输标记 */
if(!SD_SendData(0, 0xFD))
{
res = 1;
}
}
}
SD_CS_HIGH();
SPI1_ReadWriteByte(0xFF);
return res;
}
/* 发送数据 */
uint8_t SD_SendData(const uint8_t *buf, uint8_t token)
{
uint16_t i;
uint8_t res;
/* 等待SD卡准备就绪 */
SD_WaitReady();
/* 发送数据开始标记 */
SPI1_ReadWriteByte(token);
/* 发送数据 */
if(token != 0xFD)
{
for(i = 0; i < 512; i++)
{
SPI1_ReadWriteByte(buf[i]);
}
/* 发送CRC */
SPI1_ReadWriteByte(0xFF);
SPI1_ReadWriteByte(0xFF);
/* 获取数据响应 */
res = SPI1_ReadWriteByte(0xFF);
if((res & 0x1F) != 0x05)
{
return 1;
}
}
/* 等待SD卡完成写入 */
SD_WaitReady();
return 0;
}
/* 设置SPI速度 */
void SPI1_SetSpeed(uint16_t Speed)
{
SPI_Cmd(SPI1, DISABLE);
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = Speed;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
}
3. 头文件 (spi_sd.h)
c
#ifndef __SPI_SD_H
#define __SPI_SD_H
#include "stm32f10x.h"
/* SD卡类型定义 */
#define SD_TYPE_ERR 0x00
#define SD_TYPE_MMC 0x01
#define SD_TYPE_V1 0x02
#define SD_TYPE_V2 0x04
#define SD_TYPE_V2HC 0x06
/* SD卡命令定义 */
#define CMD0 0 // 复位SD卡
#define CMD1 1
#define CMD8 8 // 发送接口条件
#define CMD9 9 // 读取CSD数据
#define CMD10 10 // 读取CID数据
#define CMD12 12 // 停止数据传输
#define CMD16 16 // 设置块大小
#define CMD17 17 // 读取单个块
#define CMD18 18 // 读取多个块
#define CMD23 23 // 设置预擦除块数
#define CMD24 24 // 写入单个块
#define CMD25 25 // 写入多个块
#define CMD41 41 // 发送主机容量支持信息
#define CMD55 55 // 应用特定命令
#define CMD58 58 // 读取OCR寄存器
#define CMD59 59 // 设置CRC开/关
#define ACMD41 0x41 // ACMD41
/* SD卡片选控制 */
#define SD_CS_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_4)
#define SD_CS_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_4)
/* 全局变量 */
extern uint8_t CardType;
/* 函数声明 */
uint8_t SD_Initialize(void);
uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg, uint8_t crc);
uint8_t SD_WaitReady(void);
uint8_t SD_ReadDisk(uint8_t *buf, uint32_t sector, uint8_t cnt);
uint8_t SD_WriteDisk(const uint8_t *buf, uint32_t sector, uint8_t cnt);
uint8_t SD_RecvData(uint8_t *buf, uint16_t len);
uint8_t SD_SendData(const uint8_t *buf, uint8_t token);
void SPI1_Init(void);
uint8_t SPI1_ReadWriteByte(uint8_t TxData);
void SPI1_SetSpeed(uint16_t Speed);
#endif
4. FatFs磁盘接口 (diskio.c)
c
#include "diskio.h"
#include "spi_sd.h"
/* 获取磁盘状态 */
DSTATUS disk_status(BYTE pdrv)
{
return 0;
}
/* 初始化磁盘 */
DSTATUS disk_initialize(BYTE pdrv)
{
if(SD_Initialize() == 0)
{
return 0;
}
return STA_NOINIT;
}
/* 读取扇区 */
DRESULT disk_read(BYTE pdrv, BYTE *buff, DWORD sector, UINT count)
{
if(SD_ReadDisk(buff, sector, count) == 0)
{
return RES_OK;
}
return RES_ERROR;
}
/* 写入扇区 */
DRESULT disk_write(BYTE pdrv, const BYTE *buff, DWORD sector, UINT count)
{
if(SD_WriteDisk(buff, sector, count) == 0)
{
return RES_OK;
}
return RES_ERROR;
}
/* 其他控制命令 */
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff)
{
switch(cmd)
{
case CTRL_SYNC:
return RES_OK;
case GET_SECTOR_COUNT:
*(DWORD*)buff = 1024*1024; // 假设SD卡容量为512MB
return RES_OK;
case GET_SECTOR_SIZE:
*(WORD*)buff = 512;
return RES_OK;
case GET_BLOCK_SIZE:
*(DWORD*)buff = 8;
return RES_OK;
default:
return RES_PARERR;
}
}
5. 头文件 (diskio.h)
c
#ifndef _DISKIO
#define _DISKIO
#include "integer.h"
/* 状态码 */
#define STA_NOINIT 0x01 /* 驱动器未初始化 */
#define STA_NODISK 0x02 /* 无介质 */
#define STA_PROTECT 0x04 /* 写保护 */
/* 命令码 */
#define CTRL_SYNC 0 /* 完成挂起的写入过程 */
#define GET_SECTOR_COUNT 1 /* 获取扇区数 */
#define GET_SECTOR_SIZE 2 /* 获取扇区大小 */
#define GET_BLOCK_SIZE 3 /* 获取擦除块大小 */
#define CTRL_POWER 4 /* 控制电源状态 */
#define CTRL_LOCK 5 /* 锁定/解锁介质移除 */
#define CTRL_EJECT 6 /* 弹出介质 */
#define MMC_GET_TYPE 10 /* 获取卡类型 */
#define MMC_GET_CSD 11 /* 读取CSD */
#define MMC_GET_CID 12 /* 读取CID */
#define MMC_GET_OCR 13 /* 读取OCR */
#define MMC_GET_SDSTAT 14 /* 读取SD状态 */
/* 结果码 */
typedef enum {
RES_OK = 0, /* 成功 */
RES_ERROR, /* 读写错误 */
RES_WRPRT, /* 写保护 */
RES_NOTRDY, /* 设备未就绪 */
RES_PARERR /* 无效参数 */
} DRESULT;
/* 函数声明 */
DSTATUS disk_initialize(BYTE pdrv);
DSTATUS disk_status(BYTE pdrv);
DRESULT disk_read(BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
DRESULT disk_write(BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff);
#endif
使用说明
- 将上述代码分别保存到对应的文件中
- 创建一个新的STM32工程,添加这些文件
- 需要包含FatFs文件系统(可以从官网http://elm-chan.org/fsw/ff/00index_e.html下载)
- 编译并下载到STM32F103开发板
- 连接SD卡模块
- 通过串口调试工具(如Putty)查看输出结果
功能说明
这个程序实现了以下功能:
- 初始化SD卡(支持SDv1, SDv2和SDHC卡)
- 挂载FAT文件系统
- 创建/打开文件"test.txt"
- 向文件写入两行文本
- 从文件读取内容并通过串口输出
- 关闭文件并卸载文件系统
注意事项
- 确保SD卡已格式化为FAT16或FAT32文件系统
- 如果使用不同的SPI引脚,需要修改GPIO配置
- SD卡工作电压为3.3V,不要使用5V电压
- 如果读写失败,可以尝试降低SPI时钟速度
希望这个完整的SD卡读写程序对你有帮助!