STM32存储左右互搏 SPI总线FATS文件读写SD/MicroSD/TF卡
SD/MicroSD/TF卡是基于FLASH的一种常见非易失存储单元,由接口协议电路和FLASH构成。市面上由不同尺寸和不同容量的卡,手机领域用的TF卡实际就是MicroSD卡,尺寸比SD卡小,而电路和协议操作则是一样。这里介绍STM32CUBEIDE开发平台HAL库SPI总线FATS文件操作读写SD/MicroSD/TF卡的例程。
除了在硬件板子上集成SD/MicroSD/TF卡插槽的方式,也可以使用SD/MicroSD/TF卡模块,如下图所示为其中一种(支持MicroSD/TF卡):
SD/MicroSD/TF卡访问接口
SD/MicroSD/TF卡可以通过访问更快的SDIO专用协议接口或是访问慢一些的普通SPI接口进行操作,两种协议接口复用管脚。通过SPI接口进行操作,上面介绍的模块的接口连接特性如下:
共6个引脚(GND、VCC、MISO、MOSI、SCK、CS)与标准SPI接口对应。除了供电为5V,通讯管脚的电平由于模块内部进行了转换,直接和STM32的一个SPI接口连接即可。
如果不采用模块,直接集成卡槽使用,SDIO协议管脚和SPI协议管脚的复用关系如下:
例程采用STM32F401CCU6芯片(兼容STM32F401RCT6, 仅封装不同)对4GB的TF卡进行操作
STM32工程配置
首先建立基本工程并设置时钟:
配置SPI1:
不配置DMA:
选择PA4管脚为软件代码控制的片选,单独配置为GPIO输出, 需要注意,SPI FATS访问SD/MicroSD/TF卡必须采用PA4作为软件片选的管脚,采用其它管脚如PA0/PA1/PA15等作为SP1的软件片选时,读文件操作正常,而写文件操作在写成功后STM32会挂死,原因未明,芯片内部机制和FATS库配合存在潜在的问题,需要规避。在采用SPI2时,也要采用PA4作为软件片选的管脚。
配置FATAS参数:
配置UART1做为通讯口:
DMA不配置:
保存并生成初始代码:
STM32工程代码
UART串口printf打印输出实现参考:STM32 UART串口printf函数应用及浮点打印代码空间节省 (HAL)
建立SPI操作SD/MicroSD/TF卡库头文件SDdriver.h:
#include "main.h"
/*
CID 128bit 卡标识号;
RCA 16bit 相对卡地址(Relative card address):本地系统中卡的地址,动态变化。在主机初始的时候确定。SPI模式中没有;
CSD 128bit 卡描述数据:卡操作条件相关的信息数据;
SCR 64bit SD配置寄存器:SD卡特定信息数据;
OCR 32bit 操作条件寄存器。
*/
extern uint8_t SD_TYPE;
//SD卡类型
#define ERR 0x00
#define MMC 0x01
#define V1 0x02
#define V2 0x04
#define V2HC 0x06
#define DUMMY_BYTE 0xFF
#define MSD_BLOCKSIZE 512
//CMD定义
#define CMD0 0 //卡复位
#define CMD1 1
#define CMD8 8 //命令8 ,SEND_IF_COND
#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 CMD23 23 //命令23,设置多sector写入前预先擦除N个block
#define CMD24 24 //命令24,写sector
#define CMD25 25 //命令25,写Multi sector
#define CMD41 41 //命令41,应返回0x00
#define CMD55 55 //命令55,应返回0x01
#define CMD58 58 //命令58,读OCR信息
#define CMD59 59 //命令59,使能/禁止CRC,应返回0x00
//数据写入回应字意义
#define MSD_DATA_OK 0x05
#define MSD_DATA_CRC_ERROR 0x0B
#define MSD_DATA_WRITE_ERROR 0x0D
#define MSD_DATA_OTHER_ERROR 0xFF
//SD卡回应标记字
#define MSD_RESPONSE_NO_ERROR 0x00
#define MSD_IN_IDLE_STATE 0x01
#define MSD_ERASE_RESET 0x02
#define MSD_ILLEGAL_COMMAND 0x04
#define MSD_COM_CRC_ERROR 0x08
#define MSD_ERASE_SEQUENCE_ERROR 0x10
#define MSD_ADDRESS_ERROR 0x20
#define MSD_PARAMETER_ERROR 0x40
#define MSD_RESPONSE_FAILURE 0xFF
enum _CD_HOLD
{
HOLD = 0,
RELEASE = 1,
};
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*/
}
MSD_CSD;
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 */
}
MSD_CID;
typedef struct
{
MSD_CSD CSD;
MSD_CID CID;
uint32_t Capacity; /* Card Capacity */
uint32_t BlockSize; /* Card Block Size */
uint16_t RCA;
uint8_t CardType;
uint32_t SpaceTotal; /* Total space size in file system */
uint32_t SpaceFree; /* Free space size in file system */
}
MSD_CARDINFO, *PMSD_CARDINFO;
extern MSD_CARDINFO SD0_CardInfo;
int SD_RST(void);
uint8_t SD_init(void);
void SD_CS(uint8_t p);
uint32_t SD_GetSectorCount(void);
uint8_t SD_GETCID (uint8_t *cid_data);
uint8_t SD_GETCSD(uint8_t *csd_data);
int MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo);
uint8_t SD_ReceiveData(uint8_t *data, uint16_t len);
uint8_t SD_SendBlock(uint8_t*buf,uint8_t cmd);
uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt);
uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt);
void SPI_setspeed(uint32_t speed);
uint8_t spi_readwrite(uint8_t Txdata);
建立SPI操作SD/MicroSD/TF卡库头文件SDdriver.c:
#include "SDdriver.h"
extern SPI_HandleTypeDef hspi1;
extern void PY_Delay_us_t(uint32_t Delay);
uint8_t SD_TYPE=0x00;
MSD_CARDINFO SD0_CardInfo;
#define SD_CS_Pin GPIO_PIN_4
#define SD_CS_GPIO_Port GPIOA
#define SD_CS_EN HAL_GPIO_WritePin(SD_CS_GPIO_Port,SD_CS_Pin,GPIO_PIN_RESET)
#define SD_CS_DEN HAL_GPIO_WritePin(SD_CS_GPIO_Port,SD_CS_Pin,GPIO_PIN_SET)
int SD_RST(void)
{
uint8_t rst;
SD_CS_DEN;
PY_Delay_us_t(20000);
SD_CS_EN;
PY_Delay_us_t(1);
spi_readwrite(CMD0 | 0x40);
spi_readwrite(0 >> 24);
spi_readwrite(0 >> 16);
spi_readwrite(0 >> 8);
spi_readwrite(0);
spi_readwrite(0x95);
for(uint32_t i=0; i<1000; i++)
{
rst=spi_readwrite(0xFF);
if((rst&0X80)==0) break;
PY_Delay_us_t(100);
}
if((rst&0X80)==0) return 0x01;
else return 0;
}
int SD_sendcmd(uint8_t cmd,uint32_t arg,uint8_t crc)
{
uint8_t rst;
uint8_t idle;
SD_CS_DEN;
PY_Delay_us_t(20000);
SD_CS_EN;
PY_Delay_us_t(1);
do{
idle=spi_readwrite(0xFF);
PY_Delay_us_t(1);
}while(idle!=0xFF); //Check SD idle status
spi_readwrite(cmd | 0x40);
spi_readwrite(arg >> 24);
spi_readwrite(arg >> 16);
spi_readwrite(arg >> 8);
spi_readwrite(arg);
spi_readwrite(crc);
if(cmd==CMD12) spi_readwrite(0xFF); //Stop data transmission
do{
rst=spi_readwrite(0xFF);
PY_Delay_us_t(1);
}while(rst&0x80);
return rst;
}
/////////////////////////////////////////////////////////////
//SD卡初始化
////////////////////////////////////////////////////////////
uint8_t SD_init(void)
{
uint8_t rst;
uint8_t buff[6] = {0};
uint16_t retry;
uint8_t i;
SPI_setspeed(SPI_BAUDRATEPRESCALER_256);
SD_CS_DEN;
PY_Delay_us_t(1);
for(retry=0;retry<10;retry++) //向总线最少发送74个脉冲,为了让SD卡正常启动 (唤醒SD卡)
{
spi_readwrite(0xFF);
};
//发送新的命令之前,需要取消之前的片选,额外发多 8个 CLK (发送0xFF无效数据),结束之前的操作。
//SD卡进入IDLE状态
do{
rst = SD_RST();
PY_Delay_us_t(1);
}while(rst!=0x01);
//查看SD卡的类型
SD_TYPE=0;
rst = SD_sendcmd(CMD8, 0x1AA, 0x87);
if(rst==0x01)
{
for(i=0;i<4;i++) buff[i]=spi_readwrite(0xFF); //Get trailing return value of R7 resp
if(buff[2]==0X01&&buff[3]==0XAA)//卡是否支持2.7~3.6V
{
retry=0XFFFE;
do{
SD_sendcmd(CMD55,0,0X01); //发送CMD55
rst=SD_sendcmd(CMD41,0x40000000,0X01);//发送CMD41
}while(rst&&retry--);
if(retry&&SD_sendcmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
{
for(i=0;i<4;i++)buff[i]=spi_readwrite(0XFF);//得到OCR值
if(buff[0]&0x40){
SD_TYPE=V2HC;
}else {
SD_TYPE=V2;
}
}
}else{
SD_sendcmd(CMD55,0,0X01); //发送CMD55
rst=SD_sendcmd(CMD41,0,0X01); //发送CMD41
if(rst<=1)
{
SD_TYPE=V1;
retry=0XFFFE;
do //等待退出IDLE模式
{
SD_sendcmd(CMD55,0,0X01); //发送CMD55
rst=SD_sendcmd(CMD41,0,0X01);//发送CMD41
}while(rst&&retry--);
}else//MMC卡不支持CMD55+CMD41识别
{
SD_TYPE=MMC;//MMC V3
retry=0XFFFE;
do //等待退出IDLE模式
{
rst=SD_sendcmd(CMD1,0,0X01);//发送CMD1
}while(rst&&retry--);
}
if(retry==0||SD_sendcmd(CMD16,512,0X01)!=0)SD_TYPE=ERR;//错误的卡
}
}
SD_CS_DEN;
SPI_setspeed(SPI_BAUDRATEPRESCALER_2);
return SD_TYPE;
}
//读取指定长度数据
uint8_t SD_ReceiveData(uint8_t *data, uint16_t len)
{
uint8_t rst;
SD_CS_EN;
do
{
rst = spi_readwrite(0xFF);
PY_Delay_us_t(100);
}while(rst != 0xFE);
while(len--)
{
*data = spi_readwrite(0xFF);
data++;
}
spi_readwrite(0xFF);
spi_readwrite(0xFF);
return 0;
}
//向sd卡写入一个数据包的内容 512字节
uint8_t SD_SendBlock(uint8_t*buf,uint8_t cmd)
{
uint16_t t;
uint8_t rst;
do{
rst=spi_readwrite(0xFF);
}while(rst!=0xFF);
spi_readwrite(cmd);
if(cmd!=0XFD)//不是结束指令
{
for(t=0;t<512;t++)spi_readwrite(buf[t]);//提高速度,减少函数传参时间
spi_readwrite(0xFF);//忽略crc
spi_readwrite(0xFF);
t=spi_readwrite(0xFF);//接收响应
if((t&0x1F)!=0x05)return 2;//响应错误
}
return 0;//写入成功
}
//获取CID信息
uint8_t SD_GETCID (uint8_t *cid_data)
{
uint8_t rst;
rst=SD_sendcmd(CMD10,0,0x01); //读取CID寄存器
if(rst==0x00)
{
rst=SD_ReceiveData(cid_data,16);
}
SD_CS_DEN;
if(rst)return 1;
else return 0;
}
//获取CSD信息
uint8_t SD_GETCSD(uint8_t *csd_data){
uint8_t rst;
rst=SD_sendcmd(CMD9,0,0x01);//发CMD9命令,读CSD寄存器
if(rst==0)
{
rst=SD_ReceiveData(csd_data, 16);//接收16个字节的数据
}
SD_CS_DEN;//取消片选
if(rst)return 1;
else return 0;
}
//获取SD卡的总扇区数
uint32_t SD_GetSectorCount(void)
{
uint8_t csd[16];
uint32_t Capacity;
uint8_t n;
uint16_t csize;
//取CSD信息,如果期间出错,返回0
if(SD_GETCSD(csd)!=0) return 0;
//如果为SDHC卡,按照下面方式计算
if((csd[0]&0xC0)==0x40) //V2.00的卡
{
csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
Capacity = (uint32_t)csize << 10;//得到扇区数
}
else//V1.XX的卡
{
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
Capacity= (uint32_t)csize << (n - 9);//得到扇区数
}
return Capacity;
}
int MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo)
{
uint8_t rst;
uint8_t CSD_Tab[16];
uint8_t CID_Tab[16];
/* Send CMD9, Read CSD */
rst = SD_sendcmd(CMD9, 0, 0xFF);
if(rst != 0x00)
{
return rst;
}
if(SD_ReceiveData(CSD_Tab, 16))
{
return 1;
}
/* Send CMD10, Read CID */
rst = SD_sendcmd(CMD10, 0, 0xFF);
if(rst != 0x00)
{
return rst;
}
if(SD_ReceiveData(CID_Tab, 16))
{
return 2;
}
/* Byte 0 */
SD0_CardInfo->CSD.CSDStruct = (CSD_Tab[0] & 0xC0) >> 6;
SD0_CardInfo->CSD.SysSpecVersion = (CSD_Tab[0] & 0x3C) >> 2;
SD0_CardInfo->CSD.Reserved1 = CSD_Tab[0] & 0x03;
/* Byte 1 */
SD0_CardInfo->CSD.TAAC = CSD_Tab[1] ;
/* Byte 2 */
SD0_CardInfo->CSD.NSAC = CSD_Tab[2];
/* Byte 3 */
SD0_CardInfo->CSD.MaxBusClkFrec = CSD_Tab[3];
/* Byte 4 */
SD0_CardInfo->CSD.CardComdClasses = CSD_Tab[4] << 4;
/* Byte 5 */
SD0_CardInfo->CSD.CardComdClasses |= (CSD_Tab[5] & 0xF0) >> 4;
SD0_CardInfo->CSD.RdBlockLen = CSD_Tab[5] & 0x0F;
/* Byte 6 */
SD0_CardInfo->CSD.PartBlockRead = (CSD_Tab[6] & 0x80) >> 7;
SD0_CardInfo->CSD.WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6;
SD0_CardInfo->CSD.RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5;
SD0_CardInfo->CSD.DSRImpl = (CSD_Tab[6] & 0x10) >> 4;
SD0_CardInfo->CSD.Reserved2 = 0; /* Reserved */
SD0_CardInfo->CSD.DeviceSize = (CSD_Tab[6] & 0x03) << 10;
/* Byte 7 */
SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[7]) << 2;
/* Byte 8 */
SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[8] & 0xC0) >> 6;
SD0_CardInfo->CSD.MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3;
SD0_CardInfo->CSD.MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07);
/* Byte 9 */
SD0_CardInfo->CSD.MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5;
SD0_CardInfo->CSD.MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2;
SD0_CardInfo->CSD.DeviceSizeMul = (CSD_Tab[9] & 0x03) << 1;
/* Byte 10 */
SD0_CardInfo->CSD.DeviceSizeMul |= (CSD_Tab[10] & 0x80) >> 7;
SD0_CardInfo->CSD.EraseGrSize = (CSD_Tab[10] & 0x7C) >> 2;
SD0_CardInfo->CSD.EraseGrMul = (CSD_Tab[10] & 0x03) << 3;
/* Byte 11 */
SD0_CardInfo->CSD.EraseGrMul |= (CSD_Tab[11] & 0xE0) >> 5;
SD0_CardInfo->CSD.WrProtectGrSize = (CSD_Tab[11] & 0x1F);
/* Byte 12 */
SD0_CardInfo->CSD.WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7;
SD0_CardInfo->CSD.ManDeflECC = (CSD_Tab[12] & 0x60) >> 5;
SD0_CardInfo->CSD.WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2;
SD0_CardInfo->CSD.MaxWrBlockLen = (CSD_Tab[12] & 0x03) << 2;
/* Byte 13 */
SD0_CardInfo->CSD.MaxWrBlockLen |= (CSD_Tab[13] & 0xc0) >> 6;
SD0_CardInfo->CSD.WriteBlockPaPartial = (CSD_Tab[13] & 0x20) >> 5;
SD0_CardInfo->CSD.Reserved3 = 0;
SD0_CardInfo->CSD.ContentProtectAppli = (CSD_Tab[13] & 0x01);
/* Byte 14 */
SD0_CardInfo->CSD.FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7;
SD0_CardInfo->CSD.CopyFlag = (CSD_Tab[14] & 0x40) >> 6;
SD0_CardInfo->CSD.PermWrProtect = (CSD_Tab[14] & 0x20) >> 5;
SD0_CardInfo->CSD.TempWrProtect = (CSD_Tab[14] & 0x10) >> 4;
SD0_CardInfo->CSD.FileFormat = (CSD_Tab[14] & 0x0C) >> 2;
SD0_CardInfo->CSD.ECC = (CSD_Tab[14] & 0x03);
/* Byte 15 */
SD0_CardInfo->CSD.CSD_CRC = (CSD_Tab[15] & 0xFE) >> 1;
SD0_CardInfo->CSD.Reserved4 = 1;
if(SD0_CardInfo->CardType == V2HC)
{
/* Byte 7 */
SD0_CardInfo->CSD.DeviceSize = (uint16_t)(CSD_Tab[8]) *256;
/* Byte 8 */
SD0_CardInfo->CSD.DeviceSize += CSD_Tab[9] ;
}
SD0_CardInfo->Capacity = SD0_CardInfo->CSD.DeviceSize * MSD_BLOCKSIZE * 1024;
SD0_CardInfo->BlockSize = MSD_BLOCKSIZE;
/* Byte 0 */
SD0_CardInfo->CID.ManufacturerID = CID_Tab[0];
/* Byte 1 */
SD0_CardInfo->CID.OEM_AppliID = CID_Tab[1] << 8;
/* Byte 2 */
SD0_CardInfo->CID.OEM_AppliID |= CID_Tab[2];
/* Byte 3 */
SD0_CardInfo->CID.ProdName1 = CID_Tab[3] << 24;
/* Byte 4 */
SD0_CardInfo->CID.ProdName1 |= CID_Tab[4] << 16;
/* Byte 5 */
SD0_CardInfo->CID.ProdName1 |= CID_Tab[5] << 8;
/* Byte 6 */
SD0_CardInfo->CID.ProdName1 |= CID_Tab[6];
/* Byte 7 */
SD0_CardInfo->CID.ProdName2 = CID_Tab[7];
/* Byte 8 */
SD0_CardInfo->CID.ProdRev = CID_Tab[8];
/* Byte 9 */
SD0_CardInfo->CID.ProdSN = CID_Tab[9] << 24;
/* Byte 10 */
SD0_CardInfo->CID.ProdSN |= CID_Tab[10] << 16;
/* Byte 11 */
SD0_CardInfo->CID.ProdSN |= CID_Tab[11] << 8;
/* Byte 12 */
SD0_CardInfo->CID.ProdSN |= CID_Tab[12];
/* Byte 13 */
SD0_CardInfo->CID.Reserved1 |= (CID_Tab[13] & 0xF0) >> 4;
/* Byte 14 */
SD0_CardInfo->CID.ManufactDate = (CID_Tab[13] & 0x0F) << 8;
/* Byte 15 */
SD0_CardInfo->CID.ManufactDate |= CID_Tab[14];
/* Byte 16 */
SD0_CardInfo->CID.CID_CRC = (CID_Tab[15] & 0xFE) >> 1;
SD0_CardInfo->CID.Reserved2 = 1;
return 0;
}
//写SD卡
//buf:数据缓存区
//sector:起始扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
uint8_t rst;
if(SD_TYPE!=V2HC) sector *= 512;//转换为字节地址
if(cnt==1)
{
rst=SD_sendcmd(CMD24,sector,0X01);//读命令
if(rst==0)//指令发送成功
{
rst=SD_SendBlock(buf,0xFE);//写512个字节
}
}
else
{
if(SD_TYPE!=MMC)
{
SD_sendcmd(CMD55,0,0X01);
SD_sendcmd(CMD23,cnt,0X01);//发送指令
}
rst=SD_sendcmd(CMD25,sector,0X01);//连续读命令
if(rst==0)
{
do
{
rst=SD_SendBlock(buf,0xFC);//接收512个字节
buf+=512;
}while(--cnt && rst==0);
rst=SD_SendBlock(0,0xFD);//接收512个字节
}
}
SD_CS_DEN;//取消片选
return rst;//
}
//读SD卡
//buf:数据缓存区
//sector:扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
uint8_t rst;
if(SD_TYPE!=V2HC)sector <<= 9;//转换为字节地址
if(cnt==1)
{
rst=SD_sendcmd(CMD17,sector,0X01);//读命令
if(rst==0)//指令发送成功
{
rst=SD_ReceiveData(buf,512);//接收512个字节
}
}
else
{
rst=SD_sendcmd(CMD18,sector,0X01);//连续读命令
do
{
rst=SD_ReceiveData(buf,512);//接收512个字节
buf+=512;
}while(--cnt && rst==0);
SD_sendcmd(CMD12,0,0X01); //发送停止命令
}
SD_CS_DEN;//取消片选
return rst;//
}
uint8_t spi_readwrite(uint8_t Txdata)
{
uint8_t rd = 0xa5;
uint8_t td = Txdata;
HAL_SPI_TransmitReceive(&hspi1, &td, &rd, 1 ,2700);
return rd;
}
//SPI1波特率设置
void SPI_setspeed(uint32_t speed)
{
hspi1.Init.BaudRatePrescaler = speed;
}
对ffconf.h添加包含信息:
#include "main.h"
#include "stm32f4xx_hal.h"
修改user_diskio.c,对文件操作函数与底层SD/MicroSD/TF卡读写提供连接:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file user_diskio.c
* @brief This file includes a diskio driver skeleton to be completed by the user.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
#ifdef USE_OBSOLETE_USER_CODE_SECTION_0
/*
* Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0)
* To be suppressed in the future.
* Kept to ensure backward compatibility with previous CubeMx versions when
* migrating projects.
* User code previously added there should be copied in the new user sections before
* the section contents can be deleted.
*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
#endif
/* USER CODE BEGIN DECL */
/**************************s*******************************/
#include "diskio.h" /* Declarations of disk functions */
#include "SDdriver.h"
/**************************e*******************************/
/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include "ff_gen_drv.h"
/**************************s*******************************/
#define SD_CS_Pin GPIO_PIN_4
#define SD_CS_GPIO_Port GPIOA
#define SD_CS_EN HAL_GPIO_WritePin(SD_CS_GPIO_Port,SD_CS_Pin,GPIO_PIN_RESET)
#define SD_CS_DEN HAL_GPIO_WritePin(SD_CS_GPIO_Port,SD_CS_Pin,GPIO_PIN_SET)
/**************************e*******************************/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Disk status */
static volatile DSTATUS Stat = STA_NOINIT;
/* USER CODE END DECL */
/* Private function prototypes -----------------------------------------------*/
DSTATUS USER_initialize (BYTE pdrv);
DSTATUS USER_status (BYTE pdrv);
DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
#if _USE_WRITE == 1
DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
#endif /* _USE_IOCTL == 1 */
Diskio_drvTypeDef USER_Driver =
{
USER_initialize,
USER_status,
USER_read,
#if _USE_WRITE
USER_write,
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
USER_ioctl,
#endif /* _USE_IOCTL == 1 */
};
/* Private functions ---------------------------------------------------------*/
/**
* @brief Initializes a Drive
* @param pdrv: Physical drive number (0..)
* @retval DSTATUS: Operation status
*/
DSTATUS USER_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
/* USER CODE BEGIN INIT */
/**************************s*******************************/
uint8_t res;
res = SD_init();//SD_Initialize()
if(res) return RES_OK;
else return STA_NOINIT;
/**************************e*******************************/
/*
Stat = STA_NOINIT;
return Stat;
*/
/* USER CODE END INIT */
}
/**
* @brief Gets Disk Status
* @param pdrv: Physical drive number (0..)
* @retval DSTATUS: Operation status
*/
DSTATUS USER_status (
BYTE pdrv /* Physical drive number to identify the drive */
)
{
/* USER CODE BEGIN STATUS */
/**************************s*******************************/
switch (pdrv)
{
case 0 :
return RES_OK;
case 1 :
return RES_OK;
case 2 :
return RES_OK;
default:
return STA_NOINIT;
}
/**************************e*******************************/
/*
Stat = STA_NOINIT;
return Stat;
*/
/* USER CODE END STATUS */
}
/**
* @brief Reads Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data buffer to store read data
* @param sector: Sector address (LBA)
* @param count: Number of sectors to read (1..128)
* @retval DRESULT: Operation result
*/
DRESULT USER_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to read */
)
{
/* USER CODE BEGIN READ */
/**************************s*******************************/
uint8_t res;
if( !count )
{
return RES_PARERR; /* count不能等于0,否则返回参数错误 */
}
switch (pdrv)
{
case 0:
res=SD_ReadDisk(buff,sector,count);
if(res == 0){
return RES_OK;
}else{
return RES_ERROR;
}
default:
return RES_ERROR;
}
/**************************e*******************************/
/*
return RES_OK;
*/
/* USER CODE END READ */
}
/**
* @brief Writes Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data to be written
* @param sector: Sector address (LBA)
* @param count: Number of sectors to write (1..128)
* @retval DRESULT: Operation result
*/
#if _USE_WRITE == 1
DRESULT USER_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to write */
)
{
/* USER CODE BEGIN WRITE */
/* USER CODE HERE */
/**************************s*******************************/
uint8_t res;
if( !count )
{
return RES_PARERR; /* count不能等于0,否则返回参数错误 */
}
switch (pdrv)
{
case 0:
res=SD_WriteDisk((uint8_t *)buff,sector,count);
if(res == 0){
return RES_OK;
}else{
return RES_ERROR;
}
default:return RES_ERROR;
}
/**************************e*******************************/
/*
return RES_OK;
*/
/* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */
/**
* @brief I/O control operation
* @param pdrv: Physical drive number (0..)
* @param cmd: Control code
* @param *buff: Buffer to send/receive control data
* @retval DRESULT: Operation result
*/
#if _USE_IOCTL == 1
DRESULT USER_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
/* USER CODE BEGIN IOCTL */
/**************************s*******************************/
DRESULT res;
switch(cmd)
{
case CTRL_SYNC:
SD_CS_EN;
do{
HAL_Delay(20);
}while(spi_readwrite(0xFF)!=0xFF);
res=RES_OK;
SD_CS_DEN;
break;
case GET_SECTOR_SIZE:
*(WORD*)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(WORD*)buff = 8;
res = RES_OK;
break;
case GET_SECTOR_COUNT:
*(DWORD*)buff = SD_GetSectorCount();
res = RES_OK;
break;
default:
res = RES_PARERR;
break;
}
return res;
/**************************e*******************************/
/*
DRESULT res = RES_ERROR;
return res;
*/
/* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */
注意这里设置了卡大小为4GB,可以根据实际情况修改:
代码实现在main.c文件里,实现如下功能:
- 串口收到0x01指令,初始化SD/MicroSD/TF卡
- 串口收到0x02指令,装载FATS文件操作系统
- 串口收到0x03指令,识别容量,此操作慢
- 串口收到0x04指令,创建/打开文件并从头位置写入数据
- 串口收到0x05指令,打开文件并从头位置读入数据
- 串口收到0x06指令,创建/打开文件并从特定位置写入数据
- 串口收到0x07指令,打开文件并从特定位置读入数据
- 串口收到0x08指令,读取CID信息
- 串口收到0x09指令,读取CSD信息
完整main.c代码如下:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fatfs.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "usart.h"
#include "SDdriver.h"
#include "string.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
__IO float usDelayBase;
void PY_usDelayTest(void)
{
__IO uint32_t firstms, secondms;
__IO uint32_t counter = 0;
firstms = HAL_GetTick()+1;
secondms = firstms+1;
while(uwTick!=firstms) ;
while(uwTick!=secondms) counter++;
usDelayBase = ((float)counter)/1000;
}
void PY_Delay_us_t(uint32_t Delay)
{
__IO uint32_t delayReg;
__IO uint32_t usNum = (uint32_t)(Delay*usDelayBase);
delayReg = 0;
while(delayReg!=usNum) delayReg++;
}
void PY_usDelayOptimize(void)
{
__IO uint32_t firstms, secondms;
__IO float coe = 1.0;
firstms = HAL_GetTick();
PY_Delay_us_t(1000000) ;
secondms = HAL_GetTick();
coe = ((float)1000)/(secondms-firstms);
usDelayBase = coe*usDelayBase;
}
void PY_Delay_us(uint32_t Delay)
{
__IO uint32_t delayReg;
__IO uint32_t msNum = Delay/1000;
__IO uint32_t usNum = (uint32_t)((Delay%1000)*usDelayBase);
if(msNum>0) HAL_Delay(msNum);
delayReg = 0;
while(delayReg!=usNum) delayReg++;
}
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi1;
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
uint8_t uart1_rx[16];
uint8_t cmd;
uint8_t SD_Status = 0; //SD initialization status (0: none; 1: OK)
uint8_t SD_mount_status = 0; //SD fats mount status indication (0: unmount; 1: mount)
uint8_t FATS_Buff[_MAX_SS]; //Buffer for f_mkfs() operation
FRESULT retSD;
FIL file;
FATFS *fs;
DWORD fre_clust, AvailableSize, UsedSize;
uint16_t TotalSpace;
UINT bytesread;
UINT byteswritten;
uint8_t rBuffer[20]; //Buffer for read
uint8_t WBuffer[20] ={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; //Buffer for write
extern char USERPath[4];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#define sector_byte_size 512
uint8_t sdbuffer[sector_byte_size];
uint8_t sdinfo[16];
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
SD_mount_status = 0;
uint32_t SD_Read_Size;
char * dpath = "0:"; //Disk Path
for(uint8_t i=0; i<4; i++)
{
USERPath[i] = *(dpath+i);
}
const TCHAR* filepath = "0:test.txt";
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI1_Init();
MX_USART1_UART_Init();
MX_FATFS_Init();
/* USER CODE BEGIN 2 */
PY_usDelayTest();
PY_usDelayOptimize();
HAL_UART_Receive_IT(&huart1, uart1_rx, 1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(cmd==1) //SD Init
{
cmd = 0;
SD_Status = SD_init();
if(SD_Status == 0) printf("\r\nSD initial failure\r\n");
else
{
printf("\r\nSD type number: %d\r\n", SD_Status);
printf("MMC: 1\r\n");
printf("V1: 2\r\n");
printf("V2: 4\r\n");
printf("V2HC: 6\r\n");
}
}
else if(cmd==2) //SD File System Mount
{
cmd = 0;
if(SD_Status == 0) printf("\r\nSD initial failure\r\n");
else
{
retSD=f_mount(&USERFatFS, (TCHAR const*)USERPath, 1);
if (retSD != FR_OK)
{
printf("File system mount failure: %d\r\n", retSD);
if(retSD==FR_NO_FILESYSTEM)
{
printf("No file system. Now to format......\r\n");
retSD = f_mkfs((TCHAR const*)USERPath, FM_FAT, 1024, FATS_Buff, sizeof(FATS_Buff)); //SD formatting
if(retSD == FR_OK)
{
printf("SD formatting success!\r\n");
}
else
{
printf("SD formatting failure!\r\n");
}
}
}
else
{
SD_mount_status = 1;
printf("File system mount success\r\n");
}
}
}
else if(cmd==3) //SD capacity recognition
{
cmd = 0;
if(SD_mount_status==0) printf("\r\nSD File system not mounted: %d\r\n",retSD);
else
{
printf("Executing capacity recognition...It's slow...Waiting for minutes...\r\n");
retSD = f_getfree((TCHAR const*)USERPath, &fre_clust, &fs); //root directory
if ( retSD == FR_OK )
{
TotalSpace=(uint16_t)(((fs->n_fatent - 2) * fs->csize ) / 2 /1024);
AvailableSize=(uint16_t)((fre_clust * fs->csize) / 2 /1024);
UsedSize=TotalSpace-AvailableSize;
//Print free space in unit of MB (assuming 512 bytes/sector)
printf("\r\n%d MB total drive space.\r\n""%d MB available.\r\n""%d MB used.\r\n",(int)TotalSpace, (int)AvailableSize, (int)UsedSize);
}
else
{
printf("Get SDCard Capacity Failed (%d)\r\n", retSD);
}
}
}
else if(cmd==4) //File creation and write
{
cmd = 0;
if(SD_mount_status==0) printf("\r\nSD File system not mounted: %d\r\n",retSD);
else
{
retSD = f_open( &file, filepath, FA_CREATE_ALWAYS | FA_WRITE ); //Open or create file
if(retSD == FR_OK)
{
printf("\r\nFile open or creation successful\r\n");
retSD = f_write( &file, (const void *)WBuffer, sizeof(WBuffer), &byteswritten); //Write data
if(retSD == FR_OK)
{
printf("\r\nFile write successful\r\n");
}
else
{
printf("\r\nFile write error: %d\r\n",retSD);
}
f_close(&file); //Close file
}
else
{
printf("\r\nFile open or creation error %d\r\n",retSD);
}
}
}
else if(cmd==5) //File read
{
cmd = 0;
if(SD_mount_status==0) printf("\r\nSD File system not mounted: %d\r\n",retSD);
else
{
retSD = f_open( &file, filepath, FA_OPEN_EXISTING | FA_READ); //Open file
if(retSD == FR_OK)
{
printf("\r\nFile open successful\r\n");
retSD = f_read( &file, (void *)rBuffer, sizeof(rBuffer), &bytesread); //Read data
if(retSD == FR_OK)
{
printf("\r\nFile read successful\r\n");
PY_Delay_us_t(200000);
SD_Read_Size = sizeof(rBuffer);
for(uint16_t i = 0;i < SD_Read_Size;i++)
{
printf("%d ", rBuffer[i]);
}
printf("\r\n");
}
else
{
printf("\r\nFile read error: %d\r\n", retSD);
}
f_close(&file); //Close file
}
else
{
printf("\r\nFile open error: %d\r\n", retSD);
}
}
}
else if(cmd==6) //File locating write
{
cmd = 0;
if(SD_mount_status==0) printf("\r\nSD File system not mounted: %d\r\n",retSD);
else
{
retSD = f_open( &file, filepath, FA_CREATE_ALWAYS | FA_WRITE); //Open or create file
if(retSD == FR_OK)
{
printf("\r\nFile open or creation successful\r\n");
retSD=f_lseek( &file, f_tell(&file) + sizeof(WBuffer) ); //move file operation pointer, f_tell(&file) gets file head locating
if(retSD == FR_OK)
{
retSD = f_write( &file, (const void *)WBuffer, sizeof(WBuffer), &byteswritten);
if(retSD == FR_OK)
{
printf("\r\nFile locating write successful\r\n");
}
else
{
printf("\r\nFile locating write error: %d\r\n", retSD);
}
}
else
{
printf("\r\nFile pointer error: %d\r\n",retSD);
}
f_close(&file); //Close file
}
else
{
printf("\r\nFile open or creation error %d\r\n",retSD);
}
}
}
else if(cmd==7) //File locating read
{
cmd = 0;
if(SD_mount_status==0) printf("\r\nSD File system not mounted: %d\r\n",retSD);
else
{
retSD = f_open(&file, filepath, FA_OPEN_EXISTING | FA_READ); //Open file
if(retSD == FR_OK)
{
printf("\r\nFile open successful\r\n");
retSD = f_lseek(&file,f_tell(&file)+ sizeof(WBuffer)/2); //move file operation pointer, f_tell(&file) gets file head locating
if(retSD == FR_OK)
{
retSD = f_read( &file, (void *)rBuffer, sizeof(rBuffer), &bytesread);
if(retSD == FR_OK)
{
printf("\r\nFile locating read successful\r\n");
PY_Delay_us_t(200000);
SD_Read_Size = sizeof(rBuffer);
for(uint16_t i = 0;i < SD_Read_Size;i++)
{
printf("%d ",rBuffer[i]);
}
printf("\r\n");
}
else
{
printf("\r\nFile locating read error: %d\r\n",retSD);
}
}
else
{
printf("\r\nFile pointer error: %d\r\n",retSD);
}
f_close(&file);
}
else
{
printf("\r\nFile open error: %d\r\n",retSD);
}
}
}
else if(cmd==8) //Get CID
{
cmd = 0;
if(SD_Status == 0) printf("\r\nSD initial failure\r\n");
else
{
if(SD_GETCID((uint8_t *)sdinfo)==0)
{
printf("CID: ");
for(uint32_t i=0; i<16; i++)
{
printf("%.2x ", sdinfo[i]);
}
printf("\r\n");
}
}
}
else if(cmd==9) //Get CSD
{
cmd = 0;
if(SD_Status == 0) printf("\r\nSD initial failure\r\n");
else
{
if(SD_GETCSD((uint8_t *)sdinfo)==0)
{
printf("CSD: ");
for(uint32_t i=0; i<16; i++)
{
printf("%.2x ", sdinfo[i]);
}
printf("\r\n");
}
}
}
else;
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
RCC_OscInitStruct.PLL.PLLQ = 7;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @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_SOFT;
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();
}
/* USER CODE BEGIN SPI1_Init 2 */
/* USER CODE END SPI1_Init 2 */
}
/**
* @brief USART1 Initialization Function
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
/*Configure GPIO pin : SPI1_CS_Pin */
GPIO_InitStruct.Pin = SPI1_CS_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(SPI1_CS_GPIO_Port, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart==&huart1)
{
cmd = uart1_rx[0];
HAL_UART_Receive_IT(&huart1, uart1_rx, 1);
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
STM32例程测试
串口指令0x01测试效果如下:
串口指令0x02测试效果如下:
串口指令0x03测试效果如下:
串口指令0x04测试效果如下:
串口指令0x05测试效果如下:
串口指令0x06测试效果如下:
串口指令0x07测试效果如下:
串口指令0x08测试效果如下:
串口指令0x09测试效果如下:
STM32例程下载
STM32F401CCU6 SPI总线FATS读写SD/MicroSD/TF卡例程下载文章来源:https://www.toymoban.com/news/detail-857822.html
–End–文章来源地址https://www.toymoban.com/news/detail-857822.html
到了这里,关于STM32存储左右互搏 SPI总线FATS文件读写SD/MicroSD/TF卡的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!