3.2 spi硬件时序方式
上面的3.1小节是采用spi模拟时序驱动sd nand,stm32本身集成有spi硬件模块,可以直接利用stm32硬件spi接口读写。
下面贴出底层的适配代码。 上面贴出的驱动代码里,已经将驱动接口部分和协议逻辑部分区分开了,替换底层的sip读写代码非常方便。
(1)主要替换的代码/*
函数功能:spi初始化(模拟spi)
硬件连接:
miso--->pb14
mosi--->pb15
sclk--->pb13
*/
void spi_init(void)
{
/*开启时钟*/
rcc->apb1enr|=1
gpiob->odr|=0x7cr1|=0 cnt=0;
while((spi2->sr&1dr; //返回收到的数据
}
函数功能:sd卡底层接口,通过spi时序向sd卡读写一个字节
函数参数:data是要写入的数据
返 回 值:读到的数据
*/
u8 sdcardreadwriteonebyte(u8 datatx)
{
return spi_readwriteonebyte(datatx);
}
(2)运行效果3.3 sdio方式
如果想提高sd nand的读写速度,可以采用sdio协议,stm32本身有sdio的硬件支持,配置好sdio的寄存器即可完成sd nand的操作。 sdio的数据线都比spi多,读写速度自然没法比的。
下面贴出stm32f103ze上面编写的sdio协议读写sd nand的驱动代码。
(1)整体工程代码
(2)sdio.c#include sdio_sdcard.h
#include string.h
#include sys.h
#include usart.h
static u8 cardtype=sdio_std_capacity_sd_card_v1_1; //sd卡类型(默认为1.x卡)
static u32 csd_tab[4],cid_tab[4],rca=0; //sd卡csd,cid以及相对地址(rca)数据
static u8 devicemode=sd_dma_mode; //工作模式,注意,工作模式必须通过sdio_sdcardsetdevicemode,后才算数.这里只是定义一个默认的模式(sd_dma_mode)
static u8 stopcondition=0; //是否发送停止传输标志位,dma多块读写的时候用到
volatile sdio_sd_error_info transfererror=sd_ok; //数据传输错误标志,dma读写时使用
volatile u8 transferend=0; //传输结束标志,dma读写时使用
sd_cardinfo sdcardinfo; //sd卡信息
//sdio_sdcardreaddisksector/sdio_sdcardwritedisksector函数专用buf,当这两个函数的数据缓存区地址不是4字节对齐的时候,
//需要用到该数组,确保数据缓存区地址是4字节对齐的.
__align(4) u8 sdio_data_buffer[512];
/*
sd卡与开发板的sdio方式接线关系如下:
data0---pc8
data1---pc9
data2---pc10
data3---pc11
clk-----pc1
cmd-----pd2
*/
/*
函数功能:sdio方式初始化sd卡
返回值 :错误代码;(0,无错误)
*/
sdio_sd_error_info sdio_sdcardinit(void)
{
u8 clkdiv=0;
sdio_sd_error_info errorstatus=sd_ok;
//sdio io口初始化
rcc->apb2enr|=1gpiod->crl&=0xfffff0ff;
gpiod->crl|=0x00000b00; //pd2复用输出,pd7 上拉输入
//sdio外设寄存器设置为默认值
sdio->power=0x00000000;
sdio->clkcr=0x00000000;
sdio->arg=0x00000000;
sdio->cmd=0x00000000;
sdio->dtimer=0x00000000;
sdio->dlen=0x00000000;
sdio->dctrl=0x00000000;
sdio->icr=0x00c007ff;
sdio->mask=0x00000000;
stm32_nvic_setpriority(sdio_irqn,0,0); //sdio中断配置
errorstatus=sdio_sdpoweron(); //sd卡上电
sdio_sdcardinitializecards(); //初始化sd卡
sdio_sdcardgetinfo(&sdcardinfo); //获取卡信息
sdio_sdcardselectaddr((u32)(sdcardinfo.rcaclkcr=tmpreg;
}
/*
函数功能: sdio发送命令函数
函数参数:
cmdindex:命令索引,低六位有效
waitrsp:期待的相应.00/10,无响应;01,短响应;11,长响应
arg:参数
*/
void sdio_sendcmd(u8 cmdindex,u8 waitrsp,u32 arg)
{
u32 tmpreg;
sdio->arg=arg;
tmpreg=sdio->cmd;
tmpreg&=0xfffff800; //清除index和waitrsp
tmpreg|=cmdindex&0x3f; //设置新的index
tmpreg|=waitrsp<<6; //设置新的wait rsp
tmpreg|=0<<8; //无等待
tmpreg|=1dtimer=datatimeout;
sdio->dlen=datalen&0x1ffffff; //低25位有效
tmpreg=sdio->dctrl;
tmpreg&=0xffffff08; //清除之前的设置.
tmpreg|=blksize<<4; //设置块大小
tmpreg|=0<<2; //块数据传输
tmpreg|=(dir&0x01)<<1; //方向控制
tmpreg|=1clkcr=0; //清空clkcr之前的设置
sdio->clkcr|=031)==1)?1:0); //判断sd卡上电是否完成
count++;
}
if(count>=sd_max_volt_trial)
{
errorstatus=sd_invalid_voltrange;
return errorstatus;
}
if(response&=sd_high_capacity)
{
cardtype=sdio_high_capacity_sd_card;
}
}
return(errorstatus);
}
/*
函数功能: sd卡断电
返回值:错误代码;(0,无错误)
*/
sdio_sd_error_info sd_poweroff(void)
{
sdio->power&=~(3resp1;
cid_tab[1]=sdio->resp2;
cid_tab[2]=sdio->resp3;
cid_tab[3]=sdio->resp4;
}
if((sdio_std_capacity_sd_card_v1_1==cardtype)||(sdio_std_capacity_sd_card_v2_0==cardtype)||(sdio_secure_digital_io_combo_card==cardtype)||(sdio_high_capacity_sd_card==cardtype))//判断卡类型
{
sdio_sendcmd(sd_cmd_set_rel_addr,1,0); //发送cmd3,短响应
errorstatus=sdio_cmdresp6error(sd_cmd_set_rel_addr,&rca);//等待r6响应
if(errorstatus!=sd_ok)return errorstatus; //响应错误
}
if(sdio_multimedia_card==cardtype)
{
sdio_sendcmd(sd_cmd_set_rel_addr,1,(u32)(rca<<16));//发送cmd3,短响应
errorstatus=sdio_cmdresp2error(); //等待r2响应
if(errorstatus!=sd_ok)return errorstatus; //响应错误
}
if(sdio_secure_digital_io_card!=cardtype) //非secure_digital_io_card
{
rca = rca;
sdio_sendcmd(sd_cmd_send_csd,3,(u32)(rcaresp2;
csd_tab[2]=sdio->resp3;
csd_tab[3]=sdio->resp4;
}
return sd_ok;//卡初始化成功
}
/*
函数功能:得到卡信息
函数参数:
cardinfo:卡信息存储区
返回值:错误状态
*/
sdio_sd_error_info sdio_sdcardgetinfo(sd_cardinfo *cardinfo)
{
sdio_sd_error_info errorstatus=sd_ok;
u8 tmp=0;
cardinfo->cardtype=(u8)cardtype; //卡类型
cardinfo->rca=(u16)rca; //卡rca值
tmp=(u8)((csd_tab[0]&0xff000000)>>24);
cardinfo->sd_csd.csdstruct=(tmp&0xc0)>>6; //csd结构
cardinfo->sd_csd.sysspecversion=(tmp&0x3c)>>2; //2.0协议还没定义这部分(为保留),应该是后续协议定义的
cardinfo->sd_csd.reserved1=tmp&0x03; //2个保留位
tmp=(u8)((csd_tab[0]&0x00ff0000)>>16); //第1个字节
cardinfo->sd_csd.taac=tmp; //数据读时间1
tmp=(u8)((csd_tab[0]&0x0000ff00)>>8); //第2个字节
cardinfo->sd_csd.nsac=tmp; //数据读时间2
tmp=(u8)(csd_tab[0]&0x000000ff); //第3个字节
cardinfo->sd_csd.maxbusclkfrec=tmp; //传输速度
tmp=(u8)((csd_tab[1]&0xff000000)>>24); //第4个字节
cardinfo->sd_csd.cardcomdclasses=tmp16); //第5个字节
cardinfo->sd_csd.cardcomdclasses|=(tmp&0xf0)>>4;//卡指令类低四位
cardinfo->sd_csd.rdblocklen=tmp&0x0f; //最大读取数据长度
tmp=(u8)((csd_tab[1]&0x0000ff00)>>8); //第6个字节
cardinfo->sd_csd.partblockread=(tmp&0x80)>>7; //允许分块读
cardinfo->sd_csd.wrblockmisalign=(tmp&0x40)>>6; //写块错位
cardinfo->sd_csd.rdblockmisalign=(tmp&0x20)>>5; //读块错位
cardinfo->sd_csd.dsrimpl=(tmp&0x10)>>4;
cardinfo->sd_csd.reserved2=0; //保留
if((cardtype==sdio_std_capacity_sd_card_v1_1)||(cardtype==sdio_std_capacity_sd_card_v2_0)||(sdio_multimedia_card==cardtype))//标准1.1/2.0卡/mmc卡
{
cardinfo->sd_csd.devicesize=(tmp&0x03)cardinfo->sd_csd.devicesize|=(tmp&0xc0)>>6;
cardinfo->sd_csd.maxrdcurrentvddmin=(tmp&0x38)>>3;
cardinfo->sd_csd.maxrdcurrentvddmax=(tmp&0x07);
tmp=(u8)((csd_tab[2]&0x00ff0000)>>16); //第9个字节
cardinfo->sd_csd.maxwrcurrentvddmin=(tmp&0xe0)>>5;
cardinfo->sd_csd.maxwrcurrentvddmax=(tmp&0x1c)>>2;
cardinfo->sd_csd.devicesizemul=(tmp&0x03)8); //第10个字节
cardinfo->sd_csd.devicesizemul|=(tmp&0x80)>>7;
cardinfo->cardcapacity=(cardinfo->sd_csd.devicesize+1);//计算卡容量
cardinfo->cardcapacity*=(1cardblocksize=1cardcapacity*=cardinfo->cardblocksize;
}else if(cardtype==sdio_high_capacity_sd_card) //高容量卡
{
tmp=(u8)(csd_tab[1]&0x000000ff); //第7个字节
cardinfo->sd_csd.devicesize=(tmp&0x3f)24); //第8个字节
cardinfo->sd_csd.devicesize|=(tmp16); //第9个字节
cardinfo->sd_csd.devicesize|=(tmp);
tmp=(u8)((csd_tab[2]&0x0000ff00)>>8); //第10个字节
cardinfo->cardcapacity=(long long)(cardinfo->sd_csd.devicesize+1)*512*1024;//计算卡容量
cardinfo->cardblocksize=512; //块大小固定为512字节
}
cardinfo->sd_csd.erasegrsize=(tmp&0x40)>>6;
cardinfo->sd_csd.erasegrmul=(tmp&0x3f)>7;
cardinfo->sd_csd.wrprotectgrsize=(tmp&0x7f);
tmp=(u8)((csd_tab[3]&0xff000000)>>24); //第12个字节
cardinfo->sd_csd.wrprotectgrenable=(tmp&0x80)>>7;
cardinfo->sd_csd.mandeflecc=(tmp&0x60)>>5;
cardinfo->sd_csd.wrspeedfact=(tmp&0x1c)>>2;
cardinfo->sd_csd.maxwrblocklen=(tmp&0x03)16); //第13个字节
cardinfo->sd_csd.maxwrblocklen|=(tmp&0xc0)>>6;
cardinfo->sd_csd.writeblockpapartial=(tmp&0x20)>>5;
cardinfo->sd_csd.reserved3=0;
cardinfo->sd_csd.contentprotectappli=(tmp&0x01);
tmp=(u8)((csd_tab[3]&0x0000ff00)>>8); //第14个字节
cardinfo->sd_csd.fileformatgrouop=(tmp&0x80)>>7;
cardinfo->sd_csd.copyflag=(tmp&0x40)>>6;
cardinfo->sd_csd.permwrprotect=(tmp&0x20)>>5;
cardinfo->sd_csd.tempwrprotect=(tmp&0x10)>>4;
cardinfo->sd_csd.fileformat=(tmp&0x0c)>>2;
cardinfo->sd_csd.ecc=(tmp&0x03);
tmp=(u8)(csd_tab[3]&0x000000ff); //第15个字节
cardinfo->sd_csd.csd_crc=(tmp&0xfe)>>1;
cardinfo->sd_csd.reserved4=1;
tmp=(u8)((cid_tab[0]&0xff000000)>>24); //第0个字节
cardinfo->sd_cid.manufacturerid=tmp;
tmp=(u8)((cid_tab[0]&0x00ff0000)>>16); //第1个字节
cardinfo->sd_cid.oem_appliid=tmp8); //第2个字节
cardinfo->sd_cid.oem_appliid|=tmp;
tmp=(u8)(cid_tab[0]&0x000000ff); //第3个字节
cardinfo->sd_cid.prodname1=tmp24); //第4个字节
cardinfo->sd_cid.prodname1|=tmp16); //第5个字节
cardinfo->sd_cid.prodname1|=tmp8); //第6个字节
cardinfo->sd_cid.prodname1|=tmp;
tmp=(u8)(cid_tab[1]&0x000000ff); //第7个字节
cardinfo->sd_cid.prodname2=tmp;
tmp=(u8)((cid_tab[2]&0xff000000)>>24); //第8个字节
cardinfo->sd_cid.prodrev=tmp;
tmp=(u8)((cid_tab[2]&0x00ff0000)>>16); //第9个字节
cardinfo->sd_cid.prodsn=tmp8); //第10个字节
cardinfo->sd_cid.prodsn|=tmpcardinfo->sd_cid.prodsn|=tmp;
tmp=(u8)((cid_tab[3]&0x00ff0000)>>16); //第13个字节
cardinfo->sd_cid.reserved1|=(tmp&0xf0)>>4;
cardinfo->sd_cid.manufactdate=(tmp&0x0f)8); //第14个字节
cardinfo->sd_cid.manufactdate|=tmp;
tmp=(u8)(cid_tab[3]&0x000000ff); //第15个字节
cardinfo->sd_cid.cid_crc=(tmp&0xfe)>>1;
cardinfo->sd_cid.reserved2=1;
return errorstatus;
}
/*
函数功能: 设置sdio总线宽度
函数参数:
wmode:位宽模式.0,1位数据宽度;1,4位数据宽度;2,8位数据宽度
返回值:sd卡错误状态
*/
sdio_sd_error_info sdio_sdcardenablewidebusoperation(u32 wmode)
{
sdio_sd_error_info errorstatus=sd_ok;
if((sdio_std_capacity_sd_card_v1_1==cardtype)||(sdio_std_capacity_sd_card_v2_0==cardtype)||(sdio_high_capacity_sd_card==cardtype))
{
if(wmode>=2)return sd_unsupported_feature;//不支持8位模式
else
{
errorstatus=sdio_sdcardenwidebus(wmode);
if(sd_ok==errorstatus)
{
sdio->clkcr&=~(3=9;
}
sdio_senddataconfig(sd_datatimeout,0,0,0); //清除dpsm状态机配置
if(sdio->resp1&sd_card_locked)return sd_lock_unlock_failed;//卡锁了
if((blksize>0)&&(blksizesta&((1<<5)|(1<<1)|(1<<3)|(1<<10)|(1 }else if(devicemode==sd_dma_mode)
{
sdio_sdcard_dmaconfig((u32*)buf,blksize,0);
transfererror=sd_ok;
stopcondition=0; //单块读,不需要发送停止传输指令
transferend=0; //传输结束标置位,在中断服务置1
sdio->mask|=(1<<1)|(1<<3)|(1<<8)|(1<<5)|(1if(cardtype==sdio_high_capacity_sd_card)//大容量卡
{
blksize=512;
addr>>=9;
}
sdio_senddataconfig(sd_datatimeout,0,0,0); //清除dpsm状态机配置
if(sdio->resp1&sd_card_locked)return sd_lock_unlock_failed;//卡锁了
if((blksize>0)&&(blksize1) //多块读
{
if(nblks*blksize>sd_max_data_length)return sd_invalid_parameter;//判断是否超过最大接收长度
sdio_senddataconfig(sd_datatimeout,nblks*blksize,power,1);//nblks*blksize,512块大小,卡到控制器
sdio_sendcmd(sd_cmd_read_mult_block,1,addr); //发送cmd18+从addr地址出读取数据,短响应
errorstatus=sdio_cmdresp1error(sd_cmd_read_mult_block);//等待r1响应
if(errorstatus!=sd_ok)return errorstatus; //响应错误
if(devicemode==sd_polling_mode)
{
// intx_disable();//关闭总中断(polling模式,严禁中断打断sdio读写操作!!!)
while(!(sdio->sta&((1<<5)|(1<<1)|(1<<3)|(1<<8)|(1 sdio_senddataconfig(sd_datatimeout,0,0,0); //清除dpsm状态机配置
if(sdio->resp1&sd_card_locked)return sd_lock_unlock_failed;//卡锁了
if(cardtype==sdio_high_capacity_sd_card) //大容量卡
{
blksize=512;
addr>>=9;
}
if((blksize>0)&&(blksize<=2048)&&((blksize&(blksize-1))==0))
{
power=convert_from_bytes_to_power_of_two(blksize);
sdio_sendcmd(sd_cmd_set_blocklen,1,blksize); //发送cmd16+设置数据长度为blksize,短响应
errorstatus=sdio_cmdresp1error(sd_cmd_set_blocklen); //等待r1响应
if(errorstatus!=sd_ok)return errorstatus; //响应错误
}else return sd_invalid_parameter;
sdio_sendcmd(sd_cmd_send_status,1,(u32)rca0)) //检查ready_for_data位是否置位
{
timeout--;
sdio_sendcmd(sd_cmd_send_status,1,(u32)rcasta&((1<<10)|(1<<4)|(1<<1)|(1<<3)|(1 errorstatus=sdio_sdcardprogrammingstate(&cardstate);
while((errorstatus==sd_ok)&&((cardstate==sd_card_programming)||(cardstate==sd_card_receiving)))
{
errorstatus=sdio_sdcardprogrammingstate(&cardstate);
}
return errorstatus;
}
/*
函数功能:sd卡写多个块
函数参数:
buf:数据缓存区
addr:写地址
blksize:块大小
nblks:要写入的块数
返回值:错误状态
*/
sdio_sd_error_info sdio_sdcardwritemultiblocks(u8 *buf,long long addr,u16 blksize,u32 nblks)
{
sdio_sd_error_info errorstatus = sd_ok;
u8 power = 0, cardstate = 0;
u32 timeout=0,bytestransferred=0;
u32 count = 0, restwords = 0;
u32 tlen=nblks*blksize; //总长度(字节)
u32 *tempbuff = (u32*)buf;
if(buf==null)return sd_invalid_parameter; //参数错误
sdio->dctrl=0x0; //数据控制寄存器清零(关dma)
sdio_senddataconfig(sd_datatimeout,0,0,0); //清除dpsm状态机配置
if(sdio->resp1&sd_card_locked)return sd_lock_unlock_failed;//卡锁了
if(cardtype==sdio_high_capacity_sd_card)//大容量卡
{
blksize=512;
addr>>=9;
}
if((blksize>0)&&(blksize1)
{
if(nblks*blksize>sd_max_data_length)return sd_invalid_parameter;
if((sdio_std_capacity_sd_card_v1_1==cardtype)||(sdio_std_capacity_sd_card_v2_0==cardtype)||(sdio_high_capacity_sd_card==cardtype))
{
//提高性能
sdio_sendcmd(sd_cmd_app_cmd,1,(u32)rca}
tempbuff+=sd_halffifo;
bytestransferred+=sd_halffifobytes;
}
timeout=0x3fffffff; //写数据溢出时间
}else
{
if(timeout==0)return sd_data_timeout;
timeout--;
}
}
if(sdio->sta&(1 errorstatus=sdio_sdcardprogrammingstate(&cardstate);
while((errorstatus==sd_ok)&&((cardstate==sd_card_programming)||(cardstate==sd_card_receiving)))
{
errorstatus=sdio_sdcardprogrammingstate(&cardstate);
}
return errorstatus;
}
/*
函数功能: sdio中断服务函数
*/
void sdio_irqhandler(void)
{
sdio_sdcardprocessirqsrc();//处理所有sdio相关中断
}
/*
函数功能: sdio中断处理函数
函数参数: 处理sdio传输过程中的各种中断事务
返回值:错误代码
*/
sdio_sd_error_info sdio_sdcardprocessirqsrc(void)
{
if(sdio->sta&(1if(status&((1<<0)|(1<<2)|(1<<6)))break;//crc错误/命令响应超时/已经收到响应(crc校验成功)
}
if((timeout==0)||(status&(1return errorstatus;
}
if(status&1 if(status&((1<<0)|(1<<2)|(1<<6)))break;//crc错误/命令响应超时/已经收到响应(crc校验成功)
}
if(status&(1 return sd_cmd_rsp_timeout;
}
if(status&(1 return (sdio_sd_error_info)(sdio->resp1&sd_ocr_errorbits);//返回卡响应
}
/*
函数功能: 检查r3响应的错误状态
返回值: 错误状态
*/
sdio_sd_error_info sdio_cmdresp3error(void)
{
u32 status;
while(1)
{
status=sdio->sta;
if(status&((1<<0)|(1<<2)|(1<<6)))break;//crc错误/命令响应超时/已经收到响应(crc校验成功)
}
if(status&(1 if(status&((1<<0)|(1<<2)|(1<<6)))break;//crc错误/命令响应超时/已经收到响应(crc校验成功)
}
if((timeout==0)||(status&(1 return errorstatus;
}
if(status&1if(status&((1<<0)|(1<<2)|(1<<6)))break;//crc错误/命令响应超时/已经收到响应(crc校验成功)
}
if(status&(1return sd_cmd_rsp_timeout;
}
if(status&1rspr1=sdio->resp1; //得到响应
if(sd_allzero==(rspr1&(sd_r6_general_unknown_error|sd_r6_illegal_cmd|sd_r6_com_crc_failed)))
{
*prca=(u16)(rspr1>>16); //右移16位得到,rca
return errorstatus;
}
if(rspr1&sd_r6_general_unknown_error)return sd_general_unknown_error;
if(rspr1&sd_r6_illegal_cmd)return sd_illegal_cmd;
if(rspr1&sd_r6_com_crc_failed)return sd_com_crc_failed;
return errorstatus;
}
/*
函数功能:sdio使能宽总线模式
函数参数:
enx:0,不使能;1,使能;
返回值:错误状态
*/
sdio_sd_error_info sdio_sdcardenwidebus(u8 enx)
{
sdio_sd_error_info errorstatus = sd_ok;
u32 scr[2]={0,0};
u8 arg=0x00;
if(enx)arg=0x02;
else arg=0x00;
if(sdio->resp1&sd_card_locked)return sd_lock_unlock_failed;//sd卡处于locked状态
errorstatus=sdio_sdcardfindscr(rca,scr); //得到scr寄存器数据
if(errorstatus!=sd_ok)return errorstatus;
if((scr[1]&sd_wide_bus_support)!=sd_allzero) //支持宽总线
{
sdio_sendcmd(sd_cmd_app_cmd,1,(u32)rca<<16); //发送cmd55+rca,短响应
errorstatus=sdio_cmdresp1error(sd_cmd_app_cmd);
if(errorstatus!=sd_ok)return errorstatus;
sdio_sendcmd(sd_cmd_app_sd_set_buswidth,1,arg);//发送acmd6,短响应,参数:10,4位;00,1位.
errorstatus=sdio_cmdresp1error(sd_cmd_app_sd_set_buswidth);
return errorstatus;
}else return sd_request_not_applicable; //不支持宽总线设置
}
/*
函数功能: 检查卡是否正在执行写操作
函数参数: pstatus:当前状态
返回值:错误代码
*/
sdio_sd_error_info sdio_sdcardprogrammingstate(u8 *pstatus)
{
vu32 respr1 = 0, status = 0;
sdio_sendcmd(sd_cmd_send_status,1,(u32)rcawhile(!(status&((1<<0)|(1<<6)|(1 if(status&(1return sd_cmd_crc_fail;
}
if(status&(1respr1=sdio->resp1;
*pstatus=(u8)((respr1>>9)&0x0000000f);
return sd_ok;
}
/*
函数功能: 读取当前卡状态
函数参数:
pcardstatus:卡状态
返回值 :错误代码
*/
sdio_sd_error_info sdio_sdcardsendstatus(uint32_t *pcardstatus)
{
sdio_sd_error_info errorstatus = sd_ok;
if(pcardstatus==null)
{
errorstatus=sd_invalid_parameter;
return errorstatus;
}
sdio_sendcmd(sd_cmd_send_status,1,rca>9) & 0x0f);
}
/*
函数功能:查找sd卡的scr寄存器值
函数参数:
rca:卡相对地址
pscr:数据缓存区(存储scr内容)
返回值:错误状态
*/
sdio_sd_error_info sdio_sdcardfindscr(u16 rca,u32 *pscr)
{
u32 index = 0;
sdio_sd_error_info errorstatus = sd_ok;
u32 tempscr[2]={0,0};
sdio_sendcmd(sd_cmd_set_blocklen,1,8); //发送cmd16,短响应,设置block size为8字节
errorstatus=sdio_cmdresp1error(sd_cmd_set_blocklen);
if(errorstatus!=sd_ok)return errorstatus;
sdio_sendcmd(sd_cmd_app_cmd,1,(u32)rcasta&(1=2)break;
}
}
if(sdio->sta&(1//把数据顺序按8位为单位倒过来.
*(pscr+1)=((tempscr[0]&sd_0to7bits)<<24)|((tempscr[0]&sd_8to15bits)8)|((tempscr[0]&sd_24to31bits)>>24);
*(pscr)=((tempscr[1]&sd_0to7bits)<<24)|((tempscr[1]&sd_8to15bits)8)|((tempscr[1]&sd_24to31bits)>>24);
return errorstatus;
}
/*
函数功能: 得到numberofbytes以2为底的指数
函数参数: numberofbytes:字节数
返回值:以2为底的指数值
*/
u8 convert_from_bytes_to_power_of_two(u16 numberofbytes)
{
u8 count=0;
while(numberofbytes!=1)
{
numberofbytes>>=1;
count++;
}
return count;
}
/*
函数功能: 配置sdio dma
函数参数:
mbuf:存储器地址
bufsize:传输数据量
dir:方向;1,存储器-->sdio(写数据);0,sdio-->存储器(读数据);
*/
void sdio_sdcard_dmaconfig(u32*mbuf,u32 bufsize,u8 dir)
{
dma2->ifcr|=(0xffifo;//dma2 外设地址
dma2_channel4->cmar=(u32)mbuf; //dma2,存储器地址
dma2_channel4->ccr|=1<<0; //开启dma通道
}
/*
函数功能: 读sd卡
函数参数:
buf:读数据缓存区
sector:扇区地址
cnt:扇区个数
返回值:错误状态;0,正常;其他,错误代码;
*/
u8 sdio_sdcardreaddisksector(u8*buf,u32 sector,u8 cnt)
{
u8 sta=sd_ok;
long long lsector=sector;
u8 n;
lsector<<=9;
if((u32)buf%4!=0)
{
for(n=0;n
{
sta=sdio_sdcardreadblock(sdio_data_buffer,lsector+512*n,512);//单个sector的读操作
memcpy(buf,sdio_data_buffer,512);
buf+=512;
}
}else
{
if(cnt==1)sta=sdio_sdcardreadblock(buf,lsector,512); //单个sector的读操作
else sta=sdio_sdcardreadmultiblocks(buf,lsector,512,cnt);//多个sector
}
return sta;
}
/*
函数功能:写sd卡
函数参数:
buf:写数据缓存区
sector:扇区地址
cnt:扇区个数
返回值:错误状态;0,正常;其他,错误代码;
*/
u8 sdio_sdcardwritedisksector(u8*buf,u32 sector,u8 cnt)
{
u8 sta=sd_ok;
u8 n;
long long lsector=sector;
lsector<<=9;
if((u32)buf%4!=0)
{
for(n=0;n
{
memcpy(sdio_data_buffer,buf,512);
sta=sdio_sdcardwriteblock(sdio_data_buffer,lsector+512*n,512);//单个sector的写操作
buf+=512;
}
}else
{
if(cnt==1)sta=sdio_sdcardwriteblock(buf,lsector,512); //单个sector的写操作
else sta=sdio_sdcardwritemultiblocks(buf,lsector,512,cnt); //多个sector
}
return sta;
}
(3)sdio.h#ifndef __sdio_sdcard_h
#define __sdio_sdcard_h
#include stm32f10x.h
//sdio相关标志位
#define sdio_flag_ccrcfail ((uint32_t)0x00000001)
#define sdio_flag_dcrcfail ((uint32_t)0x00000002)
#define sdio_flag_ctimeout ((uint32_t)0x00000004)
#define sdio_flag_dtimeout ((uint32_t)0x00000008)
#define sdio_flag_txunderr ((uint32_t)0x00000010)
#define sdio_flag_rxoverr ((uint32_t)0x00000020)
#define sdio_flag_cmdrend ((uint32_t)0x00000040)
#define sdio_flag_cmdsent ((uint32_t)0x00000080)
#define sdio_flag_dataend ((uint32_t)0x00000100)
#define sdio_flag_stbiterr ((uint32_t)0x00000200)
#define sdio_flag_dbckend ((uint32_t)0x00000400)
#define sdio_flag_cmdact ((uint32_t)0x00000800)
#define sdio_flag_txact ((uint32_t)0x00001000)
#define sdio_flag_rxact ((uint32_t)0x00002000)
#define sdio_flag_txfifohe ((uint32_t)0x00004000)
#define sdio_flag_rxfifohf ((uint32_t)0x00008000)
#define sdio_flag_txfifof ((uint32_t)0x00010000)
#define sdio_flag_rxfifof ((uint32_t)0x00020000)
#define sdio_flag_txfifoe ((uint32_t)0x00040000)
#define sdio_flag_rxfifoe ((uint32_t)0x00080000)
#define sdio_flag_txdavl ((uint32_t)0x00100000)
#define sdio_flag_rxdavl ((uint32_t)0x00200000)
#define sdio_flag_sdioit ((uint32_t)0x00400000)
#define sdio_flag_ceataend ((uint32_t)0x00800000)
//用户配置区
//sdio时钟计算公式:sdio_ck时钟=sdioclk/[clkdiv+2];其中,sdioclk一般为72mhz
//使用dma模式的时候,传输速率可以到24mhz,不过如果你的卡不是高速卡,可能也会出错
//出错就请降低时钟,使用查询模式的话,推荐sdio_transfer_clk_div设置为3或者更大
#define sdio_init_clk_div 0xb2 //sdio初始化频率,最大400kh
#define sdio_transfer_clk_div 0x04 //sdio传输频率,该值太小可能会导致读写文件出错
//sdio工作模式定义,通过sdio_sdcardsetdevicemode函数设置.
#define sd_polling_mode 0 //查询模式,该模式下,如果读写有问题,建议增大sdio_transfer_clk_div的设置.
#define sd_dma_mode 1 //dma模式,该模式下,如果读写有问题,建议增大sdio_transfer_clk_div的设置.
//sdio 各种错误枚举定义
typedef enum
{
//特殊错误定义
sd_cmd_crc_fail = (1), /*!< command response received (but crc check failed) */
sd_data_crc_fail = (2), /*!< data bock sent/received (crc check failed) */
sd_cmd_rsp_timeout = (3), /*!< command response timeout */
sd_data_timeout = (4), /*!< data time out */
sd_tx_underrun = (5), /*!< transmit fifo under-run */
sd_rx_overrun = (6), /*!< receive fifo over-run */
sd_start_bit_err = (7), /*!< start bit not detected on all data signals in wide bus mode */
sd_cmd_out_of_range = (8), /*!< cmd's argument was out of range.*/
sd_addr_misaligned = (9), /*!< misaligned address */
sd_block_len_err = (10), /*!< transferred block length is not allowed for the card or the number of transferred bytes does not match the block length */
sd_erase_seq_err = (11), /*!< an error in the sequence of erase command occurs.*/
sd_bad_erase_param = (12), /*!< an invalid selection for erase groups */
sd_write_prot_violation = (13), /*!< attempt to program a write protect block */
sd_lock_unlock_failed = (14), /*!< sequence or password error has been detected in unlock command or if there was an attempt to access a locked card */
sd_com_crc_failed = (15), /*!< crc check of the previous command failed */
sd_illegal_cmd = (16), /*!< command is not legal for the card state */
sd_card_ecc_failed = (17), /*!< card internal ecc was applied but failed to correct the data */
sd_cc_error = (18), /*!< internal card controller error */
sd_general_unknown_error = (19), /*!< general or unknown error */
sd_stream_read_underrun = (20), /*!< the card could not sustain data transfer in stream read operation. */
sd_stream_write_overrun = (21), /*!< the card could not sustain data programming in stream mode */
sd_cid_csd_overwrite = (22), /*!< cid/csd overwrite error */
sd_wp_erase_skip = (23), /*!< only partial address space was erased */
sd_card_ecc_disabled = (24), /*!< command has been executed without using internal ecc */
sd_erase_reset = (25), /*!< erase sequence was cleared before executing because an out of erase sequence command was received */
sd_ake_seq_error = (26), /*!< error in sequence of authentication. */
sd_invalid_voltrange = (27),
sd_addr_out_of_range = (28),
sd_switch_error = (29),
sd_sdio_disabled = (30),
sd_sdio_function_busy = (31),
sd_sdio_function_failed = (32),
sd_sdio_unknown_function = (33),
//标准错误定义
sd_internal_error,
sd_not_configured,
sd_request_pending,
sd_request_not_applicable,
sd_invalid_parameter,
sd_unsupported_feature,
sd_unsupported_hw,
sd_error,
sd_ok = 0
} sdio_sd_error_info;
//sd卡csd寄存器数据
typedef struct
{
u8 csdstruct; /*!< csd structure */
u8 sysspecversion; /*!< system specification version */
u8 reserved1; /*!< reserved */
u8 taac; /*!< data read access-time 1 */
u8 nsac; /*!< data read access-time 2 in clk cycles */
u8 maxbusclkfrec; /*!< max. bus clock frequency */
u16 cardcomdclasses; /*!< card command classes */
u8 rdblocklen; /*!< max. read data block length */
u8 partblockread; /*!< partial blocks for read allowed */
u8 wrblockmisalign; /*!< write block misalignment */
u8 rdblockmisalign; /*!< read block misalignment */
u8 dsrimpl; /*!< dsr implemented */
u8 reserved2; /*!< reserved */
u32 devicesize; /*!< device size */
u8 maxrdcurrentvddmin; /*!< max. read current @ vdd min */
u8 maxrdcurrentvddmax; /*!< max. read current @ vdd max */
u8 maxwrcurrentvddmin; /*!< max. write current @ vdd min */
u8 maxwrcurrentvddmax; /*!< max. write current @ vdd max */
u8 devicesizemul; /*!< device size multiplier */
u8 erasegrsize; /*!< erase group size */
u8 erasegrmul; /*!< erase group size multiplier */
u8 wrprotectgrsize; /*!< write protect group size */
u8 wrprotectgrenable; /*!< write protect group enable */
u8 mandeflecc; /*!< manufacturer default ecc */
u8 wrspeedfact; /*!< write speed factor */
u8 maxwrblocklen; /*!< max. write data block length */
u8 writeblockpapartial; /*!< partial blocks for write allowed */
u8 reserved3; /*!< reserded */
u8 contentprotectappli; /*!< content protection application */
u8 fileformatgrouop; /*!< file format group */
u8 copyflag; /*!< copy flag (otp) */
u8 permwrprotect; /*!< permanent write protection */
u8 tempwrprotect; /*!< temporary write protection */
u8 fileformat; /*!< file format */
u8 ecc; /*!< ecc code */
u8 csd_crc; /*!< csd crc */
u8 reserved4; /*!< always 1*/
} sd_csd;
//sd卡cid寄存器数据
typedef struct
{
u8 manufacturerid; /*!< manufacturerid */
u16 oem_appliid; /*!< oem/application id */
u32 prodname1; /*!< product name part1 */
u8 prodname2; /*!< product name part2*/
u8 prodrev; /*!< product revision */
u32 prodsn; /*!< product serial number */
u8 reserved1; /*!< reserved1 */
u16 manufactdate; /*!< manufacturing date */
u8 cid_crc; /*!< cid crc */
u8 reserved2; /*!< always 1 */
} sd_cid;
//sd卡状态
typedef enum
{
sd_card_ready = ((uint32_t)0x00000001),
sd_card_identification = ((uint32_t)0x00000002),
sd_card_standby = ((uint32_t)0x00000003),
sd_card_transfer = ((uint32_t)0x00000004),
sd_card_sending = ((uint32_t)0x00000005),
sd_card_receiving = ((uint32_t)0x00000006),
sd_card_programming = ((uint32_t)0x00000007),
sd_card_disconnected = ((uint32_t)0x00000008),
sd_card_error = ((uint32_t)0x000000ff)
}sdcardstate;
//sd卡信息,包括csd,cid等数据
typedef struct
{
sd_csd sd_csd;
sd_cid sd_cid;
long long cardcapacity; //sd卡容量,单位:字节,最大支持2^64字节大小的卡.
u32 cardblocksize; //sd卡块大小
u16 rca; //卡相对地址
u8 cardtype; //卡类型
} sd_cardinfo;
extern sd_cardinfo sdcardinfo;//sd卡信息
//sdio 指令集
#define sd_cmd_go_idle_state ((u8)0)
#define sd_cmd_send_op_cond ((u8)1)
#define sd_cmd_all_send_cid ((u8)2)
#define sd_cmd_set_rel_addr ((u8)3) /*!< sdio_send_rel_addr for sd card */
#define sd_cmd_set_dsr ((u8)4)
#define sd_cmd_sdio_sen_op_cond ((u8)5)
#define sd_cmd_hs_switch ((u8)6)
#define sd_cmd_sel_desel_card ((u8)7)
#define sd_cmd_hs_send_ext_csd ((u8)8)
#define sd_cmd_send_csd ((u8)9)
#define sd_cmd_send_cid ((u8)10)
#define sd_cmd_read_dat_until_stop ((u8)11) /*!< sd card doesn't support it */
#define sd_cmd_stop_transmission ((u8)12)
#define sd_cmd_send_status ((u8)13)
#define sd_cmd_hs_bustest_read ((u8)14)
#define sd_cmd_go_inactive_state ((u8)15)
#define sd_cmd_set_blocklen ((u8)16)
#define sd_cmd_read_single_block ((u8)17)
#define sd_cmd_read_mult_block ((u8)18)
#define sd_cmd_hs_bustest_write ((u8)19)
#define sd_cmd_write_dat_until_stop ((u8)20)
#define sd_cmd_set_block_count ((u8)23)
#define sd_cmd_write_single_block ((u8)24)
#define sd_cmd_write_mult_block ((u8)25)
#define sd_cmd_prog_cid ((u8)26)
#define sd_cmd_prog_csd ((u8)27)
#define sd_cmd_set_write_prot ((u8)28)
#define sd_cmd_clr_write_prot ((u8)29)
#define sd_cmd_send_write_prot ((u8)30)
#define sd_cmd_sd_erase_grp_start ((u8)32) /*!< to set the address of the first write
block to be erased. (for sd card only) */
#define sd_cmd_sd_erase_grp_end ((u8)33) /*!< to set the address of the last write block of the
continuous range to be erased. (for sd card only) */
#define sd_cmd_erase_grp_start ((u8)35) /*!< to set the address of the first write block to be erased.
(for mmc card only spec 3.31) */
#define sd_cmd_erase_grp_end ((u8)36) /*!< to set the address of the last write block of the
continuous range to be erased. (for mmc card only spec 3.31) */
#define sd_cmd_erase ((u8)38)
#define sd_cmd_fast_io ((u8)39) /*!< sd card doesn't support it */
#define sd_cmd_go_irq_state ((u8)40) /*!< sd card doesn't support it */
#define sd_cmd_lock_unlock ((u8)42)
#define sd_cmd_app_cmd ((u8)55)
#define sd_cmd_gen_cmd ((u8)56)
#define sd_cmd_no_cmd ((u8)64)
/**
* @brief following commands are sd card specific commands.
* sdio_app_cmd :cmd55 should be sent before sending these commands.
*/
#define sd_cmd_app_sd_set_buswidth ((u8)6) /*!< for sd card only */
#define sd_cmd_sd_app_staus ((u8)13) /*!< for sd card only */
#define sd_cmd_sd_app_send_num_write_blocks ((u8)22) /*!< for sd card only */
#define sd_cmd_sd_app_op_cond ((u8)41) /*!< for sd card only */
#define sd_cmd_sd_app_set_clr_card_detect ((u8)42) /*!< for sd card only */
#define sd_cmd_sd_app_send_scr ((u8)51) /*!< for sd card only */
#define sd_cmd_sdio_rw_direct ((u8)52) /*!< for sd i/o card only */
#define sd_cmd_sdio_rw_extended ((u8)53) /*!< for sd i/o card only */
/**
* @brief following commands are sd card specific security commands.
* sdio_app_cmd should be sent before sending these commands.
*/
#define sd_cmd_sd_app_get_mkb ((u8)43) /*!< for sd card only */
#define sd_cmd_sd_app_get_mid ((u8)44) /*!< for sd card only */
#define sd_cmd_sd_app_set_cer_rn1 ((u8)45) /*!< for sd card only */
#define sd_cmd_sd_app_get_cer_rn2 ((u8)46) /*!< for sd card only */
#define sd_cmd_sd_app_set_cer_res2 ((u8)47) /*!< for sd card only */
#define sd_cmd_sd_app_get_cer_res1 ((u8)48) /*!< for sd card only */
#define sd_cmd_sd_app_secure_read_multiple_block ((u8)18) /*!< for sd card only */
#define sd_cmd_sd_app_secure_write_multiple_block ((u8)25) /*!< for sd card only */
#define sd_cmd_sd_app_secure_erase ((u8)38) /*!< for sd card only */
#define sd_cmd_sd_app_change_secure_area ((u8)49) /*!< for sd card only */
#define sd_cmd_sd_app_secure_write_mkb ((u8)48) /*!< for sd card only */
//支持的sd卡定义
#define sdio_std_capacity_sd_card_v1_1 ((u32)0x00000000)
#define sdio_std_capacity_sd_card_v2_0 ((u32)0x00000001)
#define sdio_high_capacity_sd_card ((u32)0x00000002)
#define sdio_multimedia_card ((u32)0x00000003)
#define sdio_secure_digital_io_card ((u32)0x00000004)
#define sdio_high_speed_multimedia_card ((u32)0x00000005)
#define sdio_secure_digital_io_combo_card ((u32)0x00000006)
#define sdio_high_capacity_mmc_card ((u32)0x00000007)
//sdio相关参数定义
#define null 0
#define sdio_static_flags ((u32)0x000005ff)
#define sdio_cmd0timeout ((u32)0x00010000)
#define sdio_datatimeout ((u32)0xffffffff)
#define sdio_fifo_address ((u32)0x40018080)
//mask for errors card status r1 (ocr register)
#define sd_ocr_addr_out_of_range ((u32)0x80000000)
#define sd_ocr_addr_misaligned ((u32)0x40000000)
#define sd_ocr_block_len_err ((u32)0x20000000)
#define sd_ocr_erase_seq_err ((u32)0x10000000)
#define sd_ocr_bad_erase_param ((u32)0x08000000)
#define sd_ocr_write_prot_violation ((u32)0x04000000)
#define sd_ocr_lock_unlock_failed ((u32)0x01000000)
#define sd_ocr_com_crc_failed ((u32)0x00800000)
#define sd_ocr_illegal_cmd ((u32)0x00400000)
#define sd_ocr_card_ecc_failed ((u32)0x00200000)
#define sd_ocr_cc_error ((u32)0x00100000)
#define sd_ocr_general_unknown_error ((u32)0x00080000)
#define sd_ocr_stream_read_underrun ((u32)0x00040000)
#define sd_ocr_stream_write_overrun ((u32)0x00020000)
#define sd_ocr_cid_csd_overwriete ((u32)0x00010000)
#define sd_ocr_wp_erase_skip ((u32)0x00008000)
#define sd_ocr_card_ecc_disabled ((u32)0x00004000)
#define sd_ocr_erase_reset ((u32)0x00002000)
#define sd_ocr_ake_seq_error ((u32)0x00000008)
#define sd_ocr_errorbits ((u32)0xfdffe008)
//masks for r6 response
#define sd_r6_general_unknown_error ((u32)0x00002000)
#define sd_r6_illegal_cmd ((u32)0x00004000)
#define sd_r6_com_crc_failed ((u32)0x00008000)
#define sd_voltage_window_sd ((u32)0x80100000)
#define sd_high_capacity ((u32)0x40000000)
#define sd_std_capacity ((u32)0x00000000)
#define sd_check_pattern ((u32)0x000001aa)
#define sd_voltage_window_mmc ((u32)0x80ff8000)
#define sd_max_volt_trial ((u32)0x0000ffff)
#define sd_allzero ((u32)0x00000000)
#define sd_wide_bus_support ((u32)0x00040000)
#define sd_single_bus_support ((u32)0x00010000)
#define sd_card_locked ((u32)0x02000000)
#define sd_card_programming ((u32)0x00000007)
#define sd_card_receiving ((u32)0x00000006)
#define sd_datatimeout ((u32)0xffffffff)
#define sd_0to7bits ((u32)0x000000ff)
#define sd_8to15bits ((u32)0x0000ff00)
#define sd_16to23bits ((u32)0x00ff0000)
#define sd_24to31bits ((u32)0xff000000)
#define sd_max_data_length ((u32)0x01ffffff)
#define sd_halffifo ((u32)0x00000008)
#define sd_halffifobytes ((u32)0x00000020)
//command class supported
#define sd_cccc_lock_unlock ((u32)0x00000080)
#define sd_cccc_write_prot ((u32)0x00000040)
#define sd_cccc_erase ((u32)0x00000020)
//cmd8指令
#define sdio_send_if_cond ((u32)0x00000008)
//相关函数定义
sdio_sd_error_info sdio_sdcardinit(void);
void sdio_clockset(u8 clkdiv);
void sdio_sendcmd(u8 cmdindex,u8 waitrsp,u32 arg);
void sdio_senddataconfig(u32 datatimeout,u32 datalen,u8 blksize,u8 dir);
sdio_sd_error_info sdio_sdpoweron(void);
sdio_sd_error_info sd_poweroff(void);
sdio_sd_error_info sdio_sdcardinitializecards(void);
sdio_sd_error_info sdio_sdcardgetinfo(sd_cardinfo *cardinfo);
sdio_sd_error_info sdio_sdcardenablewidebusoperation(u32 wmode);
sdio_sd_error_info sdio_sdcardsetdevicemode(u32 mode);
sdio_sd_error_info sdio_sdcardselectaddr(u32 addr);
sdio_sd_error_info sdio_sdcardsendstatus(uint32_t *pcardstatus);
sdcardstate sdio_sdcardgetstate(void);
sdio_sd_error_info sdio_sdcardreadblock(u8 *buf,long long addr,u16 blksize);
sdio_sd_error_info sdio_sdcardreadmultiblocks(u8 *buf,long long addr,u16 blksize,u32 nblks);
sdio_sd_error_info sdio_sdcardwriteblock(u8 *buf,long long addr, u16 blksize);
sdio_sd_error_info sdio_sdcardwritemultiblocks(u8 *buf,long long addr,u16 blksize,u32 nblks);
sdio_sd_error_info sdio_sdcardprocessirqsrc(void);
sdio_sd_error_info sdio_cmderrorcheck(void);
sdio_sd_error_info sdio_cmdresp7error(void);
sdio_sd_error_info sdio_cmdresp1error(u8 cmd);
sdio_sd_error_info sdio_cmdresp3error(void);
sdio_sd_error_info sdio_cmdresp2error(void);
sdio_sd_error_info sdio_cmdresp6error(u8 cmd,u16*prca);
sdio_sd_error_info sdio_sdcardenwidebus(u8 enx);
sdio_sd_error_info sdio_sdcardprogrammingstate(u8 *pstatus);
sdio_sd_error_info sdio_sdcardfindscr(u16 rca,u32 *pscr);
u8 convert_from_bytes_to_power_of_two(u16 numberofbytes);
void sdio_sdcard_dmaconfig(u32*mbuf,u32 bufsize,u8 dir);
u8 sdio_sdcardreaddisksector(u8*buf,u32 sector,u8 cnt); //读sd卡,fatfs/usb调用
u8 sdio_sdcardwritedisksector(u8*buf,u32 sector,u8 cnt); //写sd卡,fatfs/usb调用
#endif
废气vocs在线监测仪正宗生产厂家
dfrobot记忆合金肌肉驱动器 介绍
瑞佑科技推出支持文字与绘图模式的TFT彩屏控制器
数控电源电路的工作原理、功能及故障分析
扫地机器人中的灰尘识别感应器用到了哪些技术
基于STM32采用CS创世 SD NAND(贴片SD卡)完成FATFS文件系统移植与测试(中篇)
5G真的来了 这些问题你一定想问!
2017三大领域展望:物联网(IoT)、VR/AR和机器学习
ST推出一款新的高集成度系统级芯片
光缆故障的主要产生原因及解决方案
电阻器的基本介绍和作用
基于四路同步水声信号记录仪设计方案
Redmi Note 8 Pro的首销破30万台,Redmi与荣耀的竞争已趋向白热化
摩托罗拉Moto Z2 Play手机更新到Android 9.0操作系统
德思特分享 | V2X在做什么?连接未来智能出行的车联网(上)
华为正在通过ICT技术来推动智能网联汽车行业的发展
变速齿轮怎么用 变速齿轮加速器原理
探讨深度学习在自动驾驶中的应用
空气负氧离子监测站价格是怎么定的?
小米6最新消息:小米6首次销售,京东预约142万一秒售罄