文章目录
- 一、SD卡协议
- 1.SD卡介绍
- 2.SD卡物理结构
- 2.SD卡寄存器
- 3.SD总线协议
- 4.SD卡的操作模式及切换
- 5.SD卡初始化(SPI模式)
- 6.SD卡读取与写入(SPI模式)
- 二、题目要求
- 三、STM32CubeMX建立工程
- 四、修改文件内容
- 五、连接验证
- 六、总结
- 七、参考资料
一、SD卡协议
1.SD卡介绍
SD存储卡是一种基于半导体快闪记忆器的新一代记忆设备,由于它体积小、数据传输速度快、可热插拔等优良的特性,被广泛地于便携式装置上使用,例如数码相机、平板电脑和多媒体播放器等。
SD卡具有高记忆容量、快速数据传输率、极大的移动灵活性以及很好的安全性,它被广泛地应用于便携式装置上,例如数码相机、平板电脑和多媒体播放器等。
SD卡的结构能保证数字文件传送的安全性,也很容易重新格式化,所以有着广泛的应用领域。音乐、电影等多媒体文件都可以方便地保存到SD卡中。目前市场上SD卡的品牌很多诸如:SANDISK、Kingmax、Panasonic和Kingston。
SD卡作为一种新型的存储设备,具有以下特点:
●高存储容量,最常用的容量:8GB、16GB、32GB、128GB、256GB等。
●内置加密技术,适应基于SDMI协议的著作版权保护功能。
●高速数据传送;最大读写速率为100MB/s。
●体积轻小,便于携带,具有很强的抗冲击能力。
2.SD卡物理结构
一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分,见图 362。存储单元是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;电源检测单元保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;卡及接口控制单元控制SD卡的运行状态,它包括有8个寄存器;接口驱动器控制SD卡引脚的输入输出。
2.SD卡寄存器
SD卡总共有8个寄存器,用于设定或表示SD卡信息,参考表格。这些寄存器只能通过对应的命令访问,对SD卡进行控制操作并不是像操作控制器GPIO相关寄存器那样一次读写一个寄存器的,它是通过命令来控制,SDIO定义了64个命令,每个命令都有特殊意义,可以实现某一特定功能,SD卡接收到命令后,根据命令要求对SD卡内部寄存器进行修改,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。
名称 | bit宽度 | 描述 |
---|
CID | 128 | 卡识别号(Card identification number):用来识别的卡的个体号码(唯一的) |
RCA | 16 | 相对地址(Relative card address):卡的本地系统地址,初始化时,动态地由卡建议,主机核准 |
DSR | 16 | 驱动级寄存器(Driver Stage Register):配置卡的输出驱动 |
CSD | 128 | 卡的特定数据(Card Specific Data):卡的操作条件信息 |
SCR | 64 | SD配置寄存器(CD Configuration Register):SD卡特殊特性信息 |
OCR | 32 | 操作条件寄存器(Operation conditiongs register) |
SSR | 512 | SD状态(SD Status):SD卡专有特征的信息 |
CSR | 32 | 卡状态(Card Status):卡状态信息 |
3.SD总线协议
SD总线通信是基于命令和数据传输的。通讯由一个起始位(“0”),由一个停止位(“1”)终止。SD通信一般是主机发送一个命令(Command),从设备在接收到命令后作出响应(Response),如有需要会有数据(Data)传输参与。
SD数据是以块(Black)形式传输的,SDHC卡数据块长度一般为512字节,数据可以从主机到卡,也可以是从卡到主机。数据块需要CRC位来保证数据传输成功。CRC位由SD卡系统硬件生成。STM32控制器可以控制使用单线或4线传输,本开发板设计使用4线传输。图 365为主机向SD卡写入数据块操作示意。
4.SD卡的操作模式及切换
SD卡有多个版本,STM32控制器目前最高支持《Physical Layer Simplified Specification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。
SD卡系统(包括主机和SD卡)定义了两种操作模式:卡识别模式和数据传输模式。在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。在每个操作模式下,SD卡都有几种状态,参考表 格,通过命令控制实现卡状态的切换。
5.SD卡初始化(SPI模式)
SPI操作模式下:在SD卡收到复位命令时,CS为有效电平(低电平),则SPI模式被启用,在发送CMD之前要先发送74个时钟,64个为内部供电上升时间,10个用于SD卡同步;之后才能开始CMD操作,在初始化时CLK时钟不能超过400KHz。
1、初始化与SD卡连接的硬件条件(MCU的SPI配置,IO口配置);
2、上电延时(>74个CLK);
3、复位卡(CMD0),进入IDLE状态;
4、发送CMD8,检查是否支持2.0协议;
5、根据不同协议检查SD卡(命令包括:CMD55、CMD41、CMD58和CMD1等);
6、取消片选,发多8个CLK,结束初始化
这样我们就完成了对SD卡的初始化,注意末尾发送的8个CLK是提供SD卡额外的时钟,完成某些操作。通过SD卡初始化,我们可以知道SD卡的类型(V1、V2、V2HC或者MMC),在完成了初始化之后,就可以开始读写数据了。
6.SD卡读取与写入(SPI模式)
1、发送CMD17;
2、接收卡响应R1;
3、接收数据起始令牌0XFE;
4、接收数据;
5、接收2个字节的CRC,如果不使用CRC,这两个字节在读取后可以丢掉。
6、禁止片选之后,发多8个CLK;
以上就是一个典型的读取SD卡数据过程,SD卡的写于读数据差不多,写数据通过CMD24来实现,具体过程如下:
1、发送CMD24;
2、接收卡响应R1;
3、发送写数据起始令牌0XFE;
4、发送数据;
5、发送2字节的伪CRC;
6、禁止片选之后,发多8个CLK;
以上就是一个典型的写SD卡过程。
二、题目要求
掌握SD卡协议原理,用STM32F103完成对SD卡的数据读取(fat文件模式)。
三、STM32CubeMX建立工程
1、打开STM32CubeMX新建一个工程。配置管脚,将对应的管脚设置成如图所示的样子。
2、在FATFS中勾选User-defined,其它设置保持默认不变。
3、在SYS中勾选Serial Wire
4、GPIO中默认不配置
5、SPI1使用默认的设置
6、USART1默认配置
7、配置RCC为高速时钟源
8、生成工程,设置保存路径,文件名,使用的编译器和堆栈大小。
四、修改文件内容
1、首先移植两个文件到我们的工程内,在工程目录下复制如图两个文件。我会将完整工程放在文章最后。
2、在工程内添加两个文件
3、修改接口里面的内容到SPI映射上。
我这里直接给出修改的内容:
uint8_t res;
res = SD_init();
if(res)
{
SPI_setspeed(SPI_BAUDRATEPRESCALER_256);
spi_readwrite(0xff);
SPI_setspeed(SPI_BAUDRATEPRESCALER_2);
}
if(res)return STA_NOINIT;
else return RES_OK;
main.c
#include "main.h"
#include "fatfs.h"
#include "SDdriver.h"
SPI_HandleTypeDef hspi1;
UART_HandleTypeDef huart1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
static void MX_USART1_UART_Init(void);
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;
void WritetoSD(BYTE write_buff[],uint8_t bufSize);
char SD_FileName[] = "hello.txt";
uint8_t WriteBuffer[] = "yoga 6319070304** \r\n";
uint8_t write_cnt =0;
void WritetoSD(BYTE write_buff[],uint8_t bufSize)
{
FATFS fs;
FIL file;
uint8_t res=0;
UINT Bw;
res = SD_init();
if(res == 1)
{
printf("SD卡初始化失败! \r\n");
}
else
{
printf("SD卡初始化成功! \r\n");
}
res=f_mount(&fs,"0:",1);
if(res == FR_NO_FILESYSTEM)
{
printf("没有文件系统! \r\n");
res = f_mkfs("", 0, 0);
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);
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();
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;
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);
}
}
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);
printf(" OK \r\n");
Get_SDCard_Capacity();
while (1)
{
WritetoSD(WriteBuffer,sizeof(WriteBuffer));
HAL_Delay(500);
write_cnt ++;
while(write_cnt > 10)
{
printf(" while \r\n");
HAL_Delay(500);
}
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
}
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_RESET);
GPIO_InitStruct.Pin = SD_CS_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(SD_CS_GPIO_Port, &GPIO_InitStruct);
}
void Error_Handler(void)
{
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
主函数内用一个OK来证明是否reset,这里可以你自己来设置这个标记字符串。
在SD卡写入这里将如图两行代码注释掉,否则输出会有乱码。
在主函数内改写你想写入SD卡内的内容,还可以改写文件保存类型。
五、连接验证
准备好一块SD卡:
连线方式如表格所示:这里供电一定要使用5V,否则可能无法初始化,因为驱动能力不行。
SD卡模块 | STM32 |
---|
CS | PA4 |
SCK | PA5 |
MOSI | PA6 |
MISO | PA7 |
VCC | 5V |
GND | GND |
连线结果实物图:
串口测试:
如图显示初始化成功,挂载成功。
最后用读卡器打开SD卡看到如图所示内容,与我们工程中编写的内容一致,说明成功完成实验。
六、总结
本次实验主要使用了SD卡,深入了解了SD卡的协议相关以及如何对SD卡进行初始化和使用,在使用过程中总是出现挂载失败或者没有显示的问题,这不一定是我们的程序的问题,这里就是因为供电幅值,或者接触不良,或者USB连接有问题导致的。
七、参考资料
https://blog.csdn.net/flyleaf91/article/details/52325539/?ops_request_misc=&request_id=&biz_id=102&utm_term=SD%E5%8D%A1%E7%89%A9%E7%90%86%E7%BB%93%E6%9E%84&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-3-52325539.pc_search_result_control_group&spm=1018.2226.3001.4187
https://blog.csdn.net/qq_46467126/article/details/122033766
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)