手里有个SD卡的模块,早就想找机会玩玩。最近用STM32Cube用的比较顺手,而且刚好软件内部提供了FATFS的中间件,所以试试用FATFS文件系统来访问,操作SD卡。模块图片如下:
刚好是SPI接口的!SPI接口好久不用了,之前调试VS1003B的时候用过,用的还是ATMEL的硬件SPI接口。
用法比较简单,接口配置还是比较简单的,主要是文件系统对应的驱动那里需要修改。这部分个人经验不多,借鉴了网上一些网友分享的代码。简单说下步骤:
1、系统核心配置
这里我是用USB的虚拟串口打印调试信息,结合上一节手写的printf函数,使用比较方便,可以直接在串口助手上看到相关信息。
PA4是SPI的片选;PA15是USB接口的上拉电阻。RCC选择外部晶体,SYS选择Serial Wire。
2、接口部分
接口这里选择SPI1和USB。USB部分打勾即可;SPI1这里模式选择如上,别的地方不用动。
3、中间件部分
FATFS部分,右边Mode里打勾,别的地方不动。USB部分,跟前面配置USB虚拟串口CDC一样,选择VPC,记得改一下VID和PID。
4、时钟部分
这里注意两个地方:1、USB部分的时钟必须为48MHZ,前面说过很多遍了,不解释。2、SPI1的时钟频率最高不能超过18MHZ,所以要注意。
5、生成代码
扩大一下堆栈,然后就可以生成代码了。
6、添加SD卡驱动
因为CUBE无法生成SPI接口驱动SD卡的程序,所以这部分需要我们自己想办法。感谢万能的网友,提供了SD的驱动。添加到工程里。
7、修改usbd_cdc_if.c文件
跟之前文章提到的一样,定义usb_printf函数,并在usbd_cdc_if.h文件中添加函数声明。
/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
#include <stdarg.h>
void usb_printf(const char *format, ...)
{
va_list args;
uint32_t length;
va_start(args, format);
length = vsnprintf((char *)UserTxBufferFS, APP_TX_DATA_SIZE, (char *)format, args);
va_end(args);
CDC_Transmit_FS(UserTxBufferFS, length);
}
/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
8、修改user_diskio.c文件
这个文件是CUBE软件提供的,里面包含操作SD的接口函数,所以要把相应的SPI操作放进相应的函数里。修改部分如下:
#include "diskio.h" /* Declarations of disk functions */
#include "SDdriver.h"
/**
* @brief Initializes a Drive
* @param pdrv: Physical drive number (0..)
* @retval DSTATUS: Operation status
*/
DSTATUS USER_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
/* USER CODE BEGIN INIT */
// Stat = STA_NOINIT;
// return Stat;
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 CODE END INIT */
}
/**
* @brief Gets Disk Status
* @param pdrv: Physical drive number (0..)
* @retval DSTATUS: Operation status
*/
DSTATUS USER_status (
BYTE pdrv /* Physical drive number to identify the drive */
)
{
/* USER CODE BEGIN STATUS */
// Stat = STA_NOINIT;
// return Stat;
switch (pdrv)
{
case 0 :
return RES_OK;
case 1 :
return RES_OK;
case 2 :
return RES_OK;
default:
return STA_NOINIT;
}
/* USER CODE END STATUS */
}
/**
* @brief Reads Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data buffer to store read data
* @param sector: Sector address (LBA)
* @param count: Number of sectors to read (1..128)
* @retval DRESULT: Operation result
*/
DRESULT USER_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to read */
)
{
/* USER CODE BEGIN READ */
// return RES_OK;
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;
}
/* USER CODE END READ */
}
/**
* @brief Writes Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data to be written
* @param sector: Sector address (LBA)
* @param count: Number of sectors to write (1..128)
* @retval DRESULT: Operation result
*/
#if _USE_WRITE == 1
DRESULT USER_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to write */
)
{
/* USER CODE BEGIN WRITE */
/* USER CODE HERE */
// return RES_OK;
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;
}
/* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */
/**
* @brief I/O control operation
* @param pdrv: Physical drive number (0..)
* @param cmd: Control code
* @param *buff: Buffer to send/receive control data
* @retval DRESULT: Operation result
*/
#if _USE_IOCTL == 1
DRESULT USER_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
/* USER CODE BEGIN IOCTL */
// DRESULT res = RES_ERROR;
// return res;
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;
/* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */
9、修改main.c文件
添加获取SD容量的函数和操作SD卡的代码:
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)
{
usb_printf("SD¿¨³õʼ»¯Ê§°Ü! \r\n");
}
else
{
usb_printf("SD¿¨³õʼ»¯³É¹¦£¡ \r\n");
}
res=f_mount(&fs,"0:",1); //¹ÒÔØ
// if(test_sd == 0) //ÓÃÓÚ²âÊÔ¸ñʽ»¯
if(res == FR_NO_FILESYSTEM) //ûÓÐÎļþϵͳ£¬¸ñʽ»¯
{
// test_sd =1; //ÓÃÓÚ²âÊÔ¸ñʽ»¯
usb_printf("ûÓÐÎļþϵͳ! \r\n");
res = f_mkfs("", 0, 0); //¸ñʽ»¯sd¿¨
if(res == FR_OK)
{
usb_printf("¸ñʽ»¯³É¹¦! \r\n");
res = f_mount(NULL,"0:",1); //¸ñʽ»¯ºóÏÈÈ¡Ïû¹ÒÔØ
res = f_mount(&fs,"0:",1); //ÖØйÒÔØ
if(res == FR_OK)
{
usb_printf("SD¿¨ÒѾ³É¹¦¹ÒÔØ£¬¿ÉÒÔ½ø½øÐÐÎļþдÈë²âÊÔ!\r\n");
}
}
else
{
usb_printf("¸ñʽ»¯Ê§°Ü! \r\n");
}
}
else if(res == FR_OK)
{
usb_printf("¹ÒÔسɹ¦! \r\n");
}
else
{
usb_printf("¹ÒÔØʧ°Ü! \r\n");
}
res = f_open(&file,SD_FileName,FA_OPEN_ALWAYS |FA_WRITE);
if((res & FR_DENIED) == FR_DENIED)
{
usb_printf("¿¨´æ´¢ÒÑÂú£¬Ð´Èëʧ°Ü!\r\n");
}
f_lseek(&file, f_size(&file));//È·±£Ð´´ÊдÈë²»»á¸²¸Ç֮ǰµÄÊý¾Ý
if(res == FR_OK)
{
usb_printf("´ò¿ª³É¹¦/´´½¨Îļþ³É¹¦£¡ \r\n");
res = f_write(&file,write_buff,bufSize,&Bw); //дÊý¾Ýµ½SD¿¨
if(res == FR_OK)
{
usb_printf("ÎļþдÈë³É¹¦£¡ \r\n");
}
else
{
usb_printf("ÎļþдÈëʧ°Ü£¡ \r\n");
}
}
else
{
usb_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)
{
usb_printf("SD¿¨³õʼ»¯Ê§°Ü! \r\n");
}
else
{
usb_printf("SD¿¨³õʼ»¯³É¹¦£¡ \r\n");
}
/* ¹ÒÔØ */
res=f_mount(&FS,"0:",1); //¹ÒÔØ
if (res != FR_OK)
{
usb_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) */
usb_printf("\r\n%d MB total drive space.\r\n""%d MB available.\r\n""%d MB used.\r\n",TotalSpace, AvailableSize,UsedSize);
}
else
{
usb_printf("Get SDCard Capacity Failed (%d)\r\n", result);
}
}
注释里出现了一些乱码,这是由于字体格式不兼容导致的。
最后,修改主函数,把需要的操作放进去:
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_USB_DEVICE_Init();
/* USER CODE BEGIN 2 */
usb_printf("SD¿¨²âÊÔ³ÌÐò¿ªÊ¼£¡ \r\n");
Get_SDCard_Capacity(); //µÃµ½Ê¹ÓÃÄڴ沢ѡÔñ¸ñʽ»¯
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
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);
}
}
/* USER CODE END 3 */
}
保存、编译、下载、上电。打开串口助手:
可以看到提示操作成功!打开SD卡,看它创建了什么文件:
HELLO.TXT是创建的文件名称,内部是里面写入的内容。
相关程序代码我上传到了网盘,想要的童鞋关注公众号:单片机爱好者,回复关键词:STM32SD,即可获取下载链接。
博主您好,我用您这个历程读写32G的SD卡时总是提示如下错误:
SD卡初始化失败!
FileSystem Mounted Failed (134244000)
Get SDCard Capacity Failed (134244000)
SD卡初始化失败!
挂载失败!
打开文件失败!
请问这种情况是什么地方出了问题,我用的是STM32F103C8T6板子,其他配置跟您的例程完全一样
两个方向:1、检查硬件接口,有没有接错线,有没有上拉电阻等等;2、换成8G的内存卡试一下