当前位置: 首页 新闻资讯 技术问答

如何使用这个初始化函数驱动SDNAND?

SD NAND-贴片式TF卡-贴片式SD卡-免费测试2025-06-206

要使用提供的 SDIO 初始化函数驱动 SDNAND,需要在初始化后发送一系列 SD 协议命令来完成卡识别、配置和数据传输。下面我将详细介绍整个流程和代码实现:

sdio_sdnand.c

#include "gd32f4xx.h"
#include <stdbool.h>
#include <stdint.h>

/* SD命令定义 */
#define SD_CMD0     0   /* 复位卡 */
#define SD_CMD8     8   /* 发送接口条件 */
#define SD_CMD16    16  /* 设置块长度 */
#define SD_CMD17    17  /* 读取单块 */
#define SD_CMD24    24  /* 写入单块 */
#define SD_CMD55    55  /* 应用命令前缀 */
#define SD_ACMD41   41  /* 应用命令:初始化卡 */

/* 响应类型 */
#define SD_RESPONSE_NONE      0
#define SD_RESPONSE_R1        1
#define SD_RESPONSE_R1B       2
#define SD_RESPONSE_R2        3
#define SD_RESPONSE_R3        4
#define SD_RESPONSE_R7        7

/* 错误码 */
#define SD_SUCCESS            0
#define SD_ERROR_CMD_TIMEOUT  1
#define SD_ERROR_DATA_TIMEOUT 2
#define SD_ERROR_RESPONSE     3
#define SD_ERROR_CRC          4
#define SD_ERROR_ILLEGAL_CMD  5

/* 全局变量 */
static uint8_t sd_type = 0;  /* 0: SDSC, 1: SDHC/SDXC */

/**
 * @brief 初始化SDIO接口用于连接SDNAND
 * @param 无
 * @retval bool: 初始化成功返回true,失败返回false
 */
bool sdio_sdnand_initialize(void)
{
    /* 代码与之前提供的相同,此处省略 */
    /* ... */
}

/**
 * @brief 发送SD命令
 * @param cmd: 命令号
 * @param arg: 命令参数
 * @param response_type: 响应类型
 * @param[out] response: 响应数据
 * @retval uint8_t: 状态码,SD_SUCCESS表示成功
 */
uint8_t sdio_send_command(uint8_t cmd, uint32_t arg, uint8_t response_type, uint32_t *response)
{
    uint32_t timeout = 0xFFFF;
    
    /* 清除所有中断标志 */
    sdio_interrupt_flag_clear(SDIO, SDIO_INT_FLAG_ALL);
    
    /* 设置命令参数 */
    sdio_command_parameter_set(SDIO, cmd, arg, SDIO_RESPONSE_NO, 0);
    
    /* 根据响应类型配置SDIO */
    switch(response_type) {
        case SD_RESPONSE_NONE:
            sdio_command_response_config(SDIO, SDIO_RESPONSE_NO, 0);
            break;
            
        case SD_RESPONSE_R1:
        case SD_RESPONSE_R1B:
            sdio_command_response_config(SDIO, SDIO_RESPONSE_SHORT, 1);
            break;
            
        case SD_RESPONSE_R3:
        case SD_RESPONSE_R7:
            sdio_command_response_config(SDIO, SDIO_RESPONSE_SHORT, 1);
            break;
            
        case SD_RESPONSE_R2:
            sdio_command_response_config(SDIO, SDIO_RESPONSE_LONG, 1);
            break;
    }
    
    /* 发送命令 */
    sdio_command_send(SDIO);
    
    /* 等待命令完成 */
    while(!sdio_flag_get(SDIO, SDIO_FLAG_CMDSENT) && timeout--) {
        if(sdio_flag_get(SDIO, SDIO_FLAG_CMDTIMEOUT)) {
            sdio_flag_clear(SDIO, SDIO_FLAG_CMDTIMEOUT);
            return SD_ERROR_CMD_TIMEOUT;
        }
    }
    
    if(timeout == 0) return SD_ERROR_CMD_TIMEOUT;
    
    /* 如果需要响应,读取响应数据 */
    if(response_type != SD_RESPONSE_NONE) {
        *response = sdio_response_read(SDIO, SDIO_RESPONSE_0);
        
        /* 检查响应错误位 */
        if(*response & 0x80000000) {
            return SD_ERROR_RESPONSE;
        }
    }
    
    return SD_SUCCESS;
}

/**
 * @brief 初始化SDNAND卡
 * @param 无
 * @retval uint8_t: 状态码,SD_SUCCESS表示成功
 */
uint8_t sdio_sdnand_card_initialize(void)
{
    uint32_t response = 0;
    uint8_t i;
    uint8_t status;
    
    /* 发送至少74个时钟周期 */
    for(i = 0; i < 10; i++) {
        sdio_send_command(SD_CMD0, 0, SD_RESPONSE_NONE, NULL);
    }
    
    /* 发送CMD0: 复位卡 */
    status = sdio_send_command(SD_CMD0, 0, SD_RESPONSE_R1, &response);
    if(status != SD_SUCCESS) return status;
    
    /* 检查是否进入IDLE状态 */
    if((response & 0x01) != 0x01) return SD_ERROR_RESPONSE;
    
    /* 发送CMD8: 检查SD卡版本 */
    status = sdio_send_command(SD_CMD8, 0x000001AA, SD_RESPONSE_R7, &response);
    if(status != SD_SUCCESS) {
        /* 不支持CMD8,可能是SDv1.x或MMC卡 */
        /* 继续尝试初始化 */
    } else {
        /* 支持CMD8,是SDv2.x卡 */
        /* 检查电压范围和校验值 */
        if((response & 0xFFFFFF00) != 0x00000100) {
            return SD_ERROR_RESPONSE;
        }
    }
    
    /* 发送ACMD41: 初始化卡 */
    uint32_t arg = (sd_type == 1) ? 0x40000000 : 0; /* HCS位 */
    uint32_t timeout = 0xFFFFFF;
    
    while(timeout--) {
        /* 发送CMD55 + ACMD41 */
        status = sdio_send_command(SD_CMD55, 0, SD_RESPONSE_R1, &response);
        if(status != SD_SUCCESS) return status;
        
        status = sdio_send_command(SD_ACMD41, arg, SD_RESPONSE_R1, &response);
        if(status != SD_SUCCESS) return status;
        
        /* 检查是否退出IDLE状态 */
        if((response & 0x01) == 0) break;
    }
    
    if(timeout == 0) return SD_ERROR_CMD_TIMEOUT;
    
    /* 发送CMD58: 读取OCR寄存器 */
    status = sdio_send_command(SD_CMD58, 0, SD_RESPONSE_R3, &response);
    if(status != SD_SUCCESS) return status;
    
    /* 检查CCS位确定卡类型 */
    if(response & 0x40000000) {
        sd_type = 1;  /* SDHC/SDXC */
    } else {
        sd_type = 0;  /* SDSC */
    }
    
    /* 如果是SDSC卡,设置块长度为512字节 */
    if(sd_type == 0) {
        status = sdio_send_command(SD_CMD16, 512, SD_RESPONSE_R1, &response);
        if(status != SD_SUCCESS) return status;
    }
    
    /* 增加时钟频率到全速 */
    uint32_t sdio_clk_div = (SystemCoreClock / 25000000) - 2; /* 设置为25MHz */
    if(sdio_clk_div < 0) sdio_clk_div = 0;
    sdio_clock_divider_set(SDIO, sdio_clk_div);
    
    /* 切换到4位数据总线(如果支持) */
    status = sdio_send_command(SD_CMD6, 0x00000002, SD_RESPONSE_R1, &response);
    if(status != SD_SUCCESS) {
        /* 不支持4位模式,继续使用1位模式 */
    } else {
        /* 启用4位数据总线 */
        sdio_bus_width_set(SDIO, SDIO_BUS_WIDTH_4B);
    }
    
    return SD_SUCCESS;
}

/**
 * @brief 从SDNAND读取单个数据块
 * @param sector: 扇区地址
 * @param[out] buffer: 数据缓冲区
 * @param size: 块大小(字节)
 * @retval uint8_t: 状态码,SD_SUCCESS表示成功
 */
uint8_t sdio_sdnand_read_block(uint32_t sector, uint8_t *buffer, uint32_t size)
{
    uint32_t response = 0;
    uint32_t timeout;
    uint32_t i;
    
    /* 如果是SDHC/SDXC,扇区号直接使用;如果是SDSC,需要转换为字节地址 */
    uint32_t address = (sd_type == 1) ? sector : (sector * 512);
    
    /* 发送CMD17: 读取单块 */
    uint8_t status = sdio_send_command(SD_CMD17, address, SD_RESPONSE_R1, &response);
    if(status != SD_SUCCESS) return status;
    
    /* 等待数据开始传输 */
    timeout = 0xFFFF;
    while(!sdio_flag_get(SDIO, SDIO_FLAG_DATACT) && timeout--) {
        if(sdio_flag_get(SDIO, SDIO_FLAG_DATATIMEOUT)) {
            sdio_flag_clear(SDIO, SDIO_FLAG_DATATIMEOUT);
            return SD_ERROR_DATA_TIMEOUT;
        }
    }
    
    if(timeout == 0) return SD_ERROR_DATA_TIMEOUT;
    
    /* 读取数据 */
    for(i = 0; i < (size / 4); i++) {
        /* 等待FIFO非空 */
        timeout = 0xFFFF;
        while(!sdio_flag_get(SDIO, SDIO_FLAG_RXFIFOHF) && timeout--) {
            if(sdio_flag_get(SDIO, SDIO_FLAG_DATATIMEOUT)) {
                return SD_ERROR_DATA_TIMEOUT;
            }
        }
        
        if(timeout == 0) return SD_ERROR_DATA_TIMEOUT;
        
        /* 从FIFO读取32位数据 */
        uint32_t data = sdio_data_read(SDIO);
        
        /* 转换为字节序 */
        buffer[i*4] = (data >> 24) & 0xFF;
        buffer[i*4+1] = (data >> 16) & 0xFF;
        buffer[i*4+2] = (data >> 8) & 0xFF;
        buffer[i*4+3] = data & 0xFF;
    }
    
    /* 等待数据传输完成 */
    timeout = 0xFFFF;
    while(!sdio_flag_get(SDIO, SDIO_FLAG_DATAEND) && timeout--) {
        if(sdio_flag_get(SDIO, SDIO_FLAG_DATATIMEOUT)) {
            return SD_ERROR_DATA_TIMEOUT;
        }
    }
    
    if(timeout == 0) return SD_ERROR_DATA_TIMEOUT;
    
    return SD_SUCCESS;
}

/**
 * @brief 向SDNAND写入单个数据块
 * @param sector: 扇区地址
 * @param[in] buffer: 数据缓冲区
 * @param size: 块大小(字节)
 * @retval uint8_t: 状态码,SD_SUCCESS表示成功
 */
uint8_t sdio_sdnand_write_block(uint32_t sector, const uint8_t *buffer, uint32_t size)
{
    uint32_t response = 0;
    uint32_t timeout;
    uint32_t i;
    
    /* 如果是SDHC/SDXC,扇区号直接使用;如果是SDSC,需要转换为字节地址 */
    uint32_t address = (sd_type == 1) ? sector : (sector * 512);
    
    /* 发送CMD24: 写入单块 */
    uint8_t status = sdio_send_command(SD_CMD24, address, SD_RESPONSE_R1, &response);
    if(status != SD_SUCCESS) return status;
    
    /* 配置数据传输 */
    sdio_data_time_out_set(SDIO, 0xFFFFFF);
    sdio_data_length_set(SDIO, size);
    sdio_data_block_size_set(SDIO, 9); /* 512字节块大小 = 2^9 */
    sdio_data_direction_set(SDIO, SDIO_DATA_TRANSFER_TO_CARD);
    sdio_data_mode_set(SDIO, SDIO_DATA_MODE_STREAM);
    
    /* 开始数据传输 */
    sdio_data_transfer_start(SDIO);
    
    /* 写入数据 */
    for(i = 0; i < (size / 4); i++) {
        /* 等待FIFO半空 */
        timeout = 0xFFFF;
        while(!sdio_flag_get(SDIO, SDIO_FLAG_TXFIFOHE) && timeout--) {
            if(sdio_flag_get(SDIO, SDIO_FLAG_DATATIMEOUT)) {
                return SD_ERROR_DATA_TIMEOUT;
            }
        }
        
        if(timeout == 0) return SD_ERROR_DATA_TIMEOUT;
        
        /* 准备32位数据 */
        uint32_t data = (buffer[i*4] << 24) | 
                        (buffer[i*4+1] << 16) | 
                        (buffer[i*4+2] << 8) | 
                        buffer[i*4+3];
        
        /* 写入FIFO */
        sdio_data_write(SDIO, data);
    }
    
    /* 等待数据传输完成 */
    timeout = 0xFFFF;
    while(!sdio_flag_get(SDIO, SDIO_FLAG_DATAEND) && timeout--) {
        if(sdio_flag_get(SDIO, SDIO_FLAG_DATATIMEOUT)) {
            return SD_ERROR_DATA_TIMEOUT;
        }
    }
    
    if(timeout == 0) return SD_ERROR_DATA_TIMEOUT;
    
    /* 等待写入完成 */
    timeout = 0xFFFFFF;
    while(timeout--) {
        status = sdio_send_command(SD_CMD13, 0, SD_RESPONSE_R1, &response);
        if(status != SD_SUCCESS) return status;
        
        /* 检查写入状态 */
        if((response & 0x000001C0) == 0) break;
    }
    
    if(timeout == 0) return SD_ERROR_DATA_TIMEOUT;
    
    return SD_SUCCESS;
}    

2. 使用方法说明

  1. 初始化流程

if(sdio_sdnand_initialize()) {
    uint8_t status = sdio_sdnand_card_initialize();
    if(status == SD_SUCCESS) {
        // 初始化成功,可以进行读写操作
    } else {
        // 处理初始化错误
    }} else {
    // SDIO初始化失败}

读取数据

uint8_t buffer[512];uint8_t status = sdio_sdnand_read_block(0, buffer, 512);if(status == SD_SUCCESS) {
    // 数据已成功读取到buffer中} else {
    // 处理读取错误}

写入数据

uint8_t data[512] = {0}; // 准备要写入的数据uint8_t status = sdio_sdnand_write_block(0, data, 512);if(status == SD_SUCCESS) {
    // 数据已成功写入} else {
    // 处理写入错误}

3. 注意事项

  1. 时钟配置

    • 初始化阶段使用低频率(400kHz)

    • 初始化完成后切换到较高频率(如 25MHz)

  2. 地址计算

    • SDHC/SDXC 卡使用扇区地址(512 字节 / 扇区)

    • SDSC 卡使用字节地址

  3. 数据传输

    • 读取和写入都使用 FIFO 缓冲区

    • 支持 1 位和 4 位数据总线模式

  4. 错误处理

    • 所有函数都返回状态码,需要检查返回值

    • 包含超时处理机制,防止程序卡死

使用这套代码时,你可能需要根据具体的 GD32 型号和硬件连接情况调整引脚配置和时钟设置。另外,建议在实际应用中添加更完善的错误处理和日志记录功能。

热门标签:SD NAND FLASH 贴片式TF卡 贴片式SD卡 SD FLASH NAND FLASH


SD NAND-贴片式TF卡-贴片式SD卡-免费测试

深圳市芯存者科技有限公司

售前咨询
售前咨询
售后服务
售后服务
联系我们

电话:176-6539-0767

Q Q:135-0379-986

邮箱:1350379986@qq.com

地址:深圳市南山区蛇口街道后海大道1021号C座C422W8

在线客服 在线客服 QQ客服 微信客服 淘宝店铺 联系我们 返回顶部