前言
失败是成功之母,坚持最终胜利。调试查找错误仿佛永远没有尽头。程序明明看不到错误,运行结果永远不是预想的那样。喜出望外到失望到绝望到仿佛还有一丝希望,这种循环总是无穷无尽,也正是这种折磨,技术才一点点积累起来。大家一起互勉,不断成长吧。
目标
- SD卡读写,支持32GB以上大容量卡,本文采用64GB卡测试;
- 支持文件系统,本文使用FatFS,版本号为R0.14b (April 17, 2021),下载地址http://elm-chan.org/fsw/ff/00index_e.html;
- 支持串口调试,本文使用PB6-7管脚;
- 支持LED提示功能。
硬件准备
1-GD32F427开发板,请注意有版本差异,我的核心芯片是GD32F427RKT6;
2-自制SD读卡器;
3-SD卡一张,64GB;
4-杜邦线;
5-USB线;
6-串口工具,我使用的是PWLINK2上的串口。
硬件修改
去掉开发板上R18电阻,因为PD2需要连接SD卡。
开发配置
- MDK开发环境
- 芯片选择GD32F27RK
- 选中使用Use Micro LIB
- 优化级别选择O2
下载选择CMSIS-DAP
关键代码说明
堆栈设置
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状态。
SD卡中历次创建的文件及写入内容。
整体效果如下,程序复位后文件读出内容增加:
https://www.bilibili.com/vide...
码字不易,承蒙不弃,欢迎给个赞,谢谢。