问:我单片机没SDIO接口,只有SPI接口,想拷资料进去用SDIO模式来拷,但是读取的单片机要用SPI 模式读,你这个可以嘛?
答:可以的,这个SDNAND支持SDIO和SPI两种通信模式,硬件接线只需要参照官方数据手册就行,下图是SPI模式接线方式。
SD mode跟SPI mode只是跟host沟通方式而已,如果你的单片机没有SDIO接口,那你就可以用SPI模式来驱动就行。
软件代码实现:
1.SPI外设配置代码如下:
#ifndef __BSP_SPI_H__
#define __BSP_SPI_H__
#include "stm32f10x.h"
#define PIN_HIGH 1
#define PIN_LOW 0
int sd_spi_config(void);
void set_sd_spi_cs_pin(uint8_t state);
#endif /* __BSP_SPI_H__ */
#include "./spi/bsp_spi.h"
/**
* @brief spi gpio configuration
*
* @note CLK:PA5 MISO:PA6 MOSI:PA7 CS:PA8
*
*/
static void _spi_gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* Configure SD_SPI pins: SCK */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure SD_SPI pins: MOSI */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure SD_SPI pins: MISO */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*!< Configure SD_SPI_CS_PIN pin: SD Card CS pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/**
* @brief configer spi1 peripher.
*
* @note Data rising edge acquisition.
*/
static void _spi_config(void)
{
SPI_InitTypeDef SPI_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
/*!< SD_SPI Config */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 0;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
}
int sd_spi_config(void)
{
_spi_gpio_init;
_spi_config;
return 0;
}
void set_sd_spi_cs_pin(uint8_t state)
{
if (state)
GPIO_SetBits(GPIOA, GPIO_Pin_8);
else
GPIO_ResetBits(GPIOA, GPIO_Pin_8);
}
SD初始化代码如下,set_sd_to_idle_state 函数向SD nand发送CMD0指令,同时由于发送CMD0时,SD nand还处于SD模式,因此手动计算CRC结果为0x95并发送,发送完CMD0之后等待SD nand的R1响应,并根据响应内容,知道SD nand操作完成。
#ifndef __SD_SPI_DRV_H__
#define __SD_SPI_DRV_H__
#include "stm32f10x.h"
/**
* @brief Commands: CMDxx = CMD-number | 0x40
*/
#define SD_CMD_GO_IDLE_STATE 0 /*!< CMD0 = 0x40 */
#define SD_CMD_SEND_OP_COND 1 /*!< CMD1 = 0x41 */
#define SD_CMD_SEND_IF_COND 8 /*!< CMD8 = 0x48 */
#define SD_CMD_SEND_CSD 9 /*!< CMD9 = 0x49 */
#define SD_CMD_SEND_CID 10 /*!< CMD10 = 0x4A */
#define SD_CMD_STOP_TRANSMISSION 12 /*!< CMD12 = 0x4C */
#define SD_CMD_SEND_STATUS 13 /*!< CMD13 = 0x4D */
#define SD_CMD_SET_BLOCKLEN 16 /*!< CMD16 = 0x50 */
#define SD_CMD_READ_SINGLE_BLOCK 17 /*!< CMD17 = 0x51 */
#define SD_CMD_READ_MULT_BLOCK 18 /*!< CMD18 = 0x52 */
#define SD_CMD_SET_BLOCK_COUNT 23 /*!< CMD23 = 0x57 */
#define SD_CMD_WRITE_SINGLE_BLOCK 24 /*!< CMD24 = 0x58 */
#define SD_CMD_WRITE_MULT_BLOCK 25 /*!< CMD25 = 0x59 */
#define SD_CMD_PROG_CSD 27 /*!< CMD27 = 0x5B */
#define SD_CMD_SET_WRITE_PROT 28 /*!< CMD28 = 0x5C */
#define SD_CMD_CLR_WRITE_PROT 29 /*!< CMD29 = 0x5D */
#define SD_CMD_SEND_WRITE_PROT 30 /*!< CMD30 = 0x5E */
#define SD_CMD_SD_ERASE_GRP_START 32 /*!< CMD32 = 0x60 */
#define SD_CMD_SD_ERASE_GRP_END 33 /*!< CMD33 = 0x61 */
#define SD_CMD_UNTAG_SECTOR 34 /*!< CMD34 = 0x62 */
#define SD_CMD_ERASE_GRP_START 35 /*!< CMD35 = 0x63 */
#define SD_CMD_ERASE_GRP_END 36 /*!< CMD36 = 0x64 */
#define SD_CMD_UNTAG_ERASE_GROUP 37 /*!< CMD37 = 0x65 */
#define SD_CMD_ERASE 38 /*!< CMD38 = 0x66 */
#define SD_CMD_READ_OCR 58 /*!< CMD58 */
#define SD_CMD_APP_CMD 55 /*!< CMD55 返回0x01*/
#define SD_ACMD_SD_SEND_OP_COND 41 /*!< ACMD41 返回0x00*/
typedef enum {
/**
* @brief SD reponses and error flags
*/
SD_RESPONSE_NO_ERROR = (0x00),
SD_IN_IDLE_STATE = (0x01),
SD_ERASE_RESET = (0x02),
SD_ILLEGAL_COMMAND = (0x04),
SD_COM_CRC_ERROR = (0x08),
SD_ERASE_SEQUENCE_ERROR = (0x10),
SD_ADDRESS_ERROR = (0x20),
SD_PARAMETER_ERROR = (0x40),
SD_RESPONSE_FAILURE = (0xFF),
/**
* @brief Data response error
*/
SD_DATA_OK = (0x05),
SD_DATA_CRC_ERROR = (0x0B),
SD_DATA_WRITE_ERROR = (0x0D),
SD_DATA_OTHER_ERROR = (0xFF)
} SD_ERROR;
//SD卡的类型
#define SD_TYPE_NOT_SD 0 //非SD卡
#define SD_TYPE_V1 1 //V1.0的卡
#define SD_TYPE_V2 2 //SDSC
#define SD_TYPE_V2HC 4 //SDHC
extern uint8_t SD_Type;
void sd_power_on(void);
SD_ERROR set_sd_to_idle_state(void);
SD_ERROR get_sd_card_type(void);
#endif /* __SD_SPI_DRV_H__ */
#include "./sd_nand/sd_spi_drv.h"
#include "./spi/bsp_spi.h"
#define SD_SPI SPI1
#define SD_DUMMY_BYTE 0xFF
uint8_t SD_Type = 0;
static uint8_t _spi_read_write_byte(uint8_t data)
{
while(SPI_I2S_GetFlagStatus(SD_SPI, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SD_SPI, data);
while(SPI_I2S_GetFlagStatus(SD_SPI, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SD_SPI);
}
static void sd_send_cmd(uint8_t cmd, uint32_t arg, uint8_t crc)
{
uint8_t data[6] = {0};
/* command bit7 is always 1, bit6 is always 0, see SD manual. */
data[0] &= ~(0x80);
data[0] = cmd | 0x40;
data[1] = (uint8_t)(arg >> 24);
data[2] = (uint8_t)(arg >> 16);
data[3] = (uint8_t)(arg >> 8);
data[4] = (uint8_t)(arg);
data[5] = crc;
for (int i = 0; i < 6; i ++)
_spi_read_write_byte(data[i]);
}
static uint8_t sd_read_response(uint8_t response)
{
uint32_t repeat = 0xfff;
while (repeat --) {
if (_spi_read_write_byte(SD_DUMMY_BYTE) == response)
break;
}
if (repeat)
return SD_RESPONSE_NO_ERROR;
else
return SD_RESPONSE_FAILURE;
}
void sd_power_on(void)
{
set_sd_spi_cs_pin(PIN_HIGH);
uint32_t i = 0;
for (i = 0; i <= 9; i++) {
_spi_read_write_byte(SD_DUMMY_BYTE);
}
}
SD_ERROR set_sd_to_idle_state(void)
{
uint32_t repeat = 0xfff;
set_sd_spi_cs_pin(PIN_LOW);
sd_send_cmd(SD_CMD_GO_IDLE_STATE, 0, 0x95);
if (sd_read_response(SD_IN_IDLE_STATE)) //查询卡是否处于空闲状态
return SD_RESPONSE_FAILURE;
set_sd_spi_cs_pin(PIN_HIGH);
_spi_read_write_byte(SD_DUMMY_BYTE); //释放卡
if (repeat == 0)
return SD_RESPONSE_FAILURE;
else
return SD_RESPONSE_NO_ERROR;
}
识别过程
SD nand的识别过程颇为复杂,需要参考下图所示状态机。
其复杂的原因是,随着科技的发展,SD卡也迭代了好几轮,但是协议需要兼容所有版本的卡,因此看上去会复杂很多。
我们采用的SD nand 型号为 CSNPGCR01-AOW,为V2.0.0的卡,且容量为1Gb,因此整体识别路线为中间那条线路。
识别流程说明
V2.0卡识别流程:
1.SD nand上电首先完成初始化,并发送CMD0配置为SPI模式
2.之后发送CMD8命令,读取R7响应,判断SD nand的版本
如果响应值为0x01则判断为V2.0的卡(此时是这个)
如果响应值非0x01则需要进一步判断时V1.0的卡还是MMC卡
3.发送循环指令CMD55+ACMD41,(CMD55用来表示后面的CMD41为ACMD命令),读取R1响应,直到响应0x00表示SD 2.0卡初始化完成
4.发送CMD58命令,读取R3响应,R3中包含OCR寄存器的值,OCR寄存器的第31位(bit30)描述了此卡类型是否为SDHC类型,根据此位判断此卡属于标准容量卡还是高容量卡。