stc32g12k128 模拟SDIO四通道读取SD卡
本帖最后由 BFMIPBWVFP 于 2024-7-26 16:34 编辑废话不多说直接上源码。
本例用stc32g12k128单片机,24M主频时已经过Mbyte的平均速率,如用spi,只有用24M高高速spi的DMA模式才可以达到!本人找了网上好久没有找到相关的例程,所以自已码了一个,其中参考了前辈们关于sd的知识,代码纯手敲,给各位有须要的参考,如用得到拿去用,不喜勿喷!
核心代码共四个文件:
sdio.h设置接口等,如下。
--------------------------------------------------------分割线--------------------------------------------------#ifndef __SDIO_H
#define __SDIO_H
#include "..\sys\sys.h"
//#define MAIN_Fosc 24000000UL
//接口定义
sbit SD_D0=P7^0;
sbit SD_D1=P7^1;
sbit SD_D2=P7^2;
sbit SD_D3=P7^3;
sbit SD_da=P7^4;//读数据指示
sbit SD_EN=P7^5;//卡插入检测
sbit SD_CMD=P7^6;
sbit SD_SCK=P7^7;
#define SDin P7&0x0f//用于并行读写
#define SDout P7 //用于并行读写
#define CMDdelay 15//用于初始化命令时钟脉冲调整。使时钟速率在要求的100-400KHZ;
void SDioinit(void);
u8 SDread(void);
void SDwrite(u8 dat);
void SDsendcmd(u8 CMD);//低速速模式发命令
void SDHisendcmd(u8 CMD);//高速模式发命令
void watit_8CR();
u8 SDRScmd(void);
void SDRS_response(u8 dat);
unsigned char crc7_mmc(unsigned char *dat,int length);
#endif
--------------------------------------------------------分割线--------------------------------------------------
sdio.c用gpio模拟sdio时序,如下::::::::::::::::::::::::
/*
此源码用于用gpoi形成基本sdio时序。支持四通道读取,写入。
由于4通道写入的CRC16计算太费资源使得写入对比SPI模式没有优势,所以此例只
使用4通道读取功能,写入没有实现。
*/
#include "sdio.h"
u8 temp=0;
void sdiodelay(void) //用于延时调节命令的时钟,初始化时时钟要在100-400KHZ。
{
u8 us=CMDdelay;
while(--us);
}
void SDioinit(void)
{
SDout=0xff;
}
u8 SDread(void) //四通道读取1byte数据
{
SD_SCK=0;
SD_SCK=1;
temp=SDout&0x0f;
SD_SCK=0;
temp<<=4;
SD_SCK=1;
return(SDout&0x0f|temp);
}
void SDwrite(u8 dat) //四通道写入取1byte数据
{
SD_SCK=0;
SDout=SDout&0xf0|(dat>>4);
SD_SCK=1;
dat&=0x0f;
SD_SCK=0;
SDout=SDout&0xf0|dat;
SD_SCK=1;
}
void SDsendcmd(u8 CMD)//限速发送接收,用于初始化时使用
{
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
CMD<<=1;
}
void SDHisendcmd(u8 CMD) //全速发送命令
{
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
SD_SCK=1;
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
SD_SCK=1;
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
SD_SCK=1;
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
SD_SCK=1;
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
SD_SCK=1;
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
SD_SCK=1;
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
SD_SCK=1;
CMD<<=1;
SD_SCK=0;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
SD_SCK=1;
CMD<<=1;
}
void watit_8CR()//等待8个时钟周期。
{
u8 temp=0xff,CMD=0xff,i=8;
while(i--)
{
SD_SCK=1;
(CMD&0x80)?SD_CMD=1:SD_CMD=0;
temp<<=1;
sdiodelay();
SD_SCK=0;
sdiodelay();
temp|=SD_CMD;
CMD<<=1;
}
}
u8 SDRScmd(void)//接收回复,用于初始化时和设置卡。
{
u8 temp;
SD_SCK=0;
sdiodelay();
temp<<=1;
SD_SCK=1;
sdiodelay();
temp|=SD_CMD;
SD_SCK=0;
sdiodelay();
temp<<=1;
SD_SCK=1;
sdiodelay();
temp|=SD_CMD;
SD_SCK=0;
sdiodelay();
temp<<=1;
SD_SCK=1;
sdiodelay();
temp|=SD_CMD;
SD_SCK=0;
sdiodelay();
temp<<=1;
SD_SCK=1;
sdiodelay();
temp|=SD_CMD;
SD_SCK=0;
sdiodelay();
temp<<=1;
SD_SCK=1;
sdiodelay();
temp|=SD_CMD;
SD_SCK=0;
sdiodelay();
temp<<=1;
SD_SCK=1;
sdiodelay();
temp|=SD_CMD;
SD_SCK=0;
sdiodelay();
temp<<=1;
SD_SCK=1;
sdiodelay();
temp|=SD_CMD;
SD_SCK=0;
sdiodelay();
temp<<=1;
SD_SCK=1;
sdiodelay();
temp|=SD_CMD;
return temp;
}
void SDRS_response(u8 dat) //接收回复
{u8 i=500,j=8;
while(i--)
{
SD_SCK=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
if(SD_CMD==0)//返回开始
{
dat<<=1;
dat|=SD_CMD;
SD_SCK=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
dat<<=1;
dat|=SD_CMD;
SD_SCK=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
dat<<=1;
dat|=SD_CMD;
SD_SCK=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
dat<<=1;
dat|=SD_CMD;
SD_SCK=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
dat<<=1;
dat|=SD_CMD;
SD_SCK=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
dat<<=1;
dat|=SD_CMD;
SD_SCK=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
dat<<=1;
dat|=SD_CMD;
SD_SCK=0;
sdiodelay();
SD_SCK=1;
sdiodelay();
dat<<=1;
dat|=SD_CMD;
for(j=1;j<17;j++)dat=SDRScmd();
break;
}
}
}
unsigned char crc7_mmc(unsigned char *dat,int length) //用于计算发送命令时的CRC7
{
u8 i;
u8 crc = 0; // Initial value
while (length--)
{
crc ^= *dat++; // crc ^= *data; data++;
for (i = 0; i < 8; i++)
{
if (crc & 0x80)
crc = (crc << 1) ^ 0x12; // 0x12 = 0x09<<(8-7) 多项式 x7+x3+1 0x85
else
crc <<= 1;
}
}
return crc >> 1;
}
u16 crc16_ccitt(u8 *dat, u16 length)//CRC-16/CCITT x16+x12+x5+1
{
u8 i;
u16 crc = 0; // Initial value
while(length--)
{
crc ^= *dat++; // crc ^= *dat; dat++;
for (i = 0; i < 8; ++i)
{
if (crc & 1)
crc = (crc >> 1) ^ 0x8408; // 0x8408 = reverse 0x1021
else
crc = (crc >> 1);
}
}
return crc;
}
sd.h为sd卡的基本设置:
#ifndef __SD_H
#define __SD_H
#include "sdio.h"
extern u8 SD_Type;//SD卡的类型
extern u8 Response;//命令返回数组
//错误码定义
//-------------------------------------------------------------
#define INIT_CMD0_ERROR 0x01 //CMD0错误
#define INIT_CMD1_ERROR 0x02 //CMD1错误
#define WRITE_BLOCK_ERROR 0x03 //写块错误
#define READ_BLOCK_ERROR 0x04 //读块错误
//-------------------------------------------------------------
// SD卡类型定义
#define SD_TYPE_ERR 0X00
#define SD_TYPE_V2 0X04
#define SD_TYPE_V2HC 0X06
//SD传输数据结束后是否释放总线宏定义
#define NO_RELEASE 0
#define RELEASE 1
//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
// SD卡指令表
#define CMD0 0 //卡复位
#define CMD1 1
#define CMD2 2 //获取CID寄存器
#define CMD3 3 //获取CID寄存器
#define CMD7 7 //获取CID寄存器
#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 TRY_TIME 10 //向SD卡写入命令之后,读取SD卡的回应次数,即读TRY_TIME次,如果在TRY_TIME次中读不到回应,产生超时错误,命令写入失败
u8 SD_WaitReady(void); //等待SD卡准备
u8 SD_Init(void); //初始化
u8 SD_SendCmd(u8 cmd, u32 arg); //写命令
u8 SDRScmd(void);
void SD_readsent(u32 add);
void SD_readblock(u8 buf[],u32 add);
void SD_readMblock(u8 buf[],u32 add,u32 num);//读多个扇区
#endif
--------------------------------------------------------分割线--------------------------------------------------
sd.c为sd卡的初始化和读取函数实现。
/*
此源码用于用gpoi模拟sdio实现sd卡使用4通道读取功能,写入没有实现(CRC16校检太费时,没有优势)。
由于现市面上的卡都是2.0以上的大容量卡,所以本例只支持2.0版以上SD卡。
*/
#include "sd.h"
//#include ".\src\uart1.h"
//#include "delay.h"
u8 SD_Type=0;//SD卡的类型
u8 Response={0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};//命令返回数组
u16 SD_RCA;//卡自荐的地址,host 需要保存下来以便后续使用 CMD7 来选中此卡。
///
//等待卡准备好
//返回值:0,准备好了;其他,错误代码
u8 SD_WaitReady(void)
{
u32 t=500;
while(t--)SDsendcmd(0xff);
return 1;
}
//向SD卡发送一个命令
//输入: u8 cmd 命令
// u32 arg命令参数
// u8 crc crc校验值
//返回值:SD卡返回的响应
u8 SD_SendCmd(u8 cmd, u32 arg)
{
u8 Retry=0;
u8 crc;
u8 dat;
Response=0xff;
dat=(u8)(cmd | 0x40);
dat=(u8)(arg>>24);
dat=(u8)(arg>>16);
dat=(u8)(arg>>8);
dat=(u8)(arg);
crc=(crc7_mmc(dat,5)<<1)+1;
SDsendcmd(dat); //分别写入命令 最高2位固定为1
SDsendcmd(dat); //命令参数 2-5字节4个字节 32位
SDsendcmd(dat);
SDsendcmd(dat);
SDsendcmd(dat);
SDsendcmd(crc);
//停止传数据命令
//if(cmd==CMD12)SDsendcmd(0xff);//Skip a stuff byte when stop reading
SDRS_response(Response);
watit_8CR();
return Response;
}
//初始化SD卡
u8 SD_Init(void)
{
u8 r1; // 存放SD卡的返回值
u16 retry;// 用来进行超时计数
SD_WaitReady();
r1=SD_SendCmd(CMD0,0);//进入IDLE状态 即复位sd卡 空闲状态
watit_8CR();
SD_Type=0;//默认无卡
SD_SendCmd(CMD8,0x1AA);//SD V2.0 发送接口状态命令
//如果返回值为1 则是 SDV2.0版本
if(Response==0x01&&Response==0xaa)//卡是否支持2.7~3.6V
{
retry=64;
do
{
SD_SendCmd(55,0x00000000);
Response=0x00;
SD_SendCmd(41,0x403c0000);
}while(((Response&0x80)!=0x80)&& retry--);//OCR=1 时上电操作是否完成
//OCR=1 时,OCR (CCS) 指示了卡是否为大容量卡。
//CMD8 无响应,ACMD41 响应的 OCR=0 :SD 1.X 卡。
//CMD8 有响应,ACMD41 响应的 OCR=0 :SD 2.0 非大容量卡。
//CMD8 有响应,ACMD41 响应的 OCR=1 :SDHC 2.0 大容量卡。
if(Response&0x40)SD_Type=SD_TYPE_V2HC;//CMD8 有响应,ACMD41 响应的 OCR=1 :SDHC 2.0 大容量卡。
else SD_Type=SD_TYPE_V2;
r1=SD_SendCmd(CMD2,0x00000000); //获取CID寄存器
//R2 类响应136BIT,其中包含了 CID 寄存器 (如图13) ,是该卡的识别号,host 可以忽略 CID 的内容。
r1=SD_SendCmd(CMD3,0x00000000);//进入数据传输模式。
SD_RCA=((u16)Response<<8)|Response; // 获取RCA (16bit) ,这是卡自荐的地址,host 需要保存下来以便后续使用 CMD7 来选中此卡。
r1=SD_SendCmd(CMD7,(u32)(SD_RCA)<<16); //选中卡
r1=SD_SendCmd(55,(u32)(SD_RCA)<<16);//CMD55,参数必须是RCA
r1=SD_SendCmd(6, 0x00000002); //ACMD6,打开4bit传输模式选中卡
r1=SD_SendCmd(CMD16,0x00000200); //设置块为512byte,不能设为其它。
//SD_WaitReady();
}
SDout=0xff;
if(SD_Type)return 0; //如果没有采集到SD卡版本 返回0成功。
else if(r1)return r1; //初始化失败。
return 0xaa;//其他错误
}
void SD_readblock(u8 buf[],u32 add)//读取一个扇区数据
{
int i=0,j=-1,cn=0;
u8 CRC=0;
u8 dat;
if(SD_Type!=SD_TYPE_V2HC)add <<= 9;//转换为字节地址
dat=(u8)(17 | 0x40);
dat=(u8)(add>>24);
dat=(u8)(add>>16);
dat=(u8)(add>>8);
dat=(u8)(add);
CRC=(crc7_mmc(dat,5)<<1)+1;
SDHisendcmd(dat);//发送读命令
SDHisendcmd(dat);
SDHisendcmd(dat);
SDHisendcmd(dat);
SDHisendcmd(dat);
SDHisendcmd(CRC);
P7=0xff;
for(i=0;i<1024;i++) // 接收数据
{
SD_SCK=0;
SD_SCK=1;
if(!SD_D0)
{
P74=0;
for(j=0;j<512;j++)
{
SD_SCK=0;
SD_SCK=1;
buf=SDout;
SD_SCK=0;
buf<<=4;
SD_SCK=1;
buf|=(SDout&0x0f);
}
P74=1;
SDread();
SDread();//接收crc16,丢弃。
SDread();
SDread();
SDread();
SDread();
SDread();
SDread();
SDread();
break;
}
}
}
void SD_readMblock(u8 buf[],u32 add,s32 num)//读取num个扇区数据
{
u32 i,j,k=0;
u8 CRC=0;
u8 dat;
if(SD_Type!=SD_TYPE_V2HC)add <<= 9;//转换为字节地址
dat=(u8)(18 | 0x40);
dat=(u8)(add>>24);
dat=(u8)(add>>16);
dat=(u8)(add>>8);
dat=(u8)(add);
CRC=(crc7_mmc(dat,5)<<1)+1;
SDHisendcmd(dat);//发送读命令
SDHisendcmd(dat);
SDHisendcmd(dat);
SDHisendcmd(dat);
SDHisendcmd(dat);
SDHisendcmd(CRC);
SDout=0xff;
num--;
for(i=0;i<10240;i++) // 接收数据
{
SD_SCK=0;
SD_SCK=1;
if(!SD_D0)
{
P74=0;
for(j=0;j<512;j++)
{
SD_SCK=0;
SD_SCK=1;
buf=SDout;
SD_SCK=0;
buf<<=4;
SD_SCK=1;
buf|=(SDout&0x0f);
k++;
}
P74=1;
SDread();
SDread();//接收crc16,丢弃。
SDread();
SDread();
SDread();
SDread();
SDread();
SDread();
SDread();
if(num<1)break;
num--;
}
}
SDHisendcmd(0x4c);//发送读命令CMD12
SDHisendcmd(0);
SDHisendcmd(0);
SDHisendcmd(0);
SDHisendcmd(0);
SDHisendcmd(0x61);
for(i=0;i<1024;i++) // 接收数据
{
SD_SCK=0;
SD_SCK=1;
if(SD_CMD==0)
{
for(j=0;j<64;j++)
{
SD_SCK=0;
SD_SCK=1;
}
break;
}
}
}
————————————————
原文链接:https://blog.csdn.net/bfmipbwvfp/article/details/140374767,也是本人发的,上面有附件。
谢谢分享 这是qspi接口的程序 SD卡模拟qspi比spi硬件dma快多少?
如果flash也用qspi了话能快很多吗? 感谢分享 soma 发表于 2024-7-27 18:20
这是qspi接口的程序
并不是,QSPI比sdio少一条CMD命令线路。 感谢分享
页:
[1]