ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

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

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() */
返回列表