本帖最后由 sujingliang 于 2024-9-16 07:10 编辑
目的
通过FatFs (Generic FAT Filesystem module)访问SD卡,读写文件,列出文件目录,通过串口输出结果。
FatFs Module是一种完全免费开源的FAT文件系统模块,专为小型的嵌入式系统而设计。
FatFs Module提供了一系列的文件操作接口,如文件的打开、关闭、读写、移动指针、获取状态等。这些接口函数使得在嵌入式系统中操作文件变得像在PC上一样简单。以下是一些常用的接口函数:
f_mount:登记或注销一个工作区域。 - f_open:打开或创建文件。
- f_close:关闭一个文件。
- f_read:从文件中读取数据。
- f_write:向文件写入数据。
- f_lseek:移动文件读/写指针。
- f_stat:获取文件状态。
- f_mkdir:创建一个目录。
- f_unlink:删除文件或目录。
- f_rename:重命名或移动文件/目录。
编译环境:keil5.40
一、硬件方面
DAT0:数据传输引脚,主要用于单线双向数据传输。在四线数据传输模式中,它也是四根数据线之一,用于高速数据传输。
DAT1、DAT2、DAT3:附加数据传输引脚,在支持四线数据传输的SD卡中,这些引脚与DAT0共同工作,实现高速数据传输。在某些规范中,这些引脚可能会被省略或用作其他目的。
CMD(命令传输引脚):用于主机和SD卡之间的命令传输。主机通过CMD引脚向SD卡发送命令,SD卡则通过CMD引脚或DAT引脚(取决于命令类型)返回响应。
CLK(时钟引脚):提供时钟信号以同步数据传输。CLK引脚控制数据传输的速率和同步性,确保主机和SD卡之间的数据交换能够准确无误地进行
SD卡的DETECT引脚(通常也标记为CD引脚)主要用于检测SD卡是否已正确插入到设备中。这一引脚在SD卡与设备之间的通信中起着至关重要的作用,它允许设备在SD卡插入或拔出时采取相应的操作,如初始化SD卡、读取数据或关闭数据传输等。
二、功能实现
1、为方便调试,利用STM32H7S78-DK的BSP库函数文件stm32h7s78_discovery支持板载LED、UART4:
BSP_LED_Init(LED_GREEN);
BSP_LED_Init(LED_RED);
BSP_LED_Init(LED_ORANGE);
BSP_LED_Init(LED_BLUE);
/* Initialize COM1 port (115200, 8 bits (7-bit data + 1 stop bit), no parity */
BspCOMInit.BaudRate = 115200;
BspCOMInit.WordLength = COM_WORDLENGTH_8B;
BspCOMInit.StopBits = COM_STOPBITS_1;
BspCOMInit.Parity = COM_PARITY_NONE;
BspCOMInit.HwFlowCtl = COM_HWCONTROL_NONE;
if (BSP_COM_Init(COM1, &BspCOMInit) != BSP_ERROR_NONE)
{
Error_Handler();
}
2、SD_DETECT引脚初始化
SD_DECTECT需要设置为外部中断引脚
GPIO_InitStruct.Pin = SD_DETECT_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(SD_DETECT_GPIO_Port, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(SD_DETECT_EXTI_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(SD_DETECT_EXTI_IRQn);
3、SD卡的初始化
MX_SDMMC1_SD_Init()
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] SDMMC1 Initialization Function
* @param None
* @retval None
*/
void MX_SDMMC1_SD_Init(void)
{
/* USER CODE BEGIN SDMMC1_Init 0 */
/* USER CODE END SDMMC1_Init 0 */
/* USER CODE BEGIN SDMMC1_Init 1 */
/* USER CODE END SDMMC1_Init 1 */
hsd1.Instance = SDMMC1;
hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_FALLING;
hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
hsd1.Init.BusWide = SDMMC_BUS_WIDE_4B;
hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_ENABLE;
hsd1.Init.ClockDiv = 0x02;
if(HAL_SD_Init(&hsd1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SDMMC1_Init 2 */
/* USER CODE END SDMMC1_Init 2 */
}
4、FatFs 初始化
用到了FREERTOS建立了一个uSDThread_Entry和一个队列
void MX_FATFS_Init(void)
{
/* USER CODE BEGIN Init */
/* additional user code for init */
/*## FatFS: Link the disk I/O driver(s) ###########################*/
if (FATFS_LinkDriver(&SD_DMA_Driver, SDPath) == 0)
{
/* creation of uSDThread */
FSAppThreadHandle = osThreadNew(uSDThread_Entry, NULL, &uSDThread_attributes);
/* Create Storage Message Queue */
QueueHandle = osMessageQueueNew(1U, sizeof(uint16_t), NULL);
}
/* USER CODE END Init */
}
5、uSDThread_Entry
用来处理队列的各种消息,并做对应处理
static void uSDThread_Entry(void *argument)
{
osStatus_t status;
if(SD_IsDetected())
{
osMessageQueuePut (QueueHandle, &CARD_CONNECTED, 100, 0U);
}
/* Infinite Loop */
for( ;; )
{
status = osMessageQueueGet(QueueHandle, &osQueueMsg, NULL, 100);
if ((status == osOK) && (osQueueMsg== CARD_STATUS_CHANGED))
{
if (SD_IsDetected())
{
osMessageQueuePut (QueueHandle, &CARD_CONNECTED, 100, 0U);
}
else
{
osMessageQueuePut (QueueHandle, &CARD_DISCONNECTED, 100, 0U);
}
}
if ((status == osOK) && (osQueueMsg== CARD_CONNECTED))
{
//sHAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);
BSP_LED_On(LED_BLUE);
//FS_FileOperations();
FS_listDirectory();
statusChanged = 0;
}
if ((status == osOK) && (osQueueMsg== CARD_DISCONNECTED))
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
osDelay(200);
f_mount(NULL, (TCHAR const*)"", 0);
statusChanged = 0;
}
}
}
在收到CARD_CONNECTED消息后,会执行FS_listDirectory()
6、SD_DECTECT外部中断处理函数
判断SD_DETECT状态,如果发生改变,向队列中发送CARD_STATUS_CHANGED消息。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == SD_DETECT_Pin)
{
if (statusChanged == 0)
{
statusChanged = 1;
osMessageQueuePut ( QueueHandle, &CARD_STATUS_CHANGED, 100, 0U);
}
}
}
7、FS_listDirectory
在这里利用FatFs提供的函数:
f_mount挂载SD。
f_opendir打开"/"目录
f_readdir读取"/"目录
通过循环打印出目录下所有文件名和文件大小
static void FS_listDirectory(void)
{
FRESULT res;
DIR dir;
FILINFO fno;
uint32_t bytesread;
printf("【1】列出目录中文件内容:\r\n");
if(f_mount(&SDFatFs, (TCHAR const*)SDPath, 0) == FR_OK)
{
printf("mount SD success\r\n");
res = f_opendir(&dir, "/");
if (res == FR_OK) {
printf("open directory success\r\n");
printf("list directory / files:\r\n");
for (;;) {
res = f_readdir(&dir, &fno);
if (res != FR_OK || fno.fname[0] == 0) break; // 读取错误或结束标志
if (fno.fname[0] == '.') continue; // 跳过隐藏文件
printf("%s %d\r\n", fno.fname,fno.fsize); // 打印文件名
}
f_closedir(&dir);
}
if(f_open(&SDFile, "STM32_1.TXT", FA_READ) == FR_OK)
{
/* Read data from the text file */
res = f_read(&SDFile, ( void *)rtext, sizeof(rtext), (void *)&bytesread);
if((bytesread > 0) && (res == FR_OK))
{
printf("\r\nSTM32_1.TXT file content:%s\r\n",rtext);
/* Close the open text file */
f_close(&SDFile);
}
}
}
}
8、另外FS_FileOperations函数实现了对文件读写
f_mkfs:建立文件系统,相当于格式化。
static void FS_FileOperations(void)
{
FRESULT res; /* FatFs function common result code */
uint32_t byteswritten, bytesread; /* File write/read counts */
/* Register the file system object to the FatFs module */
if(f_mount(&SDFatFs, (TCHAR const*)SDPath, 0) == FR_OK)
{
printf("mount SD success\r\n");
/* check whether the FS has been already created */
if (isFsCreated == 0)
{
/*
if(f_mkfs(SDPath, &OptParm, workBuffer, sizeof(workBuffer)) != FR_OK)
{
printf("make file system success\r\n");
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
return;
}
isFsCreated = 1;
*/
}
/* Create and Open a new text file object with write access */
if(f_open(&SDFile, "STM32_1.TXT", FA_CREATE_ALWAYS | FA_WRITE) == FR_OK)
{
printf("open STEM32.TXT success\r\n");
/* Write data to the text file */
res = f_write(&SDFile, (const void *)wtext, sizeof(wtext), (void *)&byteswritten);
if((byteswritten > 0) && (res == FR_OK))
{
printf("write finished, close STEM32.TXT success\r\n");
/* Close the open text file */
f_close(&SDFile);
/* Open the text file object with read access */
if(f_open(&SDFile, "STM32_1.TXT", FA_READ) == FR_OK)
{
/* Read data from the text file */
res = f_read(&SDFile, ( void *)rtext, sizeof(rtext), (void *)&bytesread);
if((bytesread > 0) && (res == FR_OK))
{
printf("open finished, close STEM32.TXT success\r\n");
/* Close the open text file */
f_close(&SDFile);
/* Compare read data with the expected data */
if(bytesread == byteswritten)
{
printf("read and written bytes are same\r\n");
/* Success of the demo: no error occurrence */
//HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
BSP_LED_On(LED_RED);
return;
}
}
}
}
}
}
/* Error */
//HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
BSP_LED_On(LED_BLUE);
}
三、效果
通过串口获得结果:
列出了SD卡上的3个文件,其中SYSTEM~1是系统文件,其他2个是自己建的,并打印出了STM32_1.TXT文件内容。
将SD卡插入读卡器并连上电脑,我用的带SD卡的收音机,可以看到被识别了U盘,可以看到下面有2个文件。
|