GD32F303
简单介绍
原版示例代码位于\GD32F30x_Firmware_Library_V2.2.0\Examples\SDIO
,对SD卡读写,卡信息获取,切换高速模式等基本功能已经实现了,但是默认实现的传输方式为CPU轮询读写FIFO,速度偏慢;卡上电初始化部分指令未考虑到一些大容量卡首次上电忙时间过长的问题,这里将在原版代码基础上进一步完善。
新增内容如下:
- 写单块CMD24,写多块CMD25,读单块CMD17,读多块CMD18支持DMA传输
- 使用CMSIS-RTOS2的信号量做DMA同步控制
- 60MHz的时钟速度(超出控制器支持的48MHz,连续调用写单块(CMD24)时不太稳定)
- 在一些操作指令之间增加延时,适配一些山寨TF卡
代码下载:
https://files.cnblogs.com/files/blogs/575121/gd32f30x_sdio.zip
SDIO硬件电路
GD32F303的SDIO功能只有1组管脚可以使用,具体分配如下:
SDIO定义 |
GPIO编号 |
PIN位置 |
备注 |
SDIO_DAT0 |
PC8 |
39 |
数据0, 上拉10K |
SDIO_DAT1 |
PC9 |
40 |
数据1, 上拉10K |
SDIO_DAT2 |
PC10 |
51 |
数据2, 上拉10K |
SDIO_DAT3 |
PC11 |
52 |
数据3, 上拉10K |
SDIO_CLK |
PC12 |
53 |
时钟, 上拉10K |
SDIO_CMD |
PD2 |
54 |
命令/响应, 上拉10K |
SDIO的DMA通道为DMA1 Channel3。
函数说明
- 如果要测试60MHz时钟,在
sd_init()
中建议开启IO补偿单元。
1 2 3 4 5 6 7 8 9 10 11 12
| volatile uint32_t reg = 0; volatile uint32_t timeout = 0xffffu;
gpio_compensation_config(GPIO_COMPENSATION_ENABLE); do { reg = AFIO_CPSCTL; timeout--; }while(!(reg & AFIO_CPSCTL_CPS_RDY) && timeout); if(timeout == 0) { init_status = SD_ERROR; return SD_ERROR; }
|
- 启用DMA传输时,需要在NVIC控制器中打开DMA的中断。
1 2 3 4 5
| #define IRQ_SDIO_PRIORITY 10 #define IRQ_SDIO_DMA_PRIORITY 9
nvic_irq_enable(SDIO_IRQn, IRQ_SDIO_PRIORITY, 0); nvic_irq_enable(DMA1_Channel3_Channel4_IRQn, IRQ_SDIO_DMA_PRIORITY, 0);
|
- ACMD指令添加重试
在sd_power_on()
阶段,发送完CMD0(GO_IDLE_STATE),CMD8(SEND_IF_COND)后切换到CMD55(APP_CMD)时,部分大容量SD卡还处于忙碌状态,导致CMD55的请求未及时回复,SDIO控制器认为返回响应超时(SD_CMD_RESP_TIMEOUT)。这里需要添加重试机制,经过测试大部分卡在1ms的重试间隔下,只需要重试一次即可继续发送ACMD41(SD_SEND_OP_COND)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| retry = 0;
do { sdio_command_response_config(SD_CMD_APP_CMD, (uint32_t)0x0, SDIO_RESPONSETYPE_SHORT); sdio_wait_type_set(SDIO_WAITTYPE_NO); sdio_csm_enable();
status = r1_error_check(SD_CMD_APP_CMD); if(SD_OK == status) { break; } osDelay(1); retry++; }while(retry < 10);
|
- 新增
sd_switch_mode()
切换高速模式
按以下序列发送命令参数,先查询是否支持高速模式,不支持则返回SD_FUNCTION_UNSUPPORTED
1
| const uint32_t cmd_array[] = {0x00FFFFF0u, 0x80FFFFF0u};
|
- 修改
sd_bus_mode_config()
添加参数配置时钟速度
- 修改
sd_block_read()
函数中,readaddr由调用者控制类型,标准容量 SD 存储卡数据地址以字节为单位,高容量 SD 存储卡数据地址以块(512 字节)为单位。
1 2 3 4 5 6 7 8 9
| sd_error_enum sd_block_read(uint32_t *preadbuffer, uint32_t readaddr, uint16_t blocksize) { .... if(SDIO_HIGH_CAPACITY_SD_CARD == cardtype){ blocksize = 512; } ... }
|
- 写多数据块(CMD25)前添加
SD_R1_READY_FOR_DATA
的状态位查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| sd_error_enum sd_multiblocks_write(uint32_t *pwritebuffer, uint32_t writeaddr, uint16_t blocksize, uint32_t blocksnumber) { ... response = sdio_response_get(SDIO_RESPONSE0); timeout = SDIO_PULL_TIMEOUT; while((0 == (response & SD_R1_READY_FOR_DATA)) && (timeout > 0)){ --timeout; sdio_command_response_config(SD_CMD_SEND_STATUS, (uint32_t)sd_rca << SD_RCA_SHIFT, SDIO_RESPONSETYPE_SHORT); sdio_wait_type_set(SDIO_WAITTYPE_NO); sdio_csm_enable(); status = r1_error_check(SD_CMD_SEND_STATUS); if(SD_OK != status){ return status; } response = sdio_response_get(SDIO_RESPONSE0); } if(0 == timeout){ return SD_ERROR; } ... }
|
- 新增DMA中断处理
在调用sd_block_read,sd_multiblocks_read,sd_block_write,sd_multiblocks_write时,调用者线程会因为等待信号量而进入阻塞状态,在DMA完成中断中释放信号量,使调用者线程获得继续向下执行的机会。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void DMA1_Channel3_4_IRQHandler(void) { uint32_t flag = DMA_INTF(DMA1); if(flag & DMA_FLAG_ADD(DMA_INT_FLAG_FTF, DMA_CH3)) { dma_interrupt_flag_clear(DMA1, DMA_CH3, DMA_INT_FLAG_FTF); if(transerror == SD_OK) { osSemaphoreRelease(sem_sdio); } } } 在SDIO中断中,当传输出错时也需要释放信号量。 void SDIO_IRQHandler(void) { sd_interrupts_process(); if(transerror != SD_OK) { osSemaphoreRelease(sem_sdio); } }
|
使用示例
挂载SD卡:实例中使用了FileX文件系统。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| static FX_MEDIA sdio_media; static rt_align(4) uint8_t filex_cache[FX_MAX_SECTOR_CACHE * 512];
static sd_card_info_struct sd_cardinfo; static uint32_t cardstate = 0; static uint16_t buswidth = 0; static uint16_t busspeed = 0;
static int mount_filesystem(void) { sd_error_enum status = SD_OK; uint32_t fxstatus; uint32_t block_count;
status = sd_init(); if(SD_OK == status){ sd_card_information_get(&sd_cardinfo); }else { sd_deinit(); return -1; }
status = sd_card_select_deselect(sd_cardinfo.card_rca); if(SD_OK == status){ status = sd_cardstatus_get(&cardstate); if(cardstate & SD_CARDSTATE_LOCKED){ return -3; } }
if(SD_OK == status) { buswidth = 1; busspeed = 24; status = sd_switch_mode(SD_SPEED_MODE_HS); if(status == SD_OK) { status = sd_bus_mode_config(SD_BUSWIDTH_4BIT, SD_CLK_DIV_40MHZ); if(status == SD_OK) { buswidth = 4; busspeed = 40; } }else { status = sd_bus_mode_config(SD_BUSWIDTH_4BIT, SD_CLK_DIV_24MHZ); if(status == SD_OK) { buswidth = 4; } }
fx_system_initialize(); block_count = (sd_cardinfo.card_csd.c_size + 1) * 1024;
fxstatus = fx_media_open(&sdio_media, NULL, fx_sdcard_driver, NULL, filex_cache, sizeof(filex_cache)); if(fxstatus != FX_SUCCESS) { rt_kprintf("error1:0x%02x\n", fxstatus); fxstatus = fx_media_format(&sdio_media, fx_sdcard_driver, NULL, filex_cache, sizeof(filex_cache), "FILEX", 1, 64, 0, block_count, 512, 32, 1, 1); if(fxstatus != FX_SUCCESS) { rt_kprintf("error2:0x%02x\n", fxstatus); return -4; } fxstatus = fx_media_open(&sdio_media, NULL, fx_sdcard_driver, NULL, filex_cache, sizeof(filex_cache)); if(fxstatus != FX_SUCCESS) { rt_kprintf("error3:0x%02x\n", fxstatus); return -5; } } return 0; }else { return -2; } }
|
格式化卡:
1 2 3 4 5 6 7 8 9 10 11 12
| void cardformat(void) { if(sd_isinit() != SD_OK) { rt_kprintf("Card NOT INIT\n"); return; } rt_kprintf("waiting...\n"); uint32_t block_count = (sd_cardinfo.card_csd.c_size + 1) * 1024; uint32_t fxstatus = fx_media_format(&sdio_media, fx_sdcard_driver, NULL, filex_cache, sizeof(filex_cache), "FILEX", 1, 64, 0, block_count, 512, 32, 1, 1); rt_kprintf("format result:0x%02x\n", fxstatus); } MSH_CMD_EXPORT(cardformat, format tfcard);
|
输出SD卡信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| void cardinfo(void) { uint8_t sd_spec, sd_spec3, sd_spec4, sd_security; uint32_t block_count, block_size; uint16_t temp_ccc; uint8_t name[8]; sd_cid_struct *cid;
if(sd_isinit() != SD_OK) { rt_kprintf("Card NOT INIT\n"); return; } rt_kprintf("Card Information\n");
cid = &(sd_cardinfo.card_cid); rt_kprintf("Manufacturer ID: 0x%02x\n", cid->mid); rt_kprintf("OEM/Application ID: 0x%02x\n", cid->oid); rt_kprintf("Name: "); rt_memcpy(name, (void *)&(cid->pnm0), 4); rt_memcpy(name + 4, (void *)&(cid->pnm1), 4); for(int i = 0; i < 6; i++) { if((name[i]>=0x20) && (name[i]<0x7F)) { rt_kprintf("%c", name[i]); } } rt_kprintf("\n"); rt_kprintf("Revision: 0x%02x\n", cid->prv); rt_memcpy(name, (void *)&(cid->psn), 4); rt_kprintf("Serial number: %02x%02x%02x%02x\n", name[3], name[2],name[1],name[0]); rt_kprintf("Manufacturing date: 0x%x\n", cid->mdt);
rt_kprintf("Version: "); sd_spec = (sd_scr[1] & 0x0F000000) >> 24; sd_spec3 = (sd_scr[1] & 0x00008000) >> 15; sd_spec4 = (sd_scr[1] & 0x00000400) >> 10; if(2 == sd_spec) { if(1 == sd_spec3) { if(1 == sd_spec4) { rt_kprintf("4.xx\n"); }else { rt_kprintf("3.0x\n"); } }else { rt_kprintf("2.00\n"); } }else if(1 == sd_spec) { rt_kprintf("1.10\n"); }else if(0 == sd_spec) { rt_kprintf("1.0x\n"); }
rt_kprintf("Type: "); sd_security = (sd_scr[1] & 0x00700000) >> 20; if(2 == sd_security) { rt_kprintf("SDSC card\n"); }else if(3 == sd_security) { rt_kprintf("SDHC card\n"); }else if(4 == sd_security) { rt_kprintf("SDXC card\n"); }
block_count = (sd_cardinfo.card_csd.c_size + 1)*1024; block_size = 512; rt_kprintf("Device Size: %dKB\n", sd_card_capacity_get()); rt_kprintf("Block Size: %dB\n", block_size); rt_kprintf("Block Count: %d\n", block_count);
rt_kprintf("Bus Width: %dBit\n", buswidth); rt_kprintf("Bus Clock: %dMHz\n", busspeed);
temp_ccc = sd_cardinfo.card_csd.ccc; rt_kprintf("Card Command Classes: %x\n", temp_ccc);
rt_kprintf("Features:\n"); if(sd_cardinfo.card_csd.read_bl_partial){ rt_kprintf("Partial blocks for read allowed\n" ); } if(sd_cardinfo.card_csd.write_bl_partial){ rt_kprintf("Partial blocks for write allowed\n" ); } if((SD_CCC_BLOCK_READ & temp_ccc) && (SD_CCC_BLOCK_WRITE & temp_ccc)){ rt_kprintf("Block operation supported\n"); } if(SD_CCC_ERASE & temp_ccc){ rt_kprintf("Erase supported\n"); } if(SD_CCC_WRITE_PROTECTION & temp_ccc){ rt_kprintf("Write protection supported\n"); } if(SD_CCC_LOCK_CARD & temp_ccc){ rt_kprintf("Lock unlock supported\n"); } if(SD_CCC_APPLICATION_SPECIFIC & temp_ccc){ rt_kprintf("Application specific supported\n"); } if(SD_CCC_IO_MODE & temp_ccc){ rt_kprintf("I/O mode supported\n"); } if(SD_CCC_SWITCH & temp_ccc){ rt_kprintf("Switch function supported\n"); } } MSH_CMD_EXPORT(cardinfo, show tfcard info);
|
卡片信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Card Information Manufacturer ID: 0x03 OEM/Application ID: 0x5344 Name: 23DSG Revision: 0x85 Serial number: 54bb003e Manufacturing date: 0x161 Version: 3.0x Type: SDHC card Device Size: 31166976KB Block Size: 512B Block Count: 62333952 Bus Width: 4Bit Bus Clock: 40MHz Card Command Classes: 5b5 Features: Block operation supported Erase supported Lock unlock supported Application specific supported Switch function supported
|
速度测试
CMD25连续写20KB
时钟 |
Ticks |
速度 |
卡类型 |
备注 |
40M |
525543 |
4.4596MB/s |
SANDISK-32G |
|
60M |
416303 |
5.6299MB/s |
SANDISK-32G |
|
60M |
411787 |
5.7145MB/s |
SANDISK-32G |
|
60M |
408590 |
5.7592MB/s |
SANDISK-32G |
no print log |
60M |
409943 |
5.7172MB/s |
SANDISK-32G |
no print log |
CMD18连续读20KB
时钟 |
Ticks |
速度 |
卡类型 |
40M |
188076 |
12.4617MB/s |
SDTRUVAL-16G |
60M |
127804 |
18.3387MB/s |
SANDISK-32G |
60M |
147390 |
15.9017MB/s |
SDTRUVAL-16G |
60M |
146691 |
15.9775MB/s |
SDTRUVAL-16G |
60M |
133362 |
17.5744MB/s |
SDTRUVAL-16G |
不同时钟的CMD18连续读取
时钟 |
大小 |
Ticks |
速度 |
卡类型 |
24MHZ |
4K |
95183 |
4.9247MB/s |
SANDISK-32G |
40MHZ |
4K |
76923 |
6.0939MB/s |
SANDISK-32G |
60MHZ |
4K |
69888 |
6.7071MB/s |
SANDISK-32G |
60MHZ |
8K |
86264 |
10.8678MB/s |
SANDISK-32G |
卡时钟40MHz
相关链接(侵删)
- GD32F303 SDIO-DMA读写SD卡
=================我是分割线=================
欢迎到公众号来唠嗑: