新闻详情

新闻详情

首页 / 资讯中心 / 详情

FreeRTOS+FATFS:MCU上实现FreeRTOS驱动SPI接口sdcard

发布时间:2026/9/12 20:19:12来源:尧图网络
FreeRTOS+FATFS:MCU上实现FreeRTOS驱动SPI接口sdcard
之前sdcard一直都是使用的sdio接口驱动使用的是正点原子的sdcard驱动程序和FATFS文件系统sdcard在FreeRTOS多任务中读写文件一直都表现正常。但是后来由于串口比较多把SDIO接口占用了不得已使用SPI接口驱动sdcard在程序执行期间遇到不少问题。一、sdcard初始化由于sdcard默认是SDIO接口模式所以SPI初始化时要发送74个CLK转到SPI模式。初始化程序除了这点之外另外一个需要注意的问题是先要把SPI波特率设置到400kHz以下SPI速度设置为SPI_SPEED_256可以正常初始化。等初始化完成后再提高速度到25MHz。uint8_t sd_init(void) { uint8_t res; /* 存放SD卡的返回值 */ uint16_t retry; /* 用来进行超时计数 */ uint8_t ocr[4]; uint16_t i; uint8_t cmd; static bool s_b_spi_init FALSE; if (!s_b_spi_init) { sd_spi_init(); s_b_spi_init TRUE; } sd_spi_speed_low(); /* SPI低速模式 */ /* 发送至少74个CLK */ for (i 0; i 10; i) { sd_spi_read_write_byte(0XFF); } retry 200; do { res sd_send_cmd(CMD0, 0); } while ((res ! 0X01) retry--); sd_type 0; if (res 0X01) { u8 r1 sd_send_cmd(CMD8, 0x1AA); if (r1 1) /* SD V2.0 */ { for (i 0; i 4; i) { ocr[i] sd_spi_read_write_byte(0XFF); } retry 400; if (r1 1) { do { res sd_send_cmd(ACMD41, 1UL 30); } while (res --retry); } if (retry sd_send_cmd(CMD58, 0) 0) { for (i 0; i 4; i) { ocr[i] sd_spi_read_write_byte(0XFF); } if (ocr[0] 0x40) { sd_type SD_TYPE_V2HC; } else { sd_type SD_TYPE_V2; } } } else /* SD V1.x / MMC V3 */ { res sd_send_cmd(ACMD41, 0); retry 1000; if (res 1) { sd_type SD_TYPE_V1; cmd ACMD41; } else { sd_type SD_TYPE_MMC; cmd CMD1; } do { res sd_send_cmd(cmd, 0); } while (res retry--); r1 sd_send_cmd(CMD59, 0); if (retry 0 || sd_send_cmd(CMD16, 512) ! 0) { sd_type SD_TYPE_ERR; } } } sd_deselect(); sd_spi_speed_high(); /* SPI高速模式 */ if (sd_type) { return SD_OK; } return res; }二、读扇区正点原子的FATFS文件系统驱动在打开文件时会有一个读取扇区的操作如果读取扇区出错程序会调用sdcard初始化函数然后再读扇区一直死循环直到读扇区正确为止。这会造成拔掉sdcard后程序会卡死在这里。这个地方有两种处理方法一种是在死循环里面vTaskDelay(10)让其它任务可以正常执行直到sdcard插入跳出死循环另一种方法是给循环加次数限制超过一定次数退出循环sdcara报错但是在程序的其它地方要实时检测sdcard的插拔及时初始化sdcard否则会导致sdcard插卡后无法读写文件。目前我的程序是采用第二种办法。DRESULT disk_read ( BYTE pdrv, /* Physical drive number 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 */ ) { uint8_t res 0; if (!count) return RES_PARERR; /* count不能等于0否则返回参数错误 */ switch (pdrv) { case SD_CARD: /* SD卡 */ { res sd_read_disk(buff, sector, count); uint32_t i 0; while (res) /* 读出错 */ { //printf(sd rd error:%d\r\n, res); res sd_init(); /* 重新初始化SD卡 */ if (res 0x08) res sd_read_disk(buff, sector, count); if (i 100) break; vTaskDelay(10); } } break; case EX_FLASH: /* 外部flash */ res 0; break; default: res 1; } /* 处理返回值将返回值转成ff.c的返回值 */ if (res 0x00) { return RES_OK; } else { return RES_ERROR; } }三、sdcard文件读写加互斥锁由于SPI读写必须以CS片选信号由高变低作为开始所以FreeRTOS多任务读写sdcard的时候必须加互斥锁或者是进入临界状态保证每次SPI读写都是完整的。最初的想法是在CS开始信号执行之前加锁在CS结束信号后解锁但是这样会导致死锁。所以在读写sdcard文件函数上加锁。FRESULT sdcard_creat_file(char *pfilename) { // 等待spi空闲 xSemaphoreTake(xSdcardSpiMutex, portMAX_DELAY); /* 检查文件名如果相应文件名没有创建创建文件 */ FIL fsnew; FRESULT res_flash; /* 文件操作结果 */ res_flash f_open(fsnew, pfilename, FA_CREATE_ALWAYS ); //创建烟感参数文件 if (res_flash ! FR_OK) { yangan_data.sdcard_state 2; //sd卡状态: 读写错误 g_sdcard_event_flag | SDCARD_EVT_DET_DONE; xSemaphoreGive(xSdcardEventSem); } f_close(fsnew); //关闭文件 xSemaphoreGive(xSdcardSpiMutex); return res_flash; } FRESULT sdcard_delete_file(char* pfilename) { // 等待spi空闲 xSemaphoreTake(xSdcardSpiMutex, portMAX_DELAY); FRESULT res_flash; /* 文件操作结果 */ res_flash f_unlink(pfilename); xSemaphoreGive(xSdcardSpiMutex); return res_flash; } FRESULT sdcard_fil_write(FIL* fp, /* Open file to be written */ const TCHAR* path, const void* buff, /* Data to be written */ UINT btw, /* Number of bytes to write */ UINT* bw, /* Number of bytes written */ FSIZE_t ofs ) { // 等待spi空闲 xSemaphoreTake(xSdcardSpiMutex, portMAX_DELAY); FRESULT res_flash FR_DISK_ERR; if ((!g_sd_pwr_off) (yangan_data.sdcard_state ! 1)) { g_sd_busy TRUE; /* 打开文件属性为可读写 */ res_flash f_open(fp, path, FA_OPEN_EXISTING | FA_WRITE ); if (res_flash FR_OK) { f_lseek(fp, ofs); //光标移动 res_flash f_write(fp, buff, btw, bw); res_flash f_close(fp); } else if ((res_flash ! FR_NO_FILE) (res_flash ! FR_NO_PATH)) { yangan_data.sdcard_state 2; g_sdcard_event_flag | SDCARD_EVT_DET_DONE; xSemaphoreGive(xSdcardEventSem); } g_sd_busy FALSE; } xSemaphoreGive(xSdcardSpiMutex); return res_flash; } FRESULT sdcard_fil_read ( FIL* fp, /* Open file to be read */ const TCHAR* path, void* buff, /* Data buffer to store the read data */ UINT btr, /* Number of bytes to read */ UINT* br, /* Number of bytes read */ FSIZE_t ofs ) { // 等待spi空闲 xSemaphoreTake(xSdcardSpiMutex, portMAX_DELAY); FRESULT res_flash FR_DISK_ERR; if (yangan_data.sdcard_state ! 1) { res_flash f_open(fp, path, FA_OPEN_EXISTING | FA_READ ); if (res_flash FR_OK) { f_lseek(fp, ofs); //光标移动 res_flash f_read(fp, buff, btr, br); f_close(fp); } else if ((res_flash ! FR_NO_FILE) (res_flash ! FR_NO_PATH)) { yangan_data.sdcard_state 2; g_sdcard_event_flag | SDCARD_EVT_DET_DONE; xSemaphoreGive(xSdcardEventSem); } } xSemaphoreGive(xSdcardSpiMutex); return res_flash; } FRESULT sdcard_fil_reading ( FIL* fp, /* Open file to be read */ void* buff, /* Data buffer to store the read data */ UINT btr, /* Number of bytes to read */ UINT* br /* Number of bytes read */ ) { // 等待spi空闲 xSemaphoreTake(xSdcardSpiMutex, portMAX_DELAY); FRESULT res_flash; if (yangan_data.sdcard_state ! 1) { res_flash f_read(fp, buff, btr, br); } else { yangan_data.sdcard_state 2; g_sdcard_event_flag | SDCARD_EVT_DET_DONE; xSemaphoreGive(xSdcardEventSem); } xSemaphoreGive(xSdcardSpiMutex); return res_flash; } FRESULT sdcard_open_file ( FIL* fp, /* Open file to be read */ const TCHAR* path ) { // 等待spi空闲 xSemaphoreTake(xSdcardSpiMutex, portMAX_DELAY); FRESULT res_flash FR_DISK_ERR; if (yangan_data.sdcard_state ! 1) { res_flash f_open(fp, path, FA_OPEN_EXISTING | FA_READ ); if (res_flash FR_OK) { f_lseek(fp, 0); //光标移动 } else if ((res_flash ! FR_NO_FILE) (res_flash ! FR_NO_PATH)) { yangan_data.sdcard_state 2; g_sdcard_event_flag | SDCARD_EVT_DET_DONE; xSemaphoreGive(xSdcardEventSem); } } xSemaphoreGive(xSdcardSpiMutex); return res_flash; } u32 sdcard_get_file_size ( FIL* fp, /* Open file to be read */ const TCHAR* path ) { // 等待spi空闲 xSemaphoreTake(xSdcardSpiMutex, portMAX_DELAY); u32 len; FRESULT res_flash FR_DISK_ERR; if (yangan_data.sdcard_state ! 1) { res_flash f_open(fp, path, FA_OPEN_EXISTING | FA_READ ); if (res_flash FR_OK) { f_lseek(fp, 0); //光标移动 len f_size(fp); f_close(fp); } else if ((res_flash ! FR_NO_FILE) (res_flash ! FR_NO_PATH)) { yangan_data.sdcard_state 2; g_sdcard_event_flag | SDCARD_EVT_DET_DONE; xSemaphoreGive(xSdcardEventSem); } } xSemaphoreGive(xSdcardSpiMutex); return len; } FRESULT sdcard_file_close ( FIL* fp /* Open file to be read */ ) { // 等待spi空闲 xSemaphoreTake(xSdcardSpiMutex, portMAX_DELAY); FRESULT res_flash FR_DISK_ERR; if (yangan_data.sdcard_state ! 1) { res_flash f_close(fp); if ((res_flash ! FR_OK) (res_flash ! FR_INVALID_OBJECT)) { yangan_data.sdcard_state 2; // g_sdcard_event_flag | SDCARD_EVT_DET_DONE; xSemaphoreGive(xSdcardEventSem); } } xSemaphoreGive(xSdcardSpiMutex); return res_flash; }四、sdcard文件目录中/英文转换正点原子的FATFS文件系统默认支持中文路径但是中文字库占用flash比较大差不多200K大小对于单片机来说没有必要。修改FF_CODE_PAGE宏定义只支持英文字符就行。#define FF_CODE_PAGE 437 /* This option specifies the OEM code page to be used on the target system. / Incorrect code page setting can cause a file open failure. / / 437 - U.S. / 720 - Arabic / 737 - Greek / 771 - KBL / 775 - Baltic / 850 - Latin 1 / 852 - Latin 2 / 855 - Cyrillic / 857 - Turkish / 860 - Portuguese / 861 - Icelandic / 862 - Hebrew / 863 - Canadian French / 864 - Arabic / 865 - Nordic / 866 - Russian / 869 - Greek 2 / 932 - Japanese (DBCS) / 936 - Simplified Chinese (DBCS) / 949 - Korean (DBCS) / 950 - Traditional Chinese (DBCS) / 0 - Include all code pages above and configured by f_setcp() */
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Flask项目集成Ueditor-for-python:配置上传与部署实践 2026/9/12 21:10:20

Flask项目集成Ueditor-for-python:配置上传与部署实践

简介:该项目是一套基于Flask框架的Ueditor富文本编辑器设计源码,面向需要快速集成在线编辑与多媒体上传能力的Python Web开发者。核心功能覆盖图片、视频、附件、涂鸦及远程抓图等上传管理场景,适合用于内容管理系统、后台管理平台的二次开发…

阅读更多 →
CMSIS-4静态工程评测:嵌入式底层标准的源码级审计方法 2026/9/12 21:10:20

CMSIS-4静态工程评测:嵌入式底层标准的源码级审计方法

1. 这不是一次简单的“代码搬运”,而是一场对嵌入式底层标准的考古与重审 CMSIS-4,这个在Cortex-M开发圈里被反复提及、却少有人真正拆开细看的“黑盒子”,它既不是某个具体芯片的驱动,也不是某家厂商的私有SDK,而是AR…

阅读更多 →
毛利率同比怎么分析?毛利率同比分析有哪些注意事项? 2026/9/12 21:10:20

毛利率同比怎么分析?毛利率同比分析有哪些注意事项?

做毛利率分析时,很多财务人员都会遇到同样的问题:收入与成本口径不一致导致同比数据不可比,多产品线手工拆解结构影响费时又容易出错,变动原因说不清楚,最终报告被业务部门质疑。本文提供一套可以直接套用的毛利率分步…

阅读更多 →
头歌实践教学平台:Java面向对象-包装类(二) 2026/9/12 21:10:20

头歌实践教学平台:Java面向对象-包装类(二)

第2关:包装类转换成其他数据类型任务描述 本关任务:将包装类转换成其他数据类型。相关知识 为了完成本关任务,你需要掌握:1.如何将包装类转换成其他基本数据类型。将包装类转换成其他数据类型 很简单,我们来看一个例子…

阅读更多 →
如何 5 分钟把文件转 Markdown:MarkItDown 安装、批量转换命令与报错排查完整指南 2026/9/12 21:10:20

如何 5 分钟把文件转 Markdown:MarkItDown 安装、批量转换命令与报错排查完整指南

如何 5 分钟把文件转 Markdown:MarkItDown 安装、批量转换命令与报错排查完整指南 【免费下载链接】markitdown Python tool for converting files and office documents to Markdown. 项目地址: https://gitcode.com/GitHub_Trending/ma/markitdown MarkItD…

阅读更多 →
Actual Budget 收款人位置(Payee Locations)功能完全指南:移动端就近记一笔 2026/9/12 21:07:20

Actual Budget 收款人位置(Payee Locations)功能完全指南:移动端就近记一笔

Actual Budget 收款人位置(Payee Locations)功能完全指南:移动端就近记一笔 【免费下载链接】actual A local-first personal finance app 项目地址: https://gitcode.com/GitHub_Trending/ac/actual 导读 Payee Locations 是 Actual…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞