求stm32 adc例程在sd模式下读写sd卡的文件和例程指导,感激不尽!!

STM32 SDIO方式驱动SD卡之SD协议-电子产品世界论坛
STM32 SDIO方式驱动SD卡之SD协议
写在前面的话试过官方的V4.5.0和V4.3.0都没成功,偶然间找到了V4.4.0竟然能用(3.5的库),激动ing,接下来就认真分析一下代码吧。当然分析代码前要先了解一下SD的协议和硬件接口。
1 SD的协议
&&1.1&&SD卡协议目前已经发展到了V4.1,可以去SD协会官网下载.官网的文件N多个,此刻最需要的是 Part1 Physical Layer...这个.其他的文档作用不妨参考下图了解一下吧.不过下载的是精简版,每年交N多美金成为SD协会的会员才能搞到完整版.
& && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && &&&SD说明 文档结构
&&1.2&&SD卡分类.从容量上分有&&标准容量SDSC(&=2G);大容量SDHC(2G到32G);超大容量SDXC(32G到2T),一般卡面上会有HC或XC等字样.从速度上可以分为class2,class4,calss6,class10(10MHZ/Sec).
& && &&这里的协议适用于标准SD,miniSD和MicroSD.
&&1.3&&总线协议
& && &&&总线上的命令和数据都是由一个以0开头以1结尾的bit Stream构成的.命令是一个操作的开始点,它在CMD line上串行传输,由host发送给SD卡.命令又分为广播命令和点对点命令;响应是由某个目标卡或者总线上的所有卡针对之前收到的命令回复给Host的,响应也是在CMD line上传输;数据通过数据线(Dat0-Dat3)传输,可以选择一位数据线也可以选择4位宽的数据线,数据的传输的双向的,Host由卡读数据时,数据由卡到Host.Host向卡写数据时,数据由Host到卡.
& && && && && &
& && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && &无响应或无数据操作
& && &&&SD卡总线上最基本的传输方式就是命令和响应方式,host发命令然后SD卡给予相对应的响应.数据是以数据块的方式传输的,可以单独一个块传输也可以多块传输.同样的数据量多块传输要比一个块一个块传输快,多块传输以CMD line上的停止命令结束.下面是多块读和多块写的操作图示.
& && && &&一个命令的组成看下图,由1位起始位,参数,7位CRC,1位结束位构成,共计48bits
& && && &响应分短响应(48bits)和长响应(136bits)内含参数和CRC.看图如下:
& && &&&命令在CMD line上是MSB在前LSB在后.
& &&&数据在数据线上的传输格式分为两大类,分别是Usual Data(8位)和wide width data(512位) 每大类由分为standard bus 1bit和 wide bus 4bit两类.看图
&&1.4 引脚功能定义
& && &&& & SD内部的寄存器和内部结构框图
2 SD功能描述
2.1 基本描述
& &&&host和SD之间的通讯都是有host控制的,host发送两种命令:广播命令和点对点命令.
& &&&SD和host之间有两种操作模式,识别模式和数据传输模式.
& &&&识别模式:复位后进入识别模式,在识别模式主要是寻找bus上的SD卡,SD卡会一直停留在识别模式直到收到主机给他分配的相对地址RCA(用CMD3实现).
& &&&数据传输模式:收到CMD3就会进入数据传输模式.
& && &SD卡的状态和操作模式之间的对应关系如下图
2.2 卡识别模式
& && &在卡识别模式,host复位所有的卡,验证他们的操作电压,并向每个卡发布相对地址, 所有的数据通信只在CMD line 进行传输.
& && &CMD0 是软件复位命令.除了卡当前处于在Inactive state外,CMD0都会使SD卡进入Idle state.
& && &SD卡上电后都会处于Idle state,即使卡之前是Inactive state.
& && &SD卡上电或接收到CMD0之后,所有卡的CMD line都处于输入模式,等待命令的起始位'0',卡初始化用的RCA=0x0000.stm32的引脚配置成什么什么模式参考下图
& && &操作电压的验证:上电或复位后双方不知道对方能提供货能支持的电压范围,host发送一个带有操作电压范围参数的命令CMD8给SD卡,SD卡通过分析CMD8的参数检查操作电压的合法性,电压参数在VHS[3:0]中,这四bits中只能由1bit是'1',如果卡能支持host提供的电压,则会给予一个响应.否则不会响应并停留在idle state.
& && & 初始化SDHC和SDXC时,发送第一个ACMD41之前必须发送CMD8,能够接受CMD8并给予正确响应,说明该卡支持Physical layer Version 2.00或Physical layer Version 2.00之后的协议,并且卡使能一些新的功能.
& && & ACMD41参数中需设置HCSbit和OCR[23:0],OCR[23:0]会设置一个VHS[3:0]指定范围中的一个具体值.该命令的功能是辨出那些不支持host提供电压的卡,这些卡脱离总线进入Inactive state.ACMD41是特殊应用型命令,发送特殊应用型命令前必须发送CMD55,CMD55的参数是RCA=0x000.
& && & 下面是ACMD41命令描述和OCR寄存器描述
& && & SD卡状态转移图(识别模式)
& && && &&SD卡初始化和识别的处理过程:初始化从ACMD41开始,该命令设置HCS(host支持的容量标志)和OCR寄存的的23:0bit。HCS=1 表示host支持SDHC或SDXC,HCS=0表示host既不支持SDHC也不支持SDXC.接收到CMD8会扩展ACMD41的功能.不对CMD8响应的卡不理睬HCS的值,然而host应该HCS=0,如果卡不对CMD8进行响应,SDHC不在乎HSC的值,但host如果设置HCS=0,那会SDHC和SDXC类型的卡永远不会转入到 ready state(busy=0),OCR寄存器中的busy bit用来指示SD卡是否初始化完成,busy=0,初始化仍在进行中;busy=1,初始化已经完成.卡初始化应该在ACMD41发出来1秒钟内完成,卡会在ACMD41第一次发出带有voltage window的参数时检查操作条件和OCR中的HCS位.在重复发ACMD41的过程中,SD卡不接收其他的命令除了CMD0.
& && & 如果卡回应了CMD8,那么对ACMD41的响应中包含有CCS(卡容量标志)位,当busy=1时CCS位有效,CCS=0,sd卡为SDSC卡;CCS=1,SD卡为SDHC或SDXC.
& && & Host对总线上的新卡完成相同的初始化的过程,不兼容的卡进入Inactive state.接下来Host发布CMD2给所有的SD卡,为了获得他们的唯一卡识别号(Card ID---CID).SD卡响应过自己的CID后进入Identification state.接下来Host发出CMD3,要求各SD卡发布自己新的RCA(比CID短,用在将来的数传输模式),等到RCA被Host接收的时候,SD卡进入Ready state.之后哦如果host想给卡一个新的RCA,那么就发送一个CMD3给目标卡,最后一个RCA才是卡的真是RCA.
& && &&&Host重复识别模式(CMD2--CMD3循环),完成对系统中每个卡的识别.
& && && && &下图是卡初始化和识别过程的流程图(写驱动的重要参考)
& && &&& &关于ACMD41这个命令:如果在第一次ACMD41命令参数里的voltage window field(bit23:0)不为0,这时第一个ACMD41就开始初始化,参数中其余的位有效.接下来重复的ACMD的参数应该跟第一次ACMD41的参数一样.
& && & 下图时ACMD41的参数格式和他的响应R3
& && && &上图中的XPC控制SDXC的功耗,对于32G以下的卡没有影响,SR18跟UHS-I卡有关,此刻不关心它.
2.3 数据传输模式
& &&&由于一些卡在识别模式对操作频率有限制,所以直到CMD3结束SD卡的频率应在Fod(400K)以下.在数据传输模式下方可以在Fpp的频率操作SD卡.Host发送CMD9命令获得SD卡的CSD(card special data) 寄存器的内容,比如数据块长度,卡容量等等.
& &&&host发送CMD4广播命令所有已识别卡的driver stages,根据布线layout和总线上卡的数量和数据传输频率编程SD卡的DSR寄存器.时钟频率有Fod到Fpp的转变也是在此时发生的.CMD4命令对host和SD卡来说都是可选的.
& && &CMD7用来选择一个特定的目标卡使他进入 transfer state,同一个时间系统内只能有一个卡处于transfer state ,选中一个卡后其他卡都回到ready state.带RCA=0x0000的CMD7命令使系统中的所用SD卡都进入到ready state.
& && &下图时数据传输模式 卡状态的转移图
& && && &&数据转移模式下的SD卡状态转移关系总结如下:
& && & 所有的数据读写命令均可以被CMD12中断,写数据是应该先中断写操作,而后才能执行CMD7重新选择另一个SD卡;写数据过程中,一旦数据传输结束SD卡将离开write state转向program state(传输成功时)或转向transfer state(传输失败);如果数据块传输被停止,那么最后一个数据块是有效的,数据将被存储;SD卡为块写操作提供了缓存空间,这就允许当前块正在编程时下一个块被发送至SD卡,但当缓存区已满并且SD卡正处于编程状态,DAT0 line将会保持低电平;写CSD,写保护,擦出功能不提供缓存区,这就是说当正在执行上述命令时不会接受其他数据传输命令,DAT0保持低电平;卡正处于program state时参数设置命令不允许使用,参数设置命令有CMD16(设置块小),CMD32(擦出开始地址命令),CMD33(擦出结束地址命令),读命令也不被允许在program state中.CMD7后不会立即停止擦出和编程操作;复位命令(CMD0和CMD15)将会停止任何等待和编程操作,这样操作破换SD卡的数据内容,host有责任阻止这样的情况发生;
2.4&&宽总线的选择
& && &在卡未被LOCK且处于transfer state时用ACMD6选择宽总线(4bits DAT3--DAT0),复位或上电后
默认1bit宽度(DAT0);
2.5&&数据块的读写,和擦出就是那几个命令,查阅文档可得知,说明一点的是 擦出命令又三条,这三条命令时有先后顺序要求的.
2.6&&数据保护这一部分就不学习了.
2.7&&CMD8的补充介绍
& && & CMD8命令的定义为了初始化兼容V2.0版本后的SD卡,CMD8在Idle state有效,他有两个作用:1电压检查;2扩展现有的命令和响应.CMD8的命令结构如下:
& && & 当卡处于Idle state时,host应该在ACMD41之前发送CMD8,参数中的check pattern为任意8bit值,卡检查是否可以在host提供的电压下运行,可以运行响应命令R7,响应中的参数包含跟命令中一样的VHS和卡不支持host提供的电压不予以响应且停留在Idle state.
2.8&&SD响应格式详解,命令详解,状态转移,CCCs in SD Mode,Card status和SD status参考文档
2.9&&SD卡上电时间也是由要求的
& && &SD卡从电压达到VDDmin开始1ms内准备好接收第一个命令.
到此为止,SDIO操作SD卡基本就这么多了,SPI方式操作文档里也有详细的描述.
匿名不能发帖!请先 [
Copyright (C) 《电子产品世界》杂志社 版权所有关于SPI的DMA方式读写SD卡 - STM32 - 意法半导体STM32/STM8技术社区
后使用快捷导航没有帐号?
查看: 6715|回复: 6
关于SPI的DMA方式读写SD卡
主题帖子积分
初级会员, 积分 138, 距离下一级还需 62 积分
初级会员, 积分 138, 距离下一级还需 62 积分
近期在做CH376的驱动,由于官方提供了参考手册,和驱动程序,在稍加移植后测试了简单的U盘读写函数,获取设备信息等功能并测试了读取文件的速度。由于之前移植了FATFS读写SD卡,想对比一下二者读写速度如何,于是采用内部RTC测时间加UART输出结果的方式测量了一下读文件的速度,测试结果CH376读取U盘速度约260KB/s(采用SPI接口,时钟为18MHz),SPI读取SD卡11M的MP3文件约330KB/s左右(SPI时钟为18MHz),后来优化了一下读SD卡的函数,速度提升390KB/s。可还是觉得速度不够快,于是考虑用DMA方式读取。好了,上面废话了那么多,下面进入正题。
SPI既可以用工作于DMA发送方式,也可以工作于DMA接收方式。SPI 采用DMA发送时一般没什么问题,然而接收时则出问题了。虽然都配置好了SPI_RX的DMA通道,也将SPI-&CR2里的RXDMAEN置位了,但始终接收不到SD卡的数据,程序始终在等待DMA传输完成位置位。虽然知道SPI只有在DR写入数据开始发送时才会输出时钟,但在做DMA接收时认为时钟是自动输出的,于是始终解决不了问题,后来在网上找了很久,终于找到一个帖子,里面说的情况跟我遇到的类似,从中了解到SPI在DMA接收时并没有时钟输出,后来想想,这种SPI DMA接收应该是工作在从机模式的吧,因为主机会发送时钟。如果需要工作在主机接收模式,则还是要靠写DR来发送时钟,为此可以在开启接收DMA的同时开启发送DMA,并且接收DMA的优先级高于发送DMA,发送DMA配置为内存到外设,内存地址不自增,发送字节数与接收字节数相同,并且始终发送0xFF,这样发送通道的DMA每发送完一字节,RXNE置位,触发接收通道DMA,由于接收通道DMA优先级高于发送DMA,于是接收DMA接收一字节,而后TXE置位,又触发发送DMA,像这样循环,从而实现了SPI 的DMA连续接收数据。下面附上移植的FATFS里的rcvr_mmc()数据读函数:
/*-----------------------------------------------------------------------*/
/* Receive bytes from the card (bitbanging)
/*-----------------------------------------------------------------------*/
unsigned char g_SPIDMASEND=0xFF;
static unsigned char DMA_Config_Flag=0;
void rcvr_mmc (
BYTE *buff, /* Pointer to read buffer */
/* Number of bytes to receive */
if(bc&=50)
if(DMA_Config_Flag==0)
MYDMA_Config(DMA1_Channel4,(u32)&SPI2-&DR,(u32)buff,bc,0x2080); //SPI2_DMA_RX
MYDMA_Config(DMA1_Channel5,(u32)&SPI2-&DR,(u32)&g_SPIDMASEND,bc,0x1010);//SPI2_DMA_TX
DMA_Config_Flag=1;
DMA1_Channel4-&CNDTR=
DMA1_Channel4-&CMAR=(u32)
DMA1_Channel5-&CNDTR=
while((SPI2-&SR&2)==0);
SPI2-&CR2|=3;
//enable TXDMA,RXDMA
MYDMA_Enable(DMA1_Channel4);
MYDMA_Enable(DMA1_Channel5);
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
while(!(DMA1-&ISR&(1DR=0
while((SPI2-&SR&1)==0);
*buff++ = SPI2-&DR;
当读取数据小于50时采用非DMA读写,大于等于50采用DMA读写,以提高效率。这样改写后,再测试读取同一文件的速度达到696KB/s,相比之前提升了近3/4,DMA的速度果然够快。大家也可以尝试一下。
09_1.jpg (20.36 KB, 下载次数: 2)
23:28 上传
主题帖子积分
初级会员, 积分 138, 距离下一级还需 62 积分
初级会员, 积分 138, 距离下一级还需 62 积分
RE:关于SPI的DMA方式读写SD卡
主题帖子积分
新手上路, 积分 31, 距离下一级还需 19 积分
新手上路, 积分 31, 距离下一级还需 19 积分
回复:关于SPI的DMA方式读写SD卡
好文! 用DMA的性能都杠杠的。
主题帖子积分
高级会员, 积分 926, 距离下一级还需 74 积分
高级会员, 积分 926, 距离下一级还需 74 积分
RE:关于SPI的DMA方式读写SD卡
求个完整例程。谢谢
主题帖子积分
新手上路, 积分 0, 距离下一级还需 50 积分
新手上路, 积分 0, 距离下一级还需 50 积分
回复:关于SPI的DMA方式读写SD卡
好东东啊,学习了……
主题帖子积分
初级会员, 积分 73, 距离下一级还需 127 积分
初级会员, 积分 73, 距离下一级还需 127 积分
RE:关于SPI的DMA方式读写SD卡
就需要这么一点程序吗
主题帖子积分
新手上路, 积分 0, 距离下一级还需 50 积分
新手上路, 积分 0, 距离下一级还需 50 积分
回复:关于SPI的DMA方式读写SD卡
回复第 6 楼 于 11:21:26发表:
就需要这么一点程序吗
你好,最近我也在用CH376,也用DMA方式,能不能给个完整的程序,感激不尽啊!邮箱是:
站长推荐 /1
报名参加STM32全国研讨会,即有机会获得STM32开发板;推荐好友参会,还可获得ST定制好礼。还在犹豫什么,赶快来报名参加吧。
Tel: 3-8056
备案号: 苏ICP备号-2
Powered by[转载]STM32的BootLoader&从SD卡更新固件
STM32的BootLoader 从SD卡更新固件
coolweedman
本篇文章的百度网盘下载地址(PDF格式):/s/1CfTh0
顺便来个广告,本工程的源码和相关资料(欢迎大家索取)。
自从几个月前接触到有Bootloader这回事,就有一种强烈的冲动,想写一个BootLoader出来。很快在飞思卡尔的Cortex-M4单片机上实现,已经是好几个月前的事情了。然后关于BootLoader的事搁在一边好久了,这次弄个STM32的BootLoader出来,Cortex-M3的,顺便发表下博客,跟大家分享一下。
又过了大半年了吧,慢慢对BootLoader的认识也有点长进啦。特别是跟网友讨论后发现BootLoader的实现还是需要靠BootLoader程序和App程序的配合才能正常使用。在这里特别感谢网友cary_yingj&,对本BootLoader的研究后发现App程序需要重定位中断向量表,才能正常工作。
在其他网友的反馈下,本人准备再将次文档完善,把不够详细的地方写得再详细,并且力求通俗易懂一点。希望对学习BootLoader的同学们有所帮助。
可能有的同学听说过BootLoader,有的同学没有听说过,这个都很正常。关于BootLoader的概念大家可以上网查一下,有比较详细的说明,我在这里说说我自己比较片面的理解,并且是针对Cortex
M3说明的,实现平台为STM32F103VET6。
这里借用一下百度百科对BootLoader的解释。在嵌入式操作系统中,BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。在嵌入式系统中,通常并没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成。在一个基于ARM7TDMI
core的嵌入式系统中,系统在上电或复位时通常都从地址0x处开始执行,而在这个地址处安排的通常就是系统的BootLoader程序。
的简单理解
BootLoader就是单片机启动时候运行的一段小程序,这段程序负责单片机固件更新,也就是单片机选择性的自己给自己下程序。可以更新也可以不更新,更新的话,BootLoader更新完程序后,跳转到新程序运行;不更新的话,BootLoader直接跳转到原来的程序去运行。
& 需要注意的是:BootLoader下载新程序后并不擦除自己(BootLoader程序还在),下次启动依然先运行BootLoader程序,又可以选择性的更新或者不更新程序,所以BootLoader就是用来管理单片机程序的更新。
& 这是本人的大概理解,大家有不明白请网上搜索一下更详细介绍吧。
BootLoader使单片机能自己给自己下载程序,所以在程序升级方面非常有作用。比如我们的BootLoader是通过GSM更新程序的,我们在升级单片机程序的时候,只要把新程序通过GSM发送给单片机,单片机自己实现程序更新,然后跳转到新程序执行,这样就省去我们很多升级的功夫啦。
可以想象一下如果把单片机安装在非常高的地方,或者危险的工业现场,或者封装得很难拆下来,我们很难直接给单片机下载程序,那么BootLoader的作用就体现出来了。简单的说,有了BootLoader,我们更新程序的话是省心又省力。
想想是不是很高级,还带点小兴奋哈哈。不用急,下面我们会继续介绍,让大家都能自己实现BootLoader。至于是通过什么方式升级,这个大家自由发挥,相信会设计出丰富多彩的BootLoader升级方式呢。
我们这里是为ARM的Cortex-M3单片机写的BootLoader,需要了解一下M3内核的架构,并且要了解M3单片机是怎么启动的等等。这个方面的知识,可以参考《Cortex-M3权威指南》,这里的话我只是为了实现BootLoader简单介绍一下,大家有什么不清楚的请参考权威指南。并且这里是以STM32为例说明问题的,使用的开发环境是RVMDK(Keil)。
这里参考的是《Cortex-M3权威指南》的3.8节,复位序列。
M3单片机复位后,从0x取栈指针(SP),
从0x取复位向量(PC),有了栈指针和复位向量后,单片机就按照正常流程运行了,在BootLoader里面,我们更新完程序后需要做的步骤之一就是设置栈指针,跳转到复位向量。
栈是一种数据结构,后进先出LIFO。借用百度百科的解释:栈由编译器自动分配释放
,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。它使用的是一级缓存,他们通常都是被调用时处于存储空间中,调用完毕立即释放。
复位向量是一个函数地址,在Cortex
M3单片机里是复位函数的地址。也就是单片机启动后第一个执行的函数。
这里参考《Cortex-M3权威指南》的7.3节,向量表。
BootLoader是一个完整的程序,下载的新程序(以下称为App)也是一个完整的程序。都包含中断向量表,所以的话,我们是有两个中断向量表,相信因为有两个向量表,大家都知道我们应该需要对这两个向量表做点什么吧。
的中断向量表
我们只看前16个向量,因为其余的向量属于外设使用,与Cortex
M3内核无关。
__Vectors&&&&&&
__initial_spTop&&&
Top of Stack
&&&&&&&&&&&&&&&
Reset_Handler&&&&&&&&&&&&
; Reset Handler
&&&&&&&&&&&&&&&
NMI_Handler&&&&&&&&&&&&&&
; NMI Handler
&&&&&&&&&&&&&&&
HardFault_Handler&&&&&&&&
; Hard Fault Handler
&&&&&&&&&&&&&&&
MemManage_Handler&&&&&&&&
; MPU Fault Handler
&&&&&&&&&&&&&&&
BusFault_Handler&&&&&&&&&
; Bus Fault Handler
&&&&&&&&&&&&&&&
UsageFault_Handler&&&&&&&
; Usage Fault Handler
&&&&&&&&&&&&&&&
0&&&&&&&&&&&&&&&&&&&&&&&&
; Reserved
&&&&&&&&&&&&&&&
0&&&&&&&&&&&&&&&&&&&&&&&&
; Reserved
&&&&&&&&&&&&&&&
0&&&&&&&&&&&&&&&&&&&&&&&&
; Reserved
&&&&&&&&&&&&&&&
0&&&&&&&&&&&&&&&&&&&&&&&&
; Reserved
&&&&&&&&&&&&&&&
SVC_Handler&&&&&&&&&&&&&&
; SVCall Handler
&&&&&&&&&&&&&&&
&&DebugMon_Handler&&&&&&&&&
; Debug Monitor Handler
&&&&&&&&&&&&&&&
0&&&&&&&&&&&&&&&&&&&&&&&&
; Reserved
&&&&&&&&&&&&&&&
PendSV_Handler&&&&&&&&&&&
; PendSV Handler
&&&&&&&&&&&&&&&
SysTick_Handler&&&&&&&&&&
; SysTick Handler
__initial_spTop就是栈指针,Reset_Handler是复位向量。这里只显示了16个向量,CortexM3单片机的话总共有256个向量,也就是从栈指针的地址开始有1KB的区域属于中断向量表。
单片机启动默认先运行BootLoader,所以默认的中断向量表位置是BootLoader的中断向量表。为了App可以正常运行,下载完App后,我们还需要把中断向量表重新定位到App程序那里。根据《Cortex
M3权威指南》,介绍一下怎样重定位中断向量表。
Cortex-M3单片机有一个管理中断向量表的寄存器,叫做向量表偏移量寄存器(VTOR)(地址:0xE000_ED08)。具体可以看看截图:
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/bmiddle/002137cigy6HYyqIc5v05&690" V:SHAPES="_x" TITLE="" NAME="image_operate_8329"
ALT="[转载]STM32的BootLoader&从SD卡更新固件" />
STM332程序的起始地址一般在0x。所以BootLoader程序是在0x,不是在0x是因为STM32的重映射技术(不符合Cortex-M3的设计,有点搞另类的感觉)。所以BootLoader的中断向量表在0x那里。如果我们的App程序起始地址在0x,并且App的中断向量表在起始地址,那么BootLoader程序下载App后,为了App程序能正确运行,开始App程序的运行后第一步,就要把中断向量表重定位到0x那里。
具体实现下面会再介绍,接下来介绍分散加载文件相关内容。
这一节涉及的内容主要属于分散加载文件,大家具体上网了解,这里只是介绍了能够实现BootLoader的一小部分。
语言的函数地址
我们知道C语言的函数名就是函数的地址,并且STM32单片机ROM的起始地址是在0x,那么使用编译器编译程序的话(这里使用的是RVMDK),函数的地址默认都在以0x为首的一段ROM里面了。比如我们一个函数Delay(),它的地址可以是0x(Cortex
M3中函数的地址0bit位一般是1),也就是Delay函数的代码在0x,C语言函数调用Delay时,就是执行0x的代码。
我们需要注意的问题是,如果不修改程序默认的起始地址的话,那么BootLoader和新App程序的起始地址都是0x,也就是他们重叠了(代码重叠),这样的话肯定相互之间有影响,程序是不能正常工作的。
这里的解决方法是,BootLoader程序依然占用0x为首的那段ROM,因为STM32的默认就是从0x运行程序的。保持BootLoader程序先能正确运行。然后App使用除BootLoader占用ROM以外的空间。这里需要知道BootLoader到底占用了多少ROM,很简单,查看MAP文件就行了。这里以我的BootLoader的MAP文件为例说明一下,看截图:
Memory Map of the image
& Image Entry point :
& Load Region LR_IROM1 (Base: 0x,
Size: 0x00006da4, Max: 0x, ABSOLUTE)
Execution Region ER_IROM1 (Base: 0x, Size: 0x00006d54, Max:
0x, ABSOLUTE)
主要是这句话“Base:
0x, Size: 0x00006da4, Max: 0x”,这句话说明了我的BootLoader程序是从0x开始,占用了0x00006DA4大小。只要我们的App不要和BootLoader程序占用的空间冲突就可以了。我的App程序的起始地址选择为0x,不与BootLoader程序冲突。具体怎么修改ROM起始地址,下面介绍。
编译新程序的时候,我们要修改程序的起始地址,我的修改方法如下(开发环境是RVMDK):打开Target
Option...,切换到Target选项卡,如下
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/bmiddle/002137cigy6HYysgrRCe8&690" V:SHAPES="_x" TITLE="" NAME="image_operate_68521"
ALT="[转载]STM32的BootLoader&从SD卡更新固件" />
修改IROM1的起始地址和长度:
比如,为了不产生地址冲突,我将起始地址0x修改成0x0807000,将ROM长度0x80000修改成0x10000。如下图所示(左图为修改前、右图为修改后):
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/bmiddle/002137cigy6HYyt6OSld1&690" V:SHAPES="_x" TITLE="" NAME="image_operate_49318"
ALT="[转载]STM32的BootLoader&从SD卡更新固件" /> &<img src="/blog7style/images/common/sg_trans.gif" real_src ="/bmiddle/002137cigy6HYytGys919&690" V:SHAPES="_x" TITLE="" NAME="image_operate_30631"
ALT="[转载]STM32的BootLoader&从SD卡更新固件" />
注意:BootLoader程序是不需要修改的,只是App需要修改(App就是使用BootLoader下载的程序)。
文件和bin文件
平时我们用j-Link或者串口下载程序的话,都是打开hex文件下载的,因为hex文件包含地址信息,下载程序的时候知道程序下载到ROM的哪个区域。从另一个角度上说,也就是hex文件是不能直接写进ROM的,一边写需要一边转换(解码出地址信息,将对应内容写入ROM)。
bin文件的话,很好理解,是直接的可执行代码。也就是bin文件的内容跟下载ROM里面的内容是一样的。bin文件是没有包含地址信息的,所以在下载之前要知道bin文件是要下载到ROM的那个区域。
我们的BootLoader下载的是bin文件,直接写进STM32的Flash里面,地址信息的话就是上一节的IROM,0x,从0x开始连续写入,中间不间断。
默认情况下编译后生成的是hex文件,不过很轻松可以生成bin文件。介绍具体怎么生成bin文件,工具的话是使用fromelf.exe(目录一般是在Keil安装目录里面,本人的fromelf.exe目录是在C:KeilARMARMCCbin),我们是使用fromelf工具将axf文件转换为bin文件。
熟悉命令行的同学可能会选择直接敲命令,不过这里介绍使用RVMDK提供的用户命令(编译时可以自动生成bin,省去每次生成bin文件都要敲命令的过程)。
打开Target
Option...,切换到User选项卡,如下
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/bmiddle/002137cigy6HYywBeTeb0&690" V:SHAPES="_x" TITLE="" NAME="image_operate_29288"
ALT="[转载]STM32的BootLoader&从SD卡更新固件" />
主要是在运行用户命令,Run
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/bmiddle/002137cigy6HYyxnzEie6&690" V:SHAPES="_x" TITLE="" NAME="image_operate_5999"
ALT="[转载]STM32的BootLoader&从SD卡更新固件" />
具体命令是(记得在Run
#1前打勾,才会在编译后执行用户命令生成bin文件):
C:KeilARMARMCCbinfromelf.exe
.OutputMY_STM32.bin
.OutputMY_STM32.axf
命令可以分为五部分,简化后是fromelf&
--bin& -o&
xxx.bin& xxx.axf,需要注意的是命令的五个部分之间要有空格。还需要说明的是路径问题,这里的路径都是相对.uvproj文件的,下面是我的目录(注意MY_STM32.uvproj文件和Output文件夹)。
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/bmiddle/002137cigy6HYyy6hHA02&690" V:SHAPES="_x" TITLE="" NAME=""
ALT="[转载]STM32的BootLoader&从SD卡更新固件" />
我的bin文件和axf文件都在Output文件夹里面,并且路径是相对MY_STM32.uvproj的,Output文件夹里的bin文件(MY_STM32.bin)相对于MY_STM32.uvproj应该写成“.OutputMY_STM32.bin”。
这部分是fromelf.exe文件的路径,根据自己的安装目录而变。我这里因为Keil是安装在C盘的,所以我的路径如下所示。
参考命令:C:KeilARMARMCCbinfromelf.exe
这部分是固定的,--bin表示生成bin文件。
参考命令:--bin
这部分也是固定的,-o表示输出。
参考命令:-o
这部分是生成文件的目录和文件名,我是输出在Output文件夹的,也就是bin文件在Output文件夹里面。
参考命令:.OutputMY_STM32.bin
这部分是axf文件的目录和文件名,我们的bin文件是根据axf文件生成的,也就是说axf文件相当于输入,bin文件相当于输出。我的axf文件也在Output文件夹的。
参考命令:.OutputMY_STM32.axf
介绍了这些基本知识后,我们可以来实现BootLoader了。
有了前面的基础知识后,应该是比较容易理解BootLoader需要怎么实现了。这一章,我们分几个步骤,一步一步实现BootLoader。
我们的BootLoader是从SD卡更新程序的,把在电脑上编译后的App程序,也就是bin文件,复制到SD卡中,然后让单片机读取相应的bin文件,就可以实现程序的更新。需要注意的是,App程序需要修改ROM的起始地址,再编译,并且要生成bin文件才支持正常下载。
我跑的文件系统是FATFS_R0.07c,很经典的一个版本。如果大家对文件系统方面不了解的话,请自己网上查找教程,或者说很多同学对这一步应该已经很熟悉啦。
只要单片机上实现读取bin文件,结合Flash写入程序,就可以实现程序更新。下面介绍读写Flash。
要实现BootLoader,还有一个前提是可以写入Flash了。如果是STM32单片机的话是很容易实现的,因为我们有官方库。本人使用的是3.0.0版本,参考官方例程,很容易实现Flash的读写,这里同样是为了实现BootLoader简单介绍一下。
验证读写是否正确
调用的库函数
FLASH_Unlock(void)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
FLASH_Status FLASH_ErasePage(uint32_t
Page_Address)&&&&&&&&&&&
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t
Data)& Flash写入
稍微封装一下STM32的官方库函数,就能实现Flash的读写,并验证读写是否正确,具体我实现的接口函数为以下截图,大家可以参考一下:
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/bmiddle/002137cigy6HYyzliKy56&690" V:SHAPES="_x" TITLE="" NAME="image_operate_70334"
ALT="[转载]STM32的BootLoader&从SD卡更新固件" />
来到这里,我们可以实现在bin文件写入Flash了,写入完后,就要跳转到App程序执行了,接下来继续介绍。
& 这一节要结合上面提到过的,Cortex-M3启动做了什么事情,然后我们的BootLoader下载App程序后,App程序就需要做同样的事情。主要有三个步骤,其中BootLoader程序需要做的是:
跳转到复位向量
App需要做的是:
重定位中断向量表
设置栈指针
BootLoader程序需要做的是跳转到复位向量,具体实现可以参考以下代码。
&( (void (*)()) (Reset)
//跳转到复位向量
( (void (*)()) (Reset) )();是一去就不返回的,执行完这条语句,单片机就直接跳转到App程序运行的,所以BootLoader程序下载完App后,做一些简单的处理(根据自己的应用,也可以不做任何处理),就用这条语句跳转到App执行。
BootLoader跳转到App后,App需要做的是先设置栈指针,然后重定位中断向量表地址,具体可以参考以下代码。
__set_MSP( Msp
);&&&&&&&&&&&&&&
&&&//设置栈指针
NVIC_SetVectorTable( base, offset
//重定位中断向量表
其中Msp是栈指针,也就是中断向量表第一个字的内容,我们这里的内容是*(
(uint32_t)(0x) )。
base是中断向量表的基地址,一般情况下就是ROM的起始地址,这里是0x。
至此,BootLoader实现步骤完了,相信熟悉了这几个步骤后,大家可以自己给自己的单片机写个BootLoader。顺便说一下,Cortex-M4的BootLoader跟Cortex-M3几乎是一样的。我在STM32上的实现完全是参考自己上次在飞思卡尔Cortex-M4上的实现。下面说一下我的主函数吧,我们再看看具体的BootLoader流程,再熟悉一下BootLoader。
& 先看看我的主函数,再啰嗦一下具体流程,可能有的同学已经有点厌烦啦,其实感觉有点多余。
先看截图。
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/bmiddle/002137cigy6HYyAkgFa18&690" V:SHAPES="_x" TITLE="" NAME="image_operate_20140"
ALT="[转载]STM32的BootLoader&从SD卡更新固件" />
主函数的流程如下所示:
时钟初始化
LED初始化(无关紧要)
调试接口初始化(无关紧要)
Flash初始化(解锁Flash)
FAT初始化(挂载文件系统)
我们的BootLoader(重点,下面展开继续介绍)
主循环(实际不会运行到这里)
然后在具体讲解BootLoader_FromSDCard函数,这就是我们的重点,传说中STM32的BootLoader从SD卡更新固件。
老样子,先上截图:
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/bmiddle/002137cigy6HYyBojLWb8&690" V:SHAPES="_x" TITLE="" NAME="image_operate_19772"
ALT="[转载]STM32的BootLoader&从SD卡更新固件" />
具体流程如下所示:
打开bin文件,检查文件打开是否正确
设置Flash下载起始地址(App程序起始地址)
读取bin文件,检查读取是否正确
获取栈指针SP和复位向量PC
进入循环(这里是第5步),条件为如果读取bin文件字节数不为零
将读取到的bin写入Flash,并判写入状态
调整Flash地址,根据写入字节调整
继续读取bin文件,检查读取是否正确,回到5继续循环
来到这里已经是退出循环了,也就是说我们已经将bin写入Flash完成了,准备跳转到新程序运行
其实上面已经讲过了,这里继续啰嗦,截图:
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/bmiddle/002137cigy6HYyCIFtL19&690" V:SHAPES="_x" TITLE="" NAME="image_operate_18678"
ALT="[转载]STM32的BootLoader&从SD卡更新固件" />
重定位中断向量
设置栈指针
跳转到复位向量(开始运行App程序)
说明一下,在这里重定位中断向量其实是多余的,App程序执行初始化后,又回到STM32初始状态,所以在App程序中需要执行重定位中断向量表操作,具体同以上操作相同。
啰嗦了又一遍,BootLoader完全结束,感谢大家都支持啦~
#include "main.h"
int main(void)
SystemInit();&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&
//配置系统时钟为72M
LED_GPIO_Config();&&&&&&&&&&&&&&&&&&&&&&&&&&&&
//初始化LED端口
Debug_TraceIOEnable();&&&&&&&&&&&&&&&&&&&&&&&&&&
//使能调试printf的IO口
Flash_Init();&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
//初始化Flash
FAT_Init();&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
//初始化文件系统
BootLoader_FromSDCard();&&&&&&&&&&&&&&&&&&&&&&&&
//Bootloader从SD卡更新固件
LED_StatShow( FuncErr
);&&&&&&&&&&&&&&&&&&&&&
//LED显示Bootloader状态
版本:V1.01
作者:coolweedman
更新:说明Bootloader程序和App程序需要配合才能实现BootLoader,App程序需要重定位中断向量表
特别感谢:网友cary_yingj的研究和分享
《CM3权威指南CnR2》Joseph Yiu 著&&
ST官方库3.0.0
《C和指针》Kenneth
A. Reek著&&
FATFS文件系统
其他互联网资料
最后来一个广告,本工程源代码等资料,欢迎索取:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。}

我要回帖

更多关于 stm32 adc例程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信