ch32v307_mp3_dac: update spi sd driver
This commit is contained in:
parent
037e3bff6f
commit
21ea48ff4a
83
ch32v307_mp3_dac/User/3rdparty/fatfs/diskio.c
vendored
83
ch32v307_mp3_dac/User/3rdparty/fatfs/diskio.c
vendored
@ -10,7 +10,7 @@
|
||||
#include "ff.h" /* Obtains integer types */
|
||||
#include "diskio.h" /* Declarations of disk functions */
|
||||
#include "debug.h"
|
||||
#include "bsp_spi_sd.h"
|
||||
#include "bsp_sd_spi.h"
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
@ -33,11 +33,16 @@ DSTATUS disk_initialize (
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
if(spisd_init() == SPISD_RESULT_OK) {
|
||||
return 0;
|
||||
if(bsp_sd_init() != bsp_sd_error_none) {
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
return STA_NOINIT;
|
||||
bsp_card_info_t card_info;
|
||||
bsp_sd_spi_get_card_info(&card_info);
|
||||
|
||||
LOG_I("sd card successfully initialized, capacity: %dMB", (uint32_t)card_info.sector_count >> 11);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -53,19 +58,19 @@ DRESULT disk_read (
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
spisd_result_t sd_result;
|
||||
bsp_sd_error_t sd_error;
|
||||
|
||||
if (count == 1) {
|
||||
sd_result = spisd_read_block(sector, (uint8_t*)buff);
|
||||
sd_error = bsp_sd_read_block(buff, sector);
|
||||
} else {
|
||||
sd_result = spisd_read_multi_block(sector, (uint8_t*)buff, count);
|
||||
sd_error = bsp_sd_read_multi_blocks(buff, sector, count);
|
||||
}
|
||||
|
||||
if(sd_result == SPISD_RESULT_OK) {
|
||||
if(sd_error == bsp_sd_error_none) {
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
LOG_E("error while reading disk, error code=%d", sd_result);
|
||||
LOG_E("error while reading disk, error code=%d", sd_error);
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
@ -78,50 +83,32 @@ DRESULT disk_read (
|
||||
#if FF_FS_READONLY == 0
|
||||
|
||||
DRESULT disk_write (
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
LBA_t sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to write */
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
LBA_t sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
DRESULT res;
|
||||
int result;
|
||||
bsp_sd_error_t sd_result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
// translate the arguments here
|
||||
if (count > 1) {
|
||||
sd_result = bsp_sd_write_multi_blocks(buff, sector, count);
|
||||
} else {
|
||||
sd_result = bsp_sd_write_block(buff, sector);
|
||||
}
|
||||
|
||||
result = RAM_disk_write(buff, sector, count);
|
||||
if(sd_result == bsp_sd_error_none) {
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_MMC :
|
||||
// translate the arguments here
|
||||
|
||||
result = MMC_disk_write(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_USB :
|
||||
// translate the arguments here
|
||||
|
||||
result = USB_disk_write(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
LOG_E("error while writing disk, error code=%d", sd_error);
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Miscellaneous Functions */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
@ -135,8 +122,16 @@ DRESULT disk_ioctl (
|
||||
switch(cmd) {
|
||||
case CTRL_SYNC:
|
||||
return RES_OK;
|
||||
case GET_SECTOR_SIZE:
|
||||
case GET_BLOCK_SIZE:
|
||||
*(DWORD *)buff = BSP_SD_BLOCK_SIZE;
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
bsp_card_info_t card_info;
|
||||
bsp_sd_spi_get_card_info(&card_info);
|
||||
*(DWORD *)buff = card_info.sector_count;
|
||||
return RES_OK;
|
||||
default:
|
||||
return RES_PARERR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
564
ch32v307_mp3_dac/User/bsp/bsp_sd_spi.c
Normal file
564
ch32v307_mp3_dac/User/bsp/bsp_sd_spi.c
Normal file
@ -0,0 +1,564 @@
|
||||
#include "bsp_sd_spi.h"
|
||||
#include <string.h>
|
||||
|
||||
#define SD_DUMMY_BYTE 0xFF
|
||||
|
||||
#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_SD_APP_OP_COND 41 /* CMD41 = 0x69 */
|
||||
#define SD_CMD_APP_CMD 55 /* CMD55 = 0x77 */
|
||||
#define SD_CMD_READ_OCR 58 /* CMD55 = 0x7A */
|
||||
|
||||
#define SD_R1_NO_ERROR 0x00
|
||||
#define SD_R1_RESP_IDLE_BIT 0x01
|
||||
#define SD_R1_ILLEGAL_COMMAND 0x04
|
||||
#define SD_DATA_RESP_OK 0x05
|
||||
|
||||
#define SD_SPI_PRESCALER_LOW SPI_BaudRatePrescaler_256
|
||||
#define SD_SPI_PRESCALER_HIGH SPI_BaudRatePrescaler_4
|
||||
|
||||
#define SD_WAIT_CMD_RESP_TIMEOUT 10000U //等待指令响应的超时
|
||||
#define SD_WAIT_READ_WRITE_TIMEOUT 100000U //等待读写操作的超时
|
||||
|
||||
static bsp_card_info_t card_info = { 0 };
|
||||
|
||||
static void sd_spi_init(void)
|
||||
{
|
||||
RCC_APB2PeriphClockCmd(BSP_SD_CS_GPIO_CLK | BSP_SD_SPI_MOSI_GPIO_CLK |
|
||||
BSP_SD_SPI_MISO_GPIO_CLK | BSP_SD_SPI_SCK_GPIO_CLK, ENABLE);
|
||||
BSP_SD_SPI_CLK_ENABLE();
|
||||
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
|
||||
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
GPIO_InitStructure.GPIO_Pin = BSP_SD_SPI_SCK_PIN;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
|
||||
GPIO_Init(BSP_SD_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = BSP_SD_SPI_MOSI_PIN;
|
||||
GPIO_Init(BSP_SD_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = BSP_SD_SPI_MISO_PIN;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||
GPIO_Init(BSP_SD_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = BSP_SD_CS_PIN;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
GPIO_Init(BSP_SD_CS_GPIO_PORT, &GPIO_InitStructure);
|
||||
|
||||
SPI_InitTypeDef SPI_InitStructure;
|
||||
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 = SD_SPI_PRESCALER_LOW;
|
||||
|
||||
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
|
||||
SPI_InitStructure.SPI_CRCPolynomial = 7;
|
||||
SPI_Init(BSP_SD_SPI, &SPI_InitStructure);
|
||||
|
||||
SPI_Cmd(BSP_SD_SPI, ENABLE);
|
||||
}
|
||||
|
||||
static void sd_set_spi_prescaler(uint32_t prescaler)
|
||||
{
|
||||
BSP_SD_SPI->CTLR1 &= 0xFFC7;
|
||||
BSP_SD_SPI->CTLR1 |= prescaler;
|
||||
}
|
||||
|
||||
static void sd_select(void)
|
||||
{
|
||||
GPIO_ResetBits(BSP_SD_CS_GPIO_PORT, BSP_SD_CS_PIN);
|
||||
}
|
||||
|
||||
static void sd_deselect(void)
|
||||
{
|
||||
GPIO_SetBits(BSP_SD_CS_GPIO_PORT, BSP_SD_CS_PIN);
|
||||
}
|
||||
|
||||
static __always_inline uint8_t sd_write_read_byte(uint8_t tx_byte)
|
||||
{
|
||||
while((BSP_SD_SPI->STATR & SPI_I2S_FLAG_TXE) == RESET);
|
||||
BSP_SD_SPI->DATAR = tx_byte;
|
||||
while((BSP_SD_SPI->STATR & SPI_I2S_FLAG_RXNE) == RESET);
|
||||
return BSP_SD_SPI->DATAR;
|
||||
}
|
||||
|
||||
static void sd_write_bytes(const uint8_t *buffer, uint16_t length)
|
||||
{
|
||||
if (length < BSP_SD_BLOCK_SIZE) { //数据量较小时使用查询方式
|
||||
for (uint32_t i = 0; i < length; i ++) {
|
||||
sd_write_read_byte(buffer[i]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t spi_rx_dummy_byte;
|
||||
|
||||
/* 等待上次传输完成 */
|
||||
while((BSP_SD_SPI->STATR & SPI_I2S_FLAG_TXE) == 0);
|
||||
|
||||
/* 首先配置DMA接收通道 */
|
||||
DMA_InitTypeDef DMA_InitStructure;
|
||||
DMA_InitStructure.DMA_BufferSize = length;
|
||||
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&BSP_SD_SPI->DATAR;
|
||||
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&spi_rx_dummy_byte;
|
||||
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
|
||||
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
|
||||
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //SPI处于全双工模式时,需要DMA RX通道从而复位SPI_I2S_FLAG_RXNE标志位
|
||||
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
||||
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
|
||||
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
|
||||
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
|
||||
DMA_Init(BSP_SD_RX_DMA_CHANNEL, &DMA_InitStructure);
|
||||
|
||||
/* 然后配置DMA发送通道 */
|
||||
DMA_InitStructure.DMA_BufferSize = length;
|
||||
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(BSP_SD_SPI->DATAR);
|
||||
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer;
|
||||
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
|
||||
DMA_InitStructure.DMA_Priority = DMA_Priority_Low; //发送DMA优先级应低于接收,防止FIFO溢出
|
||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
||||
DMA_Init(BSP_SD_TX_DMA_CHANNEL, &DMA_InitStructure);
|
||||
|
||||
SPI_I2S_DMACmd(BSP_SD_SPI, SPI_I2S_DMAReq_Rx, ENABLE);
|
||||
SPI_I2S_DMACmd(BSP_SD_SPI, SPI_I2S_DMAReq_Tx, ENABLE);
|
||||
DMA_Cmd(BSP_SD_RX_DMA_CHANNEL, ENABLE);
|
||||
DMA_Cmd(BSP_SD_TX_DMA_CHANNEL, ENABLE);
|
||||
|
||||
while (DMA_GetITStatus(BSP_SD_TX_DMA_IT_TC_FLAG) != SET); //使用RTOS时改为中断模式
|
||||
|
||||
SPI_I2S_DMACmd(BSP_SD_SPI, SPI_I2S_DMAReq_Rx, DISABLE);
|
||||
SPI_I2S_DMACmd(BSP_SD_SPI, SPI_I2S_DMAReq_Tx, DISABLE);
|
||||
DMA_DeInit(BSP_SD_RX_DMA_CHANNEL);
|
||||
DMA_DeInit(BSP_SD_TX_DMA_CHANNEL);
|
||||
}
|
||||
|
||||
static void sd_read_bytes(uint8_t *buffer, uint16_t length)
|
||||
{
|
||||
if (length < BSP_SD_BLOCK_SIZE) { //数据量较小时使用查询方式
|
||||
for (uint32_t i = 0; i < length; i ++) {
|
||||
buffer[i] = sd_write_read_byte(SD_DUMMY_BYTE);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t spi_tx_dummy_byte = SD_DUMMY_BYTE;
|
||||
|
||||
/* 等待上次传输完成 */
|
||||
while((BSP_SD_SPI->STATR & SPI_I2S_FLAG_TXE) == 0);
|
||||
|
||||
/* 配置DMA接收通道 */
|
||||
DMA_InitTypeDef DMA_InitStructure;
|
||||
DMA_InitStructure.DMA_BufferSize = length;
|
||||
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&BSP_SD_SPI->DATAR;
|
||||
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer;
|
||||
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
|
||||
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
|
||||
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
||||
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
||||
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
|
||||
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
|
||||
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
|
||||
DMA_Init(BSP_SD_RX_DMA_CHANNEL, &DMA_InitStructure);
|
||||
|
||||
/* 配置DMA发送通道产生SPI时钟 */
|
||||
DMA_InitStructure.DMA_BufferSize = length;
|
||||
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(BSP_SD_SPI->DATAR);
|
||||
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&spi_tx_dummy_byte;
|
||||
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
|
||||
DMA_InitStructure.DMA_Priority = DMA_Priority_Low; //发送DMA优先级应低于接收,防止FIFO溢出
|
||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //重复发送dummy字节从而产生时钟
|
||||
DMA_Init(BSP_SD_TX_DMA_CHANNEL, &DMA_InitStructure);
|
||||
|
||||
SPI_I2S_DMACmd(BSP_SD_SPI, SPI_I2S_DMAReq_Rx, ENABLE);
|
||||
SPI_I2S_DMACmd(BSP_SD_SPI, SPI_I2S_DMAReq_Tx, ENABLE);
|
||||
DMA_Cmd(BSP_SD_RX_DMA_CHANNEL, ENABLE);
|
||||
DMA_Cmd(BSP_SD_TX_DMA_CHANNEL, ENABLE);
|
||||
|
||||
while (DMA_GetITStatus(BSP_SD_TX_DMA_IT_TC_FLAG) != SET); //使用RTOS时改为中断模式
|
||||
|
||||
SPI_I2S_DMACmd(BSP_SD_SPI, SPI_I2S_DMAReq_Rx, DISABLE);
|
||||
SPI_I2S_DMACmd(BSP_SD_SPI, SPI_I2S_DMAReq_Tx, DISABLE);
|
||||
DMA_DeInit(BSP_SD_RX_DMA_CHANNEL);
|
||||
DMA_DeInit(BSP_SD_TX_DMA_CHANNEL);
|
||||
}
|
||||
|
||||
static void sd_enable(void)
|
||||
{
|
||||
sd_write_read_byte(SD_DUMMY_BYTE); //发送额外的8个时钟
|
||||
sd_select();
|
||||
}
|
||||
|
||||
static void sd_disable(void)
|
||||
{
|
||||
sd_deselect();
|
||||
sd_write_read_byte(SD_DUMMY_BYTE); //发送额外的8个时钟
|
||||
}
|
||||
|
||||
static bsp_sd_error_t sd_wait_response(uint8_t response)
|
||||
{
|
||||
uint32_t timeout = SD_WAIT_READ_WRITE_TIMEOUT;
|
||||
|
||||
do {
|
||||
if (sd_write_read_byte(SD_DUMMY_BYTE) == response) {
|
||||
return bsp_sd_error_none;
|
||||
}
|
||||
} while (timeout --);
|
||||
|
||||
return bsp_sd_error_timeout;
|
||||
}
|
||||
|
||||
static uint8_t sd_send_command(uint8_t command, uint32_t argument, uint8_t crc)
|
||||
{
|
||||
sd_enable(); //拉低片选
|
||||
|
||||
sd_write_bytes((uint8_t[]){ command | 0x40, argument >> 24, argument >> 16, argument >> 8, argument, crc }, 6); //发送指令+数据+CRC校验值
|
||||
|
||||
uint8_t r1_response = 0xFF;
|
||||
uint32_t timeout = SD_WAIT_CMD_RESP_TIMEOUT;
|
||||
do {
|
||||
r1_response = sd_write_read_byte(SD_DUMMY_BYTE); //等待卡响应或超时
|
||||
} while ((r1_response == 0xFF || r1_response == 0x7F) && timeout --); //一些较旧的卡的CMD12回应首字节为0x7F,此后才为0xFF
|
||||
|
||||
return r1_response;
|
||||
}
|
||||
|
||||
static bsp_sd_error_t sd_wait_ready(void)
|
||||
{
|
||||
bsp_sd_error_t error = bsp_sd_error_timeout;
|
||||
|
||||
uint32_t timeout = SD_WAIT_READ_WRITE_TIMEOUT;
|
||||
do {
|
||||
uint8_t r1_response = sd_send_command(SD_CMD_SEND_STATUS, 0x00, 0x00); //发送CMD13请求卡片状态
|
||||
if (r1_response != SD_R1_NO_ERROR) {
|
||||
error = bsp_sd_error_invalid_response;
|
||||
goto error;
|
||||
}
|
||||
|
||||
uint8_t status_register[4];
|
||||
for (uint8_t i = 0; i < 4; i ++) { //继续读取R7格式回应的剩余4字节
|
||||
status_register[i] = sd_write_read_byte(SD_DUMMY_BYTE);
|
||||
}
|
||||
|
||||
if (status_register[2] & 0x01) { //READY_FOR_DATA=1
|
||||
error = bsp_sd_error_none;
|
||||
goto error;
|
||||
}
|
||||
} while (timeout --);
|
||||
|
||||
error:
|
||||
sd_disable();
|
||||
return error;
|
||||
}
|
||||
|
||||
bsp_sd_error_t bsp_sd_init(void)
|
||||
{
|
||||
bsp_sd_error_t error = bsp_sd_error_none;
|
||||
|
||||
sd_spi_init();
|
||||
|
||||
for (uint8_t i = 0; i < 10; i ++) {
|
||||
sd_write_read_byte(0xFF); //在MOSI上发送不少于74个脉冲
|
||||
}
|
||||
|
||||
memset(&card_info, 0, sizeof(card_info)); //复位卡信息结构体
|
||||
|
||||
uint8_t r1_response;
|
||||
uint32_t timeout = SD_WAIT_CMD_RESP_TIMEOUT;
|
||||
do {
|
||||
r1_response = sd_send_command(SD_CMD_GO_IDLE_STATE, 0, 0x95); //等待卡进入IDLE状态
|
||||
} while ((r1_response != SD_R1_RESP_IDLE_BIT) && timeout --);
|
||||
|
||||
if (timeout == 0) {
|
||||
error = bsp_sd_error_timeout;
|
||||
goto error;
|
||||
}
|
||||
|
||||
r1_response = sd_send_command(SD_CMD_SEND_IF_COND, 0x1AA, 0x87); //发送CMD8判断卡是否为SD V2.0
|
||||
if (r1_response & SD_R1_ILLEGAL_COMMAND) { //CMD8无响应则卡为SD V1.0或MMC卡
|
||||
sd_disable(); //结束CMD8
|
||||
/* ACMD41有响应为SD V1.0否则为MMC卡 */
|
||||
timeout = SD_WAIT_CMD_RESP_TIMEOUT;
|
||||
do {
|
||||
sd_send_command(SD_CMD_APP_CMD, 0x00, 0x00); //在发送ACMD之前都需要发送CMD55
|
||||
r1_response = sd_send_command(SD_CMD_SD_APP_OP_COND, 0x00, 0x00); //发送ACMD41尝试初始化卡
|
||||
} while (r1_response != SD_R1_NO_ERROR && timeout --); //等待R1就绪直到超时
|
||||
|
||||
if (timeout == 0) { //ACMD41无响应超时
|
||||
timeout = SD_WAIT_CMD_RESP_TIMEOUT;
|
||||
do {
|
||||
r1_response = sd_send_command(SD_CMD_SEND_OP_COND, 0x00, 0x00); //发送CMD1判断是否为MMC卡
|
||||
} while (r1_response != SD_R1_NO_ERROR && timeout --);
|
||||
|
||||
if (timeout == 0) { //CMD1无响应超时
|
||||
error = bsp_sd_error_unknown_card_type; //卡类型未知
|
||||
goto error;
|
||||
}
|
||||
|
||||
card_info.card_type = bsp_sd_type_mmc; //CMD1有响应为MMC卡
|
||||
} else {
|
||||
card_info.card_type = bsp_sd_type_sdv1;
|
||||
}
|
||||
} else if (r1_response == SD_R1_RESP_IDLE_BIT) { //CMD8有响应则卡为SD V2.0
|
||||
uint8_t r7_response[4];
|
||||
for (uint8_t i = 0; i < 4; i ++) { //继续读取R7格式回应的剩余4字节
|
||||
r7_response[i] = sd_write_read_byte(SD_DUMMY_BYTE);
|
||||
}
|
||||
|
||||
if (r7_response[2] == 0x01 && r7_response[3] == 0xAA) { //判断工作电压范围是否为2.7-3.6V
|
||||
timeout = SD_WAIT_CMD_RESP_TIMEOUT;
|
||||
do {
|
||||
sd_send_command(SD_CMD_APP_CMD, 0x00, 0x00); //在发送ACMD之前都需要发送CMD55
|
||||
r1_response = sd_send_command(SD_CMD_SD_APP_OP_COND,0x40000000,0x01); //发送ACMD41尝试初始化卡
|
||||
} while(r1_response != SD_R1_NO_ERROR && timeout --);
|
||||
|
||||
if(timeout == 0) {
|
||||
error = bsp_sd_error_timeout;
|
||||
goto error;
|
||||
}
|
||||
|
||||
r1_response = sd_send_command(SD_CMD_READ_OCR, 0x00, 0x00); //发送CMD58判断卡是否为HC
|
||||
if(r1_response != SD_R1_NO_ERROR) {
|
||||
error = bsp_sd_error_timeout;
|
||||
goto error;
|
||||
}
|
||||
for (uint8_t i = 0; i < 4; i ++) { //继续读取R7格式回应的剩余4字节
|
||||
r7_response[i] = sd_write_read_byte(SD_DUMMY_BYTE);
|
||||
}
|
||||
|
||||
card_info.card_type = (r7_response[0] & 0x40) ? bsp_sd_type_sdv2hc : bsp_sd_type_sdv2;
|
||||
} else {
|
||||
error = bsp_sd_error_unknown_card_type;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
sd_disable(); //开始获取SD卡Block数量
|
||||
|
||||
r1_response = sd_send_command(SD_CMD_SEND_CSD, 0x00, 0x00); //发送读取CSD命令
|
||||
if (r1_response != 0) {
|
||||
error = bsp_sd_error_invalid_response;
|
||||
goto error;
|
||||
}
|
||||
|
||||
error = sd_wait_response(0xFE); //等待读数据就绪的Token
|
||||
if (error != bsp_sd_error_none) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
uint8_t csd[16];
|
||||
sd_read_bytes(csd, sizeof(csd)); //读取16字节CSD寄存器数据
|
||||
|
||||
if ((csd[0] & 0xC0) == 0x40) { //SD V2.0
|
||||
uint32_t csize = csd[9] + ((uint32_t)csd[8] << 8) + ((uint32_t)(csd[7] & 0x3F) << 16) + 1;
|
||||
card_info.sector_count = csize << 10;
|
||||
} else { // SD V1.0或MMC卡
|
||||
uint32_t n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
|
||||
uint32_t csize = (csd[8] >> 6) + ((uint32_t)csd[7] << 2) + ((uint32_t)(csd[6] & 3) << 10) + 1;
|
||||
card_info.sector_count = csize << (n - 9);
|
||||
}
|
||||
|
||||
sd_set_spi_prescaler(SD_SPI_PRESCALER_HIGH);
|
||||
|
||||
error:
|
||||
sd_disable();
|
||||
return error;
|
||||
}
|
||||
|
||||
bsp_sd_error_t bsp_sd_read_block(uint8_t* buffer, uint32_t sector_address)
|
||||
{
|
||||
bsp_sd_error_t error = bsp_sd_error_none;
|
||||
|
||||
if (card_info.card_type != bsp_sd_type_sdv2hc) {
|
||||
sector_address <<= 9;
|
||||
}
|
||||
|
||||
uint8_t r1_response = sd_send_command(SD_CMD_READ_SINGLE_BLOCK, sector_address, 0x00); //发送单Block读取命令
|
||||
if (r1_response != 0) {
|
||||
error = bsp_sd_error_invalid_response;
|
||||
goto error;
|
||||
}
|
||||
|
||||
error = sd_wait_response(0xFE); //等待读数据就绪的Token
|
||||
if (error != bsp_sd_error_none) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
sd_read_bytes(buffer, BSP_SD_BLOCK_SIZE); //读取单个Block的数据
|
||||
sd_write_bytes((uint8_t[]){ 0xFF, 0xFF }, 2); //跳过2字节CRC
|
||||
|
||||
error:
|
||||
sd_disable();
|
||||
return error;
|
||||
}
|
||||
|
||||
bsp_sd_error_t bsp_sd_read_multi_blocks(uint8_t* buffer, uint32_t sector_address, uint16_t sector_count)
|
||||
{
|
||||
bsp_sd_error_t error = bsp_sd_error_none;
|
||||
|
||||
if (card_info.card_type != bsp_sd_type_sdv2hc) {
|
||||
sector_address <<= 9;
|
||||
}
|
||||
|
||||
if (sector_count <= 1) {
|
||||
return bsp_sd_error_invalid_parameter;
|
||||
} else {
|
||||
uint8_t r1_response = sd_send_command(SD_CMD_READ_MULT_BLOCK, sector_address, 0x00);
|
||||
if (r1_response != SD_R1_NO_ERROR) {
|
||||
error = bsp_sd_error_invalid_response;
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < sector_count; i ++) { //循环读取多个Block
|
||||
error = sd_wait_response(0xFE); //等待读数据就绪的Token
|
||||
if (error != bsp_sd_error_none) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
sd_read_bytes(buffer, BSP_SD_BLOCK_SIZE); //读取单个Block的数据
|
||||
sd_write_bytes((uint8_t[]){ 0xFF, 0xFF }, 2); //跳过2字节CRC
|
||||
|
||||
buffer += BSP_SD_BLOCK_SIZE; //准备读取下一个Block
|
||||
}
|
||||
|
||||
r1_response = sd_send_command(SD_CMD_STOP_TRANSMISSION, 0, 0x00); //结束单个Block的读取
|
||||
if (r1_response != SD_R1_NO_ERROR) {
|
||||
error = bsp_sd_error_invalid_response;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
sd_disable();
|
||||
return error;
|
||||
}
|
||||
|
||||
bsp_sd_error_t bsp_sd_write_block(const uint8_t *buffer, uint32_t sector_address)
|
||||
{
|
||||
bsp_sd_error_t error = bsp_sd_error_none;
|
||||
|
||||
if (card_info.card_type != bsp_sd_type_sdv2hc) {
|
||||
sector_address <<= 9;
|
||||
}
|
||||
|
||||
sd_wait_ready(); //一些较慢的卡在连续写操作前需要检查卡是否忙
|
||||
|
||||
uint8_t r1_response = sd_send_command(SD_CMD_WRITE_SINGLE_BLOCK, sector_address, 0x00);
|
||||
if (r1_response != 0) {
|
||||
error = bsp_sd_error_invalid_response;
|
||||
goto error;
|
||||
}
|
||||
|
||||
sd_write_read_byte(0xFE); //发送单Block写的Token
|
||||
|
||||
sd_write_bytes(buffer, BSP_SD_BLOCK_SIZE); //写入单个Block的数据
|
||||
sd_write_bytes((uint8_t[]){ 0xFF, 0xFF }, 2); //跳过2字节CRC
|
||||
|
||||
uint8_t data_response = sd_write_read_byte(0xFF) & 0x1F;
|
||||
if (data_response != SD_DATA_RESP_OK) { //判断卡是否成功接收数据
|
||||
error = bsp_sd_error_not_ready;
|
||||
goto error;
|
||||
}
|
||||
|
||||
error = sd_wait_response(0xFF); //等待写入操作完成
|
||||
if (error != bsp_sd_error_none) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
sd_disable();
|
||||
return error;
|
||||
}
|
||||
|
||||
bsp_sd_error_t bsp_sd_write_multi_blocks(const uint8_t *buffer, uint32_t sector_address, uint16_t sector_count)
|
||||
{
|
||||
bsp_sd_error_t error = bsp_sd_error_none;
|
||||
|
||||
if (card_info.card_type != bsp_sd_type_sdv2hc) {
|
||||
sector_address <<= 9;
|
||||
}
|
||||
|
||||
sd_wait_ready(); //一些较慢的卡在连续写操作前需要检查卡是否忙
|
||||
|
||||
uint8_t r1_response;
|
||||
if (card_info.card_type != bsp_sd_type_mmc) { //非MMC卡必须要发送CMD55与ACMD23
|
||||
sd_send_command(SD_CMD_APP_CMD, 0x00, 0x00);
|
||||
|
||||
r1_response = sd_send_command(SD_CMD_SET_BLOCK_COUNT, sector_count, 0x00);
|
||||
if (r1_response != SD_R1_NO_ERROR) {
|
||||
error = bsp_sd_error_invalid_response;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
r1_response = sd_send_command(SD_CMD_WRITE_MULT_BLOCK, sector_address, 0x00);
|
||||
if (r1_response != SD_R1_NO_ERROR) {
|
||||
error = bsp_sd_error_invalid_response;
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < sector_count; i ++) { //循环写入多个Block
|
||||
sd_write_read_byte(0xFC); //发送多Block写开始的Token
|
||||
|
||||
sd_write_bytes(buffer, BSP_SD_BLOCK_SIZE);
|
||||
sd_write_bytes((uint8_t[]){ 0xFF, 0xFF }, 2); //跳过2字节CRC
|
||||
|
||||
uint8_t data_response = sd_write_read_byte(0xFF) & 0x1F;
|
||||
if (data_response != SD_DATA_RESP_OK) { //判断卡是否成功接收数据
|
||||
error = bsp_sd_error_not_ready;
|
||||
goto error;
|
||||
}
|
||||
|
||||
error = sd_wait_response(0xFF); //等待写入操作完成
|
||||
if (error != bsp_sd_error_none) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
buffer += BSP_SD_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
sd_write_read_byte(0xFD); //发送多Block写结束的Token
|
||||
|
||||
error = sd_wait_response(0xFF); //等待写入操作完成
|
||||
if (error != bsp_sd_error_none) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
sd_disable();
|
||||
return error;
|
||||
}
|
||||
|
||||
void bsp_sd_spi_get_card_info(bsp_card_info_t *card_info_out)
|
||||
{
|
||||
memcpy(card_info_out, &card_info, sizeof(bsp_card_info_t));
|
||||
}
|
||||
60
ch32v307_mp3_dac/User/bsp/bsp_sd_spi.h
Normal file
60
ch32v307_mp3_dac/User/bsp/bsp_sd_spi.h
Normal file
@ -0,0 +1,60 @@
|
||||
#ifndef __BSP_SD_SPI_H
|
||||
#define __BSP_SD_SPI_H
|
||||
|
||||
#include "ch32v30x.h"
|
||||
|
||||
#define BSP_SD_BLOCK_SIZE 512
|
||||
|
||||
#define BSP_SD_SPI SPI2
|
||||
#define BSP_SD_SPI_CLK_ENABLE() RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE)
|
||||
|
||||
#define BSP_SD_SPI_SCK_PIN GPIO_Pin_13 /* PB.13 */
|
||||
#define BSP_SD_SPI_SCK_GPIO_PORT GPIOB /* GPIOB */
|
||||
#define BSP_SD_SPI_SCK_GPIO_CLK RCC_APB2Periph_GPIOB
|
||||
#define BSP_SD_SPI_MISO_PIN GPIO_Pin_14 /* PB.14 */
|
||||
#define BSP_SD_SPI_MISO_GPIO_PORT GPIOB /* GPIOB */
|
||||
#define BSP_SD_SPI_MISO_GPIO_CLK RCC_APB2Periph_GPIOB
|
||||
#define BSP_SD_SPI_MOSI_PIN GPIO_Pin_15 /* PB.15 */
|
||||
#define BSP_SD_SPI_MOSI_GPIO_PORT GPIOB /* GPIOB */
|
||||
#define BSP_SD_SPI_MOSI_GPIO_CLK RCC_APB2Periph_GPIOB
|
||||
#define BSP_SD_CS_PIN GPIO_Pin_12 /* PB.12 */
|
||||
#define BSP_SD_CS_GPIO_PORT GPIOB /* GPIOB */
|
||||
#define BSP_SD_CS_GPIO_CLK RCC_APB2Periph_GPIOB
|
||||
|
||||
#define BSP_SD_TX_DMA_CHANNEL DMA1_Channel5
|
||||
#define BSP_SD_RX_DMA_CHANNEL DMA1_Channel4
|
||||
#define BSP_SD_TX_DMA_IT_TC_FLAG DMA1_IT_TC4
|
||||
|
||||
typedef enum {
|
||||
bsp_sd_type_unknown = 0,
|
||||
bsp_sd_type_mmc,
|
||||
bsp_sd_type_sdv1,
|
||||
bsp_sd_type_sdv2,
|
||||
bsp_sd_type_sdv2hc,
|
||||
} bsp_sd_type_t;
|
||||
|
||||
typedef enum {
|
||||
bsp_sd_error_none = 0,
|
||||
bsp_sd_error_no_card,
|
||||
bsp_sd_error_invalid_parameter,
|
||||
bsp_sd_error_unknown_card_type,
|
||||
bsp_sd_error_not_ready,
|
||||
bsp_sd_error_invalid_response,
|
||||
bsp_sd_error_timeout,
|
||||
} bsp_sd_error_t;
|
||||
|
||||
typedef struct {
|
||||
bsp_sd_type_t card_type;
|
||||
uint32_t sector_count;
|
||||
} bsp_card_info_t;
|
||||
|
||||
bsp_sd_error_t bsp_sd_init(void);
|
||||
|
||||
bsp_sd_error_t bsp_sd_read_block(uint8_t* buffer, uint32_t sector_address);
|
||||
bsp_sd_error_t bsp_sd_read_multi_blocks(uint8_t* buffer, uint32_t sector_address, uint16_t sector_count);
|
||||
bsp_sd_error_t bsp_sd_write_block(const uint8_t *buffer, uint32_t sector_address);
|
||||
bsp_sd_error_t bsp_sd_write_multi_blocks(const uint8_t *buffer, uint32_t sector_address, uint16_t sector_count);
|
||||
|
||||
void bsp_sd_spi_get_card_info(bsp_card_info_t *card_info_out);
|
||||
|
||||
#endif
|
||||
@ -1,809 +0,0 @@
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "bsp_spi_sd.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* Private types -------------------------------------------------------------*/
|
||||
typedef enum card_type_e {
|
||||
CARD_TYPE_MMC = 0x00,
|
||||
CARD_TYPE_SDV1 = 0x01,
|
||||
CARD_TYPE_SDV2 = 0x02,
|
||||
CARD_TYPE_SDV2HC = 0x04
|
||||
} card_type_t;
|
||||
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
#define DUMMY_BYTE 0xFF
|
||||
#define BLOCK_SIZE 512
|
||||
|
||||
#define CMD0 0 /* Reset */
|
||||
#define CMD1 1 /* Send Operator Condition - SEND_OP_COND */
|
||||
#define CMD8 8 /* Send Interface Condition - SEND_IF_COND */
|
||||
#define CMD9 9 /* Read CSD */
|
||||
#define CMD10 10 /* Read CID */
|
||||
#define CMD12 12 /* Stop data transmit */
|
||||
#define CMD16 16 /* Set block size, should return 0x00 */
|
||||
#define CMD17 17 /* Read single block */
|
||||
#define CMD18 18 /* Read multi block */
|
||||
#define ACMD23 23 /* Prepare erase N-blocks before multi block write */
|
||||
#define CMD24 24 /* Write single block */
|
||||
#define CMD25 25 /* Write multi block */
|
||||
#define ACMD41 41 /* should return 0x00 */
|
||||
#define CMD55 55 /* should return 0x01 */
|
||||
#define CMD58 58 /* Read OCR */
|
||||
#define CMD59 59 /* CRC disable/enable, should return 0x00 */
|
||||
|
||||
#define CMD_WAIT_RESP_TIMEOUT (100000U)
|
||||
#define WAIT_IDLE_TIMEOUT (20000U)
|
||||
|
||||
#define SPI_PRESCALER_LOW (SPI_BaudRatePrescaler_256)
|
||||
#define SPI_PRESCALER_HIGH (SPI_BaudRatePrescaler_4)
|
||||
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
static card_type_t _card_type;
|
||||
|
||||
/* Private functions ---------------------------------------------------------*/
|
||||
#define SPISD_LOG(...) LOG_E(__VA_ARGS__)
|
||||
|
||||
static void _spi_init(void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
SPI_InitTypeDef SPI_InitStructure;
|
||||
|
||||
/*!< SD_SPI_CS_GPIO, SD_SPI_MOSI_GPIO, SD_SPI_MISO_GPIO, SD_SPI_DETECT_GPIO
|
||||
and SD_SPI_SCK_GPIO Periph clock enable */
|
||||
RCC_APB2PeriphClockCmd(SD_CS_GPIO_CLK | SD_SPI_MOSI_GPIO_CLK | SD_SPI_MISO_GPIO_CLK |
|
||||
SD_SPI_SCK_GPIO_CLK | SD_DETECT_GPIO_CLK, ENABLE);
|
||||
|
||||
/*!< SD_SPI Periph clock enable */
|
||||
SD_SPI_CLK_ENABLE();
|
||||
|
||||
/*!< DMA1 Periph clock enable */
|
||||
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
|
||||
|
||||
|
||||
/*!< Configure SD_SPI pins: SCK */
|
||||
GPIO_InitStructure.GPIO_Pin = SD_SPI_SCK_PIN;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
|
||||
GPIO_Init(SD_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
|
||||
|
||||
/*!< Configure SD_SPI pins: MOSI */
|
||||
GPIO_InitStructure.GPIO_Pin = SD_SPI_MOSI_PIN;
|
||||
GPIO_Init(SD_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);
|
||||
|
||||
/*!< Configure SD_SPI pins: MISO */
|
||||
GPIO_InitStructure.GPIO_Pin = SD_SPI_MISO_PIN;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||
GPIO_Init(SD_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);
|
||||
|
||||
/*!< Configure SD_SPI_CS_PIN pin: SD Card CS pin */
|
||||
GPIO_InitStructure.GPIO_Pin = SD_CS_PIN;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
GPIO_Init(SD_CS_GPIO_PORT, &GPIO_InitStructure);
|
||||
|
||||
/*!< Configure SD_SPI_DETECT_PIN pin: SD Card detect pin */
|
||||
GPIO_InitStructure.GPIO_Pin = SD_DETECT_PIN;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
|
||||
GPIO_Init(SD_DETECT_GPIO_PORT, &GPIO_InitStructure);
|
||||
|
||||
/*!< 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_PRESCALER_LOW;
|
||||
|
||||
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
|
||||
SPI_InitStructure.SPI_CRCPolynomial = 7;
|
||||
SPI_Init(SD_SPI, &SPI_InitStructure);
|
||||
|
||||
SPI_Cmd(SD_SPI, ENABLE); /*!< SD_SPI enable */
|
||||
}
|
||||
|
||||
static void _set_spi_prescaler(uint32_t prescaler)
|
||||
{
|
||||
SD_SPI->CTLR1 &= 0xFFC7;
|
||||
SD_SPI->CTLR1 |= prescaler;
|
||||
}
|
||||
|
||||
static void _select(void)
|
||||
{
|
||||
GPIO_ResetBits(SD_CS_GPIO_PORT, SD_CS_PIN);
|
||||
}
|
||||
|
||||
static void _release(void)
|
||||
{
|
||||
GPIO_SetBits(SD_CS_GPIO_PORT, SD_CS_PIN);
|
||||
}
|
||||
|
||||
static bool _is_present(void)
|
||||
{
|
||||
return GPIO_ReadInputData(SD_DETECT_GPIO_PORT) & SD_DETECT_PIN ? false : true;
|
||||
}
|
||||
|
||||
static __always_inline uint8_t _wr_rd_byte(uint8_t byte)
|
||||
{
|
||||
while((SD_SPI->STATR & SPI_I2S_FLAG_TXE) == RESET);
|
||||
SD_SPI->DATAR = byte;
|
||||
while((SD_SPI->STATR & SPI_I2S_FLAG_RXNE) == RESET);
|
||||
return SD_SPI->DATAR;
|
||||
}
|
||||
|
||||
static void _write(uint8_t const *buffer, uint32_t size)
|
||||
{
|
||||
if (size < BLOCK_SIZE) { //For small transactions, use polling
|
||||
for (uint32_t i = 0; i < size; i ++) {
|
||||
_wr_rd_byte(buffer[i]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t spi_rx_dummy_byte;
|
||||
|
||||
/* Wait for last transaction to be done */
|
||||
while((SD_SPI->STATR & SPI_I2S_FLAG_TXE) == 0);
|
||||
|
||||
/* Configure Rx channel first */
|
||||
DMA_InitTypeDef DMA_InitStructure;
|
||||
DMA_InitStructure.DMA_BufferSize = size;
|
||||
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SD_SPI->DATAR;
|
||||
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&spi_rx_dummy_byte;
|
||||
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
|
||||
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
|
||||
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //Repeatedly Rx dummy bytes to claer SPI_I2S_FLAG_RXNE flag
|
||||
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
||||
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
|
||||
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
|
||||
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
|
||||
DMA_Init(DMA1_Channel4, &DMA_InitStructure); //SPI2_RX
|
||||
|
||||
/* Tx dummy bytes to generate clock */
|
||||
DMA_InitStructure.DMA_BufferSize = size;
|
||||
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SD_SPI->DATAR);
|
||||
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer;
|
||||
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
|
||||
DMA_InitStructure.DMA_Priority = DMA_Priority_Low; //Tx priority should be lower than Rx to avoid FIFO overrun
|
||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
||||
DMA_Init(DMA1_Channel5, &DMA_InitStructure); //SPI2_TX
|
||||
|
||||
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Rx, ENABLE);
|
||||
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Tx, ENABLE);
|
||||
DMA_Cmd(DMA1_Channel4, ENABLE);
|
||||
DMA_Cmd(DMA1_Channel5, ENABLE);
|
||||
|
||||
while (DMA_GetITStatus(DMA1_IT_TC4) != SET) { //Poll for Rx done, use interrupt instead when using RTOS
|
||||
// __WFI();
|
||||
}
|
||||
|
||||
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Rx, DISABLE);
|
||||
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Tx, DISABLE);
|
||||
DMA_DeInit(DMA1_Channel4);
|
||||
DMA_DeInit(DMA1_Channel5);
|
||||
}
|
||||
|
||||
static void _read(uint8_t *buffer, uint32_t size)
|
||||
{
|
||||
uint8_t spi_tx_dummy_byte = DUMMY_BYTE;
|
||||
|
||||
/* Wait for last transaction to be done */
|
||||
while((SD_SPI->STATR & SPI_I2S_FLAG_TXE) == 0);
|
||||
|
||||
/* Configure Rx channel first */
|
||||
DMA_InitTypeDef DMA_InitStructure;
|
||||
DMA_InitStructure.DMA_BufferSize = size;
|
||||
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SD_SPI->DATAR;
|
||||
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer;
|
||||
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
|
||||
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
|
||||
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
||||
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
||||
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
|
||||
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
|
||||
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
|
||||
DMA_Init(DMA1_Channel4, &DMA_InitStructure); //SPI2_RX
|
||||
|
||||
/* Tx dummy bytes to generate clock */
|
||||
DMA_InitStructure.DMA_BufferSize = size;
|
||||
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SD_SPI->DATAR);
|
||||
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&spi_tx_dummy_byte;
|
||||
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
|
||||
DMA_InitStructure.DMA_Priority = DMA_Priority_Low; //Tx priority should be lower than Rx to avoid FIFO overrun
|
||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //Repeatedly Tx dummy byte for clock generation
|
||||
DMA_Init(DMA1_Channel5, &DMA_InitStructure); //SPI2_TX
|
||||
|
||||
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Rx, ENABLE);
|
||||
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Tx, ENABLE);
|
||||
DMA_Cmd(DMA1_Channel4, ENABLE);
|
||||
DMA_Cmd(DMA1_Channel5, ENABLE);
|
||||
|
||||
while (DMA_GetITStatus(DMA1_IT_TC4) != SET) { //Poll for Rx done, use interrupt instead when using RTOS
|
||||
// __WFI();
|
||||
}
|
||||
|
||||
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Rx, DISABLE);
|
||||
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Tx, DISABLE);
|
||||
DMA_DeInit(DMA1_Channel4);
|
||||
DMA_DeInit(DMA1_Channel5);
|
||||
}
|
||||
|
||||
static uint8_t _send_command(uint8_t cmd, uint32_t arg, uint8_t crc)
|
||||
{
|
||||
uint8_t response = 0xFF;
|
||||
|
||||
/* Dummy byte and chip enable */
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
_select();
|
||||
|
||||
uint8_t packet[] = {cmd | 0x40, arg >> 24, arg >> 16, arg >> 8, arg, crc};
|
||||
_write(packet, sizeof(packet));
|
||||
|
||||
/* Wait response, quit till timeout */
|
||||
for (uint32_t i = 0; i < CMD_WAIT_RESP_TIMEOUT; i++) {
|
||||
response = _wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
if (response != 0xFF) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Chip disable and dummy byte */
|
||||
_release();
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
static uint8_t _send_command_recv_response(uint8_t cmd, uint32_t arg, uint8_t crc, uint8_t* data, size_t size)
|
||||
{
|
||||
/* Dummy byte and chip enable */
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
_select();
|
||||
|
||||
uint8_t packet[] = {cmd | 0x40, arg >> 24, arg >> 16, arg >> 8, arg, crc};
|
||||
_write(packet, sizeof(packet));
|
||||
|
||||
uint8_t response = DUMMY_BYTE;
|
||||
|
||||
/* Wait response, quit till timeout */
|
||||
for (uint32_t i = 0; i < CMD_WAIT_RESP_TIMEOUT; i++) {
|
||||
|
||||
response = _wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
if (response != DUMMY_BYTE) {
|
||||
_read(data, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Chip disable and dummy byte */
|
||||
_release();
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
static uint8_t _send_command_hold(uint8_t cmd, uint32_t arg, uint8_t crc)
|
||||
{
|
||||
/* Dummy byte and chip enable */
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
_select();
|
||||
|
||||
|
||||
uint8_t packet[] = {cmd | 0x40, arg >> 24, arg >> 16, arg >> 8, arg, crc};
|
||||
_write(packet, sizeof(packet));
|
||||
|
||||
|
||||
/* Wait response, quit till timeout */
|
||||
for (uint32_t i = 0; i < CMD_WAIT_RESP_TIMEOUT; i++) {
|
||||
uint8_t response = _wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
if (response != 0xFF) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static spisd_result_t _read_buffer(uint8_t *buff, uint32_t len)
|
||||
{
|
||||
uint8_t response = 0;
|
||||
|
||||
/* Wait start-token 0xFE */
|
||||
for (uint32_t i = 0; i < CMD_WAIT_RESP_TIMEOUT; i++) {
|
||||
response = _wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
if (response == 0xFE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (response != 0xFE) {
|
||||
return SPISD_RESULT_ERROR;
|
||||
}
|
||||
|
||||
_read(buff, len);
|
||||
|
||||
/* 2bytes dummy CRC */
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
return SPISD_RESULT_OK;
|
||||
}
|
||||
|
||||
spisd_result_t spisd_init(void)
|
||||
{
|
||||
_spi_init();
|
||||
|
||||
if ( !_is_present() ) {
|
||||
SPISD_LOG("There is no card detected!");
|
||||
return SPISD_RESULT_NO_CARD;
|
||||
}
|
||||
|
||||
_release();
|
||||
_set_spi_prescaler(SPI_PRESCALER_LOW);
|
||||
|
||||
/* Start send 74 clocks at least */
|
||||
for (uint32_t i = 0; i < 20; i++) {
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
}
|
||||
|
||||
uint32_t timeout = WAIT_IDLE_TIMEOUT;
|
||||
uint8_t response = 0;
|
||||
|
||||
do {
|
||||
response = _send_command(CMD0, 0, 0x95);
|
||||
timeout--;
|
||||
} while ((response != SPISD_R1_IDLE_FLAG) && timeout > 0 );
|
||||
|
||||
if (!timeout) {
|
||||
SPISD_LOG("Reset card into IDLE state failed!");
|
||||
return SPISD_RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
uint8_t buff[4];
|
||||
response = _send_command_recv_response(CMD8, 0x1AA, 0x87, buff, sizeof(buff));
|
||||
|
||||
if (response == SPISD_R1_IDLE_FLAG) {
|
||||
|
||||
/* Check voltage range be 2.7-3.6V */
|
||||
if (buff[2] == 0x01 && buff[3] == 0xAA) {
|
||||
|
||||
for (uint32_t i = 0; i < 0xFFF; i++) {
|
||||
response = _send_command(CMD55, 0, 0); /* should be return 0x01 */
|
||||
|
||||
if (response != 0x01) {
|
||||
SPISD_LOG("Send CMD55 should return 0x01, response=0x%02x", response);
|
||||
return SPISD_RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
response = _send_command(ACMD41, 0x40000000, 0); /* should be return 0x00 */
|
||||
|
||||
if (response == 0x00) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (response != 0x00) {
|
||||
SPISD_LOG("Send ACMD41 should return 0x00, response=0x%02x", response);
|
||||
return SPISD_RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Read OCR by CMD58 */
|
||||
response = _send_command_recv_response(CMD58, 0, 0, buff, sizeof(buff));
|
||||
|
||||
if (response != 0x00) {
|
||||
SPISD_LOG("Send CMD58 should return 0x00, response=0x%02x", response);
|
||||
return SPISD_RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
/* OCR -> CCS(bit30) 1: SDV2HC 0: SDV2 */
|
||||
_card_type = (buff[0] & 0x40) ? CARD_TYPE_SDV2HC : CARD_TYPE_SDV2;
|
||||
|
||||
_set_spi_prescaler(SPI_PRESCALER_HIGH);
|
||||
}
|
||||
#if USE_MMC_CARD == 1
|
||||
} else if (response & SPISD_R1_ILLEGAL_CMD_FLAG) {
|
||||
|
||||
_card_type = CARD_TYPE_SDV1;
|
||||
|
||||
/* End of CMD8, chip disable and dummy byte */
|
||||
_release();
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
/* SD1.0/MMC start initialize */
|
||||
/* Send CMD55+ACMD41, No-response is a MMC card, otherwise is a SD1.0 card */
|
||||
for (uint32_t i = 0; i < 0xFFF; i++) {
|
||||
response = _send_command(CMD55, 0, 0);
|
||||
|
||||
if (response != 0x01) {
|
||||
SPISD_LOG("Send CMD55 should return 0x01, response=0x%02x", response);
|
||||
return SPISD_RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
response = _send_command(ACMD41, 0, 0);
|
||||
|
||||
if (response == 0x00) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* MMC card initialize start */
|
||||
if (response != 0x00) {
|
||||
for (uint32_t i = 0; i < 0xFFF; i++) {
|
||||
response = _send_command(CMD1, 0, 0);
|
||||
|
||||
if (response == 0x00) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Timeout return */
|
||||
if (response != 0x00) {
|
||||
SPISD_LOG("Send CMD1 should return 0x00, response=0x%02x", response);
|
||||
return SPISD_RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
_card_type = CARD_TYPE_MMC;
|
||||
SPISD_LOG("Card Type : MMC");
|
||||
} else {
|
||||
SPISD_LOG("Card Type : SD V1");
|
||||
}
|
||||
|
||||
_set_spi_prescaler(true);
|
||||
|
||||
/* CRC disable */
|
||||
response = _send_command(CMD59, 0, 0x01);
|
||||
|
||||
if (response != 0x00) {
|
||||
SPISD_LOG("Send CMD59 should return 0x00, response=0x%02x", response);
|
||||
return SPISD_RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Set the block size */
|
||||
response = _send_command(CMD16, BLOCK_SIZE, 0xFF);
|
||||
|
||||
if (response != 0x00) {
|
||||
SPISD_LOG("Send CMD16 should return 0x00, response=0x%02x", response);
|
||||
return SPISD_RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
#endif //USE_MMC_CARD
|
||||
} else {
|
||||
return SPISD_RESULT_ERROR;
|
||||
}
|
||||
|
||||
return SPISD_RESULT_OK;
|
||||
}
|
||||
|
||||
int spisd_get_card_info(spisd_info_t *cardinfo)
|
||||
{
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
/* Send CMD9, Read CSD */
|
||||
uint8_t response = _send_command(CMD9, 0, 0xFF);
|
||||
|
||||
if (response != 0x00) {
|
||||
return response;
|
||||
}
|
||||
|
||||
uint8_t temp[16];
|
||||
|
||||
_select();
|
||||
|
||||
spisd_result_t ret = _read_buffer(temp, sizeof(temp));
|
||||
/* chip disable and dummy byte */
|
||||
_release();
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
if (ret != SPISD_RESULT_OK) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Byte 0 */
|
||||
cardinfo->csd.CSDStruct = (temp[0] & 0xC0) >> 6;
|
||||
cardinfo->csd.SysSpecVersion = (temp[0] & 0x3C) >> 2;
|
||||
cardinfo->csd.Reserved1 = temp[0] & 0x03;
|
||||
/* Byte 1 */
|
||||
cardinfo->csd.TAAC = temp[1] ;
|
||||
/* Byte 2 */
|
||||
cardinfo->csd.NSAC = temp[2];
|
||||
/* Byte 3 */
|
||||
cardinfo->csd.MaxBusClkFrec = temp[3];
|
||||
/* Byte 4 */
|
||||
cardinfo->csd.CardComdClasses = temp[4] << 4;
|
||||
/* Byte 5 */
|
||||
cardinfo->csd.CardComdClasses |= (temp[5] & 0xF0) >> 4;
|
||||
cardinfo->csd.RdBlockLen = temp[5] & 0x0F;
|
||||
/* Byte 6 */
|
||||
cardinfo->csd.PartBlockRead = (temp[6] & 0x80) >> 7;
|
||||
cardinfo->csd.WrBlockMisalign = (temp[6] & 0x40) >> 6;
|
||||
cardinfo->csd.RdBlockMisalign = (temp[6] & 0x20) >> 5;
|
||||
cardinfo->csd.DSRImpl = (temp[6] & 0x10) >> 4;
|
||||
cardinfo->csd.Reserved2 = 0; /* Reserved */
|
||||
cardinfo->csd.DeviceSize = (temp[6] & 0x03) << 10;
|
||||
/* Byte 7 */
|
||||
cardinfo->csd.DeviceSize |= (temp[7]) << 2;
|
||||
/* Byte 8 */
|
||||
cardinfo->csd.DeviceSize |= (temp[8] & 0xC0) >> 6;
|
||||
cardinfo->csd.MaxRdCurrentVDDMin = (temp[8] & 0x38) >> 3;
|
||||
cardinfo->csd.MaxRdCurrentVDDMax = (temp[8] & 0x07);
|
||||
/* Byte 9 */
|
||||
cardinfo->csd.MaxWrCurrentVDDMin = (temp[9] & 0xE0) >> 5;
|
||||
cardinfo->csd.MaxWrCurrentVDDMax = (temp[9] & 0x1C) >> 2;
|
||||
cardinfo->csd.DeviceSizeMul = (temp[9] & 0x03) << 1;
|
||||
/* Byte 10 */
|
||||
cardinfo->csd.DeviceSizeMul |= (temp[10] & 0x80) >> 7;
|
||||
cardinfo->csd.EraseGrSize = (temp[10] & 0x7C) >> 2;
|
||||
cardinfo->csd.EraseGrMul = (temp[10] & 0x03) << 3;
|
||||
/* Byte 11 */
|
||||
cardinfo->csd.EraseGrMul |= (temp[11] & 0xE0) >> 5;
|
||||
cardinfo->csd.WrProtectGrSize = (temp[11] & 0x1F);
|
||||
/* Byte 12 */
|
||||
cardinfo->csd.WrProtectGrEnable = (temp[12] & 0x80) >> 7;
|
||||
cardinfo->csd.ManDeflECC = (temp[12] & 0x60) >> 5;
|
||||
cardinfo->csd.WrSpeedFact = (temp[12] & 0x1C) >> 2;
|
||||
cardinfo->csd.MaxWrBlockLen = (temp[12] & 0x03) << 2;
|
||||
/* Byte 13 */
|
||||
cardinfo->csd.MaxWrBlockLen |= (temp[13] & 0xc0) >> 6;
|
||||
cardinfo->csd.WriteBlockPaPartial = (temp[13] & 0x20) >> 5;
|
||||
cardinfo->csd.Reserved3 = 0;
|
||||
cardinfo->csd.ContentProtectAppli = (temp[13] & 0x01);
|
||||
/* Byte 14 */
|
||||
cardinfo->csd.FileFormatGrouop = (temp[14] & 0x80) >> 7;
|
||||
cardinfo->csd.CopyFlag = (temp[14] & 0x40) >> 6;
|
||||
cardinfo->csd.PermWrProtect = (temp[14] & 0x20) >> 5;
|
||||
cardinfo->csd.TempWrProtect = (temp[14] & 0x10) >> 4;
|
||||
cardinfo->csd.FileFormat = (temp[14] & 0x0C) >> 2;
|
||||
cardinfo->csd.ECC = (temp[14] & 0x03);
|
||||
/* Byte 15 */
|
||||
cardinfo->csd.CSD_CRC = (temp[15] & 0xFE) >> 1;
|
||||
cardinfo->csd.Reserved4 = 1;
|
||||
|
||||
if (cardinfo->card_type == CARD_TYPE_SDV2HC) {
|
||||
/* Byte 7 */
|
||||
cardinfo->csd.DeviceSize = (uint16_t)(temp[8]) * 256;
|
||||
/* Byte 8 */
|
||||
cardinfo->csd.DeviceSize += temp[9] ;
|
||||
}
|
||||
|
||||
cardinfo->capacity = cardinfo->csd.DeviceSize * BLOCK_SIZE * 1024;
|
||||
cardinfo->block_size = BLOCK_SIZE;
|
||||
|
||||
/* Send CMD10, Read CID */
|
||||
response = _send_command(CMD10, 0, 0xFF);
|
||||
|
||||
if (response != 0x00) {
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
_select();
|
||||
ret = _read_buffer(temp, sizeof(temp));
|
||||
/* chip disable and dummy byte */
|
||||
_release();
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
if (ret != SPISD_RESULT_OK) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
/* Byte 0 */
|
||||
cardinfo->cid.ManufacturerID = temp[0];
|
||||
/* Byte 1 */
|
||||
cardinfo->cid.OEM_AppliID = temp[1] << 8;
|
||||
/* Byte 2 */
|
||||
cardinfo->cid.OEM_AppliID |= temp[2];
|
||||
/* Byte 3 */
|
||||
cardinfo->cid.ProdName1 = temp[3] << 24;
|
||||
/* Byte 4 */
|
||||
cardinfo->cid.ProdName1 |= temp[4] << 16;
|
||||
/* Byte 5 */
|
||||
cardinfo->cid.ProdName1 |= temp[5] << 8;
|
||||
/* Byte 6 */
|
||||
cardinfo->cid.ProdName1 |= temp[6];
|
||||
/* Byte 7 */
|
||||
cardinfo->cid.ProdName2 = temp[7];
|
||||
/* Byte 8 */
|
||||
cardinfo->cid.ProdRev = temp[8];
|
||||
/* Byte 9 */
|
||||
cardinfo->cid.ProdSN = temp[9] << 24;
|
||||
/* Byte 10 */
|
||||
cardinfo->cid.ProdSN |= temp[10] << 16;
|
||||
/* Byte 11 */
|
||||
cardinfo->cid.ProdSN |= temp[11] << 8;
|
||||
/* Byte 12 */
|
||||
cardinfo->cid.ProdSN |= temp[12];
|
||||
/* Byte 13 */
|
||||
cardinfo->cid.Reserved1 |= (temp[13] & 0xF0) >> 4;
|
||||
/* Byte 14 */
|
||||
cardinfo->cid.ManufactDate = (temp[13] & 0x0F) << 8;
|
||||
/* Byte 15 */
|
||||
cardinfo->cid.ManufactDate |= temp[14];
|
||||
/* Byte 16 */
|
||||
cardinfo->cid.CID_CRC = (temp[15] & 0xFE) >> 1;
|
||||
cardinfo->cid.Reserved2 = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
spisd_result_t spisd_read_block(uint32_t sector, uint8_t *buffer)
|
||||
{
|
||||
if (_card_type != CARD_TYPE_SDV2HC) {
|
||||
sector = sector << 9;
|
||||
}
|
||||
|
||||
spisd_result_t ret = SPISD_RESULT_ERROR;
|
||||
|
||||
if (_send_command_hold(CMD17, sector, 0) == 0x00) {
|
||||
ret = _read_buffer(buffer, BLOCK_SIZE);
|
||||
}
|
||||
|
||||
_release();
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
spisd_result_t spisd_write_block(uint32_t sector, const uint8_t *buffer)
|
||||
{
|
||||
spisd_result_t ret = SPISD_RESULT_ERROR;
|
||||
|
||||
if (_card_type != CARD_TYPE_SDV2HC) {
|
||||
sector = sector << 9;
|
||||
}
|
||||
|
||||
if (_send_command(CMD24, sector, 0) != 0x00) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
_select();
|
||||
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
/* Start data write token: 0xFE */
|
||||
_wr_rd_byte(0xFE);
|
||||
|
||||
_write(buffer, BLOCK_SIZE);
|
||||
|
||||
/* 2Bytes dummy CRC */
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
/* MSD card accept the data */
|
||||
uint8_t response = _wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
if ((response & 0x1F) == 0x05) {
|
||||
/* Wait all the data program finished */
|
||||
for (uint32_t i = 0; i < 0x40000; i++) {
|
||||
if ( _wr_rd_byte(DUMMY_BYTE) != 0x00) {
|
||||
ret = SPISD_RESULT_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_release();
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
spisd_result_t spisd_read_multi_block(uint32_t sector, uint8_t *buffer, uint32_t num_sectors)
|
||||
{
|
||||
/* if ver = SD2.0 HC, sector need <<9 */
|
||||
if (_card_type != CARD_TYPE_SDV2HC) {
|
||||
sector = sector << 9;
|
||||
}
|
||||
|
||||
if (_send_command(CMD18, sector, 0) != 0x00) {
|
||||
return SPISD_RESULT_ERROR;
|
||||
}
|
||||
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
_select();
|
||||
|
||||
for (uint32_t i = 0; i < num_sectors; i++) {
|
||||
spisd_result_t ret = _read_buffer(&buffer[i * BLOCK_SIZE], BLOCK_SIZE);
|
||||
|
||||
if (ret != SPISD_RESULT_OK) {
|
||||
/* Send stop data transmit command - CMD12 */
|
||||
_send_command(CMD12, 0, 0);
|
||||
return SPISD_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
_release();
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
/* Send stop data transmit command - CMD12 */
|
||||
_send_command(CMD12, 0, 0);
|
||||
|
||||
return SPISD_RESULT_OK;
|
||||
}
|
||||
|
||||
spisd_result_t spisd_write_multi_block(uint32_t sector, uint8_t const *buffer, uint32_t num_sectors)
|
||||
{
|
||||
/* if ver = SD2.0 HC, sector need <<9 */
|
||||
if (_card_type != CARD_TYPE_SDV2HC) {
|
||||
sector = sector << 9;
|
||||
}
|
||||
|
||||
/* Send command ACMD23 before multi write if is not a MMC card */
|
||||
if (_card_type != CARD_TYPE_MMC) {
|
||||
_send_command(CMD55, 0, 0x00);
|
||||
_send_command(ACMD23, num_sectors, 0x00);
|
||||
}
|
||||
|
||||
if (_send_command(CMD25, sector, 0) != 0x00) {
|
||||
return SPISD_RESULT_ERROR;
|
||||
}
|
||||
|
||||
_select();
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
for (uint32_t i = 0; i < num_sectors; i++) {
|
||||
/* Start multi block write token: 0xFC */
|
||||
_wr_rd_byte(0xFC);
|
||||
|
||||
_write(&buffer[i * BLOCK_SIZE], BLOCK_SIZE);
|
||||
|
||||
/* 2Bytes dummy CRC */
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
/* MSD card accept the data */
|
||||
if ((_wr_rd_byte(DUMMY_BYTE) & 0x1F) != 0x05) {
|
||||
_release();
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
return SPISD_RESULT_ERROR;
|
||||
}
|
||||
|
||||
/* Wait all the data program finished */
|
||||
uint32_t timeout = 0;
|
||||
|
||||
while (_wr_rd_byte(DUMMY_BYTE) != 0xFF) {
|
||||
/* Timeout return */
|
||||
if (timeout++ == 0x40000) {
|
||||
_release();
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
return SPISD_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Send end of transmit token: 0xFD */
|
||||
if (_wr_rd_byte(0xFD) != 0x00) {
|
||||
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
/* Wait all the data program finished */
|
||||
for (uint32_t i = 0; i < 0x40000; i++) {
|
||||
if (_wr_rd_byte(DUMMY_BYTE) == 0xFF) {
|
||||
_release();
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
for (uint32_t i = 0; i < 0x40000; i++) {
|
||||
if (_wr_rd_byte(DUMMY_BYTE) == 0xFF) {
|
||||
return SPISD_RESULT_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_release();
|
||||
_wr_rd_byte(DUMMY_BYTE);
|
||||
|
||||
return SPISD_RESULT_ERROR;
|
||||
}
|
||||
@ -1,173 +0,0 @@
|
||||
#ifndef __BSP_SPI_SD_H
|
||||
#define __BSP_SPI_SD_H
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "ch32v30x.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#define SD_SPI SPI2
|
||||
#define SD_SPI_CLK_ENABLE() RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE)
|
||||
#define SD_SPI_SCK_PIN GPIO_Pin_13 /* PB.13 */
|
||||
#define SD_SPI_SCK_GPIO_PORT GPIOB /* GPIOB */
|
||||
#define SD_SPI_SCK_GPIO_CLK RCC_APB2Periph_GPIOB
|
||||
#define SD_SPI_MISO_PIN GPIO_Pin_14 /* PB.14 */
|
||||
#define SD_SPI_MISO_GPIO_PORT GPIOB /* GPIOB */
|
||||
#define SD_SPI_MISO_GPIO_CLK RCC_APB2Periph_GPIOB
|
||||
#define SD_SPI_MOSI_PIN GPIO_Pin_15 /* PB.15 */
|
||||
#define SD_SPI_MOSI_GPIO_PORT GPIOB /* GPIOB */
|
||||
#define SD_SPI_MOSI_GPIO_CLK RCC_APB2Periph_GPIOB
|
||||
#define SD_CS_PIN GPIO_Pin_12 /* PB.12 */
|
||||
#define SD_CS_GPIO_PORT GPIOB /* GPIOB */
|
||||
#define SD_CS_GPIO_CLK RCC_APB2Periph_GPIOB
|
||||
#define SD_DETECT_PIN GPIO_Pin_0 /* PB.00 */
|
||||
#define SD_DETECT_GPIO_PORT GPIOB /* GPIOE */
|
||||
#define SD_DETECT_GPIO_CLK RCC_APB2Periph_GPIOB
|
||||
|
||||
#define SPISD_R1_IDLE_FLAG (0x01)
|
||||
#define SPISD_R1_ERASE_RESET_FLAG (0x02)
|
||||
#define SPISD_R1_ILLEGAL_CMD_FLAG (0x04)
|
||||
#define SPISD_R1_CMD_CRC_FLAG (0x08)
|
||||
#define SPISD_R1_ERASE_SEQ_ERROR_FLAG (0x10)
|
||||
#define SPISD_R1_ADDR_ERROR_FLAG (0x20)
|
||||
#define SPISD_R1_PARAM_ERROR_FLAG (0x40)
|
||||
#define SPISD_R1_ZERO_FLAG (0x80)
|
||||
|
||||
typedef enum spisd_result_s {
|
||||
SPISD_RESULT_OK = 0,
|
||||
SPISD_RESULT_ERROR,
|
||||
SPISD_RESULT_NO_CARD,
|
||||
SPISD_RESULT_TIMEOUT,
|
||||
} spisd_result_t;
|
||||
|
||||
typedef union __attribute__((packed)) sppisd_r1_u {
|
||||
uint8_t raw;
|
||||
struct {
|
||||
uint8_t idle: 1; /*<!-- The card is in idle state and running the initializing process */
|
||||
uint8_t erase_reset: 1; /*<!-- An erase sequence was cleared before executing because an out of erase sequence command was received */
|
||||
uint8_t illegal_cmd: 1; /*<!-- An illegal command code was detected */
|
||||
uint8_t cmd_crc_err: 1; /*<!-- The CRC check of the last command failed */
|
||||
uint8_t erase_seq_err: 1; /*<!-- An error in the sequence of erase commands occurred */
|
||||
uint8_t addr_err: 1; /*<!-- A misaligned address that did not match the block length was used in the command */
|
||||
uint8_t param_error: 1; /*<!-- The command<6E>s argument (e.g. address, block length) was outside the allowed range for this card */
|
||||
uint8_t zero: 1; /*<!-- must be zero */
|
||||
} fields;
|
||||
} spisd_r1_t;
|
||||
|
||||
typedef struct __attribute__((packed)) spisd_r1b_s {
|
||||
uint8_t is_ready; /*<!-- A zero value indicates card is busy. A non-zero value indicates the card is ready for the next command */
|
||||
} spisd_r1b_t;
|
||||
|
||||
typedef union __attribute__((packed)) spisd_r2_s {
|
||||
uint16_t raw;
|
||||
struct {
|
||||
uint16_t card_locked: 1; /*<!-- Set when the card is locked by the user. Reset when it is unlocked */
|
||||
uint16_t wp_erase_skip: 1; /*<!-- This status bit has two functions overloaded. It is set when the host attempts to erase a write-protected sector or makes a sequence or password errors during card lock/unlock operation*/
|
||||
uint16_t err: 1; /*<!-- A general or an unknown error occurred during the operation */
|
||||
uint16_t cc_err: 1; /*<!-- Internal card controller error */
|
||||
uint16_t card_ecc_failed: 1; /*<!-- Card internal ECC was applied but failed to correct the data */
|
||||
uint16_t wp_viol: 1; /*<!-- The command tried to write a write-protected block */
|
||||
uint16_t erase_param: 1; /*<!-- An invalid selection for erase, sectors or group */
|
||||
uint16_t out_of_range: 1; /*<!-- */
|
||||
uint8_t idle: 1; /*<!-- The card is in idle state and running the initializing process */
|
||||
uint8_t erase_reset: 1; /*<!-- An erase sequence was cleared before executing because an out of erase sequence command was received */
|
||||
uint8_t illegal_cmd: 1; /*<!-- An illegal command code was detected */
|
||||
uint8_t cmd_crc_err: 1; /*<!-- The CRC check of the last command failed */
|
||||
uint8_t erase_seq_err: 1; /*<!-- An error in the sequence of erase commands occurred */
|
||||
uint8_t addr_err: 1; /*<!-- A misaligned address that did not match the block length was used in the command */
|
||||
uint8_t param_error: 1; /*<!-- The command<6E>s argument (e.g. address, block length) was outside the allowed range for this card */
|
||||
uint8_t zero: 1; /*<!-- mustbe zero */
|
||||
} fields;
|
||||
} spisd_r2_t;
|
||||
|
||||
typedef struct __attribute__((packed)) sppisd_r3_s {
|
||||
spisd_r1_t r1;
|
||||
uint32_t ocr;
|
||||
} spisd_r3_t;
|
||||
|
||||
typedef struct __attribute__((packed)) sppisd_r7_s {
|
||||
spisd_r1_t r1;
|
||||
union {
|
||||
uint32_t raw32;
|
||||
struct {
|
||||
uint32_t cmd_ver: 4;
|
||||
uint32_t reserved: 16;
|
||||
uint32_t voltage_accept: 4;
|
||||
uint32_t ehco_back: 8;
|
||||
} fields;
|
||||
};
|
||||
} spisd_r7_t;
|
||||
|
||||
typedef struct { /* Card Specific Data */
|
||||
uint8_t CSDStruct; /* CSD structure */
|
||||
uint8_t SysSpecVersion; /* System specification version */
|
||||
uint8_t Reserved1; /* Reserved */
|
||||
uint8_t TAAC; /* Data read access-time 1 */
|
||||
uint8_t NSAC; /* Data read access-time 2 in CLK cycles */
|
||||
uint8_t MaxBusClkFrec; /* Max. bus clock frequency */
|
||||
uint16_t CardComdClasses; /* Card command classes */
|
||||
uint8_t RdBlockLen; /* Max. read data block length */
|
||||
uint8_t PartBlockRead; /* Partial blocks for read allowed */
|
||||
uint8_t WrBlockMisalign; /* Write block misalignment */
|
||||
uint8_t RdBlockMisalign; /* Read block misalignment */
|
||||
uint8_t DSRImpl; /* DSR implemented */
|
||||
uint8_t Reserved2; /* Reserved */
|
||||
uint32_t DeviceSize; /* Device Size */
|
||||
uint8_t MaxRdCurrentVDDMin; /* Max. read current @ VDD min */
|
||||
uint8_t MaxRdCurrentVDDMax; /* Max. read current @ VDD max */
|
||||
uint8_t MaxWrCurrentVDDMin; /* Max. write current @ VDD min */
|
||||
uint8_t MaxWrCurrentVDDMax; /* Max. write current @ VDD max */
|
||||
uint8_t DeviceSizeMul; /* Device size multiplier */
|
||||
uint8_t EraseGrSize; /* Erase group size */
|
||||
uint8_t EraseGrMul; /* Erase group size multiplier */
|
||||
uint8_t WrProtectGrSize; /* Write protect group size */
|
||||
uint8_t WrProtectGrEnable; /* Write protect group enable */
|
||||
uint8_t ManDeflECC; /* Manufacturer default ECC */
|
||||
uint8_t WrSpeedFact; /* Write speed factor */
|
||||
uint8_t MaxWrBlockLen; /* Max. write data block length */
|
||||
uint8_t WriteBlockPaPartial; /* Partial blocks for write allowed */
|
||||
uint8_t Reserved3; /* Reserded */
|
||||
uint8_t ContentProtectAppli; /* Content protection application */
|
||||
uint8_t FileFormatGrouop; /* File format group */
|
||||
uint8_t CopyFlag; /* Copy flag (OTP) */
|
||||
uint8_t PermWrProtect; /* Permanent write protection */
|
||||
uint8_t TempWrProtect; /* Temporary write protection */
|
||||
uint8_t FileFormat; /* File Format */
|
||||
uint8_t ECC; /* ECC code */
|
||||
uint8_t CSD_CRC; /* CSD CRC */
|
||||
uint8_t Reserved4; /* always 1*/
|
||||
} spisd_csd_t;
|
||||
|
||||
typedef struct { /*Card Identification Data*/
|
||||
uint8_t ManufacturerID; /* ManufacturerID */
|
||||
uint16_t OEM_AppliID; /* OEM/Application ID */
|
||||
uint32_t ProdName1; /* Product Name part1 */
|
||||
uint8_t ProdName2; /* Product Name part2*/
|
||||
uint8_t ProdRev; /* Product Revision */
|
||||
uint32_t ProdSN; /* Product Serial Number */
|
||||
uint8_t Reserved1; /* Reserved1 */
|
||||
uint16_t ManufactDate; /* Manufacturing Date */
|
||||
uint8_t CID_CRC; /* CID CRC */
|
||||
uint8_t Reserved2; /* always 1 */
|
||||
} spisd_cid_t;
|
||||
|
||||
typedef struct {
|
||||
spisd_csd_t csd;
|
||||
spisd_cid_t cid;
|
||||
uint32_t capacity; /* Card Capacity */
|
||||
uint32_t block_size; /* Card Block Size */
|
||||
uint16_t rca;
|
||||
uint8_t card_type;
|
||||
uint32_t space_total; /* Total space size in file system */
|
||||
uint32_t space_free; /* Free space size in file system */
|
||||
} spisd_info_t;
|
||||
|
||||
spisd_result_t spisd_init(void);
|
||||
spisd_result_t spisd_read_block(uint32_t sector, uint8_t *buffer);
|
||||
spisd_result_t spisd_write_block(uint32_t sector, const uint8_t *buffer);
|
||||
|
||||
spisd_result_t spisd_read_multi_block(uint32_t sector, uint8_t *buffer, uint32_t num_sectors);
|
||||
spisd_result_t spisd_write_multi_block(uint32_t sector, uint8_t const *buffer, uint32_t num_sectors);
|
||||
|
||||
int spisd_get_card_info(spisd_info_t *cardinfo);
|
||||
|
||||
#endif //__SPI_SDCARD_DRIVER_H__
|
||||
Loading…
Reference in New Issue
Block a user