OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 18357|回复: 40

【ALIENTEK 战舰STM32开发板例程系列连载+教学】第五十三章 串口IAP实验

[复制链接]

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
发表于 2013-4-19 22:45:04 | 显示全部楼层 |阅读模式
 

第五十三章 串口IAP实验

      IAP,即在应用编程。很多单片机都支持这个功能,STM32也不例外。在之前的FLASH模拟EEPROM实验里面,我们学习了STM32FLASH自编程,本章我们将结合FLASH自编程的知识,通过STM32的串口实现一个简单的IAP功能本章分为如下几个部:

53.1 IAP简介

53.2 硬件设计

53.3 软件设计

53.4 下载验证

53.1 IAP简介   

IAPIn Application Programming)即在应用编程,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。 通常实现IAP功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(USBUSART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。这两部分项目代码都同时烧录在User Flash中,当芯片上电后,首先是第一个项目代码开始运行,它作如下操作:

1)检查是否需要对第二部分代码进行更新

2)如果不需要更新则转到4

3)执行更新操作

4)跳转到第二部分代码执行

第一部分代码必须通过其它手段,如JTAGISP烧入;第二部分代码可以使用第一部分代码IAP功能烧入,也可以和第一部分代码一起烧入,以后需要程序更新是再通过第一部分IAP代码更新。

我们将第一个项目代码称之为Bootloader程序,第二个项目代码称之为APP程序,他们存放在STM32 FLASH的不同地址范围,一般从最低地址区开始存放Bootloader,紧跟其后的就是APP程序(注意,如果FLASH容量足够,是可以设计很多APP程序的,本章我们只讨论一个APP程序的情况)。这样我们就是要实现2个程序:BootloaderAPP

STM32APP程序不仅可以放到FLASH里面运行,也可以放到SRAM里面运行,本章,我们将制作两个APP,一个用于FLASH运行,一个用于SRAM运行。

我们先来看看STM32正常的程序运行流程,如图53.1.1所示:


53.1.1 STM32正常运行流程图

       STM32的内部闪存(FLASH)地址起始于0x08000000,一般情况下,程序文件就从此地址开始写入。此外STM32是基于Cortex-M3内核的微控制器,其内部通过一张“中断向量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而这张“中断向量表”的起始地址是0x08000004,当中断来临,STM32的内部硬件机制亦会自动将PC指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。

       在图53.1.1中,STM32在复位后,先从0X08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序,如图标号①所示;在复位中断服务程序执行完之后,会跳转到我们的main函数,如图标号②所示;而我们的main函数一般都是一个死循环,在main函数执行过程中,如果收到中断请求(发生重中断),此时STM32强制将PC指针指回中断向量表处,如图标号③所示;然后,根据中断源进入相应的中断服务程序,如图标号④所示;在执行完中断服务程序以后,程序再次返回main函数执行,如图标号⑤所示。

       当加入IAP程序之后,程序运行流程如图53.1.2所示:


53.1.2 加入IAP之后程序运行流程图

       在图53.1.2所示流程中,STM32复位后,还是从0X08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到IAPmain函数,如图标号①所示,此部分同图53.1.1一样;在执行完IAP以后(即将新的APP代码写入STM32FLASH,灰底部分。新程序的复位中断向量起始地址为0X08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main函数,如图标号②和③所示,同样main函数为一个死循环,并且注意到此时STM32FLASH,在不同位置上,共有两个中断向量表。

       main函数执行过程中,如果CPU得到一个中断请求,PC指针仍强制跳转到地址0X08000004中断向量表处,而不是新程序的中断向量表,如图标号④所示;程序再根据我们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示;在执行完中断服务程序后,程序返回main函数继续运行,如图标号⑥所示。

       通过以上两个过程的分析,我们知道IAP程序必须满足两个要求:

1)  新程序必须在IAP程序之后的某个偏移量为x的地址开始;

2)  必须将新程序的中断向量表相应的移动,移动的偏移量为x

本章,我们有2APP程序,一个为FLASHAPP,另外一个位SRAMAPP,图53.1.2虽然是针对FLASH APP来说的,但是在SRAM里面运行的过程和FLASH基本一致,只是需要设置向量表的地址为SRAM的地址。

1.APP程序起始地址设置方法

随便打开一个之前的实例工程,点击Options for TargetàTarget选项卡,如图53.1.3所示:


53.1.3 FLASH APP Target选项卡设置

       默认的条件下,图中IROM1的起始地址(Start)一般为0X08000000,大小(Size)为0X80000,即从0X08000000开始的512K空间为我们的程序存储区。而图中,我们设置起始地址(Start)为0X08005000,即偏移量为0X500020K字节),因而,留给APP用的FLASH空间(Size)只有0X80000-0X5000=0X7B000480K字节)大小了。设置好StartSzie,就完成APP程序的起始地址设置。

       这里的20K字节,需要大家根据Bootloader程序大小进行选择,比如我们本章的Bootloader程序为18K左右,理论上我们只需要确保APP起始地址在Bootloader之后,并且偏移量为0X200的倍数即可(相关知识,请参考:http://www.openedv.com/posts/list/392.htm)。这里我们选择20K0X5000)字节,留了一些余量,方便Bootloader以后的升级修改。

       这是针对FLASH APP的起始地址设置,如果是SRAM APP,那么起始地址设置如图53.1.4所示:


53.1.4 SRAM APP Target选项卡设置

       这里我们将IROM1的起始地址(Start)定义为:0X20001000,大小为0XA00040K字节),即从地址0X20000000偏移0X1000开始,存放APP代码。因为整个STM32F103ZET6SRAM大小为64K字节,所以IRAM1SRAM)的起始地址变为0X2000B000,大小只有0X500020K字节)。这样,整个STM32F103ZET6SRAM分配情况为:最开始的4KBootloader程序使用,随后的40K存放APP程序,最后20K,用作APP程序的内存。这个分配关系大家可以根据自己的实际情况修改,不一定和我们这里的设置一模一样,不过也需要注意,保证偏移量为0X200的倍数(我们这里为0X1000)。

       2.中断向量表的偏移量设置方法

       此步,我们通过修改sys.c里面的MYRCC_DeInit函数实现,该函数代码如下:

[mw_shl_code=c,true]void MYRCC_DeInit(void) { RCC->APB1RSTR = 0x00000000;//复位结束 RCC->APB2RSTR = 0x00000000; RCC->AHBENR = 0x00000014; //睡眠模式闪存和SRAM时钟使能.其他关闭. RCC->APB2ENR = 0x00000000; //外设时钟关闭. RCC->APB1ENR = 0x00000000; RCC->CR |= 0x00000001; //使能内部高速时钟HSION RCC->CFGR &= 0xF8FF0000; //复位SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0] RCC->CR &= 0xFEF6FFFF; //复位HSEON,CSSON,PLLON RCC->CR &= 0xFFFBFFFF; //复位HSEBYP RCC->CFGR &= 0xFF80FFFF;//复位PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE RCC->CIR = 0x00000000; //关闭所有中断 //配置向量表 #ifdef VECT_TAB_RAM MY_NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else MY_NVIC_SetVectorTable(NVIC_VectTab_FLASH,0); #endif }[/mw_shl_code]


       该函数我们只需要修改最后两行代码,默认的情况下VECT_TAB_RAM是没有定义的,所以执行:MY_NVIC_SetVectorTable(NVIC_VectTab_FLASH,0);这是正常情况的向量表偏移量(为0),本章,我们修改这句代码为:MY_NVIC_SetVectorTable(NVIC_VectTab_FLASH,0X5000);偏移量为0X5000。其中NVIC_VectTab_RAMNVIC_VectTab_FLASH是在stm32f10x_nvic.h里面定义的两个宏定义,代表RAMFLASH的起始地址。

       以上是FLASH APP的情况,当使用SRAM APP的时候,我们需要定义VECT_TAB_RAM,点击Options for TargetàC/C++选项卡,在Preprocessor Symblols栏定义:VECT_TAB_RAM,如图54.1.5所示:


53.1.5 SRAM APP C/C++选项卡设置

       通过这个设置,我们定义VECT_TAB_RAM,故在执行MYRCC_DeInit函数的时候,会执行MY_NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); 这里的0X0是默认的设置,本章我们修改此句代码为:MY_NVIC_SetVectorTable(NVIC_VectTab_SRAM,0X1000);即设置偏移量为0X1000

       这样,我们就完成了中断向量表偏移量的设置。

通过以上两个步骤的设置,我们就可以生成APP程序了,只要APP程序的FLASHSRAM大小不超过我们的设置即可。不过MDK默认生成的文件是.hex文件,并不方便我们用作IAP更新,我们希望生成的文件是.bin文件,这样可以方便进行IAP升级(至于为什么,请大家自行百度HEXBIN文件的区别!)。这里我们通过MDK自带的格式转换工具fromelf.exe,来实现.axf文件到.bin文件的转换。该工具在MDK的安装目录\ARM\BIN40文件夹里面。

fromelf.exe转换工具的语法格式为:fromelf [options] input_file。其中options有很多选项可以设置,详细使用请参考光盘《mdk如何生成bin文件.pdf.

本章,我们通过在MDK点击Options for TargetàUser选项卡,在Run User Programs After Build/Rebuild 栏,勾选Run#1DOS16,并写入:D:\Keil3.80a\ARM\BIN40\fromelf.exe  --bin -o  ..\OBJ\TEST.bin ..\OBJ\TEST.axf ,如图53.1.6所示:


       通过这一步设置,我们就可以在MDK编译成功之后,调用fromelf.exe(注意,我的MDK是安装在D:\Keil3.80A文件夹下,如果你是安装在其他目录,请根据你自己的目录修改fromelf.exe的路径),根据当前工程的TEST.axf,生成一个TEST.bin的文件。并存放在axf文件相同的目录下,即工程的OBJ文件夹里面。在得到.bin文件之后,我们只需要将这个bin文件传送给单片机,即可执行IAP升级。

       最后再来看看APP程序的生成步骤:

1) 设置APP程序的起始地址和存储空间大小

对于在FLASH里面运行的APP程序,我们只需要设置APP程序的起始地址,和存储空间大小即可。而对于在SRAM里面运行的APP程序,我们还需要设置SRAM的起始地址和大小。无论哪种APP程序,都需要确保APP程序的大小和所占SRAM大小不超过我们的设置范围。

2) 设置中断向量表偏移量

此步,通过在MYRCC_DeInit函数里面调用MY_NVIC_SetVectorTable函数,实现对中断向量表偏移量的设置。这个偏移量的大小,其实就等于程序起始地址相对于0X08000000或者0X20000000的偏移。对于SRAM APP程序,我们还需要在C/C++选项卡定义VECT_TAB_RAM,以申明中断向量表是在SRAM里面。

3) 设置编译后运行fromelf.exe,生成.bin文件.

通过在User选项卡,设置编译后调用fromelf.exe,根据.axf文件生成.bin文件,用于IAP更新。 

以上3个步骤,我们就可以得到一个.binAPP程序,通过Bootlader程序即可实现更新。

53.2 硬件设计

本章实验(Bootloader部分)功能简介:开机的时候先显示提示信息,然后等待串口输入接收APP程序(无校验,一次性接收),在串口接收到APP程序之后,即可执行IAP。如果是SRAM APP,通过按下KEY0即可执行这个收到的SRAM APP程序。如果是FLASH APP,则需要先按下WK_UP按键,将串口接收到的APP程序存放到STM32FLASH,之后再按KEY2既可以执行这个FLASH APP程序。通过KEY1按键,可以手动清除串口接收到的APP程序。DS0用于指示程序运行状态。

本实验用到的资源如下:

1)  指示灯DS0

2)  四个按键(KEY0/KEY1/KEY2/WK_UP

3)  串口

4)  TFTLCD模块

这些用到的硬件,我们在之前都已经介绍过,这里就不再介绍了。

53.3 软件设计

本章,我们总共需要3个程序:1Bootloader2FLASH APP3SRAM APP;其中,我们选择之前做过的RTC实验(在第二十章介绍)来做为FLASH APP程序(起始地址为0X08005000),选择触摸屏实验(在第三十一章介绍)来做SRAM APP程序(起始地址为0X20001000)。Bootloader则是通过TFTLCD显示实验(在第十八章介绍)修改得来。本章,关于SRAM APPFLASH APP的生成比较简单,我们就不细说,请大家结合光盘源码,以及53.1节的介绍,自行理解。本章软件设计仅针对Bootloader程序。

复制第十八章的工程(即实验13),作为本章的工程模版(命名为:IAP Bootloader V1.0),并复制第三十九章实验(FLASH模拟EEPROM实验)的STMFLASH文件夹到本工程的HARDWARE文件夹下,打开本实验工程,并将STMFLASH文件夹内的stmflash.c加入到HARDWARE组下,同时将STMFLASH加入头文件包含路径。

HARDWARE文件夹所在的文件夹下新建一个IAP的文件夹,并在该文件夹下新建iap.ciap.h两个文件。然后在工程里面新建一个IAP的组,将iap.c加入到该组下面。最后,IAP文件夹加入头文件包含路径。

打开iap.c,输入如下代码:

#include "sys.h"

#include "delay.h"

#include "usart.h"

#include "stmflash.h"

#include "iap.h"      

iapfun jump2app;

u16 iapbuf[1024];

//appxaddr:应用程序的起始地址

//appbuf:应用程序CODE.

//appsize:应用程序大小(字节).

void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)

{

       u16 t;

       u16 i=0;

       u16 temp;

       u32 fwaddr=appxaddr;//当前写入的地址

       u8 *dfu=appbuf;

       for(t=0;t<appsize;t+=2)

       {                                           

              temp=(u16)dfu[1]<<8;

              temp+=(u16)dfu[0];       

              dfu+=2;//偏移2个字节

              iapbuf[i++]=temp;     

              if(i==1024)

              {

                     i=0;

                     STMFLASH_Write(fwaddr,iapbuf,1024);   

                     fwaddr+=2048;//偏移2048  16=2*8.所以要乘以2.

              }

       }

       if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去

}

//跳转到应用程序段

//appxaddr:用户代码起始地址.

void iap_load_app(u32 appxaddr)

{

       if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)      //检查栈顶地址是否合法.

       {

              jump2app=(iapfun)*(vu32*)(appxaddr+4);        

//用户代码区第二个字为程序开始地址(复位地址)         

              MSR_MSP(*(vu32*)appxaddr);                               

//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)

              jump2app();    //跳转到APP.

       }

}

       该文件总共只有2个函数,其中,iap_write_appbin函数用于将存放在串口接收buf里面的APP程序写入到FLASHiap_load_app函数,则用于跳转到APP程序运行,其参数appxaddrAPP程序的起始地址,程序先判断栈顶地址是否合法,在得到合法的栈顶地址后,通过MSR_MSP函数(该函数在sys.c文件)设置栈顶地址,最后通过一个虚拟的函数(jump2app)跳转到APP程序执行代码,实现IAPàAPP的跳转。

       保存iap.c,打开iap.h输入如下代码:

#ifndef __IAP_H__

#define __IAP_H__

#include "sys.h"   

typedef  void (*iapfun)(void);     //定义一个函数类型的参数.  

#define FLASH_APP1_ADDR            0x08005000        

//第一个应用程序起始地址(存放在FLASH)

//保留0X08000000~0X08004FFF的空间为Bootloader使用     

void iap_load_app(u32 appxaddr);                                                 //跳转到APP程序执行

void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 applen);   //在指定地址开始,写入bin

#endif

       这部分代码比较简单,保存iap.h。本章,我们是通过串口接收APP程序的,我们将usart.cusart.h做了稍微修改,在usart.h中,我们定义USART_REC_LEN55K字节,也就是串口最大一次可以接收55K字节的数据,这也是本Bootloader程序所能接收的最大APP程序大小。然后新增一个USART_RX_CNT的变量,用于记录接收到的文件大小,而USART_RX_STA不再使用。在usart.c里面,我们修改USART1_IRQHandler部分代码如下:

//串口1中断服务程序

//注意,读取USARTx->SR能避免莫名其妙的错误       

u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));

//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.   

//接收状态

//bit15 接收完成标志

//bit14 接收到0x0d

//bit13~0     接收到的有效字节数目

u16 USART_RX_STA=0;              //接收状态标记      

u16 USART_RX_CNT=0;                   //接收的字节数      

void USART1_IRQHandler(void)

{

       u8 res;    

#ifdef OS_CRITICAL_METHOD

//如果OS_CRITICAL_METHOD定义了,说明使用ucosII.

       OSIntEnter();   

#endif

       if(USART1->SR&(1<<5))//接收到数据

       {    

              res=USART1->DR;

              if(USART_RX_CNT<USART_REC_LEN)

              {

                     USART_RX_BUF[USART_RX_CNT]=res;

                     USART_RX_CNT++;                                                                                    

              }

       }

#ifdef OS_CRITICAL_METHOD      

//如果OS_CRITICAL_METHOD定义了,说明使用ucosII.

       OSIntExit();                                                                                

#endif

}

       这里,我们指定USART_RX_BUF的地址是从0X20001000开始,该地址也就是SRAM APP程序的起始地址!然后在USART1_IRQHandler函数里面,将串口发送过来的数据,全部接收到USART_RX_BUF,并通过USART_RX_CNT计数。代码比较简单,我们就不多说了。

改完usart.cusart.h之后,我们在test.c修改main函数如下:

int main(void)

{           

       u8 t;

       u8 key;

       u16 oldcount=0;            //老的串口接收数据值

       u16 applenth=0;            //接收到的app代码长度

       u8 clearflag=0;      

      Stm32_Clock_Init(9);    //系统时钟设置

       uart_init(72,256000);     //串口初始化为256000

       delay_init(72);                  //延时初始化

       LED_Init();                 //初始化与LED连接的硬件接口

       LCD_Init();                  //初始化LCD

      KEY_Init();                  //按键初始化

      POINT_COLOR=RED;//设置字体为红色

       LCD_ShowString(60,50,200,16,16,"Warship STM32");    

       LCD_ShowString(60,70,200,16,16,"IAP TEST");     

       LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");

       LCD_ShowString(60,110,200,16,16,"2012/9/24"); 

       LCD_ShowString(60,130,200,16,16,"WK_UP:Copy APP2FLASH");

       LCD_ShowString(60,150,200,16,16,"KEY1:Erase SRAM APP");

       LCD_ShowString(60,170,200,16,16,"KEY0:Run SRAM APP");

       LCD_ShowString(60,190,200,16,16,"KEY2:Run FLASH APP");

       POINT_COLOR=BLUE;

       //显示提示信息

       POINT_COLOR=BLUE;//设置字体为蓝色  

       while(1)

       {

             if(USART_RX_CNT)

              {

                     if(oldcount==USART_RX_CNT)

//新周期内,没有收到任何数据,认为本次数据接收完成.

                     {

                            applenth=USART_RX_CNT;

                            oldcount=0;

                            USART_RX_CNT=0;

                            printf("用户程序接收完成!\r\n");

                            printf("代码长度:%dBytes\r\n",applenth);

                     }else oldcount=USART_RX_CNT;                    

              }

              t++; delay_ms(10);

              if(t==30)

              {

                     LED0=!LED0; t=0;

                     if(clearflag)

                     {

                            clearflag--;

                            if(clearflag==0)LCD_Fill(60,210,240,210+16,WHITE);//清除显示

                     }

              }            

              key=KEY_Scan(0);

              if(key==KEY_UP)

              {

                     if(applenth)

                     {

                            printf("开始更新固件...\r\n");     

                            LCD_ShowString(60,210,200,16,16,"Copying APP2FLASH...");

                           if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)

//判断是否为0X08XXXXXX.

                            {           

iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,

applenth); //更新FLASH代码  

                                   LCD_ShowString(60,210,200,16,16,"Copy APP Successed!!");

                                   printf("固件更新完成!\r\n");      

                            }else

                            {

                                   LCD_ShowString(60,210,200,16,16,"Illegal FLASH APP!  ");         

                                   printf("FLASH应用程序!\r\n");

                            }

                    }else

                     {

                            printf("没有可以更新的固件!\r\n");

                            LCD_ShowString(60,210,200,16,16,"No APP!");

                     }

                     clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示         

              }

              if(key==KEY_DOWN)

              {

                     if(applenth)

                     {                                                                                                                    

                            printf("固件清除完成!\r\n");   

                            LCD_ShowString(60,210,200,16,16,"APP Erase Successed!");

                            applenth=0;

                     }else

                     {

                            printf("没有可以清除的固件!\r\n");

                            LCD_ShowString(60,210,200,16,16,"No APP!");

                     }

                     clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示                                                        

              }

              if(key==KEY_LEFT)

              {

                     printf("开始执行FLASH用户代码!!\r\n");

                     if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)

//判断是否为0X08XXXXXX.

                     {    

                            iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码

                     }else

                     {

                            printf("FLASH应用程序,无法执行!\r\n");

                            LCD_ShowString(60,210,200,16,16,"Illegal FLASH APP!");      

                     }                                                            

                     clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示  

              }

              if(key==KEY_RIGHT)

              {

                     printf("开始执行SRAM用户代码!!\r\n");

                     if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000)

//判断是否为0X20XXXXXX.

                     {    

                            iap_load_app(0X20001000);//SRAM地址

                     }else

                     {

                            printf("SRAM应用程序,无法执行!\r\n");

                            LCD_ShowString(60,210,200,16,16,"Illegal SRAM APP!");       

                     }                                                            

                     clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示

              }                            

               

       }             

}

       该段代码,实现了串口数据处理,以及IAP更新和跳转等各项操作。Bootloader程序就设计完成了,但是一般要求bootloader程序越小越好(给APP省空间嘛),所以,本章我们把一些不需要用到的.c文件全部去掉,最后得到工程截图如图53.3.1所示:


53.3.1 Bootloader 工程截图

       从上图可以看出,虽然去掉了一些不用的.c文件,但是Bootloader大小还是有18K左右,比较大,主要原因是液晶驱动和printf占用了比较多的flash,如果大家想进一步删减,可以去掉LCD显示和printf等,不过我们在本章为了演示效果,所以保留了这些代码。

      至此,本实验的软件设计部分结束。

       FLASH APPSRAM APP两部分代码,根据53.1节的介绍,大家自行修改都比较简单,我们这里就不介绍了,不过要提醒大家:FLASH APP的起始地址必须是0X08005000,而SRAM APP的起始地址必须是0X20001000

53.4 下载验证

在代码编译成功之后,我们下载代码到ALIENTEK战舰STM32开发板上,得到,如图53.4.1所示:


53.4.1 IAP程序界面

此时,我们可以通过串口,发送FLASH APP或者SRAM APP到战舰STM32开发板,如图53.4.2所示:


53.4.2 串口发送APP程序界面

       先用串口调试助手的打开文件按钮(如图标号1所示),找到APP程序生成的.bin文件,然后设置波特率为256000(为了提高速度,Bootloader程序将波特率被设置为256000了),最后点击发送文件(图中标号3所示),将.bin文件发送给战舰STM32开发板。

       在收到APP程序之后,我们就可以通过KEY0/KEY2运行这个APP程序了(如果是FLASH APP,则先需要通过WK_UP将其存入对应FLASH区域)。  

《STM32开发指南》第五十三章 串口IAP实验.rar

794.38 KB, 下载次数: 2247

实验48 串口IAP实验.rar

369.29 KB, 下载次数: 2166

我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
81
金钱
81
注册时间
2013-3-3
在线时间
10 小时
发表于 2013-4-20 09:51:42 | 显示全部楼层
又是精华帖啊!!!!强烈置顶哈哈哈
回复 支持 反对

使用道具 举报

0

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
81
金钱
81
注册时间
2013-3-3
在线时间
10 小时
发表于 2013-4-20 10:19:39 | 显示全部楼层
“如果是SRAM APP,通过按下KEY0即可执行这个收到的SRAM APP程序。”
sram是不能掉电存储的,这样做是不是下次上点的时候又要重新烧APP程序?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-4-20 13:14:04 | 显示全部楼层
回复【3楼】casy:
---------------------------------
是的
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

54

主题

537

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2012-2-27
在线时间
7 小时
发表于 2013-5-15 10:53:57 | 显示全部楼层
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr) 
{
    MSR MSP, r0  //set Main Stack value
    BX r14
}

这里传入的addr没看到使用啊,是不是就放在r0中了?
都上汇编了吼吼,原子哥V5

iapfun jump2app;这个。。。。。再去补补C基础,
jump2app();这句的作用是不是就是取地址(appxaddr+4)的值,也就是程序运行到(appxaddr+4)这个位置?
回复 支持 反对

使用道具 举报

54

主题

537

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2012-2-27
在线时间
7 小时
发表于 2013-5-15 11:35:37 | 显示全部楼层
jump2app=(iapfun)*(vu32*)(appxaddr+4);
这句是不是相当于把函数体写给了jump2app,函数体就是各种类型转换然后指到地址(appxaddr+4);?

typedef  void (*iapfun)(void); //定义一个函数类型的参数.
这个typedef真心看了半天没明白(*iapfun)函数指针? (*iapfun)(void)函数指针参数是void?
void (*iapfun)(void);函数指针,参数是void,返回值是void? 觉得这理解不对啊似乎
回复 支持 反对

使用道具 举报

54

主题

537

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2012-2-27
在线时间
7 小时
发表于 2013-5-15 14:49:17 | 显示全部楼层
void (*iapfun)(void);
iapfun=(void (*)(void))(appxaddr+4);
iapfun();

原子哥,这样是否可以?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-5-15 16:16:06 | 显示全部楼层
回复【7楼】wwjdwy:
---------------------------------
试试先。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

54

主题

537

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2012-2-27
在线时间
7 小时
发表于 2013-5-16 13:00:39 | 显示全部楼层
回复【8楼】正点原子:
---------------------------------
        void (*appstart)(void); 

        appstart=(void (*)(void))(appxaddr+165); 
MSR_MSP(*(vu32*)appxaddr);
        appstart(); 

追踪程序,比较区别,把便宜从4改为165之后OK了。
不明白了原子哥,是类型问题?
回复 支持 反对

使用道具 举报

54

主题

537

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2012-2-27
在线时间
7 小时
发表于 2013-5-16 13:10:05 | 显示全部楼层
回复【8楼】正点原子:
---------------------------------
appstart=(void (*)(void))*(vu32*)(appxaddr+4);
这样就可以了,看来这个*很有用处啊,程序执行地址是在(appxaddr+4);所指向的位置?并不是appxaddr+4就是程序的执行地址
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-5-16 14:19:21 | 显示全部楼层
是的
回复 支持 反对

使用道具 举报

54

主题

537

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2012-2-27
在线时间
7 小时
发表于 2013-5-16 21:50:35 | 显示全部楼层
回复【11楼】正点原子:
---------------------------------
感觉自己总算有了一点点的进步

参照您的从SD更新字库的函数,改写了一个从SD更新bin的小函数,试了一下OK的

// 从SD卡更新bin到片内Flash
// appxaddr:片内Flash的起始地址
//  fxpath:bin在SD的路径
u8 update_app(u32 appxaddr,u8 *fxpath)
{
    u32 tempaddr=appxaddr;//当前写入的地址
FIL * fftemp;
u8 *tempbuf;
  u8 res;
u16 bread;
u8 rval;

fftemp=(FIL*)mymalloc(SRAMIN,sizeof(FIL)); //分配内存
if(fftemp==NULL)rval=1;
tempbuf=mymalloc(SRAMIN,4096); //分配4096个字节空间
if(tempbuf==NULL)rval=1;

res=f_open(fftemp,(const TCHAR*)fxpath,FA_READ); 
while(res==FR_OK)//死循环执行
{
  res=f_read(fftemp,tempbuf,4096,(UINT *)&bread); //读取数据  
if(res!=FR_OK)break;  //执行错误
iap_write_appbin(tempaddr,tempbuf, bread);
tempaddr+=bread;     
if(bread!=4096)break; //读完了.
  } 
f_close(fftemp);

return rval;
}
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-5-16 22:55:09 | 显示全部楼层
回复【12楼】wwjdwy:
---------------------------------
恭喜。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

54

主题

537

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2012-2-27
在线时间
7 小时
发表于 2013-5-17 09:15:21 | 显示全部楼层
哈哈睡了一觉又有些想法和原子哥探讨一下。
我觉得升级产品固件时候使用USB口应该是最通用的,尤其消费电子,工控可能还是可以用串口。
1,用USB升级固件思路,我想用USB像串口那样直接接收文件写入片内flash,并不是USB作为读卡器读到SD卡或者外设Flash再写入。这样的话我应该选择哪种USB配置,或者参考哪个USB例程啊原子哥。
2,如果使用FSMC挂载外部NOR或者NAND,是否可以将app写在外挂存储由这里启动app?
3,在Options for TargetàTarget选项卡中修改了ROM或者RAM位置之后,是不是在程序中就不用再设置向量偏移了?我没有在代码中写,发现代码正常运行了。。。。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-5-17 10:35:47 | 显示全部楼层
1,st貌似有提供一个usb dfu例程,你找找。用hid.
2,nor可以,nand有坏快,不成。
3,要设置
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

54

主题

537

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2012-2-27
在线时间
7 小时
发表于 2013-5-17 21:20:50 | 显示全部楼层
回复【15楼】正点原子:
---------------------------------
多谢啊,多谢
回复 支持 反对

使用道具 举报

54

主题

537

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2012-2-27
在线时间
7 小时
发表于 2013-5-18 17:18:54 | 显示全部楼层
回复【15楼】正点原子:
---------------------------------
原子哥,DFU是要改boot设置,就类似串口下载程序启动。boot0为1.
这个和IAP功能不一样吧?

不过系统存储器启动这个还没太理解。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-5-18 17:59:20 | 显示全部楼层
回复【17楼】wwjdwy:
---------------------------------
你要IAP,那就得自己写代码了,写USB协议,然后做处理,就可以了。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

6

帖子

0

精华

初级会员

Rank: 2

积分
81
金钱
81
注册时间
2013-10-15
在线时间
10 小时
发表于 2013-10-21 23:07:15 | 显示全部楼层
mark  mark
回复 支持 反对

使用道具 举报

20

主题

122

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
222
金钱
222
注册时间
2013-9-21
在线时间
0 小时
发表于 2013-12-4 12:32:35 | 显示全部楼层
回复【楼主位】正点原子:
--------------------------------
原子哥,请问
这句是怎么理解
MSR MSP, r0  //set Main Stack value
r0里的值是什么呢?
没看见对r0的操作啊。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-12-4 19:12:50 | 显示全部楼层
r0是个寄存器用来存储函数的参数的。
具体过程我也不太懂,哈哈...你凑合着看吧。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

6

主题

25

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
217
金钱
217
注册时间
2013-10-25
在线时间
44 小时
发表于 2014-10-30 11:19:10 | 显示全部楼层
m  a  r  k
回复 支持 反对

使用道具 举报

13

主题

104

帖子

0

精华

初级会员

Rank: 2

积分
179
金钱
179
注册时间
2011-8-2
在线时间
0 小时
发表于 2014-11-24 11:33:13 | 显示全部楼层
bootloder程序的SRAM设置是不是要和APP程序的SRAM分开呢? 比如bootloader从0x2000 0000到0x2000 0fff,那么APP程序的SRAM分配就从0x2000 1000开始呢?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2014-11-25 00:45:28 | 显示全部楼层
回复【23楼】提刀上马:
---------------------------------
当然要
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

13

主题

104

帖子

0

精华

初级会员

Rank: 2

积分
179
金钱
179
注册时间
2011-8-2
在线时间
0 小时
发表于 2014-11-25 10:38:37 | 显示全部楼层
回复【24楼】正点原子:
---------------------------------
我指的是2个程序都下载到Flash时 他们所用的ram分不分开呢?  我看例子里两个程序的ram都是从0x2000 0000开始的
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2014-11-25 16:29:07 | 显示全部楼层
回复【25楼】提刀上马:
---------------------------------
共用的。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
74
金钱
74
注册时间
2012-8-3
在线时间
9 小时
发表于 2015-1-5 20:50:46 | 显示全部楼层
记号,学习 了,。
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
33
金钱
33
注册时间
2015-1-8
在线时间
2 小时
发表于 2015-1-23 15:06:49 | 显示全部楼层
好贴,必须顶。,。。。
回复 支持 反对

使用道具 举报

5

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
54
金钱
54
注册时间
2014-11-18
在线时间
1 小时
发表于 2015-1-26 21:03:36 | 显示全部楼层
当升级完后,按reset, 似乎先会运行第一个项目代码, 如不升级, 再第二个项目代码。逻辑上,为什么不是由第一个项目reset_handler地址+偏移,直接运行第二个项目代码呢?
是不是因为,这时 向量表偏移量仍为0? 多谢!
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2015-1-26 23:48:34 | 显示全部楼层
回复【29楼】a16176:
---------------------------------
跳转的控制都是在bootloader程序,你改bootloader即可。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

6

主题

46

帖子

0

精华

初级会员

Rank: 2

积分
172
金钱
172
注册时间
2014-4-25
在线时间
17 小时
发表于 2015-2-4 16:59:48 | 显示全部楼层
回复【10楼】wwjdwy:
---------------------------------
good
学习会使你永远立于不败之地。
回复 支持 反对

使用道具 举报

6

主题

46

帖子

0

精华

初级会员

Rank: 2

积分
172
金钱
172
注册时间
2014-4-25
在线时间
17 小时
发表于 2015-2-4 17:18:29 | 显示全部楼层
void iap_load_app(u32 appxaddr)
{
 if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) 
 {
  jump2app=(iapfun)*(vu32*)(appxaddr+4);    
  MSR_MSP(*(vu32*)appxaddr);     
  jump2app();         
 }
}
这个函数如果传入的参数是0x08005000,if的条件也成立吗? 为什么?
学习会使你永远立于不败之地。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2015-2-4 22:48:54 | 显示全部楼层
回复【32楼】zhijun1988:
---------------------------------
因为是取0x08005000地址所存储的值
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

6

主题

46

帖子

0

精华

初级会员

Rank: 2

积分
172
金钱
172
注册时间
2014-4-25
在线时间
17 小时
发表于 2015-2-5 08:19:35 | 显示全部楼层
回复【33楼】正点原子:
---------------------------------
0x08005000位置是栈顶地址,是吗?栈在RAM中。
学习会使你永远立于不败之地。
回复 支持 反对

使用道具 举报

6

主题

46

帖子

0

精华

初级会员

Rank: 2

积分
172
金钱
172
注册时间
2014-4-25
在线时间
17 小时
发表于 2015-2-5 08:21:22 | 显示全部楼层
回复【33楼】正点原子:
---------------------------------
多谢原子哥的解答,有问必有回,论坛会越来越火。。。
学习会使你永远立于不败之地。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2015-2-5 23:27:14 | 显示全部楼层
回复【34楼】zhijun1988:
---------------------------------
是的
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

6

主题

46

帖子

0

精华

初级会员

Rank: 2

积分
172
金钱
172
注册时间
2014-4-25
在线时间
17 小时
发表于 2015-2-9 09:14:26 | 显示全部楼层
回复【34楼】zhijun1988:
---------------------------------

网上看到的,分享一下
============================

问题问题问题::::
有客户在初次使用 STM32 MCU IAP 的例程的,可能会对跳转到 APP 部分的函数的实现产生疑问:
问题 1 : JumpAddress 地址为什么指向 APPLICATION_ADDRESS + 4 ;
MSP 主堆栈指针为什么指向 APPLICATION_ADDRESS ;
问题 2:为什么需要做以下判断:
if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
{
}
此 Tips 对于这两个个问题做简要分析.进行所以说主要代码如下:
 /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
 if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
 {
 /* Jump to user application */
 JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
 Jump_To_Application = (pFunction) JumpAddress;
 /* Initialize user application's Stack ointer */
 __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
 Jump_To_Application();
 }
调研调研调研::::
问题 1 分析:
 从 startup_stm32f4xx.s 中的启动代码可以看出:程序开始第一条指令地址为 CSTACK,第二
条指令地址为复位指令 ,参考代码中的红色部分(蓝色为注释);
 EXTERN __iar_program_start
 EXTERN SystemInit
 UBLIC __vector_table
 DATA
__vector_table
 DCD sfe(CSTACK)  ; APPLICATION_ADDRESS
 DCD Reset_Handler ; Reset Handler  ; APPLICATION_ADDRESS + 4
 DCD NMI_Handler ; NMI Handler
 DCD HardFault_Handler ; Hard Fault Handler
 DCD MemManage_Handler ; MPU Fault Handler
 DCD BusFault_Handler ; Bus Fault Handler
 DCD UsageFault_Handler ; Usage Fault Handler
 DCD 0 ; Reserved
 DCD 0 ; Reserved
 DCD 0 ; Reserved
 DCD 0 ; Reserved
 DCD SVC_Handler ; SVCall Handler
 DCD DebugMon_Handler ; Debug Monitor Handler
 DCD 0 ; Reserved
 DCD endSV_Handler ; endSV Handler
 DCD SysTick_Handler ; SysTick Handler
当程序启动时首先要执行复位程序,因此 JumpAddress 地址指向复位指令地址(即
APPLICATION_ADDRESS + 4) ;
 MSP 对应的是主堆栈指针,指向 CSTACK 地址(即 APPLICATION_ADDRESS).
问题 2 分析:
 ApplicationAddress 存放的是程序的主堆栈地址, CSTACK 堆栈地址指向 RAM,而 RAM 的
起始地址是 0x20000000;
因此上面的判断语句执行:判断用户代码的堆栈地址是否落在:0x20000000~0x2001ffff 区间
中,这个区间的大小为 128K.
例程中使用芯片 RAM=128K,因此做上面的判断;如果芯片 RAM 比 128K 大的话,可以在
此判断语句做调整.
结论结论结论:::
 以上分析可以看出,程序跳转部分的设计与 APP 启动代码和芯片 RAM 的大小有关系;移植和
调试时需加以注意.
学习会使你永远立于不败之地。
回复 支持 反对

使用道具 举报

1

主题

9

帖子

0

精华

初级会员

Rank: 2

积分
102
金钱
102
注册时间
2015-3-6
在线时间
18 小时
发表于 2015-3-6 15:45:51 | 显示全部楼层
找来找去,还是原子大牛的教程最全最详细,认真学习
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
43
金钱
43
注册时间
2014-5-6
在线时间
5 小时
发表于 2015-11-27 20:54:53 | 显示全部楼层
原子哥大爱无疆啊
回复 支持 反对

使用道具 举报

5

主题

19

帖子

0

精华

初级会员

Rank: 2

积分
78
金钱
78
注册时间
2015-8-25
在线时间
18 小时
发表于 2016-8-25 13:49:41 | 显示全部楼层
正点原子 发表于 2014-11-25 00:45
回复【23楼】提刀上马:
---------------------------------
当然要

原子哥,既然bootloader 程序设置的SRAM要和APP程序的SRAM分开,那为什么我看您写的历程中,bootloader和APP程序在设置SRAM的时候都是从0x20000000开始的呢,而且大小也都一样
回复 支持 反对

使用道具 举报

8

主题

145

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
462
金钱
462
注册时间
2016-12-14
在线时间
75 小时
发表于 2017-8-16 17:45:13 | 显示全部楼层
顶,收藏了。。。
书山有路勤为径,学海无涯苦作舟!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-24 21:09

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表