在 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 */
这个代码实现了以下功能:
使用时需要注意:
如果你的 SDNAND 有特殊的初始化要求或工作模式,可能需要进一步调整代码中的命令参数。