kwin · 2023年01月09日 · 北京市

【GD32F427开发板试用】SDIO+FatFS+UART0(支持Printf)

前言

失败是成功之母,坚持最终胜利。调试查找错误仿佛永远没有尽头。程序明明看不到错误,运行结果永远不是预想的那样。喜出望外到失望到绝望到仿佛还有一丝希望,这种循环总是无穷无尽,也正是这种折磨,技术才一点点积累起来。大家一起互勉,不断成长吧。

目标

  1. SD卡读写,支持32GB以上大容量卡,本文采用64GB卡测试;
  2. 支持文件系统,本文使用FatFS,版本号为R0.14b (April 17, 2021),下载地址http://elm-chan.org/fsw/ff/00index_e.html
  3. 支持串口调试,本文使用PB6-7管脚;
  4. 支持LED提示功能。

硬件准备

1-GD32F427开发板,请注意有版本差异,我的核心芯片是GD32F427RKT6;
image.png
2-自制SD读卡器;
image.png
3-SD卡一张,64GB;
image.png
4-杜邦线;
5-USB线;
6-串口工具,我使用的是PWLINK2上的串口。
image.png

硬件修改

去掉开发板上R18电阻,因为PD2需要连接SD卡。
image.png

开发配置

  1. MDK开发环境
    image.png
  2. 芯片选择GD32F27RK
    image.png
  3. 选中使用Use Micro LIB
    image.png
  4. 优化级别选择O2
    image.png
  5. 下载选择CMSIS-DAP
    image.png

    关键代码说明

堆栈设置

image.png

Stack_Size      EQU     0x00001000    

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp


; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

;Heap_Size       EQU     0x00000400
Heap_Size       EQU     0x00001000
                AREA    HEAP, NOINIT, READWRITE, ALIGN=3

LED控制代码

 /* enable the LEDs GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOC);

    /* configure LED1 GPIO port */
    gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_6);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
    /* reset LED1 GPIO pin */
    gpio_bit_reset(GPIOC, GPIO_PIN_6);

UART0代码

由于其它IO被占用,这里使用PB6-7作为串口通信。

void uart_init()
{
    /* enable GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOB);

    /* enable USART clock */
    rcu_periph_clock_enable(RCU_USART0);

    /* configure the USART0 TX pin and USART0 RX pin */
    gpio_af_set(GPIOB, GPIO_AF_7, GPIO_PIN_6);
    gpio_af_set(GPIOB, GPIO_AF_7, GPIO_PIN_7);

    /* configure USART0 TX as alternate function push-pull */
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_6);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);

    /* configure USART0 RX as alternate function push-pull */
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_7);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);

    /* USART configure */
    usart_deinit(USART0);
    usart_baudrate_set(USART0, 115200U);
    usart_receive_config(USART0, USART_RECEIVE_ENABLE);
    usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
    usart_enable(USART0);
}

支持Printf输出:

int fputc(int ch, FILE *f)
{
    usart_data_transmit(USART0, (uint8_t)ch);
    while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
    return ch;
}

SDIO代码

sd_error_enum sd_config(void)
{
    sd_error_enum status = SD_OK;
    uint32_t cardstate = 0;
    /* initialize the card */
    status = sd_init();
    if(SD_OK == status){
        status = sd_card_information_get(&sd_cardinfo);
    }
    if(SD_OK == status){
        status = sd_card_select_deselect(sd_cardinfo.card_rca);
    }
    status = sd_cardstatus_get(&cardstate);
    if(cardstate & 0x02000000){
        printf("\r\n The card is locked!");
#if 0
        /* unlock the card if necessary */
        status = sd_lock_unlock(SD_UNLOCK);
        if(SD_OK != status){
            printf("\r\n Unlock failed!");
            while (1){
            }
        }else{
            printf("\r\n The card is unlocked! Please reset MCU!");
        }
#endif
        while (1){
        }
    }
    if ((SD_OK == status) && (!(cardstate & 0x02000000)))
    {
        /* set bus mode */
        status = sd_bus_mode_config(SDIO_BUSMODE_4BIT);
//        status = sd_bus_mode_config( SDIO_BUSMODE_1BIT );
    }
    if (SD_OK == status)
    {
        /* set data transfer mode */
//        status = sd_transfer_mode_config( SD_DMA_MODE );
        status = sd_transfer_mode_config( SD_POLLING_MODE );
    }
    return status;
}

/*!
    \brief      get the card information and print it out by USRAT
    \param[in]  none
    \param[out] none
    \retval     none
*/
void card_info_get(void)
{
    uint8_t sd_spec, sd_spec3, sd_spec4, sd_security;
    uint32_t block_count, block_size;
    uint16_t temp_ccc;
    printf("\r\n Card information:");
    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){
                printf("\r\n## Card version 4.xx ##");
            }else{
                printf("\r\n## Card version 3.0x ##");
            }
        }else{
            printf("\r\n## Card version 2.00 ##");
        }
    }else if(1 == sd_spec){
        printf("\r\n## Card version 1.10 ##");
    }else if(0 == sd_spec){
        printf("\r\n## Card version 1.0x ##");
    }
    
    sd_security = (sd_scr[1] & 0x00700000) >> 20;
    if(2 == sd_security){
        printf("\r\n## SDSC card ##");
    }else if(3 == sd_security){
        printf("\r\n## SDHC card ##");
    }else if(4 == sd_security){
        printf("\r\n## SDXC card ##");
    }
    
    block_count = (sd_cardinfo.card_csd.c_size + 1)*1024;
    block_size = 512;
    printf("\r\n## Device size is %dKB ##", sd_card_capacity_get());
    printf("\r\n## Block size is %dB ##", block_size);
    printf("\r\n## Block count is %d ##", block_count);
    
    if(sd_cardinfo.card_csd.read_bl_partial){
        printf("\r\n## Partial blocks for read allowed ##" );
    }
    if(sd_cardinfo.card_csd.write_bl_partial){
        printf("\r\n## Partial blocks for write allowed ##" );
    }
    temp_ccc = sd_cardinfo.card_csd.ccc;
    printf("\r\n## CardCommandClasses is: %x ##", temp_ccc);
    if((SD_CCC_BLOCK_READ & temp_ccc) && (SD_CCC_BLOCK_WRITE & temp_ccc)){
        printf("\r\n## Block operation supported ##");
    }
    if(SD_CCC_ERASE & temp_ccc){
        printf("\r\n## Erase supported ##");
    }
    if(SD_CCC_WRITE_PROTECTION & temp_ccc){
        printf("\r\n## Write protection supported ##");
    }
    if(SD_CCC_LOCK_CARD & temp_ccc){
        printf("\r\n## Lock unlock supported ##");
    }
    if(SD_CCC_APPLICATION_SPECIFIC & temp_ccc){
        printf("\r\n## Application specific supported ##");
    }
    if(SD_CCC_IO_MODE & temp_ccc){
        printf("\r\n## I/O mode supported ##");
    }
    if(SD_CCC_SWITCH & temp_ccc){
        printf("\r\n## Switch function supported ##");
    }
}

Fatfs代码

Diskio.c代码
/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
    BYTE pdrv,        /* Physical drive nmuber to identify the drive */
    BYTE *buff,        /* Data buffer to store read data */
    LBA_t sector,    /* Start sector in LBA */
    UINT count        /* Number of sectors to read */
)
{
    if (NULL==buff)return RES_PARERR;
    if (!count)return RES_PARERR;//count²»ÄܵÈÓÚ0£¬·ñÔò·µ»Ø²ÎÊý´íÎó        
//    DRESULT res=RES_OK;
//    int result;

    switch (pdrv) {
//    case DEV_RAM :
//        // translate the arguments here

//        //result = RAM_disk_read(buff, sector, count);

//        // translate the reslut code here

//        return res;

    case DEV_MMC :
        // translate the arguments here

        //result = MMC_disk_read(buff, sector, count);

        // translate the reslut code here
            {
                sd_error_enum sta=SD_OK;
                //uint32_t lsector=sector;
                //uint16_t n;
                //lsector<<=9;
                if(count>1)
                {
                    sta=sd_multiblocks_read((uint32_t *)buff,(uint32_t)sector<<9,512,(uint32_t)count);//¶à¸öectorµÄ¶Á²Ù×÷
                }else
                {
                    sta=sd_block_read((uint32_t *)buff,(uint32_t)sector<<9,512);
                }
                
                //INTX_DISABLE();//¹Ø±Õ×ÜÖжÏ(POLLINGģʽ,ÑϽûÖжϴò¶ÏSDIO¶Áд²Ù×÷!!!)
                if(sta==SD_OK)
                    return RES_OK;
                else return RES_PARERR;
            }
//    case DEV_USB :
//        // translate the arguments here

//        //result = USB_disk_read(buff, sector, count);

//        // translate the reslut code here

//        return res;
//    return RES_OK;
    }

    return RES_PARERR;
}

/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

#if FF_FS_READONLY == 0
DRESULT disk_write (
    BYTE pdrv,            /* Physical drive nmuber to identify the drive */
    const BYTE *buff,    /* Data to be written */
    LBA_t sector,        /* Start sector in LBA */
    UINT count            /* Number of sectors to write */
)
{
    //DRESULT res=RES_OK;
//    int result;
    if (NULL==buff)return RES_PARERR;
    if (!count)return RES_PARERR;//count²»ÄܵÈÓÚ0£¬·ñÔò·µ»Ø²ÎÊý´íÎó            
    switch (pdrv) {
//    case DEV_RAM :
//        // translate the arguments here

//        //result = RAM_disk_write(buff, sector, count);

//        // translate the reslut code here

//        return res;

    case DEV_MMC :
        // translate the arguments here

        //result = MMC_disk_write(buff, sector, count);

        // translate the reslut code here
            {
                sd_error_enum sta=SD_OK;
                        if(count>1)
                {
                    sta=sd_multiblocks_write((uint32_t *)buff,(uint32_t)sector<<9,512,(uint32_t)count);//¶à¸öectorµÄ¶Á²Ù×÷
                }else
                {
                    sta=sd_block_write((uint32_t *)buff,(uint32_t)sector<<9,512);
                }
                if(sta==SD_OK)
                    return RES_OK;
                else return RES_PARERR;
            }

//    case DEV_USB :
//        // translate the arguments here

//        //result = USB_disk_write(buff, sector, count);

//        // translate the reslut code here

//        return res;
    }

    return RES_PARERR;
}

#endif

Ffconf.h配置如下:

#define FF_FS_READONLY     0
#define FF_FS_MINIMIZE    0
#define FF_USE_FIND        1
#define FF_USE_MKFS        0
#define FF_USE_FASTSEEK    1
#define FF_USE_EXPAND    0
#define FF_USE_CHMOD    0
#define FF_USE_LABEL    0
#define FF_USE_FORWARD    0
#define FF_USE_STRFUNC    0
#define FF_PRINT_LLI    0
#define FF_PRINT_FLOAT    0
#define FF_STRF_ENCODE    0
#define FF_CODE_PAGE    437
#define FF_USE_LFN        3
#define FF_MAX_LFN        255
#define FF_LFN_UNICODE    0
#define FF_LFN_BUF        255
#define FF_SFN_BUF        12
#define FF_FS_RPATH        0
#define FF_VOLUMES        2
#define FF_STR_VOLUME_ID    0
#define FF_VOLUME_STRS        "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
#define FF_MULTI_PARTITION    0
#define FF_MIN_SS        512
#define FF_MAX_SS        512
#define FF_FS_TINY        0
#define FF_FS_EXFAT        1
//其它默认就行。

测试代码,main函数

int main(void)
{
    sd_error_enum sd_error;
    uint16_t i = 5;
#ifdef DATA_PRINT
    uint8_t *pdata;
#endif /* DATA_PRINT */
        nvic_config();
    /* configure systick */
    systick_config();
        uart_init();

    /* enable the LEDs GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOC);

    /* configure LED1 GPIO port */
    gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_6);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
    /* reset LED1 GPIO pin */
    gpio_bit_reset(GPIOC, GPIO_PIN_6);

    do{
        /* initialize the card, get the card information and configurate the bus mode and transfer mode */
        sd_error = sd_config();
    }while((SD_OK != sd_error) && (--i));
    
    if(i){
        printf("\r\n Card init success!\r\n");
    }else{
        printf("\r\n Card init failed!\r\n");
        while (1){
        }
    }
    
    /* get the information of the card and print it out by USART */
    card_info_get();
    
    /* init the write buffer */
    for(i=0; i<512; i++){
        buf_write[i] = i;
    }
    
    printf("\r\n\r\n Card test:");
    
    /* single block operation test */
    sd_error = sd_block_write(buf_write, 100*512, 512);
    if(SD_OK != sd_error){
        printf("\r\n Block write fail!");
        while (1){
        }
    }else{
        printf("\r\n Block write success!");
    }
    sd_error = sd_block_read(buf_read, 100*512, 512);
    if(SD_OK != sd_error){
        printf("\r\n Block read fail!");
        while (1){
        }
    }else{
        printf("\r\n Block read success!");
#ifdef DATA_PRINT
        pdata = (uint8_t *)buf_read;
        /* print data by USART */
        printf("\r\n");
        for(i = 0; i < 128; i++){
            printf(" %3d %3d %3d %3d ", *pdata, *(pdata+1), *(pdata+2), *(pdata+3));
            pdata += 4;
            if(0 == (i + 1) % 4){
                printf("\r\n");
            }
        }
#endif /* DATA_PRINT */
    }
    
    /* lock and unlock operation test */
    if(SD_CCC_LOCK_CARD & sd_cardinfo.card_csd.ccc){
        /* lock the card */
        sd_error = sd_lock_unlock(SD_LOCK);
        if(SD_OK != sd_error){
            printf("\r\n Lock failed!");
            while (1){
            }
        }else{
            printf("\r\n The card is locked!");
        }
        sd_error = sd_erase(100*512, 101*512);
        if(SD_OK != sd_error){
            printf("\r\n Erase failed!");
        }else{
            printf("\r\n Erase success!");
        }
        
        /* unlock the card */
        sd_error = sd_lock_unlock(SD_UNLOCK);
        if(SD_OK != sd_error){
            printf("\r\n Unlock failed!");
            while (1){
            }
        }else{
            printf("\r\n The card is unlocked!");
        }
        sd_error = sd_erase(100*512, 101*512);
        if(SD_OK != sd_error){
            printf("\r\n Erase failed!");
        }else{
            printf("\r\n Erase success!");
        }
        
        sd_error = sd_block_read(buf_read, 100*512, 512);
        if(SD_OK != sd_error){
            printf("\r\n Block read fail!");
            while (1){
            }
        }else{
            printf("\r\n Block read success!");
#ifdef DATA_PRINT
        pdata = (uint8_t *)buf_read;
        /* print data by USART */
        printf("\r\n");
        for(i = 0; i < 128; i++){
            printf(" %3d %3d %3d %3d ", *pdata, *(pdata+1), *(pdata+2), *(pdata+3));
            pdata += 4;
            if(0 == (i + 1) % 4){
                printf("\r\n");
            }
        }
#endif /* DATA_PRINT */
        }
    }
    
    /* multiple blocks operation test */
    sd_error = sd_multiblocks_write(buf_write, 200*512, 512, 3);
    if(SD_OK != sd_error){
        printf("\r\n Multiple block write fail!");
        while (1){
        }
    }else{
        printf("\r\n Multiple block write success!");
    }
    sd_error = sd_multiblocks_read(buf_read, 200*512, 512, 3);
    if(SD_OK != sd_error){
        printf("\r\n Multiple block read fail!");
        while (1){
        }
    }else{
        printf("\r\n Multiple block read success!");
#ifdef DATA_PRINT
        pdata = (uint8_t *)buf_read;
        /* print data by USART */
        printf("\r\n");
        for(i = 0; i < 512; i++){
            printf(" %3d %3d %3d %3d ", *pdata, *(pdata+1), *(pdata+2), *(pdata+3));
            pdata += 4;
            if(0 == (i + 1) % 4){
                printf("\r\n");
            }
        }
#endif /* DATA_PRINT */    
            }
        
        res=f_mount(&fs, "1:", 1);
        if(res==FR_NO_FILESYSTEM)
            {printf("\r\n NO file system in SD:%d \r\n",res);while(1);}
        else if(res == FR_OK)
            {
                printf("\r\nfile systme existed in SD");
        }else {printf("\r\nFILE mount Error For SD:%d",res);
                printf("\r\nInitiization error maybe");}
        
        res=f_open(&nfile1,"1:/kwin08.txt",FA_READ | FA_WRITE | FA_OPEN_APPEND);
        printf("\r\n f_open:%d",res);
        if(FR_OK    ==    res)
        {
            printf("\r\n Create nfile1 OK");
            
            res    =    f_write(&nfile1,tx,sizeof(tx),&bw);
            if(FR_OK    ==    res){printf("\r\n write done1:%d",bw);}
//            res    =    f_write(&nfile1,tx,sizeof(tx),&bw);
//            if(FR_OK    ==    res){printf("\r\n write done2:%d",bw);}
        }
        for(uint16_t i=0;i<512;i++)
        {
            mybuff[i]=0;                        
        }
        nfile1.fptr=0;
        res=f_read(&nfile1,mybuff,sizeof(mybuff),&br);
        printf("\r\n f_read:%d-byete:%d",res,br);
        printf("\r\nbegin:\r\n");
        for(uint16_t i=0;i<512;i++)
        {
            printf("%c",mybuff[i]);                        
        }
        f_close(&nfile1);        
            
            
    while(1) {
        /* turn on LED1 */
        gpio_bit_set(GPIOC, GPIO_PIN_6);
        delay_1ms(1000);
                printf("LED ON\r\n");

        /* turn off LED1 */
        gpio_bit_reset(GPIOC, GPIO_PIN_6);
        delay_1ms(1000);
                printf("LED OFF\r\n");
    }
}

测试效果

程序每次重新启动写入一行“Hello my file”。然后进入LED闪烁,并串口打印LED状态。
kk1.png
SD卡中历次创建的文件及写入内容。
kk2.png
整体效果如下,程序复位后文件读出内容增加:

https://www.bilibili.com/vide...

码字不易,承蒙不弃,欢迎给个赞,谢谢。

推荐阅读
关注数
0
文章数
1
专注硬件及嵌入式开发,欢迎技术交流。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息