利用SPI协议模拟SDIO读写SD卡
一、HAL库配置
配置USART1
配置SPI1
配置FATFS
GPIO配置
时钟配置
Serial Wire配置
生成工程
二、移植并添加工程
(一)移植驱动文件
将SDdriver.c
和SDdriver.h
文件添加到工程中
复制到与工程同一路径后后,在工程里面添加SDdriver.c
文件,并添加包含该文件的路径。
(二)修改user_diskio.c文件
①添加包含的头文件
#include "diskio.h" /* Declarations of disk functions */
#include "SDdriver.h"
接下来分别对USER_initialize、USER_status、USER_read、USE_WRITE、USE_IOCTL函数进行修改,将其内容替换为下面对应函数相关内容
②修改USER_initialize
函数
uint8_t res;
res = SD_init();//SD_Initialize()
if(res)//STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常
{
SPI_setspeed(SPI_BAUDRATEPRESCALER_256);
spi_readwrite(0xff);//提供额外的8个时钟
SPI_setspeed(SPI_BAUDRATEPRESCALER_2);
}
if(res)
return STA_NOINIT;
else
return RES_OK; //初始化成功
③修改USER_status
函数
switch (pdrv)
{
case 0 :
return RES_OK;
case 1 :
return RES_OK;
case 2 :
return RES_OK;
default:
return STA_NOINIT;
}
④修改USER_read
函数
uint8_t res;
if( !count )
{
return RES_PARERR; /* count不能等于0,否则返回参数错误 */
}
switch (pdrv)
{
case 0:
res=SD_ReadDisk(buff,sector,count);
if(res == 0){
return RES_OK;
}else{
return RES_ERROR;
}
default:
return RES_ERROR;
}
⑤修改USE_WRITE
函数
uint8_t res;
if( !count )
{
return RES_PARERR; /* count不能等于0,否则返回参数错误 */
}
switch (pdrv)
{
case 0:
res=SD_WriteDisk((uint8_t *)buff,sector,count);
if(res == 0){
return RES_OK;
}else{
return RES_ERROR;
}
default:return RES_ERROR;
}
⑥修改USE_IOCTL
函数
DRESULT res;
switch(cmd)
{
case CTRL_SYNC:
SD_CS(1);
do{
HAL_Delay(20);
}while(spi_readwrite(0xFF)!=0xFF);
res=RES_OK;
SD_CS(0);
break;
case GET_SECTOR_SIZE:
*(WORD*)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(WORD*)buff = 8;
res = RES_OK;
break;
case GET_SECTOR_COUNT:
*(DWORD*)buff = SD_GetSectorCount();
res = RES_OK;
break;
default:
res = RES_PARERR;
break;
}
return res;
(三)main文件配置
包含头文件
#include "SDdriver.h"
在main函数前添加变量与函数定义
uint16_t uart_value[3];
uint8_t aRxBuffer1; //uart rx buff
void WritetoSD(BYTE write_buff[],uint8_t bufSize);
char SD_FileName[] = "test.txt";
uint8_t WriteBuffer[] = "山有木兮\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");
}
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);
}
}
将main函数替代为以下内容
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI1_Init();
MX_FATFS_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1,&aRxBuffer1,1); //enable uart
printf(" mian \r\n");
Get_SDCard_Capacity(); //得到使用内存并选择格式化
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
WritetoSD(WriteBuffer,sizeof(WriteBuffer));
printf("writing\r\n");
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
(四)其他配置及接线
在usart.c中重定向printf()函数
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (unsigned char *)&ch, 1, 0xFFFF); //修改【4】,串口重定向
return ch;
}
在usart.h声明并包含头文件stdio.h
打开Micro LIB
编译完成即可
本实验是基于源码自己重新新建了一个工程,如果怕麻烦,可直接下载源码
三、实例演示
接线方式
SD卡 | stm32系统板 |
---|---|
CS | PA4 |
SCK | PA5 |
MISO | PA6 |
MOSI | PA7 |
VCC | 5V |
GND | GND |
上位机显示
初始化成功,正在写入数据
写数据成功,查看已写好的数据
总结
驱动SD卡的时候,最好将供电电压提高到5V,提高更强的驱动能力。我之前默认使用3.3v由于驱动能力不够,所以一直初始化失败,卡顿了好久。所以基于硬件,稍微有一点有错就会导致整体失败,要特别注意细节。