文章目录
最近移植了大佬的SD卡驱动,这里做下记录
1、spi驱动移植
需求来源:
- 需要将数据发送出来,就是在掉电后还能对数据进行保存:
- 使用eeprom的方案,但是这个东西他本身数据量就很小
- 使用sd卡的方案,我感觉是不错的,或者我还想过使用串行flah的方案,但是flah不是掉电还能保存的
本文用到的实验平台:
- 野火MINI-stm32开发板
- STM32CUBE-IDE开发工具
所以这里优先考虑sd卡的方案,野火开发板上的sd卡接口采用的是spi的驱动方式:
可以看到除了一个CS的片选引脚,其他就是spi的引脚了,这里我们只需要额外设置片选引脚即可
关于上面说到的驱动方式,野火的教程也有说明,描述如下所示:
根据原理图上对应的引脚使能spi
在中间件中开启fatfs的功能,参数设置为默认即可
对片选信号的编辑如下所示
下面来添加驱动程序
添加的驱动程序如下所示:
SDdriver.c
#include "SDdriver.h"
#include "ff.h"
uint8_t DFF = 0xFF;
uint8_t test;
uint8_t SD_TYPE = 0x00;
MSD_CARDINFO SD0_CardInfo;
extern SPI_HandleTypeDef hspi1;
void SD_CS(uint8_t p) {
if (p == 0) {
HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_RESET);
}
}
//发送命令,发送完成释放
int SD_sendcmd(uint8_t cmd, uint32_t arg, uint8_t crc) {
uint8_t r1;
uint8_t retry;
SD_CS(0);
HAL_Delay(20);
SD_CS(1);
do {
retry = spi_readwrite(DFF);
} while (retry != 0xFF);
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(DFF);
do {
r1 = spi_readwrite(0xFF);
} while (r1 & 0X80);
return r1;
}
//sd卡初始化
uint8_t SD_init(void) {
uint8_t r1;
uint8_t buff[6] = {
0 };
uint16_t retry;
uint8_t i;
SPI_setspeed(SPI_BAUDRATEPRESCALER_256); //这里是内存卡的特点,一开始的时候不能给太高的速度,降下来
SD_CS(0);
for (retry = 0; retry < 10; retry++) {
spi_readwrite(DFF);
}
//sd卡进入IDLE的状态
do {
r1 = SD_sendcmd(CMD0, 0, 0x95);
} while (r1 != 0x01);
//查看SD卡类型
SD_TYPE = 0;
r1 = SD_sendcmd(CMD8, 0x1AA, 0x87);
if (r1 == 0x01) {
for (i = 0; i < 4; i++)
buff[i] = spi_readwrite(DFF);
if (buff[2] == 0X01 && buff[3] == 0XAA)
{
retry = 0XFFFE;
do {
SD_sendcmd(CMD55, 0, 0X01);
r1 = SD_sendcmd(CMD41, 0x40000000, 0X01);
} while (r1 && retry--);
if (retry && SD_sendcmd(CMD58, 0, 0X01) == 0)
{
for (i = 0; i < 4; i++)
buff[i] = spi_readwrite(0XFF);
if (buff[0] & 0x40) {
SD_TYPE = V2HC;
} else {
SD_TYPE = V2;
}
}
} else {
SD_sendcmd(CMD55, 0, 0X01);
r1 = SD_sendcmd(CMD41, 0, 0X01);
if (r1 <= 1) {
SD_TYPE = V1;
retry = 0XFFFE;
do
{
SD_sendcmd(CMD55, 0, 0X01);
r1 = SD_sendcmd(CMD41, 0, 0X01);
} while (r1 && retry--);
} else
{
SD_TYPE = MMC;
retry = 0XFFFE;
do
{
r1 = SD_sendcmd(CMD1, 0, 0X01);
} while (r1 && retry--);
}
if (retry == 0 || SD_sendcmd(CMD16, 512, 0X01) != 0)
SD_TYPE = ERR;
}
}
SD_CS(0);
SPI_setspeed(SPI_BAUDRATEPRESCALER_2);
if (SD_TYPE)
return 0;
else
return 1;
}
//读取指定数据长度
uint8_t SD_ReceiveData(uint8_t *data, uint16_t len) {
uint8_t r1;
SD_CS(1);
do {
r1 = spi_readwrite(0xFF);
HAL_Delay(100);
} while (r1 != 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 r1;
do {
r1 = spi_readwrite(0xFF);
} while (r1 != 0xFF);
spi_readwrite(cmd);
if (cmd != 0XFD)
{
for (t = 0; t < 512; t++)
spi_readwrite(buf[t]);
spi_readwrite(0xFF);
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 r1;
r1 = SD_sendcmd(CMD10, 0, 0x01);
if (r1 == 0x00) {
r1 = SD_ReceiveData(cid_data, 16);
}
SD_CS(0);
if (r1)
return 1;
else
return 0;
}
//获取CSD信息
uint8_t SD_GETCSD(uint8_t *csd_data) {
uint8_t r1;
r1 = SD_sendcmd(CMD9, 0, 0x01);
if (r1 == 0) {
r1 = SD_ReceiveData(csd_data, 16);
}
SD_CS(0);
if (r1)
return 1;
else
return 0;
}
//获取SD卡的总扇区数
uint32_t SD_GetSectorCount(void) {
uint8_t csd[16];
uint32_t Capacity;
uint8_t n;
uint16_t csize;
if (SD_GETCSD(csd) != 0)
return 0;
if ((csd[0] & 0xC0) == 0x40)
{
csize = csd[9] + ((uint16_t) csd[8] << 8) + 1;
Capacity = (uint32_t) csize << 10;
} else
{
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 r1;
uint8_t CSD_Tab[16];
uint8_t CID_Tab[16];
r1 = SD_sendcmd(CMD9, 0, 0xFF);
if (r1 != 0x00) {
return r1;
}
if (SD_ReceiveData(CSD_Tab, 16)) {
return 1;
}
r1 = SD_sendcmd(CMD10, 0, 0xFF);
if (r1 != 0x00) {
return r1;
}
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卡
* 参数1 数据缓存区
* 参数2 起始扇区
* 参数3 扇区数
*
* 返回值 0成功 其他失败
*******************************/
uint8_t SD_WriteDisk(uint8_t *buf, uint32_t sector, uint8_t cnt) {
uint8_t r1;
if (SD_TYPE != V2HC)
sector *= 512;
if (cnt == 1) {
r1 = SD_sendcmd(CMD24, sector, 0X01);
if (r1 == 0)
{
r1 = SD_SendBlock(buf, 0xFE);
}
} else {
if (SD_TYPE != MMC) {
SD_sendcmd(CMD55, 0, 0X01);
SD_sendcmd(CMD23, cnt, 0X01);
}
r1 = SD_sendcmd(CMD25, sector, 0X01);
if (r1 == 0) {
do {
r1 = SD_SendBlock(buf, 0xFC);
buf += 512;
} while (--cnt && r1 == 0);
r1 = SD_SendBlock(0, 0xFD);
}
}
SD_CS(0);
return r1;
}
/*******************************
* 函数名 读SD卡
* 参数1 数据缓存区
* 参数2 起始扇区
* 参数3 扇区数
*
* 返回值 0成功 其他失败
*******************************/
uint8_t SD_ReadDisk(uint8_t *buf, uint32_t sector, uint8_t cnt) {
uint8_t r1;
if (SD_TYPE != V2HC)
sector <<= 9;
if (cnt == 1) {
r1 = SD_sendcmd(CMD17, sector, 0X01);
if (r1 == 0)
{
r1 = SD_ReceiveData(buf, 512);
}
} else {
r1 = SD_sendcmd(CMD18, sector, 0X01);
do {
r1 = SD_ReceiveData(buf, 512);
buf += 512;
} while (--cnt && r1 == 0);
SD_sendcmd(CMD12, 0, 0X01);
}
SD_CS(0);
return r1;
}
uint8_t spi_readwrite(uint8_t Txdata) {
//spi读数据
uint8_t Rxdata;
HAL_SPI_TransmitReceive(&hspi1, &Txdata, &Rxdata, 1, 100);
return Rxdata;
}
void SPI_setspeed(uint8_t speed) {
//spi写数据
hspi1.Init.BaudRatePrescaler = speed;
}
SDdriver.h
#ifndef __SDDRIVER_H
#define __SDDRIVER_H
#include "main.h"
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;
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(uint8_t speed);
uint8_t spi_readwrite(uint8_t Txdata);
#endif
之后需要我们去绑定spi的驱动和文件系统
里面都预留了接口,需要我们来编写,完整代码如下所示:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file user_diskio.c
* @brief This file includes a diskio driver skeleton to be completed by the user.
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2022 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* 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 */
/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include "ff_gen_drv.h"
#include "../../BSP/SDdriver.h"
/* 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 */
uint8_t res;
res = SD_init(); //sd卡初始化
if (res)
{
SPI_setspeed(SPI_BAUDRATEPRESCALER_256);
spi_readwrite(0xff);
SPI_setspeed(SPI_BAUDRATEPRESCALER_2);
}
if (res)
return STA_NOINIT;
else
return RES_OK;
/* 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 */
switch (pdrv) {
case 0:
return RES_OK;
case 1:
return RES_OK;
case 2:
return RES_OK;
default:
return STA_NOINIT;
}
/* 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 */
uint8_t res;
if (!count) {
return RES_PARERR;
}
switch (pdrv) {
case 0:
res = SD_ReadDisk(buff, sector, count);
if (res == 0) {
return RES_OK;
} else {
return RES_ERROR;
}
default:
return RES_ERROR;
}
/* 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 */
uint8_t res;
if (!count) {
return RES_PARERR;
}
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;
}
/* 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 */
DRESULT res;
switch (cmd) {
case CTRL_SYNC:
SD_CS(1);
do {
HAL_Delay(20);
} while (spi_readwrite(0xFF) != 0xFF);
res = RES_OK;
SD_CS(0);
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;
/* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
之后就可以在主函数中测试读写了:
定义读写缓冲区:
初始化结束后进行读写测试
将程序下载到开发板,可以看到效果如下,成功读入和写入
拔出sd卡,并接上电脑,也可以看到正确的数据
最后一点就是关于这里堆栈的问题,我这里还没有扩大,这个测试是正常的,如果做大量读取的话建议还是设置的大一点
2、sdio驱动移植
上面讲到读写SD卡是有两种方式的,我寻思上面都看了这个spi的方式,不如干脆就把这个sdio的方式也来弄下,这样也方便以后使用
先说下我个人的感觉,SDIO确实是官方的方法,非常方便好用,自己移植的话代码量很小,用起来也确实就很方便,但是好像他在单片机里面的代码量很大,比较吃内存。
下面我统计了两种方式下移植的代码量大小
- 使用SPI驱动
- 使用SDIO驱动
通过上面两张图可以看到区别还是很大的,所以建议具体使用那种方式还是根据实际情况考虑!!!
下面就是正式开始移植程序了,因为野火mini开发板没有对应的SDIO的接口,所以这里平台我用的是正点原子精英开发板
本文用到的实验平台:
- 正点原子精英开发板
- STM32CUBE-IDE开发工具
SD卡接口如下所示
在CUBEMX中使能SDIO借口(从这些外设接口所在的引脚也能知道,就是一些引脚比较少的ST的芯片应该就没有这个SDIO的外设了)
然后这里关注下这个部分,这个部分就决定了我们SD卡读写的速度,这里可以认为是一个分频系数,他计算的方法在下面的部分也已经给出了(这里给一个参考值,一般读写速度不会大于24mhz,然后有的卡比较垃圾,所以速度设置的越慢越好)
之后是FATFS的中间件配置,这里我们勾选SD卡还有用户定义(用户定义这里可选可不选,因为没用到就是)
开启SDIO的中断
之后就可以生成代码了,保险起见我们也可以适当调大栈的大小
下面是关于这个硬件部分,因为这个开发板他没有做这个接口,所以是需要我们用这个打卡对小卡的卡套的
然后关于这个卡套有一个特别神奇的地方,就是这个LOCK这里,可以看到这个是可以上下滑动的,就是你按照lock的方向滑动的话,他就锁住了,这个时候里面的文件是只读的,这个不是我们需要的,有不知道的可以注意下这里:
下面是对只读模式下进行格式化时候windows的报错信息!
之后我们就可以编辑代码了,就是跟上面的SPI的过程差不多,挂载文件系统,读写操作然后注销,下面我对代码也进行了相关注释:
源代码如下:
printf("SD卡测试\r\n");
if (FATFS_LinkDriver(&SD_Driver, SDPath) == 0) //注册一个设备
{
f_res = f_mount(&fs, (TCHAR const*) SDPath, 1); //挂载文件系统
printf_fatfs_error(f_res);
if(f_res == FR_NO_FILESYSTEM) //没有挂载文件系统
{
f_res = f_mkfs((TCHAR const*) SDPath, 0, 0); //格式化
if(f_res == FR_OK)
{
//格式化后先取消在重新挂载
f_res = f_mount(NULL, (TCHAR const*) SDPath, 1);
f_res = f_mount(&fs, (TCHAR const*) SDPath, 1);
}
}
}
f_res = f_open(&file, "test.txt",FA_CREATE_ALWAYS | FA_WRITE); // 打开文件
f_write(&file, WriteBuffer, sizeof(WriteBuffer), &fnum);// 写入内容
f_close(&file);// 写结束,关闭文件
printf("写入内容为%s\r\n",WriteBuffer);
f_res = f_open(&file, "test.txt",FA_OPEN_EXISTING | FA_READ); //打开文件
f_read(&file, ReadBuffer, sizeof(ReadBuffer), &fnum); // 读取内容
f_close(&file); // 读结束,关闭文件
printf("读取内容为%s\r\n",ReadBuffer);
f_mount(NULL, (TCHAR const*) SDPath, 1); // 取消挂载
FATFS_UnLinkDriver(SDPath); // 注销设备
然后一些变量参数,还有不同返回值下对应的报错信息,有了这些信息可以帮助我们判断到底是出了什么问题!
源代码如下
#define printf printf
void printf_fatfs_error(FRESULT fresult)
{
switch(fresult)
{
case FR_OK:
printf("!!The operation was successful.\r\n");
break;
case FR_DISK_ERR:
printf("!!Hardware I / O driver error.\r\n");
break;
case FR_INT_ERR:
printf("!!Assertion error.\r\n");
break;
case FR_NOT_READY:
printf("!!The physical device is not working.\r\n");
break;
case FR_NO_FILE:
printf("!!Unable to find file.\r\n");
break;
case FR_NO_PATH:
printf("!!The path could not be found.\r\n");
break;
case FR_INVALID_NAME:
printf("!!Invalid pathname.\r\n");
break;
case FR_DENIED:
case FR_EXIST:
printf("!!Access denied.\r\n");
break;
case FR_INVALID_OBJECT:
printf("!!Invalid file or path.\r\n");
break;
case FR_WRITE_PROTECTED:
printf("!!Logical device write protection.\r\n");
break;
case FR_INVALID_DRIVE:
printf("!!Invalid logical device.\r\n");
break;
case FR_NOT_ENABLED:
printf("!!Invalid workspace.\r\n");
break;
case FR_NO_FILESYSTEM:
printf("!!Invalid file system.\r\n");
break;
case FR_MKFS_ABORTED:
printf("!!Fmkfs function operation failed due to function parameter problem.\r\n");
break;
case FR_TIMEOUT:
printf("!!The operation timed out.\r\n");
break;
case FR_LOCKED:
printf("!!The file is protected.\r\n");
break;
case FR_NOT_ENOUGH_CORE:
printf("!!Long file name support failed to get heap space.\r\n");
break;
case FR_TOO_MANY_OPEN_FILES:
printf("!!Too many files open.\r\n");
break;
case FR_INVALID_PARAMETER:
printf("!!Invalid parameter.\r\n");
break;
}
}
将程序下载到开发板,效果如下:
拔掉SD卡,插入电脑可以看到我们刚刚写的文件
文件信息完全符合!