掌握SD卡协议原理,用STM32F103 完成对SD卡的数据读取(FAT文件模式)。
一、前言
SD卡的读写驱动程序是运用FATFS的基础,学了FATFS就可以在SD卡上创建文件夹及文件了。
1、SD卡
SD存储卡
(Secure Digital Memory Card)是一种基于半导体快闪存储器的新一代高速存储设备。SD存储卡的技术是从MMC卡(MultiMedia Card格式上发展而来,在兼容SD存储卡基础上发展了SDIO(SD Input/ Output)卡,此兼容性包括机械,电子,电力,信号和软件,通常将SD、SDIO卡俗称SD存储卡
一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5 个部分。
存储单元是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;
电源检测单元保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;
卡及接口控制单元控制SD卡的运行状态,它包括有8个寄存器;
接口驱动器控制 SD 卡引脚的输入输出。
驱动模式
SD卡有两种驱动模式:SPI模式与SDIO模式。它们所使用的接口信号是不同的。在SPI模式下,只会用到SD卡的4根信号线,即CS、DI、SCLK与DO(分别是SD卡的片选、数据输入、时钟与数据输出)。
传输模式
SD卡共支持三种传输模式:SPI模式(独立序列输入和序列输出),1位SD模式(独立指令和数据通道,独有的传输格式),4位SD模式(使用额外的针脚以及某些重新设置的针脚。支持四位宽的并行传输)。
本篇文章主要使用
SPI
方式进行验证,关于SPI介绍可以参考小编之前的博客:
https://blog.csdn.net/qq_54496810/article/details/121434661
2、FATFS
FATFS
是一个通用的文件系统(FAT/exFAT)模块,用于在小型嵌入式系统中实现FAT文件系统。 FatFs 组件的编写遵循ANSI C(C89),完全分离于磁盘 I/O 层,因此不依赖于硬件平台。它可以嵌入到资源有限的微控制器中,如 8051, PIC, AVR, ARM, Z80, RX等等,不需要做任何修改。
FATFS提供了以下
文件访问
函数:
f_open – 打开/创建文件
f_close – 关闭打开的文件
f_read – 从文件中读取数据
f_write – 将数据写入文件
f_lseek – 移动读/写指针,扩展大小
f_truncate – 截断文件大小
f_sync – 刷新缓存的数据
f_forward – 将数据转发到流
f_expand – 为文件分配连续块
f_gets – 读取字符串
f_putc – 写一个字符
f_puts – 编写字符串
f_printf – 编写格式化字符串
f_tell – 获取当前读/写指针
f_eof – 测试文件结尾
f_size – 获取尺寸
f_error – 测试错误
关于更多FATFS的详情可参考:
http://elm-chan.org/fsw/ff/00index_e.html
二、工程分析
这里由于小编能力有限,故在已有的工程上进行的修改验证。
具体工程下载链接:
https://pan.baidu.com/s/1L5E4BG8cqvpvtDxOdUUXoQ
提取码:e63q
1、代码分析
下载完成后,解压打开工程
1.1.SD卡写入文件名及写入内容
char SD_FileName[] = "hello.txt";
uint8_t WriteBuffer[] = "小小星亮晶晶 631907030123\n";
1.2.main函数
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
MX_FATFS_Init();
MX_USART1_UART_Init();
HAL_UART_Receive_IT(&huart1,&aRxBuffer1,1); //enable uart
printf(" mian \r\n");
Get_SDCard_Capacity(); //得到使用内存并选择格式化
while (1)
{
WritetoSD(WriteBuffer,sizeof(WriteBuffer)); //写入SD卡
HAL_Delay(500);
WriteBuffer[0] = WriteBuffer[0] +10;
WriteBuffer[1] = WriteBuffer[1] +10;
write_cnt ++;
while(write_cnt > 10)
{
printf(" while \r\n");
HAL_Delay(500);
}
}
}
1.3.SD初始化并得到使用内存函数
void Get_SDCard_Capacity(void)
{
FRESULT result;
FATFS FS;
FATFS *fs;
DWORD fre_clust,AvailableSize,UsedSize;
uint16_t TotalSpace;
uint8_t res;
res = SD_init(); //SD卡初始化
if(res == 1)
{
printf("SD卡初始化失败! \r\n");
}
else
{
printf("SD卡初始化成功! \r\n");
}
/* 挂载 */
res=f_mount(&FS,"0:",1); //挂载
if (res != FR_OK)
{
printf("FileSystem Mounted Failed (%d)\r\n", result);
}
res = f_getfree("0:", &fre_clust, &fs); /* 根目录 */
if ( res == FR_OK )
{
TotalSpace=(uint16_t)(((fs->n_fatent - 2) * fs->csize ) / 2 /1024);
AvailableSize=(uint16_t)((fre_clust * fs->csize) / 2 /1024);
UsedSize=TotalSpace-AvailableSize;
/* Print free space in unit of MB (assuming 512 bytes/sector) */
printf("\r\n%d MB total drive space.\r\n""%d MB available.\r\n""%d MB used.\r\n",TotalSpace, AvailableSize,UsedSize);
}
else
{
printf("Get SDCard Capacity Failed (%d)\r\n", result);
}
}
1.4.SD卡写入函数
void WritetoSD(BYTE write_buff[],uint8_t bufSize)
{
FATFS fs;
FIL file;
uint8_t res=0;
UINT Bw;
res = SD_init(); //SD卡初始化
if(res == 1)
{
printf("SD卡初始化失败! \r\n");
}
else
{
printf("SD卡初始化成功! \r\n");
}
res=f_mount(&fs,"0:",1); //挂载
// if(test_sd == 0) //用于测试格式化
if(res == FR_NO_FILESYSTEM) //没有文件系统,格式化
{
// test_sd =1; //用于测试格式化
printf("没有文件系统! \r\n");
res = f_mkfs("", 0, 0); //格式化sd卡
if(res == FR_OK)
{
printf("格式化成功! \r\n");
res = f_mount(NULL,"0:",1); //格式化后先取消挂载
res = f_mount(&fs,"0:",1); //重新挂载
if(res == FR_OK)
{
printf("SD卡已经成功挂载,可以进进行文件写入测试!\r\n");
}
}
else
{
printf("格式化失败! \r\n");
}
}
else if(res == FR_OK)
{
printf("挂载成功! \r\n");
}
else
{
printf("挂载失败! \r\n");
}
res = f_open(&file,SD_FileName,FA_OPEN_ALWAYS |FA_WRITE);
if((res & FR_DENIED) == FR_DENIED)
{
printf("卡存储已满,写入失败!\r\n");
}
f_lseek(&file, f_size(&file));//确保写词写入不会覆盖之前的数据
if(res == FR_OK)
{
printf("打开成功/创建文件成功! \r\n");
res = f_write(&file,write_buff,bufSize,&Bw); //写数据到SD卡
if(res == FR_OK)
{
printf("文件写入成功! \r\n");
}
else
{
printf("文件写入失败! \r\n");
}
}
else
{
printf("打开文件失败!\r\n");
}
f_close(&file); //关闭文件
f_mount(NULL,"0:",1); //取消挂载
}
2、连线
接线如下:
SD卡 | STM32 |
---|---|
CS | PA4 |
SCK | PA5 |
MISO | PA6 |
MISI | PA7 |
VCC | 5V |
GUN | GUN |
注意:
-
确保SD格式化成FAT文件模式
-
STM32的供电和SD卡模块的供电最好是5V,不然可能带不动
-
连接使用的杜邦线尽可能的短,且连线最好紧一点,稍微的松动都可能写入失败(别问!问就是有经验)
-
可能要等一会才初始化成功,别急(太久就算了,放弃吧!)
3、编译工程并烧录
点击编译按钮进行编译
编译无报错,进行.hex文件烧录
4、验证结果
可以发现串口收到数据,包括总内存、可使用内存以及已使用内存,SD卡初始化成功、挂载成功、文件写入成功等显示,若写入超过10次后,输出变为while(这是自己设置的,可以在while语句中里进行更改,没有什么影响)
使用读卡器打开SD卡可以看到里面生成了一个HELLO.TXT文件
打开该TXT文件发现出现了乱码:第一行正确,后面写入的开始出现问题
5、代码修改
由于写入出现错误,这里小编对main.c文件的主函数里面的while语句进行了修改
while (1)
{
WritetoSD(WriteBuffer,sizeof(WriteBuffer));
HAL_Delay(500);
//WriteBuffer[0] = WriteBuffer[0] +10;
//WriteBuffer[1] = WriteBuffer[1] +10;
write_cnt ++;
while(write_cnt > 10)
{
printf(" while \r\n");
HAL_Delay(500);
}
}
将
WriteBuffer[0] = WriteBuffer[0] +10;
和
WriteBuffer[1] = WriteBuffer[1] +10;
这两句代码注释掉即可
(
6、正确结果
再次编译、烧录,打开写入SD卡的文件可以看到写入正确
三、小结
本次实验其实不难,只是容易出错,遇到了烧录写数据时由于接线不稳导致写入SD卡不成功,工程原代码有错等问题,虽然经过多次尝试才得以完成实验,但在过程中得到了宝贵的经验,可能在分析问题的方面还存在着不足之处,敬请读者指正。
四、参考链接
1.
工程创建详情
2.
https://blog.csdn.net/m0_58414679/article/details/122036435