STM32在FATFS文件系统模式下SPI访问SD卡

手里有个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,即可获取下载链接。

留下评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据