感谢:Savior2016 的模拟SPI进行TF卡操作+Fatfs文件系统移植:https://www.jianshu.com/p/48bf324a17d3
SD卡SPI模式 读写block 详情可以看这里 http://blog.sina.com.cn/s/blog_6ade392501015vv7.html
1、添加SD卡SPI模式驱动
STM32CubeMX配置SPI模式
/**
* @brief SPI1 Initialization Function
* @param None
* @retval None
*/
static void MX_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
/* SPI1 parameter configuration*/
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_HARD_OUTPUT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI1_Init 2 */
/* USER CODE END SPI1_Init 2 */
}
编写SD卡SPI驱动 SDCard.c
#include "main.h"
#include "SDCard.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/* USART1 init function */
/**************************************************************************/
#define Dummy_Byte 0xFF
/* 私有变量 ------------------------------------------------------------------*/
SPI_HandleTypeDef hspiflash;
extern SPI_HandleTypeDef hspi1;
u8 SD_Type=0;
/* function ------------------------------------------------------------------*/
/**
* 函数功能: 从串行Flash读取一个字节数据
* 输入参数: 无
* 返 回 值: uint8_t:读取到的数据
* 说 明:This function must be used only if the Start_Read_Sequence
* function has been previously called.
*/
uint8_t SPI_FLASH_ReadByte(void)
{
uint8_t d_read,d_send=Dummy_Byte;
if(HAL_SPI_TransmitReceive(&hspiflash,&d_send,&d_read,1,0xFFFFFF)!=HAL_OK)
d_read=Dummy_Byte;
return d_read;
}
void SPI_I2S_SendData(SPI_TypeDef* SPIx, u16 Data)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
/* Write in the DR register the data to be sent */
SPIx->DR = Data;
}
u16 SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
// assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction));
/* Return the data in the DR register */
return SPIx->DR;
}
/**
* 函数功能: 往串行Flash读取写入一个字节数据并接收一个字节数据
* 输入参数: byte:待发送数据
* 返 回 值: uint8_t:接收到的数据
* 说 明:无
*/
uint8_t SPI_FLASH_SendByte(uint8_t byte)
{
uint8_t d_read,d_send=byte;
// if(HAL_SPI_TransmitReceive(&hspiflash,&d_send,&d_read,1,0xFFFFFF)!=HAL_OK)
// {
// d_read=Dummy_Byte;
// }
//等待发送缓冲区空
// while(__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_TXE));
//发一个字节
// SPI_I2S_SendData(SPI1, d_send);
HAL_SPI_Transmit(&hspi1,&d_send,1,1000);
// HAL_SPI_Receive(&hspi1,&d_read,1,1000);
//等待数据接收
while(__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_RXNE));
//取数据
d_read = SPI_I2S_ReceiveData(SPI1);
return d_read;
}
/*******************************************************************************
* Function Name : SD_WaitReady
* Description : 等待SD卡Ready
* Input : None
* Output : None
* Return : u8
* 0: 成功
* other:失败
*******************************************************************************/
u8 SD_WaitReady(void)
{
u8 r1;
u16 retry;
retry = 0;
do
{
r1 = SPI_FLASH_SendByte(0xFF);
if(retry==0xfffe)
{
return 1;
}
}while(r1!=0xFF);
return 0;
}
/*******************************************************************************
* Function Name : SD_SendCommand
* Description : 向SD卡发送一个命令
* Input : u8 cmd 命令
* u32 arg 命令参数
* u8 crc crc校验值
* Output : None
* Return : u8 r1 SD卡返回的响应
*******************************************************************************/
u8 SD_SendCommand(u8 cmd, u32 arg, u8 crc)
{
unsigned char r1;
unsigned char Retry = 0;
//????????
SPI_FLASH_SendByte(0xff);
//片选端置低,选中SD卡
FLASH_SPI_CS_ENABLE();
//发送
SPI_FLASH_SendByte(cmd | 0x40); //分别写入命令
SPI_FLASH_SendByte(arg >> 24);
SPI_FLASH_SendByte(arg >> 16);
SPI_FLASH_SendByte(arg >> 8);
SPI_FLASH_SendByte(arg);
SPI_FLASH_SendByte(crc);
//等待响应,或超时退出
while((r1 = SPI_FLASH_SendByte(0xFF))==0xFF)
{
Retry++;
if(Retry > 200)
{
break;
}
}
//关闭片选
FLASH_SPI_CS_DISABLE();
//在总线上额外增加8个时钟,让SD卡完成剩下的工作
SPI_FLASH_SendByte(0xFF);
//返回状态值
return r1;
}
/*******************************************************************************
* Function Name : SD_SendCommand_NoDeassert
* Description : 向SD卡发送一个命令(结束是不失能片选,还有后续数据传来)
* Input : u8 cmd 命令
* u32 arg 命令参数
* u8 crc crc校验值
* Output : None
* Return : u8 r1 SD卡返回的响应
*******************************************************************************/
u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg, u8 crc)
{
unsigned char r1;
unsigned char Retry = 0;
//????????
SPI_FLASH_SendByte(0xff);
//片选端置低,选中SD卡
FLASH_SPI_CS_ENABLE();
//发送
SPI_FLASH_SendByte(cmd | 0x40); //分别写入命令
SPI_FLASH_SendByte(arg >> 24);
SPI_FLASH_SendByte(arg >> 16);
SPI_FLASH_SendByte(arg >> 8);
SPI_FLASH_SendByte(arg);
SPI_FLASH_SendByte(crc);
//等待响应,或超时退出
while((r1 = SPI_FLASH_SendByte(0xFF))==0xFF)
{
Retry++;
if(Retry > 200)
{
break;
}
}
//返回响应值
return r1;
}
void SPI_SetSpeed(u8 SpeedSet)
{
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_HARD_OUTPUT;
//如果速度设置输入0,则低速模式,非0则高速模式
if(SpeedSet==SPI_SPEED_LOW)
{
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
}
else
{
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
}
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
// if(SpeedSet==SPI_SPEED_LOW)
// {
// hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
// }
// else
// {
// hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
// }
// HAL_SPI_Init(&hspi1);
//moon.mp3: 4707774 Byte size 目标文件 设为buffer[512]
//speed:实验测试数据,最大速度 392314 Byte/S,
//Prescaler_128, 59592 Byte/S
//Prescaler_64, 104617 Byte/S
//Prescaler_32, 168134 Byte/S 162337 Byte/S
//Prescaler_16, 261543 Byte/S 247777 Byte/S
//Prescaler_8, 313851 Byte/S 336269 Byte/S
//Prescaler_4, 392314 Byte/S 392314 Byte/S
//Prescaler_2, 392314 Byte/S
}
/*******************************************************************************
* Function Name : SD_Init
* Description : 初始化SD卡
* Input : None
* Output : None
* Return : u8
* 0:NO_ERR
* 1:TIME_OUT
* 99:NO_CARD
*******************************************************************************/
u8 SD_Init(void)
{
u16 i; // 用来循环计数
u8 r1; // 存放SD卡的返回值
u16 retry; // 用来进行超时计数
u8 buff[6];
//如果没有检测到卡插入,直接退出,返回错误标志
// if(!SD_DET())
// {
// //return 99;
// return STA_NODISK; // FatFS错误标志:没有插入磁盘
// }
//SD卡上电
//SD_PWR_ON();
// 纯延时,等待SD卡上电完成
//for(i=0;i<0xf00;i++);
/*******************************************************
//这个地方要加一句,设置SPI速度为低速。
//为什么有的卡可以呢?因为SPI初始化时是低速的,SD卡初始化
//完成后设置为高速,有的卡只要初始化一次就行,程序就ok;
//但有的卡需要多次复位,呵呵,这个地方差这一句,
//这种卡就用不成咯!
*******************************************************/
SPI_SetSpeed(0); //设置SPI速度为低速
//先产生>74个脉冲,让SD卡自己初始化完成
for(i=0;i<100;i++)
{
SPI_FLASH_SendByte(0xFF);
}
//-----------------SD卡复位到idle开始-----------------
//循环连续发送CMD0,直到SD卡返回0x01,进入IDLE状态
//超时则直接退出
retry = 0;
do
{
//发送CMD0,让SD卡进入IDLE状态
r1 = SD_SendCommand(CMD0, 0, 0x95);
retry++;
}while((r1 != 0x01) && (retry<200));
//跳出循环后,检查原因:初始化成功?or 重试超时?
if(retry==200)
{
return 1; //超时返回1
}
//-----------------SD卡复位到idle结束-----------------
//获取卡片的SD版本信息
r1 = SD_SendCommand_NoDeassert(8, 0x1aa, 0x87);
//如果卡片版本信息是v1.0版本的,即r1=0x05,则进行以下初始化
if(r1 == 0x05)
{
printf("\r\n SD卡版本信息:V1.0 \r\n");
//设置卡类型为SDV1.0,如果后面检测到为MMC卡,再修改为MMC
SD_Type = SD_TYPE_V1;
//如果是V1.0卡,CMD8指令后没有后续数据
//片选置高,结束本次命令
FLASH_SPI_CS_DISABLE();
//多发8个CLK,让SD结束后续操作
SPI_FLASH_SendByte(0xFF);
//-----------------SD卡、MMC卡初始化开始-----------------
//发卡初始化指令CMD55+ACMD41
// 如果有应答,说明是SD卡,且初始化完成
// 没有回应,说明是MMC卡,额外进行相应初始化
retry = 0;
do
{
//先发CMD55,应返回0x01;否则出错
r1 = SD_SendCommand(CMD55, 0, 0);
if(r1 != 0x01)
{
return r1;
}
//得到正确响应后,发ACMD41,应得到返回值0x00,否则重试200次
r1 = SD_SendCommand(ACMD41, 0, 0);
retry++;
}while((r1!=0x00) && (retry<400));
// 判断是超时还是得到正确回应
// 若有回应:是SD卡;没有回应:是MMC卡
//----------MMC卡额外初始化操作开始------------
if(retry==400)
{
printf("\r\n SD卡信息: MMC卡 \r\n");
retry = 0;
//发送MMC卡初始化命令(没有测试)
do
{
r1 = SD_SendCommand(1, 0, 0);
retry++;
}while((r1!=0x00)&& (retry<400));
if(retry==400)
{
return 1; //MMC卡初始化超时
}
//写入卡类型
SD_Type = SD_TYPE_MMC;
}
else
{
printf("\r\n SD卡信息: SD卡 \r\n");
}
//----------MMC卡额外初始化操作结束------------
//设置SPI为高速模式
SPI_SetSpeed(1);
SPI_FLASH_SendByte(0xFF);
//禁止CRC校验
/*
r1 = SD_SendCommand(CMD59, 0, 0x01);
if(r1 != 0x00)
{
return r1; //命令错误,返回r1
}
*/
//设置Sector Size
r1 = SD_SendCommand(CMD16, 512, 0xff);
if(r1 != 0x00)
{
return r1; //命令错误,返回r1
}
//-----------------SD卡、MMC卡初始化结束-----------------
}//SD卡为V1.0版本的初始化结束
//下面是V2.0卡的初始化
//其中需要读取OCR数据,判断是SD2.0还是SD2.0HC卡
else if(r1 == 0x01)
{
printf("\r\n SD卡版本信息:V2.0 \r\n");
//V2.0的卡,CMD8命令后会传回4字节的数据,要跳过再结束本命令
buff[0] = SPI_FLASH_SendByte(0xFF); //should be 0x00
buff[1] = SPI_FLASH_SendByte(0xFF); //should be 0x00
buff[2] = SPI_FLASH_SendByte(0xFF); //should be 0x01
buff[3] = SPI_FLASH_SendByte(0xFF); //should be 0xAA
FLASH_SPI_CS_DISABLE();
//the next 8 clocks
SPI_FLASH_SendByte(0xFF);
//判断该卡是否支持2.7V-3.6V的电压范围
if(buff[2]==0x01 && buff[3]==0xAA)
{
//支持电压范围,可以操作
retry = 0;
//发卡初始化指令CMD55+ACMD41
do
{
r1 = SD_SendCommand(CMD55, 0, 0);
if(r1!=0x01)
{
return r1;
}
r1 = SD_SendCommand(ACMD41, 0x40000000, 0);
if(retry>200)
{
return r1; //超时则返回r1状态
}
}while(r1!=0);
//初始化指令发送完成,接下来获取OCR信息
//-----------鉴别SD2.0卡版本开始-----------
r1 = SD_SendCommand_NoDeassert(CMD58, 0, 0);
if(r1!=0x00)
{
return r1; //如果命令没有返回正确应答,直接退出,返回应答
}
//读OCR指令发出后,紧接着是4字节的OCR信息
buff[0] = SPI_FLASH_SendByte(0xFF);
buff[1] = SPI_FLASH_SendByte(0xFF);
buff[2] = SPI_FLASH_SendByte(0xFF);
buff[3] = SPI_FLASH_SendByte(0xFF);
//OCR接收完成,片选置高
FLASH_SPI_CS_DISABLE();
SPI_FLASH_SendByte(0xFF);
//检查接收到的OCR中的bit30位(CCS),确定其为SD2.0还是SDHC
//如果CCS=1:SDHC CCS=0:SD2.0
if(buff[0]&0x40) //检查CCS
{
SD_Type = SD_TYPE_V2HC;
printf("\r\n SD卡信息: SDHC \r\n");
}
else
{
SD_Type = SD_TYPE_V2;
printf("\r\n SD卡信息: SD2.0 \r\n");
}
//-----------鉴别SD2.0卡版本结束-----------
//设置SPI为高速模式
SPI_SetSpeed(1);
}
}
return r1;
}
/*******************************************************************************
* Function Name : SD_ReceiveData
* Description : 从SD卡中读回指定长度的数据,放置在给定位置
* Input : u8 *data(存放读回数据的内存>len)
* u16 len(数据长度)
* u8 release(传输完成后是否释放总线CS置高 0:不释放 1:释放)
* Output : None
* Return : u8
* 0:NO_ERR
* other:错误信息
*******************************************************************************/
u8 SD_ReceiveData(u8 *data, u16 len, u8 release)
{
u16 retry;
u8 r1;
// 启动一次传输
FLASH_SPI_CS_ENABLE();
//等待SD卡发回数据起始令牌0xFE
retry = 0;
do
{
r1 = SPI_FLASH_SendByte(0xFF);
retry++;
if(retry>2000) //2000次等待后没有应答,退出报错
{
FLASH_SPI_CS_DISABLE();
return 1;
}
}while(r1 != 0xFE);
//开始接收数据
while(len--)
{
*data = SPI_FLASH_SendByte(0xFF);
data++;
}
//下面是2个伪CRC(dummy CRC)
SPI_FLASH_SendByte(0xFF);
SPI_FLASH_SendByte(0xFF);
//按需释放总线,将CS置高
if(release == RELEASE)
{
//传输结束
FLASH_SPI_CS_DISABLE();
SPI_FLASH_SendByte(0xFF);
}
return 0;
}
/*******************************************************************************
* Function Name : SD_GetCID
* Description : 获取SD卡的CID信息,包括制造商信息
* Input : u8 *cid_data(存放CID的内存,至少16Byte)
* Output : None
* Return : u8
* 0:NO_ERR
* 1:TIME_OUT
* other:错误信息
*******************************************************************************/
u8 SD_GetCID(u8 *cid_data)
{
u8 r1;
//发CMD10命令,读CID
r1 = SD_SendCommand(CMD10, 0, 0xFF);
if(r1 != 0x00)
{
return r1; //没返回正确应答,则退出,报错
}
//接收16个字节的数据
SD_ReceiveData(cid_data, 16, RELEASE);
return 0;
}
/*******************************************************************************
* Function Name : SD_GetCSD
* Description : 获取SD卡的CSD信息,包括容量和速度信息
* Input : u8 *cid_data(存放CID的内存,至少16Byte)
* Output : None
* Return : u8
* 0:NO_ERR
* 1:TIME_OUT
* other:错误信息
*******************************************************************************/
u8 SD_GetCSD(u8 *csd_data)
{
u8 r1;
//发CMD9命令,读CSD
r1 = SD_SendCommand(CMD9, 0, 0xFF);
if(r1 != 0x00)
{
return r1; //没返回正确应答,则退出,报错
}
//接收16个字节的数据
SD_ReceiveData(csd_data, 16, RELEASE);
return 0;
}
/*******************************************************************************
* Function Name : SD_GetCapacity
* Description : 获取SD卡的容量
* Input : None
* Output : None
* Return : u32 capacity
* 0: 取容量出错
*******************************************************************************/
u32 SD_GetCapacity(void)
{
u8 csd[16];
u32 Capacity;
u8 r1;
u16 i;
u16 temp;
//取CSD信息,如果期间出错,返回0
if(SD_GetCSD(csd)!=0)
{
return 0;
}
//如果为SDHC卡,按照下面方式计算
if((csd[0]&0xC0)==0x40)
{
Capacity = ((((u32)csd[8])<<8) + (u32)csd[9] + 1)*(u32)1024;
}
else
{
//下面代码为网上版本
////////////formula of the capacity///////////////
//
// memory capacity = BLOCKNR * BLOCK_LEN
//
// BLOCKNR = (C_SIZE + 1)* MULT
//
// C_SIZE_MULT+2
// MULT = 2
//
// READ_BL_LEN
// BLOCK_LEN = 2
/**********************************************/
//C_SIZE
i = csd[6]&0x03;
i<<=8;
i += csd[7];
i<<=2;
i += ((csd[8]&0xc0)>>6);
//C_SIZE_MULT
r1 = csd[9]&0x03;
r1<<=1;
r1 += ((csd[10]&0x80)>>7);
//BLOCKNR
r1+=2;
temp = 1;
while(r1)
{
temp*=2;
r1--;
}
Capacity = ((u32)(i+1))*((u32)temp);
// READ_BL_LEN
i = csd[5]&0x0f;
//BLOCK_LEN
temp = 1;
while(i)
{
temp*=2;
i--;
}
//The final result
Capacity *= (u32)temp;
//Capacity /= 512;
}
return (u32)Capacity;
}
/*******************************************************************************
* Function Name : SD_ReadSingleBlock
* Description : 读SD卡的一个block
* Input : u32 sector 取地址(sector值,非物理地址)
* u8 *buffer 数据存储地址(大小至少512byte)
* Output : None
* Return : u8 r1
* 0: 成功
* other:失败
*******************************************************************************/
u8 SD_ReadSingleBlock(u32 sector, u8 *buffer)
{
u8 r1;
//设置为高速模式
SPI_SetSpeed(SPI_SPEED_HIGH);
//如果不是SDHC,将sector地址转成byte地址
// sector = sector<<9;
r1 = SD_SendCommand(CMD17, sector, 0);//读命令
if(r1 != 0x00)
{
return r1;
}
r1 = SD_ReceiveData(buffer, 512, RELEASE);
if(r1 != 0)
{
return r1; //读数据出错!
}
else
{
return 0;
}
}
/*******************************************************************************
* Function Name : SD_WriteSingleBlock
* Description : 写入SD卡的一个block
* Input : u32 sector 扇区地址(sector值,非物理地址)
* u8 *buffer 数据存储地址(大小至少512byte)
* Output : None
* Return : u8 r1
* 0: 成功
* other:失败
*******************************************************************************/
u8 SD_WriteSingleBlock(u32 sector, const u8 *data)
{
u8 r1;
u16 i;
u16 retry;
//设置为高速模式
SPI_SetSpeed(SPI_SPEED_HIGH);
//如果不是SDHC,给定的是sector地址,将其转换成byte地址
// if(SD_Type!=SD_TYPE_V2HC)
// {
// sector = sector<<9;
// }
r1 = SD_SendCommand(CMD24, sector, 0x00);
if(r1 != 0x00)
{
return r1; //应答不正确,直接返回
}
//开始准备数据传输
FLASH_SPI_CS_ENABLE();
//先放3个空数据,等待SD卡准备好
SPI_FLASH_SendByte(0xff);
SPI_FLASH_SendByte(0xff);
SPI_FLASH_SendByte(0xff);
//放起始令牌0xFE
SPI_FLASH_SendByte(0xFE);
//放一个sector的数据
for(i=0;i<512;i++)
{
SPI_FLASH_SendByte(*data++);
}
//发2个Byte的dummy CRC
SPI_FLASH_SendByte(0xff);
SPI_FLASH_SendByte(0xff);
//等待SD卡应答
r1 = SPI_FLASH_SendByte(0xff);
if((r1&0x1F)!=0x05)
{
FLASH_SPI_CS_DISABLE();
return r1;
}
//等待操作完成
retry = 0;
while(!SPI_FLASH_SendByte(0xff))
{
retry++;
if(retry>0xfffe) //如果长时间写入没有完成,报错退出
{
FLASH_SPI_CS_DISABLE();
return 1; //写入超时返回1
}
}
//写入完成,片选置1
FLASH_SPI_CS_DISABLE();
SPI_FLASH_SendByte(0xff);
return 0;
}
/*******************************************************************************
* Function Name : SD_ReadMultiBlock
* Description : 读SD卡的多个block
* Input : u32 sector 取地址(sector值,非物理地址)
* u8 *buffer 数据存储地址(大小至少512byte)
* u8 count 连续读count个block
* Output : None
* Return : u8 r1
* 0: 成功
* other:失败
*******************************************************************************/
u8 SD_ReadMultiBlock(u32 sector, u8 *buffer, u8 count)
{
u8 r1;
//设置为高速模式
SPI_SetSpeed(SPI_SPEED_HIGH);
//如果不是SDHC,将sector地址转成byte地址
// sector = sector<<9;
//SD_WaitReady();
//发读多块命令
r1 = SD_SendCommand(CMD18, sector, 0);//读命令
if(r1 != 0x00)
{
return r1;
}
//开始接收数据
do
{
if(SD_ReceiveData(buffer, 512, NO_RELEASE) != 0x00)
{
break;
}
buffer += 512;
} while(--count);
//全部传输完毕,发送停止命令
SD_SendCommand(CMD12, 0, 0);
//释放总线
FLASH_SPI_CS_DISABLE();
SPI_FLASH_SendByte(0xFF);
if(count != 0)
{
return count; //如果没有传完,返回剩余个数
}
else
{
return 0;
}
}
/*******************************************************************************
* Function Name : SD_WriteMultiBlock
* Description : 写入SD卡的N个block
* Input : u32 sector 扇区地址(sector值,非物理地址)
* u8 *buffer 数据存储地址(大小至少512byte)
* u8 count 写入的block数目
* Output : None
* Return : u8 r1
* 0: 成功
* other:失败
*******************************************************************************/
u8 SD_WriteMultiBlock(u32 sector, const u8 *data, u8 count)
{
u8 r1;
u16 i;
//设置为高速模式
SPI_SetSpeed(SPI_SPEED_HIGH);
//如果不是SDHC,给定的是sector地址,将其转换成byte地址
// if(SD_Type != SD_TYPE_V2HC)
// {
// sector = sector<<9;
// }
//如果目标卡不是MMC卡,启用ACMD23指令使能预擦除
if(SD_Type != SD_TYPE_MMC)
{
r1 = SD_SendCommand(ACMD23, count, 0x00);
}
//发多块写入指令
r1 = SD_SendCommand(CMD25, sector, 0x00);
if(r1 != 0x00)
{
return r1; //应答不正确,直接返回
}
//开始准备数据传输
FLASH_SPI_CS_ENABLE();
//先放3个空数据,等待SD卡准备好
SPI_FLASH_SendByte(0xff);
SPI_FLASH_SendByte(0xff);
//--------下面是N个sector写入的循环部分
do
{
//放起始令牌0xFC 表明是多块写入
SPI_FLASH_SendByte(0xFC);
//放一个sector的数据
for(i=0;i<512;i++)
{
SPI_FLASH_SendByte(*data++);
}
//发2个Byte的dummy CRC
SPI_FLASH_SendByte(0xff);
SPI_FLASH_SendByte(0xff);
//等待SD卡应答
r1 = SPI_FLASH_SendByte(0xff);
if((r1&0x1F)!=0x05)
{
FLASH_SPI_CS_DISABLE(); //如果应答为报错,则带错误代码直接退出
return r1;
}
//等待SD卡写入完成
if(SD_WaitReady()==1)
{
FLASH_SPI_CS_DISABLE(); //等待SD卡写入完成超时,直接退出报错
return 1;
}
//本sector数据传输完成
}while(--count);
//发结束传输令牌0xFD
r1 = SPI_FLASH_SendByte(0xFD);
if(r1==0x00)
{
count = 0xfe;
}
if(SD_WaitReady())
{
while(1)
{
}
}
//写入完成,片选置1
FLASH_SPI_CS_DISABLE();
SPI_FLASH_SendByte(0xff);
return count; //返回count值,如果写完则count=0,否则count=1
}
/* USER CODE END 1 */
SDCard.h 根据YS-F1Pro SPI Flash 改写 SD
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __SDCard_H
#define __SDCard_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f1xx_hal.h"
/* USER CODE END Includes */
/* 类型定义 ------------------------------------------------------------------*/
/* 宏定义 --------------------------------------------------------------------*/
//#define SPI_FLASH_ID 0xEF3015 //W25X16 2MB
//#define SPI_FLASH_ID 0xEF4015 //W25Q16 4MB
//#define SPI_FLASH_ID 0XEF4017 //W25Q64 8MB
#define SPI_FLASH_ID 0XEF4018 //W25Q128 16MB YS-F1Pro开发默认使用
#define FLASH_SPIx SPI1
#define FLASH_SPIx_RCC_CLK_ENABLE() __HAL_RCC_SPI1_CLK_ENABLE()
#define FLASH_SPIx_RCC_CLK_DISABLE() __HAL_RCC_SPI1_CLK_DISABLE()
#define FLASH_SPI_GPIO_ClK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define FLASH_SPI_GPIO_PORT GPIOA
#define FLASH_SPI_SCK_PIN GPIO_PIN_5
#define FLASH_SPI_MISO_PIN GPIO_PIN_6
#define FLASH_SPI_MOSI_PIN GPIO_PIN_7
#define FLASH_SPI_CS_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define FLASH_SPI_CS_PORT GPIOA
#define FLASH_SPI_CS_PIN GPIO_PIN_4
#define FLASH_SPI_CS_ENABLE() HAL_GPIO_WritePin(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN, GPIO_PIN_RESET)
#define FLASH_SPI_CS_DISABLE() HAL_GPIO_WritePin(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN, GPIO_PIN_SET)
/* 扩展变量 ------------------------------------------------------------------*/
extern SPI_HandleTypeDef hspiflash;
/* 函数声明 ------------------------------------------------------------------*/
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
/* Private define ------------------------------------------------------------*/
/* SD卡类型定义 */
#define SD_TYPE_MMC 0
#define SD_TYPE_V1 1
#define SD_TYPE_V2 2
#define SD_TYPE_V2HC 4
/* SPI总线速度设置*/
#define SPI_SPEED_LOW 0
#define SPI_SPEED_HIGH 1
/* SD传输数据结束后是否释放总线宏定义 */
#define NO_RELEASE 0
#define RELEASE 1
/* SD卡指令表 */
#define CMD0 0 //卡复位
#define CMD9 9 //命令9 ,读CSD数据
#define CMD10 10 //命令10,读CID数据
#define CMD12 12 //命令12,停止数据传输
#define CMD16 16 //命令16,设置SectorSize 应返回0x00
#define CMD17 17 //命令17,读sector
#define CMD18 18 //命令18,读Multi sector
#define ACMD23 23 //命令23,设置多sector写入前预先擦除N个block
#define CMD24 24 //命令24,写sector
#define CMD25 25 //命令25,写Multi sector
#define ACMD41 41 //命令41,应返回0x00
#define CMD55 55 //命令55,应返回0x01
#define CMD58 58 //命令58,读OCR信息
#define CMD59 59 //命令59,使能/禁止CRC,应返回0x00
/* Private macro -------------------------------------------------------------*/
//SD卡CS片选使能端操作:
#define SD_CS_ENABLE() GPIO_ResetBits(GPIOA,GPIO_PIN_4) //选中SD卡
#define SD_CS_DISABLE() GPIO_SetBits(GPIOA,GPIO_PIN_4) //不选中SD卡
//#define SD_PWR_ON() GPIO_ResetBits(GPIOD,GPIO_Pin_10) //SD卡上电
//#define SD_PWR_OFF() GPIO_SetBits(GPIOD,GPIO_Pin_10) //SD卡断电
//#define SD_DET() !GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2) //检测有卡
//1-有 0-无
/* Private function prototypes -----------------------------------------------*/
void SPI_Configuration(void);
void SPI_SetSpeed(u8 SpeedSet);
u8 SPI_ReadWriteByte(u8 TxData); //SPI总线读写一个字节
u8 SD_WaitReady(void); //等待SD卡就绪
u8 SD_SendCommand(u8 cmd, u32 arg, u8 crc); //SD卡发送一个命令
u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg, u8 crc);
u8 SD_Init(void); //SD卡初始化
//
u8 SD_ReceiveData(u8 *data, u16 len, u8 release);//SD卡读数据
u8 SD_GetCID(u8 *cid_data); //读SD卡CID
u8 SD_GetCSD(u8 *csd_data); //读SD卡CSD
u32 SD_GetCapacity(void); //取SD卡容量
u8 SD_ReadSingleBlock(u32 sector, u8 *buffer); //读一个sector
u8 SD_WriteSingleBlock(u32 sector, const u8 *buffer); //写一个sector
u8 SD_ReadMultiBlock(u32 sector, u8 *buffer, u8 count); //读多个sector
u8 SD_WriteMultiBlock(u32 sector, const u8 *data, u8 count); //写多个sector
/* USER CODE BEGIN Prototypes */
extern u8 SD_Init(void);
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /*__ usart_H */
2、添加FATFS文件系统
3、修改FATFS文件系统SD卡驱动部分sdcard_diskio.c
/* 包含头文件 ----------------------------------------------------------------*/
#include "main.h"
#include
#include "SDCard.h"
#include "ff_gen_drv.h"
/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
#define BLOCK_SIZE 512
/* 私有变量 ------------------------------------------------------------------*/
static volatile DSTATUS Stat = STA_NOINIT;
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
DSTATUS SD_initialize (BYTE);
DSTATUS SD_status (BYTE);
DRESULT SD_read (BYTE, BYTE*, DWORD, UINT);
#if _USE_WRITE == 1 // 如果允许写操作
DRESULT SD_write (BYTE, const BYTE*, DWORD, UINT);
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1 // 如果输入输出操作控制
DRESULT SD_ioctl (BYTE, BYTE, void*);
#endif /* _USE_IOCTL == 1 */
/* 定义SD卡接口函数 */
const Diskio_drvTypeDef SD_Driver =
{
SD_initialize, // SD卡初始化
SD_status, // SD卡状态获取
SD_read, // SD卡读数据
#if _USE_WRITE == 1
SD_write, // SD卡写数据
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
SD_ioctl, // 获取SD卡信息
#endif /* _USE_IOCTL == 1 */
};
/* 函数体 --------------------------------------------------------------------*/
/**
* 函数功能: SD卡初始化配置
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
//extern void MX_SPI1_Init(void);
DSTATUS SD_initialize(BYTE lun)
{
Stat = STA_NOINIT;
/* 初始化SDIO外设 */
// MX_SPI1_Init();
/* 获取SD卡状态 */
int result;
result = SD_Init();
// if(HAL_SD_GetStatus(&hsdcard)==SD_TRANSFER_OK)
// {
// Stat &= ~STA_NOINIT;
// }
if (result == 0)
{
Stat = RES_OK;
}
else
{
Stat = RES_ERROR;
}
return Stat;
}
/**
* 函数功能: SD卡状态获取
* 输入参数: lun : 不用
* 返 回 值: DSTATUS:SD卡状态返回值
* 说 明: 无
*/
DSTATUS SD_status(BYTE lun)
{
Stat = STA_NOINIT;
// if(HAL_SD_GetStatus(&hsdcard) == SD_TRANSFER_OK)
// {
// Stat &= ~STA_NOINIT;
// }
Stat = RES_OK;
return Stat;
}
/**
* 函数功能: 从SD卡读取数据到缓冲区
* 输入参数: lun : 不用
* buff:存放读取到数据缓冲区指针
* sector:扇区地址(LBA)
* count:扇区数目
* 返 回 值: DSTATUS:操作结果
* 说 明: SD卡读操作使用DMA传输
*/
DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
DRESULT res = RES_OK;
// if((DWORD)buff&3)
// {
// DWORD scratch[BLOCK_SIZE/4];
// while (count--)
// {
// res = SD_read(lun,(void *)scratch, sector++, 1);
// if (res != RES_OK)
// {
// break;
// }
// memcpy(buff, scratch, BLOCK_SIZE);
// buff += BLOCK_SIZE;
// }
// return res;
// }
//
// if(HAL_SD_ReadBlocks_DMA(&hsdcard,(uint32_t*)buff,(uint64_t)(sector * BLOCK_SIZE),BLOCK_SIZE,count) != SD_OK)
// {
// res = RES_ERROR;
// }
// if(res==RES_OK)
// {
// if(HAL_SD_CheckReadOperation(&hsdcard, 0xFFFFFFFF) != SD_OK)
// {
// res = RES_ERROR;
// }
// }
int result;
if(count==1)
{
result=SD_ReadSingleBlock(sector,buff);
}
else
{
result = SD_ReadMultiBlock(sector, buff, count);
}
if (result == 0)
{
res = RES_OK;
}
else
{
res = RES_ERROR;
}
return res;
}
/**
* 函数功能: 将缓冲区数据写入到SD卡内
* 输入参数: lun : 不用
* buff:存放待写入数据的缓冲区指针
* sector:扇区地址(LBA)
* count:扇区数目
* 返 回 值: DSTATUS:操作结果
* 说 明: SD卡写操作没有使用DMA传输
*/
#if _USE_WRITE == 1
DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
{
DRESULT res = RES_OK;
// if((DWORD)buff&3)
// {
// DRESULT res = RES_OK;
// DWORD scratch[BLOCK_SIZE / 4];
// while (count--)
// {
// memcpy( scratch,buff,BLOCK_SIZE);
// res = SD_write(lun,(void *)scratch, sector++, 1);
// if (res != RES_OK)
// {
// break;
// }
// buff += BLOCK_SIZE;
// }
// return res;
// }
//
// if(HAL_SD_WriteBlocks(&hsdcard,(uint32_t*)buff,(uint64_t)(sector * BLOCK_SIZE),BLOCK_SIZE, count) != SD_OK)
// {
// res = RES_ERROR;
// }
int result;
if(count==1)
{
result=SD_WriteSingleBlock(sector, buff);
}
else
{
result = SD_WriteMultiBlock(sector, buff, count);
}
if (result == 0)
{
res = RES_OK;
}
else
{
res = RES_ERROR;
}
return res;
}
#endif /* _USE_WRITE == 1 */
/**
* 函数功能: 输入输出控制操作(I/O control operation)
* 输入参数: lun : 不用
* cmd:控制命令
* buff:存放待写入或者读取数据的缓冲区指针
* 返 回 值: DSTATUS:操作结果
* 说 明: 无
*/
#if _USE_IOCTL == 1
DRESULT SD_ioctl(BYTE lun, BYTE cmd, void *buff)
{
DRESULT res = RES_ERROR;
// if (Stat & STA_NOINIT) return RES_NOTRDY;
//
// switch (cmd)
// {
// /* Make sure that no pending write process */
// case CTRL_SYNC :
// FLASH_SPI_CS_ENABLE();
// if(SD_WaitReady()==0)
// {
// res = RES_OK;
// }
// else
// {
// res = RES_ERROR;
// }
//// FLASH_SPI_CSDISABLE();
// break;
//
// /* 获取SD卡总扇区数目(DWORD) */
// case GET_SECTOR_COUNT :
// *(WORD*)buff = 512;
// res = RES_OK;
// break;
//
// /* 获取读写扇区大小(WORD) */
// case GET_SECTOR_SIZE :
// *(DWORD*)buff = SD_GetCapacity();
// res = RES_OK;
// break;
//
// /* 获取擦除块大小(DWORD) */
// case GET_BLOCK_SIZE :
// *(DWORD*)buff = BLOCK_SIZE;
// break;
//
// default:
// res = RES_PARERR;
// }
uint8_t CSD[16] = {0};
uint8_t csddata[16] = {0};
uint32_t csize;
uint32_t Capacity;
switch (cmd)
{
case CTRL_SYNC:
res = RES_OK;
break;
case GET_SECTOR_COUNT:
SD_GetCID(CSD);
SD_GetCSD(csddata);
// SDGetCIDCSD(CSD, csddata);
csize = csddata[9] + ((uint32_t)csddata[8] << 8) + ((uint32_t)(csddata[7] & 0x3f) << 16) + 1;
Capacity = csize << 9;
*((DWORD *)buff) = Capacity;
res = RES_OK;
break;
case GET_SECTOR_SIZE:
*(WORD *)buff = 512; //spi flash的扇区大小是 512 Bytes
return RES_OK;
case GET_BLOCK_SIZE:
*((DWORD *)buff) = 4096;
res = RES_OK;
break;
default:
res = RES_PARERR;
break;
}
return res;
}
#endif /* _USE_IOCTL == 1 */
4、在Main.c文件中添加 测试应用程序
定义一下变量
char SPIFLASHPath[4]; /* 串行Flash逻辑设备路径 */
char SDPath[4]; /* SD卡逻辑设备路径 */
FATFS fs; /* FatFs文件系统对象 */
FIL file; /* 文件对象 */
FRESULT f_res; /* 文件操作结果 */
UINT fnum; /* 文件成功读写数量 */
BYTE ReadBuffer[1024]={0}; /* 读缓冲区 */
BYTE WriteBuffer[]= "欢迎使用硬石STM32开发板 今天是个好日子,新建文件系统测试文件\n";/* 写缓冲区*/
extern Diskio_drvTypeDef SD_Driver;
添加文件系统操作结果处理
/**
* 函数功能: FatFS文件系统操作结果信息处理.
* 输入参数: FatFS文件系统操作结果:FRESULT
* 返 回 值: 无
* 说 明: 无
*/
static void printf_fatfs_error(FRESULT fresult)
{
switch(fresult)
{
case FR_OK: //(0)
printf("》操作成功。\n");
break;
case FR_DISK_ERR: //(1)
printf("!!硬件输入输出驱动出错。\n");
break;
case FR_INT_ERR: //(2)
printf("!!断言错误。\n");
break;
case FR_NOT_READY: //(3)
printf("!!物理设备无法工作。\n");
break;
case FR_NO_FILE: //(4)
printf("!!无法找到文件。\n");
break;
case FR_NO_PATH: //(5)
printf("!!无法找到路径。\n");
break;
case FR_INVALID_NAME: //(6)
printf("!!无效的路径名。\n");
break;
case FR_DENIED: //(7)
case FR_EXIST: //(8)
printf("!!拒绝访问。\n");
break;
case FR_INVALID_OBJECT: //(9)
printf("!!无效的文件或路径。\n");
break;
case FR_WRITE_PROTECTED: //(10)
printf("!!逻辑设备写保护。\n");
break;
case FR_INVALID_DRIVE: //(11)
printf("!!无效的逻辑设备。\n");
break;
case FR_NOT_ENABLED: //(12)
printf("!!无效的工作区。\n");
break;
case FR_NO_FILESYSTEM: //(13)
printf("!!无效的文件系统。\n");
break;
case FR_MKFS_ABORTED: //(14)
printf("!!因函数参数问题导致f_mkfs函数操作失败。\n");
break;
case FR_TIMEOUT: //(15)
printf("!!操作超时。\n");
break;
case FR_LOCKED: //(16)
printf("!!文件被保护。\n");
break;
case FR_NOT_ENOUGH_CORE: //(17)
printf("!!长文件名支持获取堆空间失败。\n");
break;
case FR_TOO_MANY_OPEN_FILES: //(18)
printf("!!打开太多文件。\n");
break;
case FR_INVALID_PARAMETER: // (19)
printf("!!参数无效。\n");
break;
}
}
添加测试程序:
/* 注册一个FatFS设备:SD卡 */
if(FATFS_LinkDriver(&SD_Driver, SDPath) == 0)
{
//在SD卡挂载文件系统,文件系统挂载时会对SD卡初始化
f_res = f_mount(&fs,(TCHAR const*)SDPath,1);
printf_fatfs_error(f_res);
/*----------------------- 格式化测试 ---------------------------*/
/* 如果没有文件系统就格式化创建创建文件系统 */
if(f_res == FR_NO_FILESYSTEM)
{
printf("》SD卡还没有文件系统,即将进行格式化...\n");
/* 格式化 */
f_res=f_mkfs((TCHAR const*)SDPath,0,0);
if(f_res == FR_OK)
{
printf("》SD卡已成功格式化文件系统。\n");
/* 格式化后,先取消挂载 */
f_res = f_mount(NULL,(TCHAR const*)SDPath,1);
/* 重新挂载 */
f_res = f_mount(&fs,(TCHAR const*)SDPath,1);
}
else
{
printf("《《格式化失败。》》\n");
while(1);
}
}
else if(f_res!=FR_OK)
{
printf("!!SD卡挂载文件系统失败。(%d)\n",f_res);
printf_fatfs_error(f_res);
while(1);
}
else
{
printf("》文件系统挂载成功,可以进行读写测试\n");
}
/*----------------------- 文件系统测试:写测试 -----------------------------*/
/* 打开文件,如果文件不存在则创建它 */
printf("****** 即将进行文件写入测试... ******\n");
f_res = f_open(&file, "FatFs读写测试文件.txt",FA_CREATE_ALWAYS | FA_WRITE );
if ( f_res == FR_OK )
{
printf("》打开/创建FatFs读写测试文件.txt文件成功,向文件写入数据。\n");
/* 将指定存储区内容写入到文件内 */
f_res=f_write(&file,WriteBuffer,sizeof(WriteBuffer),&fnum);
if(f_res==FR_OK)
{
printf("》文件写入成功,写入字节数据:%d\n",fnum);
printf("》向文件写入的数据为:\n%s\n",WriteBuffer);
}
else
{
printf("!!文件写入失败:(%d)\n",f_res);
}
/* 不再读写,关闭文件 */
f_close(&file);
}
else
{
printf("!!打开/创建文件失败。\n");
}
/*------------------- 文件系统测试:读测试 ------------------------------------*/
printf("****** 即将进行文件读取测试... ******\n");
f_res = f_open(&file, "FatFs读写测试文件.txt", FA_OPEN_EXISTING | FA_READ);
if(f_res == FR_OK)
{
printf("》打开文件成功。\n");
f_res = f_read(&file, ReadBuffer, sizeof(ReadBuffer), &fnum);
if(f_res==FR_OK)
{
printf("》文件读取成功,读到字节数据:%d\n",fnum);
printf("》读取得的文件数据为:\n%s \n", ReadBuffer);
}
else
{
printf("!!文件读取失败:(%d)\n",f_res);
}
}
else
{
printf("!!打开文件失败。\n");
}
/* 不再读写,关闭文件 */
f_close(&file);
/* 不再使用,取消挂载 */
f_res = f_mount(NULL,(TCHAR const*)SDPath,1);
}
/* 注销一个FatFS设备:SD卡 */
FATFS_UnLinkDriver(SDPath);
3、使用串口查看测试结果