要使用提供的 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;
}
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 { // 处理写入错误}
使用这套代码时,你可能需要根据具体的 GD32 型号和硬件连接情况调整引脚配置和时钟设置。另外,建议在实际应用中添加更完善的错误处理和日志记录功能。
下一篇:sdnand是什么意思