分享一下通过SPI方式读写SD卡的驱动源码,只需要修改SPI读写函数以及片选口就可以投入使用,可自行搭配FATFS文件系统。
SD.c
- #include "SD.h"
- #include "Basic_cfg.h"
- u8 SD_Type = 0;
- /***************************/
- //SD读写函数,便于替换SPI接口
- /***************************/
- u8 SD_ReadWriteByte(u8 TxData)
- {
- SPI3_ReadWriteByte(TxData);
- }
- /***************************/
- //忙检测,等待SD卡
- /***************************/
- u8 SD_WaitReady(void)
- {
- u32 t=0;
- u8 reg;
- for(t=0; t<0xffff; t++)
- {
- reg=SD_ReadWriteByte(0XFF);//获取返回值
- if(reg==0XFF)
- break;
- }
- if(t<0xffffff)
- return 0;
- else
- return 1;
- }
- /***************************/
- //取消卡选择
- /***************************/
- void SD_DisSelect(void)
- {
- SD_CS=1;
- SD_ReadWriteByte(0xff);//提供额外的8个时钟
- }
- /***************************/
- //卡选择,选择SD卡
- /***************************/
- u8 SD_Select(void)
- {
- SD_CS=0;
- if(SD_WaitReady()==0) //等待成功
- return 0;
- SD_DisSelect(); //等待失败
- return 1;
- }
- /***************************/
- //向SD卡发送一个命令
- //CMD:命令
- //arg:命令参数
- //crc:校验
- /***************************/
- u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc)
- {
- u8 r1=0;
- u8 Retry=0;
- SD_DisSelect(); //取消上次片选
- if(SD_Select()) //片选失效
- {
- return 0XFF;
- }
- //发送
- //分别写入命令
- SD_ReadWriteByte(cmd | 0x40);
- SD_ReadWriteByte(arg >> 24);
- SD_ReadWriteByte(arg >> 16);
- SD_ReadWriteByte(arg >> 8);
- SD_ReadWriteByte(arg);
- SD_ReadWriteByte(crc);
- if(cmd==CMD12)SD_ReadWriteByte(0xff);
- //等待响应,或超时退出
- Retry=0X1F;
- do
- {
- r1=SD_ReadWriteByte(0xFF);
- }
- while((r1&0X80) && Retry--);
- //返回状态值
- return r1;
- }
- /***************************/
- //等待SD卡回应
- //Response:要得到的回应值
- //返回值:0,成功得到了该回应值
- // 其他,得到回应值失败
- /***************************/
- u8 SD_GetResponse(u8 Response)
- {
- //等待次数
- u16 Count=0xFFFF;
- //等待得到准确的回应
- while ((SD_ReadWriteByte(0XFF)!=Response)&&Count)
- {
- Count--;
- }
- if (Count==0)
- {
- //得到回应失败
- return MSD_RESPONSE_FAILURE;
- }
- else
- {
- //正确回应
- return MSD_RESPONSE_NO_ERROR;
- }
- }
- //初始化SD卡
- u8 SD_Init(void)
- {
- u8 r1=0; // 存放SD卡的返回值
- u16 retry; // 用来进行超时计数
- u8 buf[4];
- u16 i;
- SPI3_Init(); //初始化SPI3
- SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_256); //配置为低速度模式
- for(i=0; i<10; i++) //发送至少74个脉冲
- {
- SD_ReadWriteByte(0xff);
- }
- retry=20;
- do
- {
- //进入IDLE状态
- r1=SD_SendCmd(CMD0,0,0x95);
- }
- while((r1!=0X01) && (retry--));
- //默认无卡
- SD_Type=0;
- //识别卡类型
- if(r1==0X01)
- {
- //SD V2.0
- if(SD_SendCmd(CMD8,0x1AA,0x87)==1)
- {
- //Get trailing return value of R7 resp
- for(i=0; i<4; i++)
- buf[i]=SD_ReadWriteByte(0XFF);
- //卡是否支持2.7~3.6V
- if(buf[2]==0X01&&buf[3]==0XAA)
- {
- retry=0XFFFE;
- do
- {
- //发送CMD55
- SD_SendCmd(CMD55,0,0X01);
- //发送CMD41
- r1=SD_SendCmd(CMD41,0x40000000,0X01);
- }
- while(r1&&retry--);
- //鉴别SD2.0卡版本开始
- if(retry&&SD_SendCmd(CMD58,0,0X01)==0)
- {
- //得到OCR值
- for(i=0; i<4; i++)
- buf[i]=SD_ReadWriteByte(0XFF);
- //检查CCS
- if(buf[0]&0x40)
- {
- SD_Type=SD_TYPE_V2HC;
- }
- else
- {
- SD_Type=SD_TYPE_V2;
- }
- }
- }
- }
- }
- //SD V1.x/ MMC V3
- else
- {
- //发送CMD55
- SD_SendCmd(CMD55,0,0X01);
- //发送CMD41
- r1=SD_SendCmd(CMD41,0,0X01);
- if(r1<=1)
- {
- SD_Type=SD_TYPE_V1;
- retry=0XFFFE;
- //等待退出IDLE模式
- do
- {
- //发送CMD55
- SD_SendCmd(CMD55,0,0X01);
- //发送CMD41
- r1=SD_SendCmd(CMD41,0,0X01);
- } while(r1&&retry--);
- }
- //MMC卡不支持CMD55+CMD41识别
- else
- {
- //MMC V3
- SD_Type=SD_TYPE_MMC;
- retry=0XFFFE;
- //等待退出IDLE模式
- do
- {
- //发送CMD1
- r1=SD_SendCmd(CMD1,0,0X01);
- } while(r1&&retry--);
- }
- //错误的卡
- if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)
- {
- SD_Type=SD_TYPE_ERR;
- }
- }
- //取消片选
- SD_DisSelect();
- //配置为高速度模式
- SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);
- if(SD_Type)
- {
- return 0;
- }
- else if(r1)
- {
- return r1;
- }
- //其他错误
- return 0xaa;
- }
- /***************************/
- //从sd卡读取一个数据包的内容
- //buf:数据缓存区
- //len:要读取的数据长度.
- //返回值:0,成功;其他,失败;
- /***************************/
- u8 SD_RecvData(u8*buf,u16 len)
- {
- //等待SD卡发回数据起始令牌0xFE
- if(SD_GetResponse(0xFE))
- {
- return 1;
- }
- //开始接收数据
- while(len--)
- {
- *buf=SD_ReadWriteByte(0xFF);
- buf++;
- }
- //下面是2个伪CRC(dummy CRC)
- SD_ReadWriteByte(0xFF);
- SD_ReadWriteByte(0xFF);
- //读取成功
- return 0;
- }
- /***************************/
- //获取SD卡的CSD信息,包括容量和速度信息
- //输入:u8 *cid_data(存放CID的内存,至少16Byte)
- //返回值:0:NO_ERR
- // 1:错误
- /***************************/
- u8 SD_GetCSD(u8 *csd_data)
- {
- u8 r1;
- //发CMD9命令,读CSD
- r1=SD_SendCmd(CMD9,0,0x01);
- if(r1==0)
- {
- //接收16个字节的数据
- r1=SD_RecvData(csd_data, 16);
- }
- //取消片选
- SD_DisSelect();
- if(r1)
- {
- return 1;
- }
- else
- {
- return 0;
- }
- }
- /***************************/
- //获取SD卡的总扇区数(扇区数)
- //返回值:0: 取容量出错
- // 其他:SD卡的容量(扇区数/512字节)
- //每扇区的字节数必为512,因为如果不是512,则初始化不能通过.
- /***************************/
- u32 SD_GetSectorCount(void)
- {
- u8 csd[16];
- u32 Capacity;
- u8 n;
- u16 csize;
- //取CSD信息,如果期间出错,返回0
- if(SD_GetCSD(csd)!=0)
- {
- return 0;
- }
- //如果为SDHC卡,按照下面方式计算
- //V2.00的卡
- if((csd[0]&0xC0)==0x40)
- {
- csize = csd[9] + ((u16)csd[8] << 8) + 1;
- //得到扇区数
- Capacity = (u32)csize << 10;
- }
- //V1.XX的卡
- else
- {
- n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
- csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
- //得到扇区数
- Capacity= (u32)csize << (n - 9);
- }
- return Capacity;
- }
- u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt)
- {
- u8 r1;
- //转换为字节地址
- if(SD_Type!=SD_TYPE_V2HC)
- {
- sector <<= 9;
- }
- if(cnt==1)
- {
- //读命令
- r1=SD_SendCmd(CMD17,sector,0X01);
- //指令发送成功
- if(r1==0)
- {
- //接收512个字节
- r1=SD_RecvData(buf,512);
- }
- }
- else
- {
- //连续读命令
- r1=SD_SendCmd(CMD18,sector,0X01);
- do
- {
- //接收512个字节
- r1=SD_RecvData(buf,512);
- buf+=512;
- }
- while(--cnt && r1==0);
- //发送停止命令
- SD_SendCmd(CMD12,0,0X01);
- }
- //取消片选
- SD_DisSelect();
- return r1;
- }
- /***************************/
- //读取SD卡的指定扇区的内容,并通过串口1输出
- //sec:扇区物理地址编号
- /***************************/
- void SD_Read_Sectorx(u32 sec)
- {
- //存储扇区数据
- u8 buf[512];
- u16 i;
-
- //读取0扇区的内容
- if(SD_ReadDisk(buf,sec,1)==0)
- {
- printf("SECTOR 0 DATA:\r\n");
- //打印sec扇区数据
- for(i=0;i<512;i++)
- printf("%X ",buf[i]);
- printf("\r\nDATA ENDED\r\n");
- }
- }
- /***************************/
- //获取SD卡的CID信息,包括制造商信息
- //输入: u8 *cid_data(存放CID的内存,至少16Byte)
- //返回值:0:NO_ERR
- // 1:错误
- /***************************/
- u8 SD_GetCID(u8 *cid_data)
- {
- u8 r1;
- //发CMD10命令,读CID
- r1=SD_SendCmd(CMD10,0,0x01);
- if(r1==0x00)
- {
- //接收16个字节的数据
- r1=SD_RecvData(cid_data,16);
- }
- //取消片选
- SD_DisSelect();
- if(r1)
- return 1;
- else
- return 0;
- }
- /***************************/
- //向sd卡写入一个数据包的内容 512字节
- //buf:数据缓存区
- //cmd:指令
- //返回值:0,成功;其他,失败;
- /***************************/
- u8 SD_SendBlock(u8*buf,u8 cmd)
- {
- u16 t;
- //等待准备失效
- if(SD_WaitReady())
- {
- return 1;
- }
- SD_ReadWriteByte(cmd);
- //不是结束指令
- if(cmd!=0XFD)
- {
- //提高速度,减少函数传参时间
- for(t=0;t<512;t++)
- {
- SD_ReadWriteByte(buf[t]);
- }
- //忽略crc
- SD_ReadWriteByte(0xFF);
- SD_ReadWriteByte(0xFF);
- //接收响应
- t=SD_ReadWriteByte(0xFF);
- if((t&0x1F)!=0x05)
- {
- //响应错误
- return 2;
- }
- }
- //写入成功
- return 0;
- }
- /***************************/
- //写SD卡
- //buf:数据缓存区
- //sector:起始扇区
- //cnt:扇区数
- //返回值:0,ok;其他,失败.
- /***************************/
- u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt)
- {
- u8 r1;
- //转换为字节地址
- if(SD_Type!=SD_TYPE_V2HC)
- {
- sector *= 512;
- }
- if(cnt==1)
- {
- //读命令
- r1=SD_SendCmd(CMD24,sector,0X01);
- //指令发送成功
- if(r1==0)
- {
- //写512个字节
- r1=SD_SendBlock(buf,0xFE);
- }
- }
- else
- {
- if(SD_Type!=SD_TYPE_MMC)
- {
- SD_SendCmd(CMD55,0,0X01);
- //发送指令
- SD_SendCmd(CMD23,cnt,0X01);
- }
- //连续读命令
- r1=SD_SendCmd(CMD25,sector,0X01);
- if(r1==0)
- {
- do
- {
- //接收512个字节
- r1=SD_SendBlock(buf,0xFC);
- buf+=512;
- }
- while(--cnt && r1==0);
- //接收512个字节
- r1=SD_SendBlock(0,0xFD);
- }
- }
- //取消片选
- SD_DisSelect();
- return r1;
- }
复制代码
sd.h
- #ifndef __SD_H
- #define __SD_H
- #include "sys.h"
- #define SD_CS PAout(15)
- // SD卡类型定义
- #define SD_TYPE_ERR 0X00
- #define SD_TYPE_MMC 0X01
- #define SD_TYPE_V1 0X02
- #define SD_TYPE_V2 0X04
- #define SD_TYPE_V2HC 0X06
- // SD卡指令表
- #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
- void SD_Read_Sectorx(u32 sec);
- u8 SD_Init(void);
- u8 SD_WaitReady(void);
- u8 SD_GetResponse(u8 Response);
- u32 SD_GetSectorCount(void);
- u8 SD_GetCID(u8 *cid_data);
- u8 SD_GetCSD(u8 *csd_data);
- u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt);
- u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt);
- #endif
复制代码
原理图: 无
仿真: 无
|