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

GD32F305SPI读取SDNAND

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

在 GD32F305 微控制器上使用 SPI 接口读取 SDNAND 需要实现一套完整的通信协议。下面我为你提供一个基于 GD32 标准库的实现方案,包括初始化、命令发送和数据读取的核心代码。

main.c

#include "gd32f30x.h"
#include "sdnand_spi.h"
#include <stdio.h>

#define BLOCK_SIZE 512

int main(void)
{
    uint8_t buffer[BLOCK_SIZE];
    uint8_t status;
    uint32_t block_address = 0; // 从第0块开始读取
    
    /* 系统时钟配置 */
    system_clock_config();
    
    /* 配置LED引脚用于状态指示 */
    rcu_periph_clock_enable(RCU_GPIOC);
    gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);
    gpio_bit_reset(GPIOC, GPIO_PIN_13); // 初始熄灭LED
    
    /* 初始化SPI接口 */
    sdnand_spi_init();
    
    /* 初始化SDNAND */
    status = sdnand_init();
    if (status == 0) {
        /* 初始化成功,点亮LED */
        gpio_bit_set(GPIOC, GPIO_PIN_13);
        
        /* 读取一个块 */
        status = sdnand_read_block(block_address, buffer);
        if (status == 0) {
            /* 读取成功,这里可以处理读取到的数据 */
            printf("成功读取块 %lu ", block_address);
            
            /* 例如,打印前16个字节 */
            printf("数据前16字节: ");
            for (int i = 0; i < 16; i++) {
                printf("%02X ", buffer[i]);
            }
            printf(" ");
        } else {
            printf("读取块失败,错误码: %d ", status);
        }
    } else {
        /* 初始化失败,LED闪烁 */
        for (int i = 0; i < 10; i++) {
            gpio_bit_set(GPIOC, GPIO_PIN_13);
            delay_1ms(500);
            gpio_bit_reset(GPIOC, GPIO_PIN_13);
            delay_1ms(500);
        }
    }
    
    while (1) {
        /* 主循环 */
    }
}    

sdnand_spi.c

/* 发送数据 */
    spi_i2s_data_transmit(SDNAND_SPI, data);
    
    /* 等待接收缓冲区非空 */
    while(RESET == spi_i2s_flag_get(SDNAND_SPI, SPI_FLAG_RBNE));
    
    /* 返回接收到的数据 */
    return spi_i2s_data_receive(SDNAND_SPI);
}

/* 片选使能 */
static void sdnand_select(void)
{
    gpio_bit_reset(SDNAND_SPI_CS_GPIO_PORT, SDNAND_SPI_CS_PIN);
}

/* 片选禁用 */
static void sdnand_deselect(void)
{
    gpio_bit_set(SDNAND_SPI_CS_GPIO_PORT, SDNAND_SPI_CS_PIN);
}

/* 等待SDNAND就绪 */
static uint8_t sdnand_wait_ready(void)
{
    uint32_t timeout = SDNAND_TIMEOUT;
    uint8_t response;
    
    do {
        response = spi_send_receive(0xFF);
        if (response == 0xFF) return 0; // 就绪
        timeout--;
    } while (timeout > 0);
    
    return 1; // 超时
}

/* 发送SDNAND命令 */
static uint8_t sdnand_send_command(uint8_t cmd, uint32_t arg)
{
    uint8_t response, retry = 0;
    
    /* 片选使能 */
    sdnand_select();
    
    /* 发送命令字节 (0x40 | 命令号) */
    spi_send_receive(0x40 | cmd);
    
    /* 发送参数 (4字节) */
    spi_send_receive((uint8_t)(arg >> 24));
    spi_send_receive((uint8_t)(arg >> 16));
    spi_send_receive((uint8_t)(arg >> 8));
    spi_send_receive((uint8_t)arg);
    
    /* 发送CRC (对于CMD0使用0x95,CMD8使用0x87,其他命令可以使用0xFF) */
    if (cmd == CMD0)
        spi_send_receive(0x95);
    else if (cmd == CMD8)
        spi_send_receive(0x87);
    else
        spi_send_receive(0xFF);
    
    /* 等待响应 */
    while ((response = spi_send_receive(0xFF)) == 0xFF) {
        if (retry++ > 10) {
            sdnand_deselect();
            return 0xFF; // 超时错误
        }
    }
    
    return response;
}

/* SPI接口初始化 */
void sdnand_spi_init(void)
{
    spi_parameter_struct spi_init_struct;
    gpio_initpara gpio_init_struct;
    
    /* 使能SPI和GPIO时钟 */
    rcu_periph_clock_enable(SDNAND_SPI_CLK);
    rcu_periph_clock_enable(SDNAND_SPI_GPIO_CLK);
    rcu_periph_clock_enable(SDNAND_SPI_CS_GPIO_CLK);
    
    /* 配置SPI引脚:SCK, MISO, MOSI */
    gpio_init_struct.gpio_mode = GPIO_MODE_AF_PP;
    gpio_init_struct.gpio_speed = GPIO_SPEED_50MHZ;
    gpio_init_struct.gpio_pins = SDNAND_SPI_SCK_PIN | SDNAND_SPI_MOSI_PIN;
    gpio_init(SDNAND_SPI_GPIO_PORT, &gpio_init_struct);
    
    gpio_init_struct.gpio_mode = GPIO_MODE_IN_FLOATING;
    gpio_init_struct.gpio_pins = SDNAND_SPI_MISO_PIN;
    gpio_init(SDNAND_SPI_GPIO_PORT, &gpio_init_struct);
    
    /* 配置CS引脚 */
    gpio_init_struct.gpio_mode = GPIO_MODE_OUT_PP;
    gpio_init_struct.gpio_pins = SDNAND_SPI_CS_PIN;
    gpio_init(SDNAND_SPI_CS_GPIO_PORT, &gpio_init_struct);
    
    /* 禁用片选 */
    sdnand_deselect();
    
    /* SPI参数配置 */
    spi_i2s_deinit(SDNAND_SPI);
    spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode = SPI_MASTER;
    spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
    spi_init_struct.nss = SPI_NSS_SOFT;
    spi_init_struct.prescale = SPI_PSC_256; // 低速初始化
    spi_init_struct.endian = SPI_ENDIAN_MSB;
    spi_init(SDNAND_SPI, &spi_init_struct);
    
    /* 使能SPI */
    spi_enable(SDNAND_SPI);
}

/* 初始化SDNAND */
uint8_t sdnand_init(void)
{
    uint8_t response;
    uint32_t i;
    uint8_t ocr[4];
    
    /* 发送至少74个时钟周期 (80个时钟 = 10字节0xFF) */
    sdnand_deselect();
    for (i = 0; i < 10; i++) {
        spi_send_receive(0xFF);
    }
    
    /* 发送CMD0:复位SD卡到IDLE状态 */
    response = sdnand_send_command(CMD0, 0);
    if (response != 0x01) {
        sdnand_deselect();
        return 1; // 初始化失败
    }
    
    /* 发送CMD8:检查SD卡版本 (仅SDHC/SDXC需要) */
    response = sdnand_send_command(CMD8, 0x000001AA);
    if (response == 0x01) {
        /* V2.00 SD卡 */
        for (i = 0; i < 4; i++) {
            ocr[i] = spi_send_receive(0xFF);
        }
        
        /* 检查是否支持2.7-3.6V电压范围 */
        if (ocr[2] != 0x01 || ocr[3] != 0xAA) {
            sdnand_deselect();
            return 2; // 电压不支持
        }
    } else if (response != 0x01) {
        /* 可能是V1.x SD卡或MMC卡 */
    }
    
    /* 发送ACMD41:初始化SD卡 */
    uint32_t timeout = SDNAND_TIMEOUT;
    do {
        sdnand_send_command(CMD55, 0);
        response = sdnand_send_command(ACMD41, 0x40000000); // HCS位设置
        timeout--;
    } while (response != 0x00 && timeout > 0);
    
    if (timeout == 0) {
        sdnand_deselect();
        return 3; // 初始化超时
    }
    
    /* 对于SDHC/SDXC卡,发送CMD58检查OCR寄存器 */
    response = sdnand_send_command(CMD58, 0);
    if (response == 0x00) {
        for (i = 0; i < 4; i++) {
            ocr[i] = spi_send_receive(0xFF);
        }
        /* 检查CCS位 (Card Capacity Status) */
        if ((ocr[0] & 0x40) == 0) {
            /* 标准容量SD卡 (512字节/块) */
        } else {
            /* 高容量SD卡 (扇区大小由主机决定) */
        }
    }
    
    /* 设置块大小为512字节 */
    response = sdnand_send_command(CMD16, 512);
    if (response != 0x00) {
        sdnand_deselect();
        return 4; // 设置块大小失败
    }
    
    /* 提高SPI速度 */
    spi_baudrate_prescaler_set(SDNAND_SPI, SPI_PSC_8); // 更快的通信速度
    
    sdnand_deselect();
    return 0; // 初始化成功
}

/* 从SDNAND读取单个块 (512字节) */
uint8_t sdnand_read_block(uint32_t block_address, uint8_t *buffer)
{
    uint8_t response;
    uint16_t i;
    uint8_t checksum[2];
    
    /* 发送CMD17:读取单块 */
    response = sdnand_send_command(CMD17, block_address);
    if (response != 0x00) {
        sdnand_deselect();
        return 1; // 命令失败
    }
    
    /* 等待数据令牌 (0xFE) */
    uint32_t timeout = SDNAND_TIMEOUT;
    while (spi_send_receive(0xFF) != 0xFE) {
        if (timeout-- == 0) {
            sdnand_deselect();
            return 2; // 超时
        }
    }
    
    /* 读取512字节数据 */
    for (i = 0; i < 512; i++) {
        buffer[i] = spi_send_receive(0xFF);
    }
    
    /* 读取CRC校验 (忽略) */
    checksum[0] = spi_send_receive(0xFF);
    checksum[1] = spi_send_receive(0xFF);
    
    sdnand_deselect();
    return 0; // 读取成功
}

/* 从SDNAND读取多个块 */
uint8_t sdnand_read_multiple_blocks(uint32_t start_block, uint8_t *buffer, uint32_t num_blocks)
{
    uint8_t response;
    uint32_t i, j;
    uint8_t checksum[2];
    
    /* 发送CMD18:读取多块 */
    response = sdnand_send_command(CMD18, start_block);
    if (response != 0x00) {
        sdnand_deselect();
        return 1; // 命令失败
    }
    
    for (j = 0; j < num_blocks; j++) {
        /* 等待数据令牌 (0xFE) */
        uint32_t timeout = SDNAND_TIMEOUT;
        while (spi_send_receive(0xFF) != 0xFE) {
            if (timeout-- == 0) {
                sdnand_send_command(CMD12, 0); // 停止传输
                sdnand_deselect();
                return 2; // 超时
            }
        }
        
        /* 读取512字节数据 */
        for (i = 0; i < 512; i++) {
            buffer[j * 512 + i] = spi_send_receive(0xFF);
        }
        
        /* 读取CRC校验 (忽略) */
        checksum[0] = spi_send_receive(0xFF);
        checksum[1] = spi_send_receive(0xFF);
    }
    
    /* 发送CMD12:停止传输 */
    response = sdnand_send_command(CMD12, 0);
    if (response != 0x00) {
        sdnand_deselect();
        return 3; // 停止命令失败
    }
    
    sdnand_deselect();
    return 0; // 读取成功
}    

sdnand_spi.h

#ifndef __SDNAND_SPI_H
#define __SDNAND_SPI_H

#include "gd32f30x.h"

/* 函数声明 */
void sdnand_spi_init(void);
uint8_t sdnand_init(void);
uint8_t sdnand_read_block(uint32_t block_address, uint8_t *buffer);
uint8_t sdnand_read_multiple_blocks(uint32_t start_block, uint8_t *buffer, uint32_t num_blocks);

#endif /* __SDNAND_SPI_H */    

这个代码实现了以下功能:

  1. SPI 接口初始化:配置 GD32F305 的 SPI0 为主模式,设置通信参数。

  2. SDNAND 初始化流程

    • 发送 CMD0 复位 SDNAND

    • 发送 CMD8 检查 SDNAND 版本

    • 通过 ACMD41 初始化 SDNAND

    • 设置块大小为 512 字节

  3. 数据读取功能

    • 单块读取 (CMD17)

    • 多块读取 (CMD18)

使用时需要注意:

  1. 确保硬件连接正确,特别是 SPI 引脚和片选信号

  2. 根据实际情况调整 SPI 时钟分频系数

  3. 对于大容量 SDNAND,可能需要处理不同的寻址模式

  4. 代码中包含了基本的错误处理,但在实际应用中可能需要更完善的错误恢复机制

如果你的 SDNAND 有特殊的初始化要求或工作模式,可能需要进一步调整代码中的命令参数。

热门标签: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客服 微信客服 淘宝店铺 联系我们 返回顶部