STM32读写SD卡
STM32F103调试读SD卡经验总结
WL板子EK-STM32F103调试读S D卡经验总结一开始碰到的问题:发送CMD0能执行返回01,CMD1超时没响应。
查到原因:模板程序控制SD供电逻辑反了。
#if 0#define MSD_POWER_ON() GPIO_ResetBits(GPIOD, GPIO_Pi n_10)#define MSD_POWER_OFF() GPIO_SetBits(GPIOD, GPIO_Pin_10)#else#define MSD_POWER_ON() GPIO_SetBits(GPIOD, GPIO_Pin_10)#define MSD_POWER_OFF() GPIO_ResetBits(GPIOD, GPIO_Pin_10)#endif第二个问题:单步执行CMD0,CMD1,有响应,直接运行没响应。
查到原因,上电时间少于1ms,SD卡内部复位没准备好,初始化前加廷时1ms./* delay 1ms*/delay(5000);/* MSD chip select low */MSD_CS_LOW();/* Send CMD0 (GO_IDLE_STATE) to put MSD in SPI m ode */MSD_SendCmd(MSD_GO_IDLE_STATE, 0, 0x95);第三个问题:有时执行还是没有响应。
原因,SD卡初始化SPI时钟要在100kHz到400kHz之间,更改SPI速率为180kHz.第四个问题:读SD卡CSD寄存器没返回数据。
原因:供电不足,平时只有2.9V,SPI通迅时,出现瞬间低于2.7V现像。
短接直接供3.3V,如附图。
继续其它试验。
出处:kimfufree[最后修改于2008-09-03 19:58]。
STM32+SDIO+FATFS文件系统直读SD卡
STM32+SDIO+FATFS文件系统直读SD卡STM32+SDIO+FATFS文件系统直读SD卡网上关于小型嵌入式的文件系统有好多~当然要数FATFS 很是出名一来小巧,二来免费。
当然了国产的振南的znFAT 一样开源好用而且极其的省资源~!非常适合51单片。
更重要的是国语的支持,呵呵!这次在STM32上为SD卡移植文件系统还是非常简单顺利的,这多亏了ST 官方提供的驱动,而我自己不用动手编写SD卡的命令省了很多时间而且官方做的驱动虽然效率一般但是极其严谨我很是佩服。
FATFS的官方网站是znFAT的官方网站是SD卡可以用SPI驱动也可以直接用SDIO 驱动STM32 256KB FLASH 以上的片子全部都有SDIO,我们当然要用高速快捷的SDIO 方式了!至于 SDIO 又有 1位 4位 8 位的之分我想不来8位SDIO 是怎么回事?SD卡上最多只能接4位嘛~网上有人说4位的SDIO 不好用多半是固件版本太老的缘故了。
呵呵这里还是要靠库~STM32真适合懒人用。
网上关于的FATFS 的文章很多不过都太老旧,很多东西已经不适用了。
我建议阁下到官方去下载最新的版本目前是最新是R0.08b,使用最新的版本好处是很多网上很多要改来改去的地方只要你使用了新版本那就是完全可以规避的。
另外STM32 的SDIO驱动也一定要用最新的,老版本问题很多不少人的失败就在这。
我这次用的是V3.3的库没有任何改动就可以了,现在最新的好像在3.4以上了。
好了说说移植ffconf.h是配置的头文件简单的修改宏就可以了,英文注释的很完全而且网上也有翻译我不多说了自己看主要在这里进行功能裁剪写写我的配置。
#define _FS_TINY 0 /* 0:Normal or 1:Tiny 完整的FATFS 精简版的是Tiny */#define _FS_READONLY 0 /* 0:Read/Write or 1:Read only 能读能写*/#define _FS_MINIMIZE 1 /* 0 to 3 简单的裁剪f_mkdir, f_chmod..这些功能没法用的*//* The _FS_MINIMIZE option defines minimization level to remove some functions.// 0: Full function./ 1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename/ are removed./ 2: f_opendir and f_readdir are removed in addition to 1./ 3: f_lseek is removed in addition to 2. */#define _USE_STRFUNC 0 /* 0:Disable or 1/2:Enable是否使用字符串文件接口 *//* To enable string functions, set _USE_STRFUNC to 1 or 2. */#define _USE_MKFS 0 /* 0:Disable or 1:Enable 制作文件系统我在PC上一般已经格式化好了*//* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */#define _USE_FORWARD 0 /* 0:Disable or 1:Enable 发文件流?*//* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */#define _USE_FASTSEEK 0 /* 0:Disable or 1:Enable 搜索*//* To enable fast seek feature, set _USE_FASTSEEK to 1. */#define _CODE_PAGE 1 / /1 - ASCII only (Valid for non LFN cfg.)#define _USE_LFN 0 /* 0 to 3 */#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) 这些都是长文件名或是汉字文件支持很费资源所以不开启这些*/#define _FS_SHARE 0 /* 0:Disable or >=1:Enable 不使用相对路径*/#define _FS_SHARE 0 /* 0:Disable or >=1:Enable 文件共享多任务的操作系统会用到的*/#define _FS_REENTRANT 0 /* 0:Disable or 1:Enable 这些是啥用?同步什么呢?默认就好了*/#define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */#define _SYNC_t HANDLE /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */integer.h主要定义了文件的类型若是最新的可以不用修改。
STM32单片机的SD卡存储器读写模块设计
S T M 32单片机的S D卡存储器读写模块设计*汤才刚,刘京京,沈瑞东(上海舜华新能源系统有限公司,上海201806)*基金项目:上海市科学技术委员会科研计划项目(19D Z 1206100)㊂摘要:为了满足氢气加气机在使用过程中大量数据的存储要求,设计了一种利用S D 卡来扩展存储的电路模块㊂在兼顾稳定性与读写速度的基础上,利用S TM 32单片机与S D 卡存储器件的接口技术,把S D 卡的读写以及F a t F S 文件系统的读取写入移植到此单片机上㊂在读写的过程中,使用W i n d o w s 中通用的文件系统使最终存储在S D 卡的数据可以直接被P C 机读取,使开发利用有更好的扩展性,取得了良好的效果㊂硬件设计方面利用单片机的S P I 接口,只需要4个I /O 口再加上几个上拉电阻就可实现与S D 卡的接口电路,软件设计方面利用I A R 嵌入式集成开发环境和移植S TM 32库函数,并使用开源的F a t F S 文件系统模块源代码实现单片机S D 卡的读写编程㊂关键词:S TM 32单片机;S P I 接口;S D 卡;F a t F S 文件系统中图分类号:T P 274 文献标识码:AD e s i gn o f S D C a r d R e a d -w r i t e M o d u l e B a s e d o n S T M 32T a n g C a i g a n g ,L i u J i n g j i n g ,S h e n R u i d o n g(S h a n g h a i S u n w i s e E n e r g y S y s t e m s C o .,L t d .,S h a n gh a i 201806,C h i n a )A b s t r a c t :I n o r d e r t o m e e t t h e l a r g e d a t a s t o r a g e r e q u i r e m e n t s o f t h e h y d r o g e n d i s pe n s e r i n u s e ,a c i r c u i t m o d u l e t h a t u s e s S D c a r d t o e x -p a n d s t o r a g e i s d e s i g n e d .B a s e d o n t h e b a l a n c e b e t w e e n s t a b i l i t y a n d r e a d i n g a n d w r i t i n g s p e e d ,t h e i n t e rf a c e t e c h n o l og y of S TM 32m i c r o -c o n t r o l l e r a n d S D c a r d s t o r ag e d e v i c e a r e u s e d t o t r a n s p l a n t S D c a r d r e a d a n d w r i t e a n d F a t F S f i l e s ys t e m r e a d a n d w r i t e t o t h i s m i c r o -c o n t r o l l e r .I n t h e p r o c e s s o f r e a d a n d w r i t e ,t h e g e n e r a l f i l e s y s t e m i n W i n d o w s i s u s e d ,s o t h a t t h e d a t a f i n a l l y st o r e d i n t h e S D c a r d c a n b e d i r e c t l y r e a d b y t h e P C ,t h e d e v e l o p m e n t a n d u t i l i z a t i o n h a v e b e t t e r s c a l a b i l i t y a n d a c h i e v e d g o o d r e s u l t s .T h e h a r d w a r e d e s i gn u s e s t h e S P I i n t e r f a c e o f t h e s i n g l e -c h i p m i c r o c o m p u t e r ,o n l y f o u r I /O p o r t s a n d a f e w p u l l -u p r e s i s t o r s c a n b e u s e d t o a c h i e v e t h e i n t e r f a c e c i r c u i t w i t h t h e S D c a r d .T h e s o f t w a r e d e s i g n u s e s t h e I A R e m b e d d e d i n t e g r a t e d d e v e l o p m e n t e n v i r o n m e n t a n d t r a n s p l a n t a t i o n o f S TM 32l i b r a r y f u n c t i o n s ,a n d u s e t h e s o u r c e c o d e o f t h e o p e n s o u r c e F a t F S f i l e s y s t e m m o d u l e t o r e a l i z e t h e p r o g r a mm i n g of t h e r e a d a n d w r i t e o f t h e S D c a r d o f t h e s i ng l e chi p m i c r o c o m pu t e r .K e y wo r d s :S TM 32m i c r o c o n t r o l l e r ;S P I i n t e r f a c e ;S D c a r d ;F a t F S f i l e s y s t e m 引 言在氢气加气机使用过程中,经常需要存储大量的数据,并且要求在P C 机上查看数据㊂由于受单片机自身硬件的限制,为了实现大容量数据的存储,必须要通过单片机的外部扩展来实现,现在通常使用的方法是在单片机外部扩展大容量E E P R OM 或F L A S H 芯片来实现㊂这种方法有两个缺点:①外部电路连接复杂,通用性差,对于不同的系统需要采用不同的电路;②存储的数据读取非常不便,需要通过单片机系统发给P C 机来实现㊂综合比较后发现,最适合单片机系统的莫过于S D 卡了,不仅容量大(32G B 以上),而且支持S P I 方式,方便移动,能满足不同应用的要求㊂只需要几个I /O 口就可以外扩一个最大达32G B 以上的外部存储器,容量选择范围很大,更换也很方便,而且方便移动,编程也简单,是单片机大容量外部存储器的首选[1]㊂为了方便管理S D 卡中的文件㊁高效地读写数据,需要在S D 卡中装载文件系统㊂因F a t F S 文件系统源码开源,资源占用少,兼容性好,使用范围较广,本设计利用S TM 32自带的S P I 接口与S D 卡连接通信,电路非常简单且易于实现,并成功实现了在S D 卡中建立F a t F S 文件系统,使S TM 32单片机能够对S D 卡中的文件进行读写等操作㊂1 系统结构设计S D 卡一般支持2种模式:S D 模式和S P I 模式㊂主机可以选择任意一种模式与S D 卡通信,S D 模式允许4线的高速数据传输,S P I 模式允许简单地通过S P I 接口与S D 卡通信,这种模式较S D 模式速度慢,不过利用S T M 32片内集成的S P I 接口,最大通信速度可达18M b ps ,每秒可传输数据2M B 以上,对于单片机系统一般的应用足够了[2]㊂本设计的S D 卡接口电路硬件设计主要由以S TM 32F 103V C T 6为核心的处理器㊁电源和S D 卡及连接器组成㊂系统整体结构如图1所示㊂图1 系统整体结构2 硬件电路设计2.1 S T M 32单片机简介本设计使用的主控制芯片为S TM 32F 103V C T 6单片机,主要实现S P I 模块功能,控制S D 卡的读写㊂该单片机是S T 公司推出的基于A R M C o r t e x M 3内核的高性能的微处理器芯片,该单片机功能强大,处理速度快㊂在软件开发上应用I A R E m b e d d e d W o r k b e n c h f o r A R M 嵌入式集成开发平台进行产品研发,使得整个产品的研发周期大大缩短并使整个程序移植性强,方便易懂㊂S TM 32F 103V C T 6单片机工作主频最高可达72MH z ,封装形式为L Q F P 100,内置256K B F L A S H 和48K B R AM ,包含10个定时器㊁2个12位的A D C ㊁5个异步通信串行口U S A R T ㊁3个同步通信串行口S P I ㊁2个I 2C接口㊁80个I /O 口等[4-5]㊂2.2 S D 卡接口电路设计本设计中使用S TM 32F 103V C T 6单片机P B 12㊁P B 13㊁P B 14㊁P B 15这4个I /O 口所组成的片内集成S P I 2接口与S D 卡进行通信,单片机为主设备,S D 卡为从设备㊂所对应的信号线分别为S D _C S 为片选控制端;S D _S C K 为主设备时钟输出端口,与S D 卡时钟输入端口相连;S D _M I S O 为主设备输入端口,与S D 卡数据输出端口相连;S D _MO S I 为主设备输出端口,与S D 卡数据输入端口相连㊂这4根信号线必须接上拉电阻[3],连接电路图如图2所示㊂3 软件设计3.1 F a t F S 文件系统应用F a t F S 是一个开源的㊁为小型嵌入式系统设计的通用F A T 文件系统模块,支持F A T 12㊁F A T 16与F A T 32,支持多种存储媒介,有独立的缓冲区,可对多个文件进行读图2 S D 卡与S T M 32连接电路图写,可裁剪的文件系统㊂F a t F S 的编写遵循A N S I C ,并且完全与磁盘I /O 层分开㊂因此,它独立(不依赖)于硬件架构,可以被嵌入到低成本的微控制器中(如A V R ㊁8051㊁P I C ㊁A R M ㊁Z 80㊁68K 等),而不需要做任何修改㊂F a t F S 文件系统在嵌入式软件开发中的应用图如图3所示,应用图3 F a t F S 文件系统应用图层使用F a t F S 提供的A P I 函数与F a t F S 模块通信,F a t F S 模块使用F a t F S 提供的底层存储介质接口函数对存储设备进行配置,只有这样才能正常使用F a t F S 文件系统[4]㊂3.2 程序流程图主程序总体设计思路:系统上电后单片机内部进行复位,接着对系统时钟㊁G P I O 口㊁S D 卡接口硬件进行配置㊂G P I O 口初始化时需要使能S P I 2时钟,否则无法开启单片机的S P I 模式㊂接下来配置F a t F S 底层存储介质接口函数,将S D 驱动有关的操作放在文件系统中,再调用A P I 接口函数,将S D 卡写入数据或读取S D 卡的数据,最后在P C机上对数据进行验证[5]㊂主程序设计流程如图4所示㊂3.3 S D 卡S P I 模式下的命令S P I 模式下S D 卡和单片机的通信采用发送应答机制㊂首先主机端(单片机)发送命令c o mm a n d ,S D 卡应答r e s po n s e ㊂S D 卡的命令格式如下:字节1字节2~5字节6765 031 07 1001c o mm a n d命令参数C R C1S D 卡的指令由6个字节组成,字节1的最高2位固图4 程序流程图定为01,低6位为命令号,比如C M D 17,为10001即16进制0x 11,完整的C M D 17,第一个字节为01010001,即0x 11+0x 40;字节2~5位为命令参数,有些命令没有参数;字节6的高7位为C R C 值,最低位固定为1㊂S D 卡的命令总共有12类,下面是几个重要的命令,如表1所列㊂表1 S D 卡部分操作指令命 令参 数回 应描 述C MD 0(0x 00)N O NE R 1复位S D 卡C MD 8(0x 08)V H S +C h e c kP a t t e r nR 7发送接口状态命令C M D 9(0x 09)N O N E R 1读取卡特定数据寄存器C M D 10(0x 0A )N O N E R 1读取卡标志数据寄存器C M D 16(0x 10)按大小R 1设置块大小(字节数)C M D 17(0x 11)地址R 1读取一个块的数据C M D 24(0x 18)地址R 1写入一个块的数据C M D 41(0x 29)N O N E R 3发送给主机容量支持信息和激活卡初始化过程C M D 55(0x 37)N O N E R 1告诉S D 卡,下一个是特定应用命令C MD 58(0x 3A )N O N ER 3读取O C R 寄存器单片机每发送一条命令,S D 卡都会给出一个应答,以告知主机该命令的执行情况,或者返回主机需要获取的数据㊂S P I 模式下,S D 卡针对不同的命令,应答可以是R 1~R 7,其中R 1的应答最多,其格式如下:B I T 7B I T 6B I T 5B I T 4B I T 3B I T 2B I T 1B I T 00参数错误地址错误连续擦除错误命令C R C 错误非法命令擦除错误I D L E状态3.4 S D 卡初始化对S D 卡进行初始化操作,是S D 卡进行正常数据读写的前提㊂S D 卡接入后,默认进入S D 模式,等待电压稳定需上电延时250m s 即等待至少74个时钟周期㊂将时钟周期频率设置为100~400k H z ,拉低片选信号C S,发送C M D 0㊂如果收到应答信号01H ,则表示S D 卡进入S P I 模式[6]㊂S D 卡的初始化过程如下:①初始化与S D 卡连接的硬件配置(C P U 的S P I 配置,I /O 口配置);②上电延时(大于74个C L K );③复位卡(C M D 0),进入I D L E 状态;④发送C M D 8,检查是否支持2.0协议;⑤根据不同协议检查S D 卡并判断卡类型(命令包括C M D 1㊁C M D 55㊁C M D 41和C MD 58等);⑥取消片选,发送8个C L K 后,结束初始化㊂这样就完成了对S D 卡的初始化,注意末尾发送的8个C L K 是提供S D 卡额外的时钟,完成某些操作㊂通过S D 卡初始化可以知道S D 卡的类型(V 1㊁V 2㊁V 2H C 或MM C ),在完成初始化之后,就可以开始读写数据了㊂3.5 S D 卡读写操作S D 卡的数据读写以块为单位,一个块的最大长度为512字节,在初始化中进行设置㊂单片机发送C M D 17或C MD 18进行S D 卡的单个块或多个块的读操作;单片机发送C M D 24或C M D 25进行S D 卡的单个块或多个块的写操作㊂下面以读写单个块为例,介绍具体操作过程,读写多个块的过程与此类似,命令为C M D 18和C M D 25㊂读S D 卡单个块数据,具体过程如下:①发送C M D 17;②接收卡响应R 1;③接收数据起始令牌0x F E ;④接收数据;⑤接收2个字节的C R C ,如果不使用C R C ,这两个字节在读取后可以丢掉;⑥禁止片选之后,发8个C L K ㊂写S D 卡单个块数据,具体过程如下:①发送C M D 24;②接收卡响应R 1;③发送写数据起始令牌0x F E ;④发送数据;⑤发送2字节的伪C R C ;⑥禁止片选之后,发8个C L K[7]㊂S D 卡数据的读写基本单位是块,如需大数据量的读写操作,可以有两种方法实现:单块的多次读写和多块读写的命令㊂在速度要求不高的场合可以使用单块的多次读写,在高速的数据读写场合,必须使用多块读写的命令,效率比单块的多次读写高很多㊂3.6 F a t F S 文件系统模块移植在S TM 32程序工程中需要新建两个文件夹,F a t F S用于存放F a t F S 源文件,U s e r 文件夹下S P I _S D _D r i v e r .c 文件用于存放S P I 的底层驱动文件,这个文件是S D 卡初始化和读写相关的函数㊂这些函数是在d i s k i o .c 文件中的5个函数所调用的:函数d i s k _i n i t i a l i z e ,S D 卡的初始化,调用底层的S D _I n i t ()函数;函数d i s k _s t a t u s ,获取S D卡的状态,这里可以不用管;函数d i s k _r e a d ,从S D 卡中读取数据,含读S D 卡单块数据函数和多块数据函数;函数d i s k _w r i t e ,将数据写入S D 卡,含写S D 卡单块数据函数和多块数据函数,若该文件系统为只读文件系统则不用实现该函数,含写单块函数和多块函数;函数d i s k _i o c t l ,获取S D 卡文件系统相关信息[8]㊂4 S D 卡文件读写实现F a t F S 底层存储介质接口函数已修改完成,下一步就需要使用A P I 函数实现文件的读写[9]㊂具体实现代码如下:u n s i g n e d c h a r T x F i l e B u f f e r []="F a t F S S ys t e m t e s t i s O K !\r \n ";r e s =f _m o u n t (&f s ,"0:",1);/*创建一个工作区,调用初始化函数*/i f (r e s !=F R _O K )p r i n t f ("m o u n t E R R O R \n \t ");e l s ep r i n t f ("m o u n t S U C C E S S \n \t ");r e s =f _o p e n (&F i l e S ys t e m D s t ,"0:/D e m o 1.t x t ",F A _C R E A T E _A L WA Y S |F A _WR I T E );/*在刚刚开辟的工作区的盘符0下打开一个名为D e m o 1.t x t 的文件,没有则创建文件*/i f (r e s ==F R _O K ){ p r i n t f ("F i l e O pe n S U C C E S S !\n \t "); r e s=f _w r i t e (&F i l e S y s t e m D s t ,T x F i l e B u f f e r ,s i z e o f (T x F i l e -B u f f e r ),&b w );/*将缓冲区的数组变量T x F i l e B u f f e r 的内容写到刚刚打开的D e m o 1.t x t 文件中*/ i f (r e s ) p r i n t f ("F i l e W r i t e E R R O R !\n \t "); e l s ep r i n t f ("F i l e W r i t e S U C C E S S !\n \t "); f _c l o s e (&F i l e S ys t e m D s t );/*关闭文件*/}e l s e if (r e s ==F R _E X I S T )p r i n t f ("F i l e i s a l r e a d y ex i s t \n ");e l s ep r i n t f ("D o n 't k n o w t h e e r r o r !\r \n ");以上代码实现的功能是在S D 卡中新建一个文件名为D e m o 1.t x t 的文本文件,将数据缓冲区T x F i l e B u f f e r 中的内容写入这个文件中㊂r e s =f _o p e n (&F i l e S ys t e m D s t ,"0:/D e m o 1.t x t ",F A _O P E N _E X I S T I N G |F A _R E A D );i f (r e s ) p r i n t f ("F i l e O pe n E R R O R !\n \t ");e l s ep r i n t f ("F i l e O pe n S U C C E S S !\n \t ");b r =1;f o r (i =0;i <512;i ++) F i l e R x B u f f e r [i ]=0;r e s=f _r e a d (&F i l e S y s t e m D s t ,F i l e R x B u f f e r ,s i z e o f (F i l e R x -B u f f e r ),&b r);i f (r e s) p r i n t f ("F i l e R e a d E R R O R !\n \t ");e l s ep r i n t f ("F i l e R e a d S U C C E S S !\n \t ");pr i n t f ("\r \n %s ",F i l e R x B u f f e r );f _c l o s e (&F i l e S ys t e m D s t );以上代码实现的功能是读取S D 卡中文件名为D e m -o 1.t x t 的文本文件,将读取到的数据放在数据缓冲区F i l -e R x B u f f e r 中㊂5 数据读写验证把S TM 32单片机写过数据的S D 卡插入P C 的S D 卡插槽查看数据,与单片机写入的数据一致,P C 机端数据如图5所示㊂图5 P C 机端数据单片机读取S D 卡的数据放在数据缓冲区F i l e R x -B u f f e r 中,通过I A R 平台查看缓冲区数据如图6所示,其数据与文本文件中的数据一致[10]㊂图6 缓冲区数据结 语该设计以S TM 32微处理器为控制核心,根据F a t F S文件系统规范成功实现了基于S D 卡的一系列操作(如创建㊁删除㊁读写等),经测试,该系统稳定可靠,并已成功应用于氢气加气机样机的数据存储系统中㊂该系统硬件电路简单,软件的可移植性强,非常适用于高速㊁大容量的数据存储场合㊂它支持热插拔及数据写保护功能,能正确读写S a n D i s k ㊁K i n g s t o n ㊁S AM S U N G 等厂商多种容量的S D 卡,最高读写速度可达1M B /s,可满足小型嵌入式系统的应用需求,具有较高的应用价值㊂参考文献[1]S T M i c r o e l e c t r o n i c s .S TM 32F 10x x x i na p pl i c a t i o n p r o -g r a mm i n g u s i n g t h e S P I [E B /O L ].[202006].h t t p://w w w.s t .c o m.[2]刘波文.A RM C o r t e x M 3应用开发实例详解[M ].北京:电子工业出版社,2011.电子科技大学,2018.[8]唐思超.R I S C V 处理器的C 语言启动代码设计方法[J ].单片机与嵌入式系统应用,2020(4):1417.[9]谷涛.轻松学C #[M ].北京:电子工业出版社,2013:1324.[10]缑文博.基于A V R 单片机的电动汽车蓄电池监测系统的设计[J ].电子世界,2020(7):198.[11]高淑婷.基于A V R 单片机的汽车空调控制系统设计[J ].机械装备研发,2020(11):130.[12]刘晓,陈广凯,赵汉青,等.一种基于单片机串口通信的数据缓存处理方法[J ].信息通信,2020(4):103104.[13]陈旭辉,杨红云.U S B 接口的虚拟多串口通信设备设计[J ].单片机与嵌入式系统应用,2020(4):1821.[14]黄才权,毕增军,张辉.Z Y N Q 的超低时延视频编解码系统设计[J ].单片机与嵌入式系统应用,2020(2):2730.王君(工程师),主要研究方向为预警装备保障㊂(责任编辑:薛士然 收稿日期:2020-07-07) [3]史胜伟,潘冀宁,孙慧洋.基于S TM 32的M i c r o S D 卡F a t 文件系统快速实现[J ].通讯世界,2016(17):8183.[4]李鸿征.高性能M i c r o S D 卡读写器的设计与开发[J ].焦作大学学报,2017,31(4):6972.[5]颜秋男,胡毅.S TM 32F 103V B 的S D 卡在应用编程设计[J ].单片机与嵌入式系统应用,2012(2):3638,46.[6]李敏,侯亚玲,刘颖.基于S D 卡的F A T 32文件系统设计与实现[J ].物联网技术,2017,7(7):9698,102.[7]王坤,丁红胜.基于S TM 32的图像编码与采集系统[J ].电子设计工程,2018,26(5):179183.[8]徐涛,陈赫,卢少微,等.基于S TM 32的碳纳米纸传感器信号采集系统设计[J ].仪表技术与传感器,2019(9).[9]孙海英,朱晔,罗春,等.基于S TM 32控制器的设备运行时间自动统计装置设计[J ].电子测试,2020(2):2021.[10]梁菲惜.基于S TM 32和D G U S 液晶屏的随机键盘设计[J ].电子制作,2019(1):1011.汤才刚(工程师),主要研究方向为单片机与嵌入式系统应用㊂(责任编辑:薛士然 收稿日期:2020-06-28)X i l i n x 为5G 无线电大规模部署推出突破性Z y n q RF S o C D F E 赛灵思公司(X i l i n x ,I n c .)宣布推出Z y n q RF S o C D F E ,这是一类全新的具有突破性意义的自适应无线电平台,旨在满足不断演进的5G N R 无线应用标准㊂Z y n q RF S o C D F E 将硬化的数字前端(D F E )模块与灵活应变的可编程逻辑相结合,为涵盖低㊁中㊁高频段频谱的广泛用例打造了高性能㊁低功耗且经济高效的5G N R 无线电解决方案㊂Z y n q RF S o C D F E 在采用硬化模块的A S I C 的成本效益与可编程和自适应S o C 的灵活性㊁可扩展性及上市时间优势之间,实现了最佳的技术平衡㊂5G 无线电所需的解决方案,不仅要满足广泛部署所提出的带宽㊁功耗和成本挑战,还必须适应三大关键5G 用例:增强型移动宽带(e M B B )㊁大规模机器类通信(mM T C )以及超可靠低时延通信(U R L L C )㊂此外,解决方案必须能够随不断演进的5G 标准进行扩展,如O p e n R A N (O R A N )㊁全新的颠覆性5G 商业模式㊂Z y n q R F S o C D F E 集成了针对5G N R 性能与节电要求而硬化的D F E 应用专用模块,同时还提供了结合可编程自适应逻辑的灵活性,从而为日益发展的5G 3G P P 和O R A N 无线电架构提供了面向未来的解决方案㊂赛灵思执行副总裁兼有线与无线业务部总经理L i a m M a d d e n 表示: 为满足5G 的特殊需求,赛灵思史上首次推出这样一款硬化应用专用I P 多于自适应逻辑的无线电平台㊂随着5G 相关市场需求日益演进,集成式R F 解决方案也需不断适应未来标准㊂Z y n q RF S o C D F E 在灵活应变能力与固定功能I P 之间提供了最佳平衡㊂ 与上一代产品相比,Z y n q RF S o C D F E 将单位功耗性能提升高达两倍,并且能够从小蜂窝扩展至大规模M I MO (mM I MO )宏蜂窝㊂该解决方案是一款独特的直接R F 采样平台,能够在所有F R 1频带和新兴频带(最高可达7.125GH z)内实现载波聚合/共享㊁多模式㊁多频带400MH z 瞬时带宽㊂当用作毫米波中频收发器时,Z y n q R F S o C D F E 可提供高达1600MH z 的瞬时带宽㊂Z y n q RF S o C D F E 的架构支持客户绕过或定制硬化的I P 模块㊂例如,客户既可以利用支持现有和新兴G a N P a s 的赛灵思经现场验证的D P D ,也可以插入其自有的独特D P D I P ㊂A B I R e s e a r c h 5G 高级研究总监D i m i t r i s M a v r a k i s 表示: 随着5G 商业部署和新用例持续演进,对于整个供应链而言,如何为供应商提供灵活的组件以创建具有成本效益㊁适应性强且面向未来的设备十分关键㊂尤其是O pe n R A N ,其在这方面的要求更高,灵活的设计对其成功至关重要㊂Z y n q R F S o C D F E 实现了硬化和自适应可编程逻辑之间的平衡,是一款兼具通常A S I C 才具有的成本优势以及F P G A 才拥有的设计灵活性和定制化优势的独特产品㊂。
基于stm32f103对sd卡底层的基本操作方法
基于stm32f103对sd卡底层的基本操作方法基于STM32F103的SD卡底层操作方法是指通过STM32F103系列微控制器来对SD卡进行读写操作的一组基本方法。
本文将详细介绍如何配置STM32F103的SPI接口和相关寄存器,以及如何使用SPI接口与SD卡进行通信和文件操作。
一、硬件连接首先,需要连接STM32F103与SD卡之间的硬件接口。
STM32F103的SPI接口包括四根引脚,分别是NSS(片选信号)、SCK(时钟信号)、MISO(数据输入信号)和MOSI(数据输出信号)。
通常,可以将SD卡的NSS引脚连接到STM32F103的任一GPIO引脚上作为片选信号,并通过软件控制片选信号的高低电平来选择SD卡进行读写操作。
此外,还需要将SD卡的SCK、MISO和MOSI引脚分别连接到STM32F103的SPI接口的SCK、MISO和MOSI引脚上。
为了方便起见,可以直接使用STM32F1的SPI中的SPI1进行配置。
二、SPI接口配置在STM32F103中,SPI接口由SPI1、SPI2和SPI3三个外设实现,其中SPI1位于APB2总线上,SPI2和SPI3位于APB1总线上。
在本文中,我们将使用SPI1进行SD卡的底层操作。
首先,需要在CubeMX中将SPI1的NSS、SCK、MISO和MOSI引脚分别配置为GPIO输出、SPI时钟、SPI数据输入和SPI数据输出功能。
然后,需要配置SPI1的时钟分频系数、数据位数、传输模式等参数。
SPI1的时钟分频系数由BDIV和BR两个参数决定,其中BDIV位于SPI1->CR1寄存器的位6-7位,用于设定SPI1主频的1/2、1/4、1/8还是1/16,BR位于SPI1->CR1寄存器的位3-5位,用于设定SPI1的分频系数。
根据SD卡的时钟特性,一般选择SPI1的分频系数为sclk/32,其中sclk为主控芯片时钟。
在SPI接口配置完成之后,需要打开SPI1外设时钟使能,并设置SPI1的工作模式、数据位数等参数。
STM32笔记(六)SD卡的读写和FatFS文件系统
STM32笔记(六)SD卡的读写和FatFS文件系统因为要用,学习了一下SPI操作SD卡,同时移植了一个免费开源的FAT文件系统:FatFS。
感觉挺好,在单片机上实现了读写文件的操作,接下来就可以解释我的G代码咯!我的SD卡底层操作参考了网上几种常见的代码,但又对其结构做了一定的优化,至少看起来用起来比较方便。
既可以作为文件系统的diskio使用,也可以直接使用底层函数,把SD卡作为一块flash读写。
FatFs文件系统体积蛮小,6-7K足矣,对于128Kflash的STM32来说很合适,代价不大。
同时可移植性很高,最少只需要4个函数修改既可以实现文件系统的移植。
相关文件系统的介绍请看这里。
这里给一套比较完整的参考资料,包括fatfs文件系统的原版资料、几个重要的手册和网上下载的代码。
/bbs/bbs_content.jsp?bbs_sn=3210864&bbs_page_no=1&bbs_id=3020 下面是我的代码:其中底层的SPI总线对SD卡的操作在SPI_SD_driver.c/h中,而FATFS的移植文件diskio.c中对磁盘的操作函数中将调用底层的操作函数。
下面是一些底层操作函数:u8 SPI_ReadWriteByte(u8 TxData); //SPI总线读写一个字节u8 SD_WaitReady(void); //等待SD卡就绪u8 SD_SendCommand(u8 cmd, u32 arg, u8 crc); //SD卡发送一个命令u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg, u8 crc); //SD卡发送一个命令,不断线u8 SD_Init(void); //SD卡初始化u8 SD_ReceiveData(u8 *data, u16 len, u8 release); //SD卡读数据u8 SD_GetCID(u8 *cid_data); //读SD卡CIDu8 SD_GetCSD(u8 *csd_data); //读SD卡CSDu32 SD_GetCapacity(void); //取SD卡容量u8 SD_ReadSingleBlock(u32 sector, u8 *buffer); //读一个sectoru8 SD_WriteSingleBlock(u32 sector, const u8 *buffer); //写一个sectoru8 SD_ReadMultiBlock(u32 sector, u8 *buffer, u8 count); //读多个sectoru8 SD_WriteMultiBlock(u32 sector, const u8 *data, u8 count); //写多个sector这是diskio.c中的一段代码,在disk初始化中,我们调用了SPI_SD_driver.c中的SD卡初始化函数。
新版STM32 HAL库SD卡读写不正常总结
2个版本的SD卡驱动改动较大, 看一下对比 V1.14.0 uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint64_t WriteAddr, uint32_t BlockSize, uint32_t NumOfBlocks) uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint64_t WriteAddr, uint32_t BlockSize, uint32_t NumOfBlocks)
V1.15.0 uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout) uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks)
if(BSP_SD_ReadBlocks_DMA((uint32_t*)buff,(uint32_t)(sector),count) == MSD_OK) {
while( BSP_SD_RxOK()==0 || BSP_SD_GetCardState()!=MSD_OK ) //读写完成的判断需要同时同时满足 {
sd_tx_ok = 1; } void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd) {
学习STM32之SD卡总结
学习STM32之SD卡总结STM32 SD卡使用方法和错误总结由于自己也在使用SD卡,使用的过程中也遇到了一些问题,下面是在EDN论坛上zxb1717高手的经验,希望可以帮助大家调试关键点:1. 上电时要延时足够长的时间给SD卡一个准备过程,在我的程序里是5秒,根据不同的卡设置不同的延时时间。
SD卡初始化第一步在发送CMD命令之前,在片选有效的情况下首先要发送至少74个时钟,否则将有可能出现SD卡不能初始化的问题。
2. SD卡发送复位命令CMD0后,要发送版本查询命令CMD8,返回状态一般分两种,若返回0x01表示此SD卡接受CMD8,也就是说此SD卡支持版本2;若返回0x05则表示此SD卡支持版本1。
因为不同版本的SD卡操作要求有不一样的地方,所以务必查询SD卡的版本号,否则也会出现SD卡无法正常工作的问题。
3. 理论上要求发送CMD58获得SD卡电压参数,但实际过程中由于事先都知道了SD卡的工作电压,因此可省略这一步简化程序。
协议书上也建议尽量不要用这个命令。
4. SD卡读写超时时间要按照协议说明书书上的给定值(读超时:100ms;写超时:250ms),这个值要在程序中准确计算出来,否则将会出现不能正常读写数据的问题。
我自己定义了一个计算公式:超时时间=(8/clk)*arg。
5. 2GB以内的SD卡(标准卡)和2GB以上的SD卡(大容量卡)在地址访问形式上不同,这一点尤其要注意,否则将会出现无法读写数据的问题。
如标准卡在读写操作时,对读或写命令令牌当中的地址域符初值0x10,表示对第16个字节以后的地址单元进行操作(前提是此SD 卡支持偏移读写操作),而对大容量卡读或写命令令牌当中的地址域符初值0x10时,则表示对第16块进行读写操作,而且大容量卡只支持块读写操作,块大小固定为512字节,对其进行字节操作将会出错。
6. 对某一块要进行写操作时最好先执行擦出命令,这样写入的速度就能大大提高。
基于STM32的几种读SD卡方式的速度探究
F I F O  ̄
C MD7 命令 选择 卡 , 配置 D MA2 , 发送 C MD 2 4 ( 开始 写数据 ) , 查 询D MA 通 道的使 能状 态 寄存器 , 确认 没 有通 道仍 处于 使 能状 态 。 配 置D MA2 的具 体步 骤如 下 :
1 ) 使  ̄D MA2 控 制器 并 清 除所有 的 中断 标志 位 。
特性 , 如 今越 来越 多 的芯 片集 成 了这种 通信 协 议 。
1 . 3 S D1 0
S D 卡 读取 操作 后 , 先 发送一 个起 始数 据 命令 , 接 着发 送 固定数 量 的数据 , 最 后 是2 个 字节 的C RC 校验 。 2 3 S D 卡的 文件存 储
1 . 硬 件设 计
1 . 1 ST M 3 2 F 1 0 3 Z E T6
2 . 1 . 2将S D 卡初 始 化 为S D I O 模 式 首先 上 电( p o we r - o n ) , 然后发出C MD 0, 再接着发送C MD8, 有 应 答
S T M3 2 F 1 0 3 z E T 6 基于 高 性 能3 2 位 RI S C 的AR M C o r t e x - M3 核, 其最 高 工 作频 率为 7 2 MHz 。 片上 集 成了高 速 存储 器 ,
须 与 计 算机 的文 件 系 统 兼容 。 目前 常用 的文 件 系 统 主 要 有 w i n d o WS 下 的
F A T3 2 , NT F S , L u x 下的E XT 2 , E XT 3 等。
2 4 D M A 的配置
一
S T M3 2 F 1 0 x 系列 MCU支持D MA功 能 , 可 以直接 将数 据从 一 个地 址 空 间
【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(六)-FatFs使用的思路介绍
f_open打开/创建⽂件f_close关闭⽂件f_read读取⽂件f_write写⼊⽂件f_lseek移动读/写指针,扩展⼤⼩f_truncate 截断⽂件f_sync 刷新缓存数据f_forward 转移⽂件数据到⼀个数据流f_expand 为⽂件分配⼀个连续的块f_gets 读取⼀个字符串(string )f_putc写⼊⼀个字符(character )f_puts写⼊⼀个字符串(string )f_printf写⼊⼀个格式化字符串f_tell获取当前读/写指针f_eof⽂件结束测试f_size获取长度f_error测试错误f_opendir 打开⼀个⽬录f_closedir 关闭⼀个已打开的⽬录f_readdir 读取⽬录f_findfirst 打开⼀个⽬录并读取匹配的第⼀个项⽬f_findnext 查找下⼀个匹配的项⽬【STM32】使⽤SDIO 进⾏SD 卡读写,包含⽂件管理FatFs (六)-FatFs 使⽤的思路介绍本篇要来介绍⽂件管理FatFs官⽅的⽹站是:这是⼀个⽇本⼈写的,除了⽂件管理以外,还有其他的,例如解码JPEG 、红外遥控等在官⽹链接内,最下⽅有个Return ,点击后就可以看到相关的开源库以下开始正题(本⽂⾥提到的媒介,其实就是设备了,我不想改图了...)打开官⽅⽹站,页⾯简单明了,就分为4个区块(以下图⽚为FatFs 官⽹上截取的图⽚)第⼀区块、介绍及特性(Features ):FatFs 是⽂件管理系统,可⽤于SD 卡、硬盘(ATA )、RTC 时钟,FTL 和etc 不清楚是什么,另外,也可以⽤于Flash 或是EEPROM第⼆区块、应⽤接⼝(Application Interface ):FatFs 提供了接⼝,使得我们的应⽤可以和它交互。
官⽹左侧为⼀些接⼝的介绍,分四个部分第⼀部分:File Access (⽂件存取)第⼆部分:Directory Access (⽬录访问)f_stat 检查⽂件或⼦⽬录是否存在f_unlink 删除⽂件或⼦⽬录f_rename 重命名/移动⽂件或⼦⽬录f_chmod 更改⽂件或⼦⽬录的属性f_utime更改⽂件或⼦⽬录的时间戳f_mkdir创建⼦⽬录f_chdir 更改当前⽬录f_chdive 更改当前驱动f_getcwd 检索当前⽬录和驱动f_mount注册/注销⼀个⼯作区(挂起与否)f_mkfs在逻辑驱动上创建⼀个FAT 卷f_fdisk 在物理驱动上创建分区f_getfree 获取卷上的可⽤空间f_getlabel 获取卷标f_setlabel 设置卷标f_setcp设置活动代码页disk_status获取设备状态disk_initialize 初始化设备disk_read读取数据disk_write写⼊数据disk_ioctl控制设备相关功能get_fattime 获取当前时间第三部分:File and Dirextor Management (⽂件和⽬录管理)第四部分:Volume Management and System Configuration (卷管理和系统配置)第三区块、媒介访问接⼝(Media Access Interface ):你想管理的存储设备,必须要和FatFs 链接。
STM32下SD卡的读取
STM32下SD卡的读取⼀、SD卡模块介绍1.1 什么是SD卡SD存储卡是⼀种基于半导体快闪记忆器的新⼀代记忆设备,由于它体积⼩、数据传输速度快、可热插拔等优良的特性,被⼴泛地于便携式装置上使⽤,例如数码相机、平板电脑和多媒体播放器等。
控制器对 SD 卡进⾏读写通信操作⼀般有两种通信接⼝可选,⼀种是 SPI 接⼝,另外⼀种是 SDIO 接⼝。
1.2 SD卡的物理结构⼀张SD卡包括有存储单元、存储单元接⼝、电源检测、卡及接⼝控制器和接⼝驱动器5 个部分。
存储单元是存储数据部件,存储单元通过存储单元接⼝与卡控制单元进⾏数据传输;电源检测单元保证SD卡⼯作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接⼝复位;卡及接⼝控制单元控制SD卡的运⾏状态,它包括有8个寄存器;接⼝驱动器控制 SD 卡引脚的输⼊输出。
⼆、项⽬代码已挂载在Github上烧录程序成功后:主要函数代码分析:int main(void){/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_SPI1_Init();MX_FATFS_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */HAL_UART_Receive_IT(&huart1,&aRxBuffer1,1); //enable uart printf(" mian \r\n");Get_SDCard_Capacity(); //得到使⽤内存并选择格式化/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){WritetoSD(WriteBuffer,sizeof(WriteBuffer));HAL_Delay(500);WriteBuffer[0] = WriteBuffer[0] +0;WriteBuffer[1] = WriteBuffer[1] +1;write_cnt ++;while(write_cnt > 10){printf(" while \r\n");HAL_Delay(500);}/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */}void WritetoSD(BYTE write_buff[],uint8_t bufSize){FATFS fs;FIL file;uint8_t res=0;UINT Bw;res = SD_init(); //SD卡初始化if(res == 1){printf("SD卡初始化失败! \r\n");}else{printf("SD卡初始化成功! \r\n");}res=f_mount(&fs,"0:",1); //挂载// if(test_sd == 0) //⽤于测试格式化if(res == FR_NO_FILESYSTEM) //没有⽂件系统,格式化{// test_sd =1; //⽤于测试格式化printf("没有⽂件系统! \r\n");res = f_mkfs("", 0, 0); //格式化sd卡if(res == FR_OK){printf("格式化成功! \r\n");res = f_mount(NULL,"0:",1); //格式化后先取消挂载res = f_mount(&fs,"0:",1); //重新挂载if(res == FR_OK){printf("SD卡已经成功挂载,可以进进⾏⽂件写⼊测试!\r\n");}}else{printf("格式化失败! \r\n");}}else if(res == FR_OK){printf("挂载成功! \r\n");}else{printf("挂载失败! \r\n");}res = f_open(&file,SD_FileName,FA_OPEN_ALWAYS |FA_WRITE);if((res & FR_DENIED) == FR_DENIED){printf("卡存储已满,写⼊失败!\r\n");}f_lseek(&file, f_size(&file));//确保写词写⼊不会覆盖之前的数据if(res == FR_OK){printf("打开成功/创建⽂件成功! \r\n");res = f_write(&file,write_buff,bufSize,&Bw); //写数据到SD卡if(res == FR_OK){printf("⽂件写⼊成功! \r\n");}else{printf("⽂件写⼊失败! \r\n");}}else{printf("打开⽂件失败!\r\n");}f_close(&file); //关闭⽂件f_mount(NULL,"0:",1); //取消挂载}程序⾸先进⾏相关初始化,完成后输出mian,之后进⾏SD卡格式化,成功后进⼊循环,进⼊SD卡读写函数,先后进⾏SD卡的初始化,挂载,创⽂件,写⼊,写⼊超过10次,输出while三、相关引脚的配置CS -> PB0SCK -> PA5MISO -> PA6MOSI -> PA7四、实验结果SD卡中⽣成⼀个新的hello.txt⽂件内容如下更改写⼊内容写⼊结果如下:五、⼼得体会在初始化过程中确保SD格式化成FAT⽂件模式,确保单⽚机的供电和SD卡模块的供电最好是5V,不然可能带不动SD卡驱动导致实验失败。
stm32SPI模式读写SD卡
stm32SPI模式读写SD卡SPI模式读写SD卡SD卡初始化过程:1. 初始化STM32的SPI接口使用低速模式2. 延时至少74clock3. 发送CMD0,需要返回0x01,进入Idle状态4. 循环发送CMD55+ACMD41,直到返回0x00,进入Ready状态5. 设置读写block大小为512byte5. 把STM32的SPI设置为高速模式读一个block块的过程1. 发送CMD17(单块)或CMD18(多块)读命令,返回0x002. 接收数据开始令牌0xfe + 正式数据512Bytes + CRC 校验2Bytes写一个block块的过程1. 发送CMD24(单块)或CMD25(多块)写命令,返回0x002. 发送数据开始令牌0xfe + 正式数据512Bytes + CRC校验2Bytes/******************************************************************* ************* Function Name : SD_MMC_SPI_Init* Description : SD_MMC_SPI_Init* Input : None* Output : None* Return : zero init success, non-zero init error************************************************************************ *******/u8 SD_MMC_SPI_Init(void){GPIO_InitTypeDef GPIO_InitStructure;/* Enable SPI1 and GPIO clocks */RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_SD_MMC_SPI_CS, ENABLE);/* Configure SPI1 pins: SCK, MISO and MOSI */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);/* Configure SD_MMC_SPI_CS */GPIO_InitStructure.GPIO_Pin = SD_MMC_SPI_CS_Pin_CS;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(SD_MMC_SPI_CS, &GPIO_InitStructure);///////////////////////////////////////////////////////////////////////////////* initialize SPI with lowest frequency */SD_MMC_Low_Speed();/* card needs 74 cycles minimum to start up */for(u8 i = 0; i < 10; ++i) {/* wait 8 clock cycles */ SD_MMC_ReadWrite_Byte(0x00); } /* address card */ SD_MMC_SPI_SELECT();/* reset card */u8 response;for(u16 i = 0; ; ++i){response = SD_MMC_Send_Command(CMD_GO_IDLE_STATE ,0 );if( response == 0x01 ) break;if(i == 0x1ff) {SD_MMC_SPI_DESELECT(); return 1;}}/* wait for card to get ready */ for(u16 i = 0; ; ++i) {response = SD_MMC_Send_Command(CMD_SEND_OP_COND, 0);if(!(response & (1 << R1_IDLE_STATE)))break;if(i == 0x7fff) {SD_MMC_SPI_DESELECT(); return 1;}}/* set block size to 512 bytes */if(SD_MMC_Send_Command(CMD_SET_BLOCKLEN, 512)) {SD_MMC_SPI_DESELECT();return 1;}/* deaddress card */SD_MMC_SPI_DESELECT();/* switch to highest SPI frequency possible */ SD_MMC_High_Speed();return 0;//////////////////////////////////////////////////////////////////// //////////}/******************************************************************* ************* Function Name : SD_MMC_Read_Single_Block * Description :SD_MMC_Read_Single_Block * Input : sector number and buffer data point * Output : None* Return : zero success, non-zero error************************************************************************ *******/u8 SD_MMC_Read_Single_Block(u32 sector, u8* buffer) {u8 Response;u16 i;u16 Retry = 0;//读命令 send read commandResponse =SD_MMC_Send_Command(CMD_READ_SINGLE_BLOCK, sector<<9); if(Response != 0x00)return Response;SD_MMC_SPI_SELECT();// start byte 0xfewhile(SD_MMC_ReadWrite_Byte(0xff) != 0xfe) {if(++Retry > 0xfffe){SD_MMC_SPI_DESELECT();return 1; //timeout}}for(i = 0; i < 512; ++i) {//读512个数据*buffer++ = SD_MMC_ReadWrite_Byte(0xff); }SD_MMC_ReadWrite_Byte(0xff); //伪crcSD_MMC_ReadWrite_Byte(0xff); //伪crcSD_MMC_SPI_DESELECT();SD_MMC_ReadWrite_Byte(0xff); // extra 8 CLKreturn 0;}/******************************************************************* ************* Function Name : SD_MMC_Write_Single_Block* Description : SD_MMC_Write_Single_Block * Input : sector number and buffer data point* Output : None* Return : zero success, non-zero error.************************************************************************ *******/u8 SD_MMC_Write_Single_Block(u32 sector, u8* buffer) {u8 Response;u16 i;u16 retry=0;//写命令 send write commandResponse =SD_MMC_Send_Command(CMD_WRITE_SINGLE_BLOCK, sector<<9);if(Response != 0x00)return Response;SD_MMC_SPI_SELECT();SD_MMC_ReadWrite_Byte(0xff);SD_MMC_ReadWrite_Byte(0xff);SD_MMC_ReadWrite_Byte(0xff);//发开始符 start byte 0xfeSD_MMC_ReadWrite_Byte(0xfe);//送512字节数据 send 512 bytes datafor(i=0; i<512; i++){SD_MMC_ReadWrite_Byte(*buffer++);}SD_MMC_ReadWrite_Byte(0xff); //dummy crc SD_MMC_ReadWrite_Byte(0xff); //dummy crcResponse = SD_MMC_ReadWrite_Byte(0xff);//等待是否成功 judge if it successfulif( (Response&0x1f) != 0x05){SD_MMC_SPI_DESELECT();return Response;}//等待操作完 wait no busywhile(SD_MMC_ReadWrite_Byte(0xff) != 0x00) {if(retry++ > 0xfffe){SD_MMC_SPI_DESELECT();return 1;}}SD_MMC_SPI_DESELECT();SD_MMC_ReadWrite_Byte(0xff);// extra 8 CLKreturn 0;}if( SD_MMC_SPI_Init() ==1)printf(" _SD_MMC Initialization ERROR\r\n"); else{printf(" _SD_MMC Initialization OK\r\n"); //memset(buffer,0,512); //读取一个扇区的内容这里读的是0扇区SD_MMC_Read_Single_Block( 0 , buffer );Uart1_PutString( buffer , 512 );}。
stm32f103c8t6 tf卡简单的读写函数
文章标题:探索STM32F103C8T6与TF卡的简单读写函数在嵌入式系统开发中,TF卡的读写功能一直是一个不可或缺的部分。
而对于如何在STM32F103C8T6芯片上实现简单的TF卡读写函数,也是我们经常需要探讨的一个问题。
在本文中,我们将会从简单到复杂地讨论如何在STM32F103C8T6芯片上实现TF卡的读写功能,以便读者能更深入地理解这一过程。
1. TF卡简介在开始讨论TF卡的读写函数之前,我们先简要介绍一下TF卡的基本知识。
TF卡,全称TransFlash卡,又称MicroSD卡,是一种常见的存储卡,常用于嵌入式系统、手机、相机等设备中。
TF卡具有体积小、读写速度快、存储容量大等优点,因此在嵌入式系统中得到广泛应用。
2. STM32F103C8T6与TF卡的连接在实现TF卡读写功能之前,首先需要将TF卡与STM32F103C8T6芯片进行连接。
一般来说,TF卡的连接方式是通过SPI接口进行。
SPI接口是一种串行外设接口,可以实现与外部设备的高速数据传输。
通过SPI接口连接TF卡,可以在STM32F103C8T6芯片上实现对TF卡的读写操作。
3. TF卡读写函数的实现在STM32F103C8T6芯片上实现TF卡的读写函数,一般需要以下几个步骤:初始化SPI接口、初始化TF卡、读取TF卡数据、写入TF卡数据。
需要初始化SPI接口,设置好SPI的时钟频率、数据传输格式等参数。
通过初始化TF卡,可以对TF卡进行一些基本的设置,比如设置TF卡的工作模式、文件系统类型等。
接下来,可以通过读取TF 卡数据和写入TF卡数据函数,实现对TF卡的读写操作。
4. 个人观点和总结通过深入地探讨STM32F103C8T6与TF卡的简单读写函数,我对于嵌入式系统中TF卡的应用有了更深入的理解。
在实际应用中,我们需要根据具体的需求和硬件评台来选择合适的读写函数,并且需要注意好数据的正确读取和写入,以保证系统的稳定性和可靠性。
单片机读写SD卡教程
单片机读写SD卡教程引言:SD卡(Secure Digital Card)是广泛应用于各类数字设备上的一种存储介质。
它小巧轻便,可靠性高,容量大,因此在各种嵌入式系统中都广泛使用。
本教程将介绍如何使用单片机读写SD卡,包括初始化SD卡、读写数据等基本操作。
一、硬件准备在开始之前,我们需要准备以下硬件设备:1.一个支持SPI协议的单片机开发板(例如STC89C51、STM32等);2.一个SD卡插槽,或者是一个带有SD卡插槽的扩展板;3.杜邦线、面包板等连接器。
二、软件准备除了硬件设备,我们还需要准备以下软件工具:1. Keil C51、IAR、Keil MDK等单片机编译工具;2. SD卡相关的库文件,例如FatFs;3.一个用于测试的程序(可以是一个简单的读写数据的程序)。
三、连接SD卡插槽将SD卡插入到对应的插槽中,并将插槽与单片机的硬件SPI接口连接。
根据不同的开发板,连接方式可能有所不同,一般SPI接口包括SCK(时钟线)、MOSI(主机输出从机输入线)、MISO(主机输入从机输出线)和CS(片选线)等。
四、编写读写SD卡的程序在开始编写程序之前,我们需要先了解SD卡的工作原理。
SD卡通过SPI总线与单片机进行通信,通过发送特定的命令和参数实现读写操作。
以下是一个简单的读写SD卡的流程:1.初始化SD卡a.发送CMD0命令,将SD卡设置为SPI模式;b.发送CMD8命令,验证SD卡是否支持高速SPI模式;c.发送ACMD41命令,等待SD卡初始化完成。
2.读写数据a.发送CMD17命令,指定要读取的扇区地址;b.等待SD卡回应,确认读取命令执行成功;c.读取数据;d.发送CMD18命令,继续读取下一个扇区;e.重复步骤c和d,直到读取完所有数据;f.发送CMD12命令,停止读取。
g.发送CMD24命令,指定要写入的扇区地址;h.等待SD卡回应,确认写入命令执行成功;i.写入数据;j.发送CMD25命令,继续写入下一个扇区;k.重复步骤i和j,直到写入完所有数据;l.发送CMD12命令,停止写入。
STM32的SPI1引脚重映射后读写SD卡
STM32的SPI1引脚重映射后读写SD卡初次使用STM32有些不太明白,此次调试经验奉献出来与大家分享!系统:STM32 + SD + FATFSSTM32与SD卡的连接如下定义,PB口#define SD_SCK GPIO_Pin_3#define SD_MISO GPIO_Pin_4#define SD_MOSI GPIO_Pin_5#define SD_CS GPIO_Pin_7我想使用SPI1并重映射引脚使用时注意的事项如下:1.使能PB0时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);2.使能AFIO时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);3.使能SPI1时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 , ENABLE);4.禁用JTAG时钟复用 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENA BLE);5.SPI1引脚重映射 GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);接着初始化GPIO和SPI1即可,如:void SpiOpen(void){GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;/* Configure SPI1 pins: SCKand MOSI */GPIO_InitStructure.GPIO_Pin = SD_MOSI|SD_SCK|SD_MISO;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOB, &GPIO_InitStructure);/* Configure PB7 pin: CS pin */GPIO_InitStructure.GPIO_Pin = SD_CS;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB,SD_CS);/* SPI1 Config */SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullD uplex;SPI_InitStructure.SPI_Mode = SPI_Mode_Master;SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePres caler_256;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(SPI1, &SPI_InitStructure);/* SPI1 enable */SPI_Cmd(SPI1, ENABLE);}可是这一切完成的时候结果却与想象的不同,无法在SD卡内创建文件,找一个晚上没有结果不得已采用另一种方法:软件模拟SPI测试了一下,结果另人出呼意料,一次搞定,没有任何问题,但为了效率还是希望使用硬SPI口,于是从引脚重映射上寻找原因又费了一晚上无果第三个晚上:经过无数次的改动 + 测试终于OK了,问题出在哪里了呢?原来SD卡的初始时钟不一定是<400KHz就行ST标准库初始化系统时APB2时钟为72MHz,经过256分频波特率约等于 280KHz (SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePre scaler_256;)对于我测试用的SD卡这个速度仍太快,于是降APB2时钟进行测试,OK!RCC_PCLK2Config(RCC_HCLK_Div4); //72/4=18MHz仅仅是一行代码,这就是初学代价!,哈哈.另外要说明的是,在MISO引脚上加了一个22k上接电阻.代码://#define USE_SOFT_SPI#ifdef USE_SOFT_SPIBYTE g_SPISpeedFlag=0;static void delay_bus(void){unsigned int i=100;if(g_SPISpeedFlag) //高速模式 < 25MHzi=10;while(--i) //低速模式 < 400KHz__nop();}void SpiOpen(void){//IO初始化用结构体GPIO_InitTypeDef GPIO_InitStructure;//输出引脚初始化GPIO_InitStructure.GPIO_Pin=SD_SCK|SD_CS|SD_MOSI; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure);//输入引脚配置GPIO_InitStructure.GPIO_Pin=SD_MISO;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//GPIO_ Mode_IN_FLOATING ;GPIO_Init(GPIOB, &GPIO_InitStructure);//设置端口初始电平GPIO_SetBits(GPIOB,SD_CS);GPIO_SetBits(GPIOB,SD_SCK);g_SPISpeedFlag=0;}void SpiClose(void){GPIO_SetBits(GPIOB,SD_CS);GPIO_SetBits(GPIOB,SD_SCK);Stat &= ~STA_NOINIT;}void SPI_SetHeighSpeed(void){g_SPISpeedFlag=1;}BYTE SPI_ReadWriteByte(BYTE outgoing){uint32_t i;uint8_t ret=0;delay_bus();for(i=0;i<8;i++){ret<<=1;if(outgoing &0x80) //主设备锁存数据GPIO_SetBits(GPIOB,SD_MOSI);elseGPIO_ResetBits(GPIOB,SD_MOSI);GPIO_ResetBits(GPIOB,SD_SCK); //产生第一个时钟沿,让从设备也在此时锁存数据delay_bus();if(GPIO_ReadInputDataBit(GPIOB,SD_MISO)) //主设备采样ret|=1;GPIO_SetBits(GPIOB,SD_SCK); //产生第二个时钟沿,让从设备也在此时采样delay_bus();outgoing<<=1;}return ret;}#else //硬件SPI 使用SPI1引脚重映射void SpiOpen(void){GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);/* Configure SPI1 pins: SCKand MOSI */GPIO_InitStructure.GPIO_Pin = SD_MOSI|SD_SCK|SD_MISO;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOB, &GPIO_InitStructure);/* Configure PB7 pin: CS pin */GPIO_InitStructure.GPIO_Pin = SD_CS;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB,SD_CS);/* SPI1 Config */SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullD uplex;SPI_InitStructure.SPI_Mode = SPI_Mode_Master;SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePres caler_256;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(SPI1, &SPI_InitStructure);/* SPI1 enable */SPI_Cmd(SPI1, ENABLE);}void SpiClose(void){SPI_Cmd(SPI1, DISABLE);Stat &= ~STA_NOINIT;}void SPI_SetHeighSpeed(void){SPI_InitTypeDef SPI_InitStructure;/* SPI1 Config */SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullD uplex;SPI_InitStructure.SPI_Mode = SPI_Mode_Master;SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePres caler_4;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(SPI1, &SPI_InitStructure);SPI_Cmd(SPI1, ENABLE);}BYTE SPI_ReadWriteByte(BYTE outgoing){/* Loop while DR register in not emplty */while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RE SET);/* Send byte through the SPI1 peripheral */SPI_I2S_SendData(SPI1, outgoing);/* Wait to receive a byte */while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == R ESET);/* Return the byte read from the SPI bus */return SPI_I2S_ReceiveData(SPI1);}#endif。
stm32SPI模式读写SD卡
stm32SPI模式读写SD卡SPI模式读写SD卡SD卡初始化过程:1. 初始化STM32的SPI接口使用低速模式2. 延时至少74clock3. 发送CMD0,需要返回0x01,进入Idle状态4. 循环发送CMD55+ACMD41,直到返回0x00,进入Ready状态5. 设置读写block大小为512byte5. 把STM32的SPI设置为高速模式读一个block块的过程1. 发送CMD17(单块)或CMD18(多块)读命令,返回0x002. 接收数据开始令牌0xfe + 正式数据512Bytes + CRC 校验2Bytes写一个block块的过程1. 发送CMD24(单块)或CMD25(多块)写命令,返回0x002. 发送数据开始令牌0xfe + 正式数据512Bytes + CRC校验2Bytes/******************************************************************* ************* Function Name : SD_MMC_SPI_Init* Description : SD_MMC_SPI_Init* Input : None* Output : None* Return : zero init success, non-zero init error************************************************************************ *******/u8 SD_MMC_SPI_Init(void){GPIO_InitTypeDef GPIO_InitStructure;/* Enable SPI1 and GPIO clocks */RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_SD_MMC_SPI_CS, ENABLE);/* Configure SPI1 pins: SCK, MISO and MOSI */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);/* Configure SD_MMC_SPI_CS */GPIO_InitStructure.GPIO_Pin = SD_MMC_SPI_CS_Pin_CS;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(SD_MMC_SPI_CS, &GPIO_InitStructure);///////////////////////////////////////////////////////////////////////////////* initialize SPI with lowest frequency */SD_MMC_Low_Speed();/* card needs 74 cycles minimum to start up */for(u8 i = 0; i < 10; ++i) {/* wait 8 clock cycles */ SD_MMC_ReadWrite_Byte(0x00); } /* address card */ SD_MMC_SPI_SELECT();/* reset card */u8 response;for(u16 i = 0; ; ++i){response = SD_MMC_Send_Command(CMD_GO_IDLE_STATE ,0 );if( response == 0x01 ) break;if(i == 0x1ff) {SD_MMC_SPI_DESELECT(); return 1;}}/* wait for card to get ready */ for(u16 i = 0; ; ++i) {response = SD_MMC_Send_Command(CMD_SEND_OP_COND, 0);if(!(response & (1 << R1_IDLE_STATE)))break;if(i == 0x7fff) {SD_MMC_SPI_DESELECT(); return 1;}}/* set block size to 512 bytes */if(SD_MMC_Send_Command(CMD_SET_BLOCKLEN, 512)) {SD_MMC_SPI_DESELECT();return 1;}/* deaddress card */SD_MMC_SPI_DESELECT();/* switch to highest SPI frequency possible */ SD_MMC_High_Speed();return 0;//////////////////////////////////////////////////////////////////// //////////}/******************************************************************* ************* Function Name : SD_MMC_Read_Single_Block * Description :SD_MMC_Read_Single_Block * Input : sector number and buffer data point * Output : None* Return : zero success, non-zero error************************************************************************ *******/u8 SD_MMC_Read_Single_Block(u32 sector, u8* buffer) {u8 Response;u16 i;u16 Retry = 0;//读命令 send read commandResponse =SD_MMC_Send_Command(CMD_READ_SINGLE_BLOCK, sector<<9); if(Response != 0x00)return Response;SD_MMC_SPI_SELECT();// start byte 0xfewhile(SD_MMC_ReadWrite_Byte(0xff) != 0xfe) {if(++Retry > 0xfffe){SD_MMC_SPI_DESELECT();return 1; //timeout}}for(i = 0; i < 512; ++i) {//读512个数据*buffer++ = SD_MMC_ReadWrite_Byte(0xff); }SD_MMC_ReadWrite_Byte(0xff); //伪crcSD_MMC_ReadWrite_Byte(0xff); //伪crcSD_MMC_SPI_DESELECT();SD_MMC_ReadWrite_Byte(0xff); // extra 8 CLKreturn 0;}/******************************************************************* ************* Function Name : SD_MMC_Write_Single_Block* Description : SD_MMC_Write_Single_Block * Input : sector number and buffer data point* Output : None* Return : zero success, non-zero error.************************************************************************ *******/u8 SD_MMC_Write_Single_Block(u32 sector, u8* buffer) {u8 Response;u16 i;u16 retry=0;//写命令 send write commandResponse =SD_MMC_Send_Command(CMD_WRITE_SINGLE_BLOCK, sector<<9);if(Response != 0x00)return Response;SD_MMC_SPI_SELECT();SD_MMC_ReadWrite_Byte(0xff);SD_MMC_ReadWrite_Byte(0xff);SD_MMC_ReadWrite_Byte(0xff);//发开始符 start byte 0xfeSD_MMC_ReadWrite_Byte(0xfe);//送512字节数据 send 512 bytes datafor(i=0; i<512; i++){SD_MMC_ReadWrite_Byte(*buffer++);}SD_MMC_ReadWrite_Byte(0xff); //dummy crc SD_MMC_ReadWrite_Byte(0xff); //dummy crcResponse = SD_MMC_ReadWrite_Byte(0xff);//等待是否成功 judge if it successfulif( (Response&0x1f) != 0x05){SD_MMC_SPI_DESELECT();return Response;}//等待操作完 wait no busywhile(SD_MMC_ReadWrite_Byte(0xff) != 0x00) {if(retry++ > 0xfffe){SD_MMC_SPI_DESELECT();return 1;}}SD_MMC_SPI_DESELECT();SD_MMC_ReadWrite_Byte(0xff);// extra 8 CLKreturn 0;}if( SD_MMC_SPI_Init() ==1)printf(" _SD_MMC Initialization ERROR\r\n"); else{printf(" _SD_MMC Initialization OK\r\n"); //memset(buffer,0,512); //读取一个扇区的内容这里读的是0扇区SD_MMC_Read_Single_Block( 0 , buffer );Uart1_PutString( buffer , 512 );}。
stm32Fatfs读写SD卡
stm32Fatfs读写SD卡读写SD是嵌入式系统中一个比较基础的功能,在很多应用中都可以用得上SD卡。
折腾了几天,总算移植成功了最新版Fatfs(Fatfs R0.09),成功读写SD卡下文件。
FatFs ()是一个通用的文件系统模块,用于在小型嵌入式系统中实现FAT文件系统。
FatFs 的编写遵循ANSI C,因此不依赖于硬件平台。
它可以嵌入到便宜的微控制器中,如8051, PIC, AVR, SH, Z80, H8, ARM 等等,不需要做任何修改。
1. SD卡/TF卡硬件接口SD卡有两种操作接口,SDIO和SPI。
使用SDIO口的速度比较快,SPI的速度比较慢。
SD卡引脚描述如下: SD 卡SPI接法如下:我使用的是正点原子的开发板,所以采用的是SPI接口的模式。
TF卡SDIO 模式和SPI模式引脚定义:可以发现Micro SD卡只有8个引脚是因为比SD卡少了一个Vss。
使用TF转SD的卡套套在Micro SD卡上,这样一来大小就和SD卡一样大,这时候卡套上的9个引脚就和SD卡一样了,你可以完全当做SD卡来操作。
2. SD卡底层驱动SD卡的操作比较复杂,需要多看看一些文档。
这里附上SD底层驱动代码,代码说明详见注释Sd卡SPi操作底层代码: sdcard.c sdcard.h3. Fatfs 移植FatFs 软件包中相关文件:ffconf.h FatFs 模块配置文件ff.h FatFs 和应用模块公用的包含文件ff.c FatFs 模块diskio.h FatFs and disk I/O 模块公用的包含文件integer.h 数据类型定义option 可选的外部功能diskio.c FatFs 与disk I/O 模块接口层文件(不属于 FatFs 需要由用户提供)FatFs 配置,文件系统的配置项都在 ffconf.h 文件之中:(1) _FS_TINY :这个选项在R0.07 版本之中开始出现,在之前的版本都是以独立的文件出现,现在通过一个宏来修改使用起来更方便;(2) _FS_MINIMIZE、_FS_READONLY、_USE_STRFUNC、_USE_MKFS、_USE_FORWARD 这些宏是用来对文件系统进行裁剪(3) _CODE_PAGE :本选项用于设置语言码的类型(4) _USE_LFN :取值为0~3,主要用于长文件名的支持及缓冲区的动态分配:0:不支持长文件名;1:支持长文件名存储的静态分配,一般是存储在BSS 段;2:支持长文件名存储的动态分配,存储在栈上;3:支持长文件名存储的动态分配,存储在堆上。
基于STM32F407平台实现FATFS读写大容量SD卡的心得
基于STM32F407平台实现FATFS读写大容量SD卡的心得在基于STM32F407平台实现FATFS读写大容量(128G)SD卡的过程中,我积累了一些心得和经验。
下面我将简要介绍一下这些经验。
首先,在使用FATFS文件系统之前,我们需要确保已经正确初始化了SD卡的硬件和配置。
这包括对GPIO、SPI或SDIO等外设进行初始化,并设置正确的时钟源和参数。
对于不同的硬件配置,可能会有所不同,因此需要仔细查看STM32F407的相关文档和资料。
其次,在FATFS文件系统的使用中,我们需要了解和掌握以下几个核心概念和操作:1. 初始化文件系统和 Mount 卷标:在开始进行 SD 卡的读写之前,我们需要使用 f_mount 函数初始化文件系统,并使用 f_mount 函数挂载指定的卷标。
卷标可以是 0 到 9 之间的一个整数,用于表示 SD 卡上的不同分区或逻辑盘符。
2. 打开文件和读写操作:在进行文件读写之前,我们需要使用f_open 函数打开指定的文件,并返回一个文件指针。
然后可以使用f_read 和 f_write 函数进行读写操作。
读操作可以使用 f_read 函数读取指定长度的数据到缓冲区中,写操作可以使用 f_write 函数从缓冲区中写入指定长度的数据到文件中。
对于大容量的SD卡,我们可能需要提前进行一些准备工作,以确保能够正确读写数据:1. 使用 f_mkfs 函数进行格式化:在首次使用 SD 卡之前,我们可能需要对其进行格式化。
可以使用 f_mkfs 函数来完成格式化操作,指定分区号和扇区大小等参数。
2.适当分割文件:为了提高读写性能和避免内存溢出,我们可以将大的文件分割为多个小文件进行读写。
这可以通过调整缓冲区大小和每次读取或写入的数据长度来实现。
3.合理规划缓冲区内存:在进行SD卡读写时,我们需要使用一些缓冲区来暂存读取或写入的数据。
在选择缓冲区大小时,需要考虑到STM32F407的内存限制和其他功能的内存占用。
STM32读写SD卡
//向 SD 卡发送一个命令(结束是不失能片选,还有后续数据传来) //输入:u8 cmd 命令 // u32 arg 命令参数 // u8 crc crc 校验值 //返回值:SD 卡返回的响应
u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg, u8 crc) {
u8 Retry=0; u8 r1; SPIx_ReadWriteByte(0xff);//高速写命令延时 SPIx_ReadWriteByte(0xff); SD_CS=0;//片选端置低,选中 SD 卡 //发送 SPIx_ReadWriteByte(cmd | 0x40); //分别写入命令 SPIx_ReadWriteByte(arg >> 24); SPIx_ReadWriteByte(arg >> 16); SPIx_ReadWriteByte(arg >> 8); SPIx_ReadWriteByte(arg); SPIx_ReadWriteByte(crc);
ALIENTKE MiniSTM3 开发板就带有 SD 卡接口,利用 STM32 自带的 SPI 接口,最大通信 速度可达 18Mbps,每秒可传输数据 2M 字节以上,对于一般应用足够了。本节将向大家介绍, 如何在 ALIENTEK MiniSTM32 开发板上读取 SD 卡。本节分为如下几个部分:
图 3.20.1.1 SD 卡引脚排序图 SD 卡引脚功能描述如下表所示:
STM32的SD卡读取
连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第四十四章SD卡实验2013-04-04 23:07第四十四章 SD卡实验很多单片机系统都需要大容量存储设备,以存储数据。
目前常用的有U盘,FLASH芯片,SD卡等。
他们各有优点,综合比较,最适合单片机系统的莫过于SD卡了,它不仅容量可以做到很大(32Gb以上),而且支持SPI接口,方便移动,并且有几种体积的尺寸可供选择(标准的SD卡尺寸,以及TF卡尺寸等),能满足不同应用的要求。
只需要4个IO口即可外扩一个最大达32GB以上的外部存储器,容量从几十M到几十G 选择尺度很大,更换也很方便,编程也简单,是单片机大容量外部存储器的首选。
ALIENTKE 战舰STM32开发板自带了标准的SD卡接口,可使用STM32自带的SPI/S DIO接口驱动(通过跳线帽选择驱动方式),本章我们使用SPI驱动,最高通信速度可达18Mbps,每秒可传输数据2M字节以上,对于一般应用足够了。
在本章中,我们将向大家介绍,如何在ALIENTEK战舰STM32开发板上实现SD卡的读取。
本章分为如下几个部分:44.1 SD卡简介44.2 硬件设计44.3 软件设计44.4 下载验证44.1 SD卡简介SD卡(Secure Digital Memory Card)中文翻译为安全数码卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。
SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。
大小犹如一张邮票的SD记忆卡,重量只有2克,但却拥有高记忆容量、快速数据传输率、极大的移动灵活性以及很好的安全性。
按容量分类,可以将SD卡分为3类:SD卡、SDHC卡、SDXC卡。
如表44.1.1所示:容量命名简称0~2G Standard Capacity SD Memory Card SDSC或SD2G~32G High Capacity SD Memory Card SDHC32G~2T Extended Capacity SD Memory Card SDXC表44.1.1 SD卡按容量分类SD卡和SDHC卡协议基本兼容,但是SDXC卡,同这两者区别就比较大了,本章我们讨论的主要是SD/SDHC卡(简称SD卡)。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
3.20SD卡实验很多单片机系统都需要大容量存储设备,以存储数据。
目前常用的有U盘,FLASH芯片,SD卡等。
他们各有优点,综合比较,最适合单片机系统的莫过于SD卡了,它不仅容量可以做到很大(32Gb以上),而且支持SPI接口,方便移动,有几种体积的尺寸可供选择(标准的SD 卡尺寸,以及TF卡尺寸),能满足不同应用的要求。
只需要4个IO口,就可以外扩一个最大达32GB以上的外部存储器,容量选择尺度很大,更换也很方便,而且方便移动,编程也比较简单,是单片机大容量外部存储器的首选。
ALIENTKE MiniSTM3开发板就带有SD卡接口,利用STM32自带的SPI接口,最大通信速度可达18Mbps,每秒可传输数据2M字节以上,对于一般应用足够了。
本节将向大家介绍,如何在ALIENTEK MiniSTM32开发板上读取SD卡。
本节分为如下几个部分:3.20.1 SD卡简介3.20.2 硬件设计3.20.3 软件设计3.20.4 下载与测试3.20.1 SD卡简介SD卡(Secure Digital Memory Card)中文翻译为安全数码卡,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。
SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。
大小犹如一张邮票的SD记忆卡,重量只有2克,但却拥有高记忆容量、快速数据传输率、极大的移动灵活性以及很好的安全性。
SD卡一般支持2种操作模式:1,SD卡模式;2,SPI模式;主机可以选择以上任意一种模式同SD卡通信,SD卡模式允许4线的高速数据传输。
SPI模式允许简单的通过SPI接口来和SD卡通信,这种模式同SD卡模式相比就是丧失了速度。
SD卡的引脚排序如下图所示:图3.20.1.1 SD卡引脚排序图SD卡引脚功能描述如下表所示:表3.20.1.1 SD卡引脚功能表SD卡只能使用3.3V的IO电平,所以,MCU一定要能够支持3.3V的IO端口输出。
注意:在SPI模式下,CS/MOSI/MISO/CLK都需要加10~100K左右的上拉电阻。
SD卡要进入SPI模式很简单,就是在SD卡收到复位命令(CMD0)时,CS为有效电平(低电平)则SPI模式被启用。
不过在发送CMD0之前,要发送>74个时钟,这是因为SD卡内部有个供电电压上升时间,大概为64个CLK,剩下的10个CLK用于SD卡同步,之后才能开始CMD0的操作,在卡初始化的时候,CLK时钟最大不能超过400Khz!。
ALENTEK MiniSTM32开发板使用的是SPI模式来读写SD卡,下面我们就重点介绍一下SD卡在SPI模式下的相关操作。
首先介绍SPI模式下几个重要的操作命令,如下表所示:表3.20.1.2 SPI模式下SD卡部分操作指令其中R1的回应格式如下表所示:表3.20.1.3 SD卡R1回应格式接着我们看看SD卡的初始化,SD卡的典型初始化过程如下:1、初始化与SD卡连接的硬件条件(MCU的SPI配置,IO口配置);2、上电延时(>74个CLK);3、复位卡(CMD0);4、激活卡,内部初始化并获取卡类型(CMD1(用于MMC卡)、CMD55、CMD41);5.、查询OCR,获取供电状况(CMD58);6、是否使用CRC(CMD59);7、设置读写块数据长度(CMD16);8、读取CSD,获取存储卡的其他信息(CMD9);9、发送8CLK后,禁止片选;这样我们就完成了对SD卡的初始化,这里面我们一般设置读写块数据长度为512个字节,并禁止使用CRC。
在完成了初始化之后,就可以开始读写数据了。
SD卡读取数据,这里通过CMD17来实现,具体过程如下:1、发送CMD17;2、接收卡响应R1;3、接收数据起始令牌0XFE;4、接收数据;5、接收2个字节的CRC,如果没有开启CRC,这两个字节在读取后可以丢掉。
6、8CLK之后禁止片选;以上就是一个典型的读取SD卡数据过程,SD卡的写于读数据差不多,写数据通过CMD24来实现,具体过程如下:1、发送CMD24;2、接收卡响应R1;3、发送写数据起始令牌0XFE;4、发送数据;5、发送2字节的伪CRC;6、8CLK之后禁止片选;以上就是一个典型的写SD卡过程。
关于SD卡的介绍,我们就介绍到这里,更详细的介绍请参考SD卡的参考资料。
3.20.2 硬件设计本节实验功能简介:开机的时候先初始化SD卡,如果SD卡初始化完成,则读取扇区0的数据,然后通过串口打印到电脑上。
如果没初始化通过,则在LCD上提示初始化失败。
同样用DS0来指示程序正在运行。
所要用到的硬件资源如下:1)STM32F103RBT6。
2)DS0(外部LED0)。
3)串口1。
4)TFTLCD液晶模块。
5)SD卡。
前面四部分,在之前的实例已经介绍过了,这里我们介绍一下SD卡在开发板上的连接方式,SD卡与MCU的连接原理图如下:图3.20.2.1 SD卡与STM32连接电路图3.20.3 软件设计打开上一节的工程,首先在HARDW ARE文件夹下新建一个SD的文件夹。
然后新建一个MMC_SD.C和MMC_SD.H的文件保存在SD文件夹下,并将这个文件夹加入头文件包含路径。
打开MMC_SD.C文件,输入如下代码:#include "sys.h"#include "mmc_sd.h"#include "spi.h"#include "usart.h"#include "delay.h"u8SD_Type=0;//SD卡的类型//Mini STM32开发板//SD卡驱动//正点原子@ALIENTEK//2010/5/13//增加了一些延时,实测可以支持TF卡(1G/2G),金士顿2G,4G 16G SD卡//2010/6/24//加入了u8 SD_GetResponse(u8 Response)函数//修改了u8 SD_WaitDataReady(void)函数//增加了USB读卡器支持的u8 MSD_ReadBuffer(u8* pBuffer, u32 ReadAddr, u32 NumByteToRead);//和u8 MSD_WriteBuffer(u8* pBuffer, u32 WriteAddr, u32 NumByteToWrite);两个函数//等待SD卡回应//Response:要得到的回应值//返回值:0,成功得到了该回应值//其他,得到回应值失败u8 SD_GetResponse(u8 Response){u16 Count=0xFFF;//等待次数while ((SPIx_ReadWriteByte(0XFF)!=Response)&&Count)Count--;//等待得到准确的回应if (Count==0)return MSD_RESPONSE_FAILURE;//得到回应失败else return MSD_RESPONSE_NO_ERROR;//正确回应}//等待SD卡写入完成//返回值:0,成功;//其他,错误代码;u8 SD_WaitDataReady(void){u8 r1=MSD_DATA_OTHER_ERROR;}u32 retry; retry=0; do {r1=SPIx_ReadWriteByte(0xFF)&0X1F;//读到回应 if(retry==0xfffe)return 1; retry++; switch (r1) {case MSD_DATA_OK://数据接收正确了r1=MSD_DATA_OK; break;case MSD_DATA_CRC_ERROR: //CRC校验错误return MSD_DATA_CRC_ERROR;case MSD_DATA_WRITE_ERROR://数据写入错误 return MSD_DATA_WRITE_ERROR; default://未知错误r1=MSD_DATA_OTHER_ERROR; break;}}while(r1==MSD_DATA_OTHER_ERROR); //数据错误时一直等待 retry=0;while(SPIx_ReadWriteByte(0XFF)==0)//读到数据为0,则数据还未写完成{retry++;//delay_us(10);//SD卡写等待需要较长的时间 if(retry>=0XFFFFFFFE)return 0XFF;//等待失败了};return 0;//成功了 //向SD卡发送一个命令 //输入: u8 cmd 命令//u32 arg 命令参数//u8 crc crc校验值 //返回值:SD卡返回的响应u8 SD_SendCommand(u8 cmd, u32 arg, u8 crc) {u8 r1; u8 Retry=0; SD_CS=1;SPIx_ReadWriteByte(0xff);//高速写命令延时 SPIx_ReadWriteByte(0xff); SPIx_ReadWriteByte(0xff);} //片选端置低,选中SD卡 SD_CS=0;//发送SPIx_ReadWriteByte(cmd | 0x40);//分别写入命令 SPIx_ReadWriteByte(arg >> 24); SPIx_ReadWriteByte(arg >> 16); SPIx_ReadWriteByte(arg >> 8); SPIx_ReadWriteByte(arg); SPIx_ReadWriteByte(crc); //等待响应,或超时退出while((r1=SPIx_ReadWriteByte(0xFF))==0xFF) {Retry++;if(Retry>200)break; }//关闭片选 SD_CS=1;//在总线上额外增加8个时钟,让SD卡完成剩下的工作SPIx_ReadWriteByte(0xFF); //返回状态值 return r1;//向SD卡发送一个命令(结束是不失能片选,还有后续数据传来) //输入:u8 cmd 命令//u32 arg 命令参数 // u8 crc crc校验值//返回值:SD卡返回的响应u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg, u8 crc) {u8 Retry=0; u8 r1;SPIx_ReadWriteByte(0xff);//高速写命令延时 SPIx_ReadWriteByte(0xff); SD_CS=0;//片选端置低,选中SD卡 //发送SPIx_ReadWriteByte(cmd | 0x40); //分别写入命令 SPIx_ReadWriteByte(arg >> 24); SPIx_ReadWriteByte(arg >> 16); SPIx_ReadWriteByte(arg >> 8); SPIx_ReadWriteByte(arg); SPIx_ReadWriteByte(crc);}//等待响应,或超时退出while((r1=SPIx_ReadWriteByte(0xFF))==0xFF) {Retry++;if(Retry>200)break; }//返回响应值 return r1; //把SD卡设置到挂起模式//返回值:0,成功设置 //1,设置失败u8 SD_Idle_Sta(void) {u16 i;u8 retry;for(i=0;i<0xf00;i++);//纯延时,等待SD卡上电完成//先产生>74个脉冲,让 SD卡自己初始化完成for(i=0;i<10;i++)SPIx_ReadWriteByte(0xFF);//-----------------SD卡复位到 idle开始-----------------//循环连续发送CMD0,直到SD卡返回0x01,进入IDLE状态 //超时则直接退出retry = 0; do {//发送CMD0,让SD卡进入IDLE状态 i = SD_SendCommand(CMD0, 0, 0x95);retry++;}while((i!=0x01)&&(retry<200));//跳出循环后,检查原因:初始化成功?or 重试超时? if(retry==200)return 1; //失败 return 0;//成功 }//初始化SD卡//如果成功返回,则会自动设置SPI速度为18Mhz//返回值:0:NO_ERR //1:TIME_OUT//99:NO_CARDu8 SD_Init(void) {u8 r1;// 存放SD卡的返回值 u16 retry; // 用来进行超时计数u8 buff[6];//设置硬件上与SD卡相关联的控制引脚输出//避免NRF24L01/W25X16等的影响RCC->APB2ENR|=1<<2;//PORTA时钟使能GPIOA->CRL&=0XFFF000FF;GPIOA->CRL|=0X00033300;//PA2.3.4 推挽GPIOA->ODR|=0X7<<2;//PA2.3.4上拉SPIx_Init();SPIx_SetSpeed(SPI_SPEED_256);//设置到低速模式SD_CS=1;if(SD_Idle_Sta()) return 1;//超时返回1 设置到idle 模式失败//-----------------SD卡复位到idle结束-----------------//获取卡片的SD版本信息SD_CS=0;r1 = SD_SendCommand_NoDeassert(8, 0x1aa,0x87);//如果卡片版本信息是v1.0版本的,即r1=0x05,则进行以下初始化if(r1 == 0x05){//设置卡类型为SDV1.0,如果后面检测到为MMC卡,再修改为MMCSD_Type = SD_TYPE_V1;//如果是V1.0卡,CMD8指令后没有后续数据//片选置高,结束本次命令SD_CS=1;//多发8个CLK,让SD结束后续操作SPIx_ReadWriteByte(0xFF);//-----------------SD卡、MMC卡初始化开始-----------------//发卡初始化指令CMD55+ACMD41// 如果有应答,说明是SD卡,且初始化完成// 没有回应,说明是MMC卡,额外进行相应初始化retry = 0;do{//先发CMD55,应返回0x01;否则出错r1 = SD_SendCommand(CMD55, 0, 0);if(r1 == 0XFF)return r1;//只要不是0xff,就接着发送//得到正确响应后,发ACMD41,应得到返回值0x00,否则重试200次r1 = SD_SendCommand(ACMD41, 0, 0);retry++;}while((r1!=0x00) && (retry<400));// 判断是超时还是得到正确回应// 若有回应:是SD卡;没有回应:是MMC卡//----------MMC卡额外初始化操作开始------------if(retry==400){}retry = 0;//发送MMC卡初始化命令(没有测试)do {r1 = SD_SendCommand(1,0,0); retry++;}while((r1!=0x00)&& (retry<400));if(retry==400)return 1; //MMC卡初始化超时 //写入卡类型SD_Type = SD_TYPE_MMC; //----------MMC卡额外初始化操作结束------------//设置SPI为高速模式SPIx_SetSpeed(SPI_SPEED_4);SPIx_ReadWriteByte(0xFF); //禁止CRC校验r1 = SD_SendCommand(CMD59, 0, 0x95); if(r1 != 0x00)return r1; //命令错误,返回r1 //设置Sector Sizer1 = SD_SendCommand(CMD16, 512, 0x95); if(r1 != 0x00)return r1;//命令错误,返回r1//-----------------SD卡、MMC卡初始化结束-----------------}//SD卡为 V1.0版本的初始化结束//下面是V2.0卡的初始化//其中需要读取OCR数据,判断是SD2.0还是SD2.0HC卡else if(r1 == 0x01) {//V2.0的卡,CMD8命令后会传回4字节的数据,要跳过再结束本命令buff[0] = SPIx_ReadWriteByte(0xFF); //should be 0x00 buff[1] = SPIx_ReadWriteByte(0xFF); //should be 0x00 buff[2] = SPIx_ReadWriteByte(0xFF); //should be 0x01 buff[3] = SPIx_ReadWriteByte(0xFF); //should be 0xAA SD_CS=1;SPIx_ReadWriteByte(0xFF);//the next 8 clocks //判断该卡是否支持2.7V-3.6V的电压范围//if(buff[2]==0x01 && buff[3]==0xAA) //不判断,让其支持的卡更多 {retry = 0;//发卡初始化指令CMD55+ACMD41do {r1 = SD_SendCommand(CMD55, 0, 0);if(r1!=0x01)return r1;r1 = SD_SendCommand(ACMD41, 0x40000000, 0);if(retry>200)return r1;//超时则返回r1状态}while(r1!=0);//初始化指令发送完成,接下来获取OCR信息//-----------鉴别SD2.0卡版本开始-----------r1 = SD_SendCommand_NoDeassert(CMD58, 0, 0);if(r1!=0x00){SD_CS=1;//释放SD片选信号return r1;//如果命令没有返回正确应答,直接退出,返回应答} }}//读OCR指令发出后,紧接着是4字节的OCR信息buff[0] = SPIx_ReadWriteByte(0xFF);buff[1] = SPIx_ReadWriteByte(0xFF);buff[2] = SPIx_ReadWriteByte(0xFF);buff[3] = SPIx_ReadWriteByte(0xFF);//OCR接收完成,片选置高SD_CS=1;SPIx_ReadWriteByte(0xFF);//检查接收到的OCR中的bit30位(CCS),确定其为SD2.0还是SDHC //如果CCS=1:SDHC CCS=0:SD2.0if(buff[0]&0x40)SD_Type = SD_TYPE_V2HC;//检查CCSelse SD_Type = SD_TYPE_V2;//-----------鉴别SD2.0卡版本结束-----------//设置SPI为高速模式SPIx_SetSpeed(SPI_SPEED_4);}return r1;//从SD卡中读回指定长度的数据,放置在给定位置//输入: u8 *data(存放读回数据的内存>len)//u16 len(数据长度)//u8 release(传输完成后是否释放总线CS置高 0:不释放 1:释放)//返回值:0:NO_ERR//other:错误信息u8 SD_ReceiveData(u8 *data, u16 len, u8 release){// 启动一次传输} SD_CS=0;if(SD_GetResponse(0xFE))//等待 SD 卡发回数据起始令牌0xFE {SD_CS=1;return 1;}while(len--)//开始接收数据{*data=SPIx_ReadWriteByte(0xFF);data++;}//下面是 2 个伪CRC (dummy CRC ) SPIx_ReadWriteByte(0xFF);SPIx_ReadWriteByte(0xFF);if(release==RELEASE)//按需释放总线,将 CS置高 {SD_CS=1;//传输结束SPIx_ReadWriteByte(0xFF);}return 0;//获取 SD 卡的 CID信息,包括制造商信息 //输入: u8 *cid_data(存放 CID 的内存,至少16Byte )//返回值:0:NO_ERR // 1:TIME_OUT//other :错误信息u8 SD_GetCID(u8 *cid_data){u8 r1;//发 CMD10 命令,读CID r1 = SD_SendCommand(CMD10,0,0xFF);if(r1 != 0x00)return r1; //没返回正确应答,则退出,报错SD_ReceiveData(cid_data,16,RELEASE);//接收 16个字节的数据 return 0;}//获取 SD 卡的 CSD信息,包括容量和速度信息//输入:u8 *cid_data(存放 CID 的内存,至少16Byte ) //返回值:0:NO_ERR// 1:TIME_OUT//other :错误信息u8 SD_GetCSD(u8 *csd_data){u8 r1;r1=SD_SendCommand(CMD9,0,0xFF);//发CMD9命令,读CSDif(r1)return r1;//没返回正确应答,则退出,报错SD_ReceiveData(csd_data, 16, RELEASE);//接收16个字节的数据return 0;}//获取SD卡的容量(字节)//返回值:0:取容量出错//其他:SD卡的容量(字节)u32 SD_GetCapacity(void){u8 csd[16];u32 Capacity;u8 r1;u16 i;u16 temp;//取CSD信息,如果期间出错,返回0if(SD_GetCSD(csd)!=0) return 0;//如果为SDHC卡,按照下面方式计算if((csd[0]&0xC0)==0x40){Capacity=((u32)csd[8])<<8;Capacity+=(u32)csd[9]+1;Capacity = (Capacity)*1024;//得到扇区数Capacity*=512;//得到字节数}else{i = csd[6]&0x03;i<<=8;i += csd[7];i<<=2;i += ((csd[8]&0xc0)>>6);//C_SIZE_MULTr1 = csd[9]&0x03;r1<<=1;r1 += ((csd[10]&0x80)>>7);r1+=2;//BLOCKNRtemp = 1;while(r1){}temp*=2;r1--;}Capacity = ((u32)(i+1))*((u32)temp);// READ_BL_LENi = csd[5]&0x0f;//BLOCK_LENtemp = 1;while(i){temp*=2;i--;}//The final resultCapacity *= (u32)temp;//字节为单位}return (u32)Capacity;//读 SD 卡的一个block//输入:u32 sector 取地址(sector值,非物理地址) // u8 *buffer 数据存储地址(大小至少512byte ) //返回值:0: 成功//other :失败 u8 SD_ReadSingleBlock(u32 sector, u8 *buffer){u8 r1;//设置为高速模式SPIx_SetSpeed(SPI_SPEED_4);//如果不是 SDHC ,给定的是 sector 地址,将其转换成 byte地址 if(SD_Type!=SD_TYPE_V2HC){sector = sector<<9;}r1 = SD_SendCommand(CMD17, sector, 0);//读命令if(r1 != 0x00)return r1;r1 = SD_ReceiveData(buffer, 512, RELEASE);if(r1 != 0)return r1; //读数据出错!else return 0;}/////////////////下面 2 个函数为 USB读写所需要的///////////////////////////定义SD卡的块大小#define BLOCK_SIZE 512//写入MSD/SD数据//pBuffer:数据存放区//ReadAddr:写入的首地址//NumByteToRead:要写入的字节数//返回值:0,写入完成//其他,写入失败u8 MSD_WriteBuffer(u8* pBuffer, u32 WriteAddr, u32 NumByteToWrite) {u32 i,NbrOfBlock = 0, Offset = 0;u32 sector;u8 r1;NbrOfBlock = NumByteToWrite / BLOCK_SIZE;//得到要写入的块的数目SD_CS=0;while (NbrOfBlock--)//写入一个扇区{sector=WriteAddr+Offset;if(SD_Type==SD_TYPE_V2HC)sector>>=9;//执行与普通操作相反的操作} r1=SD_SendCommand_NoDeassert(CMD24,sector,0xff);//写命令if(r1){SD_CS=1;return 1;//应答不正确,直接返回}SPIx_ReadWriteByte(0xFE);//放起始令牌0xFE//放一个sector的数据for(i=0;i<512;i++)SPIx_ReadWriteByte(*pBuffer++);//发2个Byte的dummy CRCSPIx_ReadWriteByte(0xff);SPIx_ReadWriteByte(0xff);if(SD_WaitDataReady())//等待SD卡数据写入完成{SD_CS=1;return 2;}Offset += 512;//写入完成,片选置1SD_CS=1;SPIx_ReadWriteByte(0xff); return 0;}//读取MSD/SD数据//pBuffer:数据存放区//ReadAddr:读取的首地址//NumByteToRead:要读出的字节数//返回值:0,读出完成//其他,读出失败u8 MSD_ReadBuffer(u8* pBuffer, u32 ReadAddr, u32 NumByteToRead){u32 NbrOfBlock=0,Offset=0;u32 sector=0;u8 r1=0;NbrOfBlock=NumByteToRead/BLOCK_SIZE;SD_CS=0;while (NbrOfBlock --){sector=ReadAddr+Offset;if(SD_Type==SD_TYPE_V2HC)sector>>=9;//执行与普通操作相反的操作r1=SD_SendCommand_NoDeassert(CMD17,sector,0xff);//读命令} if(r1)//命令发送错误{SD_CS=1;return r1;}r1=SD_ReceiveData(pBuffer,512,RELEASE); if(r1)//读数错误{SD_CS=1;return r1;}pBuffer+=512;Offset+=512;} SD_CS=1;SPIx_ReadWriteByte(0xff); return 0;////////////////////////////////////////////////////////////////////////// //写入SD卡的一个block(未实际测试过)//输入:u32 sector 扇区地址(sector值,非物理地址)//u8 *buffer 数据存储地址(大小至少512byte)//返回值:0:成功//other:失败u8 SD_WriteSingleBlock(u32 sector, const u8 *data){u8 r1;u16 i;u16 retry;//设置为高速模式//SPIx_SetSpeed(SPI_SPEED_HIGH);//如果不是SDHC,给定的是sector地址,将其转换成byte地址if(SD_Type!=SD_TYPE_V2HC){sector = sector<<9;}r1 = SD_SendCommand(CMD24, sector, 0x00);if(r1 != 0x00){return r1;//应答不正确,直接返回}//开始准备数据传输SD_CS=0;//先放3个空数据,等待SD卡准备好SPIx_ReadWriteByte(0xff);SPIx_ReadWriteByte(0xff);SPIx_ReadWriteByte(0xff);//放起始令牌0xFESPIx_ReadWriteByte(0xFE);//放一个sector的数据for(i=0;i<512;i++){SPIx_ReadWriteByte(*data++);}//发2个Byte的dummy CRCSPIx_ReadWriteByte(0xff);SPIx_ReadWriteByte(0xff);//等待SD卡应答r1 = SPIx_ReadWriteByte(0xff);if((r1&0x1F)!=0x05){SD_CS=1;return r1;}//等待操作完成retry = 0;while(!SPIx_ReadWriteByte(0xff)){retry++;if(retry>0xfffe)//如果长时间写入没有完成,报错退出{SD_CS=1;return 1;//写入超时返回1}}//写入完成,片选置1SD_CS=1;SPIx_ReadWriteByte(0xff);return 0;}//读SD卡的多个block(实际测试过)//输入:u32 sector 扇区地址(sector值,非物理地址)//u8 *buffer 数据存储地址(大小至少512byte)//u8 count 连续读count个block//返回值:0:成功//other:失败u8 SD_ReadMultiBlock(u32 sector, u8 *buffer, u8 count){u8 r1;//SPIx_SetSpeed(SPI_SPEED_HIGH);//设置为高速模式//如果不是SDHC,将sector地址转成byte地址if(SD_Type!=SD_TYPE_V2HC)sector = sector<<9;//SD_WaitDataReady();//发读多块命令r1 = SD_SendCommand(CMD18, sector, 0);//读命令if(r1 != 0x00)return r1;do//开始接收数据{if(SD_ReceiveData(buffer, 512, NO_RELEASE) != 0x00)break;}buffer += 512;} while(--count);//全部传输完毕,发送停止命令SD_SendCommand(CMD12, 0, 0);//释放总线SD_CS=1;SPIx_ReadWriteByte(0xFF);if(count != 0)return count;//如果没有传完,返回剩余个数 else return 0;//写入 SD 卡的 N 个block(未实际测试过)//输入:u32 sector 扇区地址(sector值,非物理地址) // u8 *buffer 数据存储地址(大小至少512byte ) // u8 count 写入的 block数目//返回值:0: 成功 //other :失败 u8 SD_WriteMultiBlock(u32 sector, const u8 *data, u8 count){u8 r1;u16 i;//SPIx_SetSpeed(SPI_SPEED_HIGH);//设置为高速模式if(SD_Type != SD_TYPE_V2HC)sector = sector<<9;//如果不是 sector 地址,将其转换成 byte地址SDHC ,给定的是 if(SD_Type != SD_TYPE_MMC) r1 = SD_SendCommand(ACMD23, count, 0x00);// 如果目标卡不是 MMC 卡,启用 ACMD23指令使能预擦除r1 = SD_SendCommand(CMD25, sector, 0x00);//发多块写入指令 if(r1 != 0x00)return r1; //应答不正确,直接返回SD_CS=0;//开始准备数据传输SPIx_ReadWriteByte(0xff);//先放 3 个空数据,等待 SD卡准备好 SPIx_ReadWriteByte(0xff);//--------下面是 N 个 sector写入的循环部分 do{//放起始令牌0xFC 表明是多块写入 SPIx_ReadWriteByte(0xFC);//放一个 sector的数据 for(i=0;i<512;i++){SPIx_ReadWriteByte(*data++);}//发 2 个 Byte 的dummy CRCSPIx_ReadWriteByte(0xff);SPIx_ReadWriteByte(0xff);}//等待SD卡应答r1 = SPIx_ReadWriteByte(0xff);if((r1&0x1F)!=0x05){SD_CS=1;//如果应答为报错,则带错误代码直接退出return r1;}//等待SD卡写入完成if(SD_WaitDataReady()==1){SD_CS=1;//等待SD卡写入完成超时,直接退出报错return 1;}}while(--count);//本sector数据传输完成//发结束传输令牌0xFDr1 = SPIx_ReadWriteByte(0xFD);if(r1==0x00){count =0xfe;}if(SD_WaitDataReady()) //等待准备好{SD_CS=1;return 1;}//写入完成,片选置1SD_CS=1;SPIx_ReadWriteByte(0xff);return count;//返回count值,如果写完则count=0,否则count=1//在指定扇区,从offset开始读出bytes个字节//输入:u32 sector 扇区地址(sector值,非物理地址)//u8 *buf数据存储地址(大小<=512byte)//u16 offset在扇区里面的偏移量//u16 bytes要读出的字节数//返回值:0:成功//other:失败u8 SD_Read_Bytes(unsigned long address,unsigned char *buf,unsigned int offset,unsigned int bytes){u8 r1;u16 i=0;r1=SD_SendCommand(CMD17,address<<9,0);//发送读扇区命令if(r1)return r1;//应答不正确,直接返回SD_CS=0;//选中SD卡if(SD_GetResponse(0xFE))//等待SD卡发回数据起始令牌0xFE{SD_CS=1; //关闭SD卡return 1;//读取失败}for(i=0;i<offset;i++)SPIx_ReadWriteByte(0xff);//跳过offset位for(;i<offset+bytes;i++)*buf++=SPIx_ReadWriteByte(0xff);//读取有用数据} for(;i<512;i++) SPIx_ReadWriteByte(0xff);SPIx_ReadWriteByte(0xff);//发送伪CRC码SPIx_ReadWriteByte(0xff);SD_CS=1;//关闭SD卡return 0;//读出剩余字节此部分代码我们在本节主要用到的就是SD卡初始化函数SD_Init和SD卡读取函数SD_ReadSingleBlock,通过前面的介绍,相信大家不难理解,这里我们就不多说了,接下来保存MMC_SD.C文件,并加入到HARDW ARE组下,然后打开MMC_SD.H,在该文件里面输入如下代码:#ifndef _MMC_SD_H_#define _MMC_SD_H_#include <stm32f10x_lib.h>//Mini STM32开发板//SD卡驱动//正点原子@ALIENTEK//2010/6/13//SD传输数据结束后是否释放总线宏定义#define NO_RELEASE0#define RELEASE 1// SD卡类型定义#define SD_TYPE_MMC0#define SD_TYPE_V1 1#define SD_TYPE_V2 2#define SD_TYPE_V2HC4// SD卡指令表#define CMD00//卡复位#define CMD1 1#define CMD99//命令9 ,读CSD数据#define CMD1010//命令10,读CID数据#define CMD1212//命令12,停止数据传输#define CMD1616//命令16,设置SectorSize 应返回0x00#define CMD1717//命令17,读sector#define CMD1818//命令18,读Multi sector#define ACMD2323//命令23,设置多sector写入前预先擦除N个block #define CMD2424//命令24,写sector#define CMD2525//命令25,写Multi sector#define ACMD4141//命令41,应返回0x00#define CMD5555//命令55,应返回0x01#define CMD5858//命令58,读OCR信息#define CMD5959//命令59,使能/禁止CRC,应返回0x00//数据写入回应字意义#define MSD_DATA_OK0x05#define MSD_DATA_CRC_ERROR0x0B#define MSD_DATA_WRITE_ERROR0x0D#define MSD_DATA_OTHER_ERROR0xFF//SD卡回应标记字#define MSD_RESPONSE_NO_ERROR0x00#define MSD_IN_IDLE_STATE0x01#define MSD_ERASE_RESET0x02#define MSD_ILLEGAL_COMMAND0x04#define MSD_COM_CRC_ERROR0x08#define MSD_ERASE_SEQUENCE_ERROR0x10#define MSD_ADDRESS_ERROR0x20#define MSD_PARAMETER_ERROR0x40#define MSD_RESPONSE_FAILURE0xFF//这部分应根据具体的连线来修改!//Mini STM32使用的是PA3作为SD卡的CS脚.#define SD_CS PAout(3) //SD卡片选引脚extern u8SD_Type;//SD卡的类型//函数申明区u8 SD_WaitReady(void);//等待SD卡就绪u8 SD_SendCommand(u8 cmd, u32 arg, u8 crc);//SD卡发送一个命令u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg, u8 crc);u8 SD_Init(void);//SD卡初始化u8 SD_Idle_Sta(void);//设置SD卡到挂起模式u8 SD_ReceiveData(u8 *data, u16 len, u8 release);//SD卡读数据u8 SD_GetCID(u8 *cid_data);//读SD卡CIDu8 SD_GetCSD(u8 *csd_data);//读SD卡CSDu32 SD_GetCapacity(void);//取SD卡容量//USB 读卡器SD卡操作函数u8 MSD_WriteBuffer(u8* pBuffer, u32 WriteAddr, u32 NumByteToWrite);u8 MSD_ReadBuffer(u8* pBuffer, u32 ReadAddr, u32 NumByteToRead);u8 SD_ReadSingleBlock(u32 sector, u8 *buffer);//读一个sectoru8 SD_WriteSingleBlock(u32 sector, const u8 *buffer);//写一个sectoru8 SD_ReadMultiBlock(u32 sector, u8 *buffer, u8 count);//读多个sectoru8 SD_WriteMultiBlock(u32 sector, const u8 *data, u8 count);//写多个sectoru8 SD_Read_Bytes(unsigned long address,unsigned char *buf,unsigned intoffset,unsigned int bytes);//读取一byte#endif该部分代码主要是一些命令的宏定义以及函数声明,在这里我们设定了SD卡的CS管脚为PA3。