以下是 STM32F103驱动SDNAND的完整指南,涵盖硬件接线(SDIO/SPI)及驱动代码实现:
SDNAND引脚 | STM32F103引脚 | 功能 |
---|---|---|
CLK | PC12 | SDIO时钟 |
CMD | PD2 | 命令/响应线 |
DAT0 | PC8 | 数据线位0 |
DAT1 | PC9 | 数据线位1 |
DAT2 | PC10 | 数据线位2 |
DAT3 | PC11 | 数据线位3 |
VCC | 3.3V | 电源 |
GND | GND | 地 |
注意事项:
在CLK和DAT0-3线上串联22Ω电阻,并靠近SDNAND放置10pF滤波电容。
确保电源稳定(3.3V±5%),建议并联100nF+10μF电容。
SDNAND引脚 | STM32F103引脚 | 功能 |
---|---|---|
CLK | PA5 (SPI1_SCK) | SPI时钟 |
DI (CMD) | PA7 (SPI1_MOSI) | 主出从入 |
DO (DAT0) | PA6 (SPI1_MISO) | 主入从出 |
CS | PA4 (自定义GPIO) | 片选信号 |
VCC | 3.3V | 电源 |
GND | GND | 地 |
注意事项:
CS引脚需通过GPIO手动控制,非硬件SPI片选。
在MISO线上拉10kΩ电阻,防止信号浮空。
#include "stm32f1xx_hal.h"SD_HandleTypeDef hsd;void SDIO_Init(void) {
hsd.Instance = SDIO;
hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
hsd.Init.BusWide = SDIO_BUS_WIDE_4B; // 4位总线
hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
hsd.Init.ClockDiv = SDIO_TRANSFER_CLK_DIV; // 主频72MHz时设为0x76(400kHz初始化)
if (HAL_SD_Init(&hsd) != HAL_OK) {
Error_Handler();
}
// 切换至高速模式(25MHz)
if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) {
Error_Handler();
}}
// 读取单个块(512字节)HAL_SD_ReadBlocks(&hsd, buffer, sector, 1, 1000);// 写入单个块HAL_SD_WriteBlocks(&hsd, buffer, sector, 1, 1000);
#include "stm32f1xx_hal.h"SPI_HandleTypeDef hspi1;void SPI_Init(void) {
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; // 初始400kHz
HAL_SPI_Init(&hspi1);}
uint8_t SD_SPI_Transfer(uint8_t data) {
uint8_t rx;
HAL_SPI_TransmitReceive(&hspi1, &data, &rx, 1, 100);
return rx;}uint8_t SD_Init_SPI(void) {
// 发送74个时钟脉冲
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
for (int i = 0; i < 10; i++) SD_SPI_Transfer(0xFF);
// CMD0: 复位到空闲状态
uint8_t cmd0[] = {0x40, 0x00, 0x00, 0x00, 0x00, 0x95};
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
for (int i = 0; i < 6; i++) SD_SPI_Transfer(cmd0[i]);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
// 等待响应0x01
uint8_t response;
do {
response = SD_SPI_Transfer(0xFF);
} while (response != 0x01);
// 后续CMD8、CMD55、ACMD41初始化流程(略)
return 0;}
uint8_t SD_ReadBlock(uint32_t sector, uint8_t *buffer) {
// 发送CMD17(读取单块)
uint8_t cmd17[] = {0x51, (sector >> 24) & 0xFF, (sector >> 16) & 0xFF,
(sector >> 8) & 0xFF, sector & 0xFF, 0xFF};
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
for (int i = 0; i < 6; i++) SD_SPI_Transfer(cmd17[i]);
// 等待数据起始令牌0xFE
uint8_t token;
do {
token = SD_SPI_Transfer(0xFF);
} while (token != 0xFE);
// 读取512字节数据
for (int i = 0; i < 512; i++) {
buffer[i] = SD_SPI_Transfer(0xFF);
}
// 丢弃CRC
SD_SPI_Transfer(0xFF);
SD_SPI_Transfer(0xFF);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
return 0;}
使用万用表测量所有接线是否连通。
用示波器检查CLK信号频率(初始化阶段应为400kHz)。
SDIO模式:通过HAL_SD_GetCardInfo()
获取卡信息,确认容量和版本。
SPI模式:用逻辑分析仪抓取CMD0和CMD8的发送与响应。
SDIO模式:将时钟分频设为SDIO_TRANSFER_CLK_DIV=0
(72MHz主频下可达24MHz SDIO时钟)。
SPI模式:初始化后提升SPI时钟至18MHz(SPI_BAUDRATEPRESCALER_4
)。
原因:SDNAND不支持高版本SD协议。
解决:在HAL_SD_Init()
前设置hsd.SdCard.CardVersion = CARD_V1_X;
。
原因:时序不匹配或信号干扰。
解决:
在MISO线上拉10kΩ电阻。
在SD_SPI_Transfer()
函数中增加超时检测。
SDIO模式示例:
STM32F1 SDIO驱动SD卡官方例程:https://github.com/STMicroelectronics/STM32CubeF1/tree/master/Projects/STM3210C_EVAL/Applications/FatFs/SD_Standalone
SPI模式优化库:
开源FatFs + SD SPI驱动:https://github.com/afiskon/stm32-spi-sdcard
通过以上方案,可实现STM32F103对SDNAND的稳定读写。建议优先使用SDIO模式以获得最佳性能,SPI模式适用于PCB空间受限场景。