一、SD卡
1.SD卡简述
SD存储卡(Secure Digital Memory Card)是一种基于半导体快闪存储器的新一代高速存储设备。SD存储卡的技术是从MMC卡(MultiMedia Card格式上发展而来,在兼容SD存储卡基础上发展了SDIO(SD Input/ Output)卡,此兼容性包括机械,电子,电力,信号和软件,通常将SD、SDIO卡俗称SD存储卡。
2.SD卡的特点
SD卡具有高记忆容量、快速数据传输率、极大的移动灵活性以及很好的安全性,它被广泛地应用于便携式装置上,例如数码相机、平板电脑和多媒体播放器等。
SD卡的结构能保证数字文件传送的安全性,也很容易重新格式化,所以有着广泛的应用领域。音乐、电影等多媒体文件都可以方便地保存到SD卡中。目前市场上SD卡的品牌很多诸如:SANDISK、Kingmax、Panasonic和Kingston。
SD卡作为一种新型的存储设备,具有以下特点:
SD卡特点 |
---|
高存储容量,最常用的容量:8GB、16GB、32GB、128GB、256GB等 |
内置加密技术,适应基于SDMI协议的著作版权保护功能 |
高速数据传送;最大读写速率为100MB/s |
体积轻小,便于携带,具有很强的抗冲击能力 |
3.SD卡的寄存器
SD卡总共有8个寄存器,用于设定或表示SD卡信息。
这些寄存器只能通过对应的命令访问,SDIO定义64个命令,每个命令都有特殊意义,可以实现某一特定功能。
寄存器及功能描述如下
寄存器名称 | 简述 |
---|---|
OCR(Operating Conditions Register)寄存器 | 32位的操作条件寄存器主要存储了VDD电压范围,SD卡操作电压范围为2~3.6V |
CID(Card IDentification Register)寄存器 | 卡识别码寄存器,长度为16个字节,存储SD卡唯一标识号,该号在卡生产厂家编程后无法修改 |
CSD(Card-Specific Data Register)寄存器 | 卡特性数据寄存器,包含了访问该卡数据时的必要配置信息 |
SCR(SD Card Configuration Register)寄存器 | SD卡配置寄存器(SCR),提供了SD卡的一些特殊特性在这张卡内,长度为64位,这个寄存器内容由制造商在生产厂内设置 |
RCA(Relative Card Address)寄存器 | 卡相对地址寄存器是一个16位可写的地址寄存器,控制器可通过地址选择对应地址的SD卡 |
DSR(Driver Stage Register)寄存器 | 驱动级寄存器,属于可选寄存器,用于配置卡的驱动输出 |
4.SPI协议
SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)、CS(片选)。
- SPI物理层
总线名称 | 简介 |
---|---|
SCK(Serial Clock) | 时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,两个设备之间通讯时,通讯速率受限于低速设备 |
MOSI(Master Output, Slave Input) | 主设备输出/从设备输入引脚。这条线上的数据从主机传送到从机 |
MISO(Master Input,,Slave Output) | 主设备输入/从设备输出引脚。这条线上的数据从从机传送到主机 |
SS(Slave Select) | 又叫NSS,片选信号线。低电平时该从设备被选中 |
- SPI协议层
与 I2C的类似,SPI协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。
SPI通信时序
二、实验操作——对SD卡进行数据读取
1.硬件连接
主要用到两个硬件:STM32F103和SD卡
- SD卡
- 硬件连接方式
stm32 | SD卡模块 |
---|---|
PA4 | SDCS |
PA5 | SCK |
PA6 | MISO |
PA7 | MOSI |
VCC | VCC |
GND | GND |
注意:VCC要接5V!!!
2.建立工程文件
详细的工程文件获取:
链接:https://pan.baidu.com/s/1MFVI8M022kbmuOlGbAl6aw
提取码:xhc1
工程文件是基于CubeMX得到的,下面介绍一下本次实作需要注意的地方
- USART1选择Asynchronous,异步模式
- SPI1选择Full-Duplex Master,全双工主模式
- FATFS中勾选User-defined
3.代码部分
代码如下:
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (unsigned char *)&ch, 1, 0xFFFF);
return ch;
}
uint16_t uart_value[3];
uint8_t aRxBuffer1; //uart rx buff
void WritetoSD(BYTE write_buff[],uint8_t bufSize);
char SD_FileName[] = "hello.txt";
uint8_t WriteBuffer[] = "01 write buff to sd \r\n";
//uint8_t test_sd =0; //用于测试格式化
uint8_t write_cnt =0; //写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); //取消挂载
}
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);
}
}
针对SD卡文件的每个步骤,我们都进行了相应的字段输出,来具体判断究竟进行到了什么地步
4.烧录与输出结果
- 当我们执行程序后,就可以将生成的hex文件烧录进入单片机中
- 打开串口助手,观察输出结果
- 通过读卡器,接入SD卡,发现有输出,打开输出文本,结果显示乱码
- 更改代码部分如下
- 再次观察,结果显示正常
总结
这篇博客主要简述了SD卡的特点和其寄存器的类型和作用,再一次阐释了SD卡中的SPI协议,并将SPI协议分为物理层和协议层进行论述。最后通过实际的例子,显示出的结果,展示了SD卡的工作特点。其实践过程有不少的困难,但是只要注意到一些细节,回过头来总结,发现也没有特别困难。