20.1 概述sd卡是嵌入式系统中最常见的存储器,不仅容量可以做的很大,并且接口通用,支持spi/sdio驱动,尺寸可供选择,能满足不同应用的要求。stm32f1系列自带了标准的4位sdio接口,最高通信速度可达24mhz,最高每秒能传输12m字节的数据。
20.1.1 sdio框图stm32f1的sdio控制器包括2部分,sdio适配器模块和ahb总线接口,功能框图如下图所示。
其中sdio适配器模块主要用于实现所有mmc/sd卡的相关功能,如时钟的产生,命令和数据的传输,ahb总线接口则用于操作sdio适配器模块中的寄存器并产生中断和dma请求信号。复位后默认情况下sdio_d0用于数据传输。初始化后主机可以改变数据总线的宽度。
如果一个多媒体卡接到了总线上,则sdio_d0、sdio_d[3:0]或sdio_d[7:0]可以用于数据传输。mmc版本v3.31和之前版本的协议只支持1位数据线,所以只能用sdio_d0(为了通用性考虑,在程序里面我们只要检测到是mmc卡就设置为1位总线数据)。
如果一个sd卡接到了总线上,可以通过主机配置数据传输使用sdio_d0或sdio_d[3:0]。所有的数据线都工作在推挽模式。
sdio_cmd有两种操作模式:
(1)用于初始化时的开路模式(仅用于mmc版本v3.31或之前版本)
(2)用于命令传输的推挽模式(sd卡和mmcv4.2在初始化时也使用推挽驱动)
20.1.2 sdio时钟从sdio框图我们可以看到sdio总共有3个时钟,分别是:
(1)卡时钟sdio_ck:每个时钟周期在命令和数据线上传输1位命令或数据。对于多媒体卡v3.31协议,时钟频率可以在0mhz至20mhz间变化;对于多媒体卡v4.0/4.2协议,时钟频率可以在0mhz至48mhz间变化;对于sd卡,时钟频率可以在0mhz至25mhz间变化。
(2)sdio适配器时钟sdioclk:该时钟用于驱动sdio适配器,其频率等于ahb总线频率hclk,并用于产生sdio_ck时钟
(3)ahb总线接口时钟hclk/2:该时钟用于驱动sdio的ahb总线接口,其频率为hclk/2。
我们的sd卡时钟sdio_ck,根据卡的不同,可能有好几个区间,这就涉及到时钟频率的设置,sdio_ck与sdioclk的关系为:
sdio_ck=sdioclk/(2+clkdiv)
其中,sdioclk为hclk,一般是72mhz,而clkdiv则是分配系数,可以通过sdio的sdio_clkcr寄存器进行设置,确保sdio_ck不超过卡的最大操作频率。
注:在sd卡刚刚初始化的时候,其时钟频率sdio_ck不能超过400khz,否则可能无法完成初始化。在初始化以后,就可以设置时钟频率到最大了,但不可超过sd卡的最大操作时钟频率。
20.1.3 sdio的命令与响应sdio的命令分为应用相关命令acmd和通用命令cmd两部分,应用相关命令acmd的发送,必须先发送通用命令cmd55,然后才能发送应用相关命令acmd。sdio的所有命令和响应都是通过sdio_cmd引脚传输的,任何命令的长度都是固定为48位,sdio的命令格式如下表所示。
bit位宽度值说明
47 1 0 起始位
46 1 1 传输位
45:40 6 - 命令索引
39:8 32 - 参数
7:1 7 - crc7
0 1 1 结束位
所有的命令都是由stm32f1发出,其中开始位、传输位、crc7和结束位由sdio硬件控制,我们需要设置的就只有命令索引和参数部分。其中命令索引在sdio_cmd寄存器里面设置,命令参数则由寄存器sdio_arg设置。一般情况下,选中的sd卡在接收到命令之后,都会回复一个应答(但是cmd0无应答),这个应答我们称之为响应,响应也是在cmd线上串行传输的。stm32f1的sdio控制器支持2种响应类型,48位的短响应和136位的长响应,这两种响应类型都带crc错误检测,不带crc的响应应该忽略crc错误标志,如cmd1的响应。
短响应的格式如下表所示。
bit位宽度值说明
47 1 0 起始位
46 1 0 传输位
45:40 6 - 命令索引
39:8 32 - 参数
7:1 7 - crc7或者1111111
0 1 1 结束位
长响应的格式如下表所示。
bit位宽度值说明
135 1 0 起始位
134 1 0 传输位
133:128 6 111111 保留
127:1 127 - cid或csd(包括内部crc7)
0 1 1 结束位
硬件为我们滤除了开始位、传输位、crc7以及结束位等信息,对于短响应,命令索引存放在sdio_respcmd寄存器,参数则存放在sdio_resp1寄存器里面。对于长响应,则仅留cid/csd位域,存放在sdio_resp1到sdio_resp4等4个寄存器。sd卡总共有5类响应(r1、r2、r3、r6、r7),这里以r1为例简单介绍一下。r1(普通响应命令)响应属于短响应,其长度为48位,r1响应的格式如下表所示。
bit位宽度值说明
47 1 0 起始位
46 1 1 传输位
45:40 6 x 命令索引
39:8 32 x 参数
7:1 7 x crc7
0 1 1 结束位
在收到r1响应后,我们可以从sdio_respcmd寄存器和sdio_resp1寄存器分别读出命令索引和卡状态信息。
20.1.4 数据块读操作对于sd卡,数据是以数据块的形式传输的,我们常用的卡就是sd卡,所以不考虑mmc形式的读写操作,因为mmc卡数据以数据块或者数据流的形式传输。
从机在收到主机相关命令后,开始发送数据块给主机,所有数据块都带有crc校验值,crc由sdio硬件自动处理,单个数据块读的时候,在收到1个数据块以后即可以停止了,不需要发送停止命令cmd12。但是多块数据读的时候,sd卡将一直发送数据给主机,直到接到主机发送的stop命令cmd12。
sdio多数据块的读操作如下图所示。
20.1.5 数据块写操作数据块写操作同数据块读操作基本类似,只是数据块写的时候,多了一个忙判断,新的数据块必须在sd卡非忙的时候发送。这里的忙信号由sd卡拉低sdio_d0,以表示忙,sdio硬件自动控制,不需要我们软件处理。
20.2 sdio相关寄存器20.2.1 sdio电源控制寄存器:sdio_power31302928272625242322212019181716
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- ctrl
bit 1~bit 0:电源控制位
00:电源关闭,卡的时钟停止
01:保留
10:保留的上电状态
11:上电状态,卡的时钟开启
20.2.2 sdio时钟控制寄存器:sdio_clkcr31302928272625242322212019181716
-
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- hwfc_en negedge widbus bypass pwrsav clken clkdiv
bit 14:硬件流控制使能
0:关闭硬件流控制
1:使能硬件流控制
bit 13:sdio_ck相位选择位
0:在主时钟sdioclk的上升沿产生sdio_ck
1:在主时钟sdioclk的下降沿产生sdio_ck
bit 12~bit 11:宽总线模式使能位
00:默认总线模式,使用sdio_d0
01:4位总线模式,使用sdio_d[3:0]
10:8位总线模式,使用sdio_d[7:0]
bit 10:旁路时钟分频器
0:关闭旁路:驱动sdio_ck输出信号之前,依据clkdiv数值对sdioclk分频
1:使能旁路:sdioclk直接驱动sdio_ck输出信号
bit 9:省电配置位(为了省电,当总线为空闲时,设置pwrsav位可以关闭sdio_ck时钟输出)
0:始终输出sdio_ck
1:仅在有总线活动时才输出sdio_ck
bit 8:时钟使能位
0:sdio_ck关闭
1:sdio_ck使能
bit 7~bit 0:时钟分频系数
这个域定义了输入时钟(sdioclk)与输出时钟(sdio_ck)间的分频系数:sdio_ck频率=sdioclk/[clkdiv+2]
20.2.3 sdio参数寄存器:sdio_arg31302928272625242322212019181716
cmdarg[31:16]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
cmdarg[16:0]
bit 31~bit 0:命令参数
属于发送到卡中命令的一部分,如果一个命令包含一个参数,必须在写命令到命令寄存器之前加载这个寄存器
20.2.4 sdio命令响应寄存器:sdio_respcmd31302928272625242322212019181716
-
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- respcmd
bit 5~bit 0:响应的命令索引
只读位,包含最后收到的命令响应中的命令索引
20.2.5 sdio相应寄存器组:sdio_resp1~sdio_resp4寄存器短响应长响应
sdio_resp1 卡状态[31:0] 卡状态[127:96]
sdio_resp2 未使用 卡状态[95:64]
sdio_resp3 未使用 卡状态[31:0]
sdio_resp4 未使用 卡状态[31:0]
注:总是先收到卡状态的最高位,sdio_resp3寄存器的最低位始终为0。
20.2.6 sdio命令寄存器:sdio_cmd31302928272625242322212019181716
-
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- ce_atacmd nien encmdcomp1 sdiosuspend cpsmen waitpend waitint waitresp cmdindex
bit 14:ce-ata命令
如果设置该位,cpsm转至cmd61
bit 13:不使能中断
如果未设置该位,则使能ce-ata设备的中断
bit 12:使能cmd完成
如果设置该位,则使能命令完成信号
bit 11:sdio暂停命令
如果设置该位,则将要发送的命令是一个暂停命令(只能用于sdio卡)
bit 10:命令通道状态机使能位
如果设置该位,则使能cpsm
bit 9:cpsm等待数据传输结束(cmdpend内部信号)
如果设置该位,则cpsm在开始发送一个命令之前等待数据传输结束
bit 8:cpsm等待中断请求
如果设置该位,则cpsm关闭命令超时控制并等待中断请求
bit 7~bit 6:等待响应位
00:无响应,期待cmdsent标志
01:短响应,期待cmdrend或ccrcfail标志
10:无响应,期待cmdsent标志
11:长响应,期待cmdrend或ccrcfail标志
bit 5~bit 0:命令索引,作为命令的一部分发送到卡中
20.2.7 sdio数据定时器:sdio_dtimer31302928272625242322212019181716
datatime[31:16]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
datatime[15:0]
bit 31~bit 0:数据超时时间,以卡总线时钟周期为单位的数据超时时间
20.2.8 sdio数据长度寄存器:sdio_dlen31302928272625242322212019181716
- datalength[24:16]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
datalength[15:0]
bit 24~bit 0:数据长度,要传输的数据字节数目
20.2.9 sdio数据控制寄存器:sdio_dctrl31302928272625242322212019181716
-
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- sdioen rwmod rwstop rwstart dblocksize dmaen dtmode dtdir dten
bit 11:sdio使能功能
如果设置了该位,则dpsm执行sdio卡特定的操作
bit 10:读等待模式
0:停止sdio_ck控制读等待
1:使用sdio_d2控制读等待
bit 9:读等待停止
0:如果设置了rwstart,执行读等待
1:如果设置了rwstart,停止读等待
bit 8:读等待开始
设置该位开始读等待操作
bit 7~bit 4:数据块长度,当选择了块数据传输模式,该域定义数据块长度
0000:块长度=1字节
0001:块长度=2字节
0010:块长度=4字节
0011:块长度=8字节
0100:块长度=16字节
0101:块长度=32字节
0110:块长度=64字节
0111:块长度=128字节
1000:块长度=256字节
1001:块长度=512字节
1010:块长度=1024字节
1011:块长度=2048字节
1100:块长度=4096字节
1101:块长度=8192字节
1110:块长度=16384字节
1111:保留
bit 3:dma使能位
0:关闭dma 1:使能dmabit 2:数据传输模式
0:块数据传输 1:流数据传输bit 1:数据传输方向
0:控制器至卡 1:卡至控制器bit 0:数据传输使能位
如果设置该位为1,则开始数据传输。根据dtsir方向位,dpsm进入wait_s或wait_r状态,如果在传输的一开始就设置了rwstart位,则dpsm进入读等待状态。不需要在数据传输结束后清除使能位,但必须更改sdio_dctrl以允许新的数据传输。20.2.10 sdio状态寄存器:sdio_sta31302928272625242322212019181716
- ceataend sdioit rxdavl txdavl rxfifoe txfifoe rxfifof txfifof
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
rxfifohf txfifohf rxact txact cmdact dbckend stbiterr dataend cmdsent cmdrend rxoverr txunderr dtmeout ctimeout dcrcfail ccrcfail
bit 23:在cmd61接收到ce-ata命令完成信号
bit 22:收到sdio中断
bit 21:在接收fifo中的数据可用
bit 20:在发送fifo中的数据可用
bit 19:接收fifo空
bit 18:发送fifo空
若使用了硬件流控制,当fifo包含2个字时,txfifoe信号变为有效。bit 17:接收fifo满
若使用了硬件流控制,当fifo还差2个字满时,rxfifof信号变为有效。bit 16:发送fifo满
bit 15:接收fifo半满,fifo中至少还有8个字
bit 14:发送fifo半空,fifo中至少还可以写入8个字。
bit 13:正在接收数据
bit 12:正在发送数据
bit 11:正在传输命令
bit 10:已发送/接收数据块(crc检测成功)
bit 9:在宽总线模式,没有在所有数据信号上检测到起始位
bit 8:数据结束(数据计数器,sdio_dcount=0)
bit 7:命令已发送(不需要响应)
bit 6:已接收到响应(crc检测成功)
bit 5:接收fifo上溢错误
bit 4:发送fifo下溢错误
bit 3:数据超时
bit 2:命令响应超时
命令超时时间是一个固定的值,为64个sdio_ck时钟周期。bit 1:已发送/接收数据块(crc检测失败)
bit 0:已收到命令响应(crc检测失败)
注:状态寄存器可以用来查询sdio控制器的当前状态。比如sdio_sta的位2表示命令响应超时,说明sdio的命令响应出了问题。我们通过设置sdio_icr的位2则可以清除这个超时标志,而设置sdio_mask的位2,则可以开启命令响应超时中断,设置为0关闭。
20.2.11 sdio数据fifo寄存器:sdio_fifo31302928272625242322212019181716
fifodata[31:16]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
fifodata[15:0]
bit 31~bit 0:接收或发送fifo数据
fifo数据占据32个32位的字,地址为:sdio基址+0x80至sdio基址+0xfc
数据fifo寄存器包括接收和发送fifo,他们由一组连续的32个地址上的32个寄存器组成,cpu可以使用fifo读写多个操作数。例如我们要从sd卡读数据,就必须读sdio_fifo寄存器,要写数据到sd卡,则要写sdio_fifo寄存器。sdio将这32个地址分为16个一组,发送接收各占一半。而我们每次读写的时候,最多就是读取发送fifo或写入接收fifo的一半大小的数据,也就是8个字(32个字节),在操作sdio_fifo(不论读出还是写入)必须是以4字节对齐的内存进行操作,否则将导致出错。
20.3 sd卡初始化20.3.1 卡上电流程
20.3.2 卡初始化流程
20.4 实验例程功能:利用stm32f1的sdio模块读取sd的容量并通过tftlcd显示。
(1)创建sdio_sdcard.h文件,并输入以下代码。
#ifndef _sdio_sdcard_h_#define _sdio_sdcard_h_#include sys.h/********************************************************************************************************* sd 卡 数 据 定 义*********************************************************************************************************///用户配置区#define sdio_init_clk_div 0x76 //sdio初始化频率,最大400kh#define sdio_transfer_clk_div 0x00 //sdio传输频率,该值太小可能会导致读写文件出错//sdio工作模式定义,通过sd_setdevicemode函数设置#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, //收到命令响应(crc校验失败) sd_data_crc_fail = 2, //发送/接收数据块(crc校验失败) sd_cmd_rsp_timeout = 3, //命令响应超时 sd_data_timeout = 4, //数据超时 sd_tx_underrun = 5, //传输fifo运行 sd_rx_overrun = 6, //接收fifo运行 sd_start_bit_err = 7, //在宽总线模式下的所有数据信号上未检测到起始位 sd_cmd_out_of_range = 8, //cmd的参数超出范围 sd_addr_misaligned = 9, //地址没有对齐 sd_block_len_err = 10, //卡不允许传输的块长度,或者传输的字节数与块长度不匹配 sd_erase_seq_err = 11, //擦除命令序列中发生错误 sd_bad_erase_param = 12, //删除组的无效选择 sd_write_prot_violation = 13, //试图对写保护块进行编程 sd_lock_unlock_failed = 14, //在unlock命令中检测到序列或密码错误,或者试图访问锁定的卡 sd_com_crc_failed = 15, //前一个命令的crc校验失败 sd_illegal_cmd = 16, //命令对卡状态无效 sd_card_ecc_failed = 17, //已应用卡内部ecc,但未能更正数据 sd_cc_error = 18, //内部卡控制错误 sd_general_unknown_error = 19, //通用或未知错误 sd_stream_read_underrun = 20, //卡无法在流读取操作中维持数据传输 sd_stream_write_overrun = 21, //卡无法维持流模式下的数据编程 sd_cid_csd_overwrite = 22, //cid/csd覆盖错误 sd_wp_erase_skip = 23, //只删除了部分地址空间 sd_card_ecc_disabled = 24, //命令已在不使用内部ecc的情况下执行 sd_erase_reset = 25, //在执行之前清除了擦除序列,因为收到了一个超出擦除序列的命令 sd_ake_seq_error = 26, //身份验证顺序错误 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 } sd_error; //sd卡csd寄存器数据 typedef struct{ u8 csdstruct; //csd结构 u8 sysspecversion; //系统版本 u8 reserved1; //保留 u8 taac; //数据读取访问时间1 u8 nsac; //在clk期间的数据读取访问时间2 u8 maxbusclkfrec; //总线最高频率 u16 cardcomdclasses; //卡命令类 u8 rdblocklen; //最大读取数据块长度 u8 partblockread; //允许读取的部分块 u8 wrblockmisalign; //写入块未对准 u8 rdblockmisalign; //读取块未对准 u8 dsrimpl; //dsr生效 u8 reserved2; //保留 u32 devicesize; //驱动大小 u8 maxrdcurrentvddmin; //最大读取流@ vdd最小 u8 maxrdcurrentvddmax; //最大读取流@ vdd最大 u8 maxwrcurrentvddmin; //最大写入流@ vdd最小 u8 maxwrcurrentvddmax; //最大写入流@ vdd最大 u8 devicesizemul; //驱动大小 u8 erasegrsize; //擦除组大小 u8 erasegrmul; //擦除组大小乘数 u8 wrprotectgrsize; //写保护组大小 u8 wrprotectgrenable; //写保护组使能 u8 mandeflecc; //制造商默认ecc u8 wrspeedfact; //写入速度因子 u8 maxwrblocklen; //最大写入数据长度 u8 writeblockpapartial; //允许写入的部分块 u8 reserved3; //保留 u8 contentprotectappli; //内容保护程序 u8 fileformatgrouop; //文件格式组 u8 copyflag; //复制标志otp u8 permwrprotect; //永久写保护 u8 tempwrprotect; //临时写保护 u8 fileformat; //文件格式 u8 ecc; //ecc 代码 u8 csd_crc; //csd crc u8 reserved4; //总是1} sd_csd; //sd卡cid寄存器数据typedef struct{ u8 manufacturerid; //制造商 u16 oem_appliid; //oem/应用程序id u32 prodname1; //名称部分1 u8 prodname2; //名称部分2 u8 prodrev; //版本 u32 prodsn; //序列号 u8 reserved1; //保留1 u16 manufactdate; //出厂日期 u8 cid_crc; //cid crc u8 reserved2; //总是1} sd_cid; //sd卡状态typedef enum{ sd_card_ready = 0x00000001, sd_card_identification = 0x00000002, sd_card_standby = 0x00000003, sd_card_transfer = 0x00000004, sd_card_sending = 0x00000005, sd_card_receiving = 0x00000006, sd_card_programming = 0x00000007, sd_card_disconnected = 0x00000008, sd_card_error = 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相关标志位,拷贝自:stm32f4xx_sdio.h#define sdio_flag_ccrcfail 0x00000001#define sdio_flag_dcrcfail 0x00000002#define sdio_flag_ctimeout 0x00000004#define sdio_flag_dtimeout 0x00000008#define sdio_flag_txunderr 0x00000010#define sdio_flag_rxoverr 0x00000020#define sdio_flag_cmdrend 0x00000040#define sdio_flag_cmdsent 0x00000080#define sdio_flag_dataend 0x00000100#define sdio_flag_stbiterr 0x00000200#define sdio_flag_dbckend 0x00000400#define sdio_flag_cmdact 0x00000800#define sdio_flag_txact 0x00001000#define sdio_flag_rxact 0x00002000#define sdio_flag_txfifohe 0x00004000#define sdio_flag_rxfifohf 0x00008000#define sdio_flag_txfifof 0x00010000#define sdio_flag_rxfifof 0x00020000#define sdio_flag_txfifoe 0x00040000#define sdio_flag_rxfifoe 0x00080000#define sdio_flag_txdavl 0x00100000#define sdio_flag_rxdavl 0x00200000#define sdio_flag_sdioit 0x00400000#define sdio_flag_ceataend 0x00800000//sdio 指令集#define sd_cmd_go_idle_state 0#define sd_cmd_send_op_cond 1#define sd_cmd_all_send_cid 2#define sd_cmd_set_rel_addr 3 //sd卡的sdio_send_rel_addr#define sd_cmd_set_dsr 4#define sd_cmd_sdio_sen_op_cond 5#define sd_cmd_hs_switch 6#define sd_cmd_sel_desel_card 7#define sd_cmd_hs_send_ext_csd 8#define sd_cmd_send_csd 9#define sd_cmd_send_cid 10#define sd_cmd_read_dat_until_stop 11 //sd卡不支持#define sd_cmd_stop_transmission 12#define sd_cmd_send_status 13#define sd_cmd_hs_bustest_read 14#define sd_cmd_go_inactive_state 15#define sd_cmd_set_blocklen 16#define sd_cmd_read_single_block 17#define sd_cmd_read_mult_block 18#define sd_cmd_hs_bustest_write 19#define sd_cmd_write_dat_until_stop 20 #define sd_cmd_set_block_count 23 #define sd_cmd_write_single_block 24#define sd_cmd_write_mult_block 25#define sd_cmd_prog_cid 26#define sd_cmd_prog_csd 27#define sd_cmd_set_write_prot 28#define sd_cmd_clr_write_prot 29#define sd_cmd_send_write_prot 30#define sd_cmd_sd_erase_grp_start 32 //设置要擦除的第一个写块的地址sd卡独有#define sd_cmd_sd_erase_grp_end 33 //设置要擦除的连续范围的最后一个写块的地址sd卡独有#define sd_cmd_erase_grp_start 35 //设置要擦除的第一个写块的地址mmc card 3.31#define sd_cmd_erase_grp_end 36 //设置要擦除的连续范围的最后一个写块的地址mmc card 3.31#define sd_cmd_erase 38#define sd_cmd_fast_io 39 //sd卡不支持#define sd_cmd_go_irq_state 40 //sd卡不支持#define sd_cmd_lock_unlock 42#define sd_cmd_app_cmd 55#define sd_cmd_gen_cmd 56#define sd_cmd_no_cmd 64#define sd_cmd_app_sd_set_buswidth 6 //sd卡独有#define sd_cmd_sd_app_staus 13 //sd卡独有#define sd_cmd_sd_app_send_num_write_blocks 22 //sd卡独有#define sd_cmd_sd_app_op_cond 41 //sd卡独有#define sd_cmd_sd_app_set_clr_card_detect 42 //sd卡独有#define sd_cmd_sd_app_send_scr 51 //sd卡独有#define sd_cmd_sdio_rw_direct 52 //sd卡io独有#define sd_cmd_sdio_rw_extended 53 //sd卡io独有#define sd_cmd_sd_app_get_mkb 43 //sd卡独有#define sd_cmd_sd_app_get_mid 44 //sd卡独有#define sd_cmd_sd_app_set_cer_rn1 45 //sd卡独有#define sd_cmd_sd_app_get_cer_rn2 46 //sd卡独有#define sd_cmd_sd_app_set_cer_res2 47 //sd卡独有#define sd_cmd_sd_app_get_cer_res1 48 //sd卡独有#define sd_cmd_sd_app_secure_read_multiple_block 18 //sd卡独有#define sd_cmd_sd_app_secure_write_multiple_block 25 //sd卡独有#define sd_cmd_sd_app_secure_erase 38 //sd卡独有#define sd_cmd_sd_app_change_secure_area 49 //sd卡独有#define sd_cmd_sd_app_secure_write_mkb 48 //sd卡独有//支持的sd卡定义#define sdio_std_capacity_sd_card_v1_1 0x00000000#define sdio_std_capacity_sd_card_v2_0 0x00000001#define sdio_high_capacity_sd_card 0x00000002#define sdio_multimedia_card 0x00000003#define sdio_secure_digital_io_card 0x00000004#define sdio_high_speed_multimedia_card 0x00000005#define sdio_secure_digital_io_combo_card 0x00000006#define sdio_high_capacity_mmc_card 0x00000007//sdio相关参数定义#define null 0#define sdio_static_flags 0x000005ff#define sdio_cmd0timeout 0x00010000 #define sdio_datatimeout 0xffffffff #define sdio_fifo_address 0x40018080//mask for errors card status r1 ocr register #define sd_ocr_addr_out_of_range 0x80000000#define sd_ocr_addr_misaligned 0x40000000#define sd_ocr_block_len_err 0x20000000#define sd_ocr_erase_seq_err 0x10000000#define sd_ocr_bad_erase_param 0x08000000#define sd_ocr_write_prot_violation 0x04000000#define sd_ocr_lock_unlock_failed 0x01000000#define sd_ocr_com_crc_failed 0x00800000#define sd_ocr_illegal_cmd 0x00400000#define sd_ocr_card_ecc_failed 0x00200000#define sd_ocr_cc_error 0x00100000#define sd_ocr_general_unknown_error 0x00080000#define sd_ocr_stream_read_underrun 0x00040000#define sd_ocr_stream_write_overrun 0x00020000#define sd_ocr_cid_csd_overwriete 0x00010000#define sd_ocr_wp_erase_skip 0x00008000#define sd_ocr_card_ecc_disabled 0x00004000#define sd_ocr_erase_reset 0x00002000#define sd_ocr_ake_seq_error 0x00000008#define sd_ocr_errorbits 0xfdffe008//masks for r6 response #define sd_r6_general_unknown_error 0x00002000#define sd_r6_illegal_cmd 0x00004000#define sd_r6_com_crc_failed 0x00008000#define sd_voltage_window_sd 0x80100000#define sd_high_capacity 0x40000000#define sd_std_capacity 0x00000000#define sd_check_pattern 0x000001aa#define sd_voltage_window_mmc 0x80ff8000#define sd_max_volt_trial 0x0000ffff#define sd_allzero 0x00000000#define sd_wide_bus_support 0x00040000#define sd_single_bus_support 0x00010000#define sd_card_locked 0x02000000#define sd_card_programming 0x00000007#define sd_card_receiving 0x00000006#define sd_datatimeout 0xffffffff#define sd_0to7bits 0x000000ff#define sd_8to15bits 0x0000ff00#define sd_16to23bits 0x00ff0000#define sd_24to31bits 0xff000000#define sd_max_data_length 0x01ffffff#define sd_halffifo 0x00000008#define sd_halffifobytes 0x00000020//command class supported #define sd_cccc_lock_unlock 0x00000080#define sd_cccc_write_prot 0x00000040#define sd_cccc_erase 0x00000020 //cmd8指令#define sdio_send_if_cond 0x00000008/********************************************************************************************************* 函 数 列 表*********************************************************************************************************/sd_error sd_init( void ) ; //初始化sd卡u8 sd_readdisk( u8*buf, u32 sector, u8 cnt ) ; //读sd卡,fatfs/usb调用u8 sd_writedisk( u8*buf, u32 sector, u8 cnt ) ; //写sd卡,fatfs/usb调用#endif(2)创建sdio_sdcard.c文件,并输入以下代码。
#include sdio_sdcard.h#include string.hsd_cardinfo sdcardinfo ; //sd卡信息u8 cardtype=sdio_std_capacity_sd_card_v1_1 ; //sd卡类型/***************************************************name :sdio_clock_setfunction :sdio发送命令函数paramater : cmdindex:命令索引,低六位有效 waitrsp:期待的相应 00/10:无响应 01:短响应 11:长响应 arg:参数return :错误代码***************************************************/void sdio_send_cmd( 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 ; tmpreg &= 0xffffff00 ; tmpreg |= clkdiv; sdio->clkcr = tmpreg ;}/***************************************************name :cmderrorfunction :检查cmd0的执行状态paramater :nonereturn :错误代码***************************************************/sd_error cmderror(){ sd_error errorstatus = sd_ok ; u32 timeout=sdio_cmd0timeout ; while( timeout-- ) { //命令已发送(无需响应) if( sdio->sta&( 1sta ; if( ( status&0x45 )!=0 ) //等待接收到应答 break ; } //响应超时 if( ( timeout==0 )||( ( status&0x04 )==0x04 ) ) { //当前卡不是2.0兼容卡,或者不支持设定的电压范围 sdio->icr |= 1
icr |= 1resp1&0xfdffe008 ) ; //返回卡响应}/***************************************************name :cmdresp3errorfunction :检查r3响应的错误状态paramater :nonereturn :错误代码***************************************************/sd_error cmdresp3error(){ u32 status ; while( 1 ) { status = sdio->sta ; if( ( status&0x45 )!=0 ) //等待接收到应答 break ; } //响应超时 if( ( status&0x04 )==0x04 ) { sdio->icr |= 1sta ; if( ( status&0x45 )!=0 ) //接收到r2响应 break ; } //响应超时 if( ( timeout==0 )||( ( status&0x04 )==0x04 ) ) { sdio->icr |= 1icr = 0x5ff ; //清除所有标记 rspr1 = 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 ;}/***************************************************name :sdio_irqhandlerfunction :sdio中断服务函数paramater :nonereturn :错误代码***************************************************/void sdio_irqhandler(){ //接收完成中断 if( sdio->sta&( 1clkcr |= 020 ; //单位换算 sprintf( ( char * )str, sd size: %4d mb, temp ) ; lcd_showstring( 30, 170, str ) ; //显示sd卡容量 while(1) { }}
工业软件与中国制造业崛起
20W A+C双口pd快充充电器方案|一站式服务
最高直降400,iQOO Z3携新版本新配色而来!618优惠力度惊人
意法半导体晶圆厂发生火灾 或影响最新一代iPhone生产
redis锁incres防止重复提交
SD卡基础读写实验
四川丹甫压缩机产线成功应用RFID读写器
小米5C移动版再度开售 3GB内存64GB存储售价1499
比特币中国将关停!9月30日全面停止交易业务,期间客户仍可提币!虚拟货币的未来走势
oppor11什么时候上市?oppor11最新消息:oppor11系列或2999元起售,规格配置全面曝光
电流隔离能有效防范因变化不定的电位所引起的接地平面中的扰动
电化学阳极氧化纳米晶多孔硅层的合成和表征研究
智能包装工业物联网数字解决方案
无源元件之——电阻器基础知识(超全)
电池材料之回收
韩国棋手利用AI作弊事件层出不穷
高大上的“量子计算机”是怎么来的?
海莱照明采购南京大展的DZDR-S 导热系数测试仪
三维目标检测失效的情况下如何实现避障
透露世界首款K波段数据转换器EV12DS460A背后的设计秘密