微控制器在硬件中作为核心,通过执行保存在内部存储器中的程序,运行各种外设,并通过引脚,控制其它电路,而将程序保存在微控制器内部存储器的过程,被称为烧录。
烧录的方式可分为以下三种:
在介绍不同烧录方式的区别前,先介绍一下微控制器的启动方式。
微控制器的启动方式有引脚Boot0和Boot1的电平决定,一般通过拨码开关或引脚+跳线帽的方式控制,根据组合有以下三种启动方式,其中‘x‘表示高电平或低电平(即高低电平皆可)。
当Boot0=0, Boot1=x时,上电复位后微控制器从内部Flash的主存储区开始运行程序,该地址一般是0x0800 0000。
设置该启动模式前,程序应该被保存在内部Flash主存储区的最前面。
当Boot0=1, Boot1=0时,上电复位后微控制器自动运行引导装载程序,即内部Flash信息块中的Bootloader区,通过运行该程序将代码引导装载至内部Flash中,完成程序烧录。
Bootloader区的地址可能随着微控制器的不同而改变,主要是由于内部Flash的主存储区大小变化。
当Boot0=1, Boot1=1时,上电复位后微控制器从内部SRAM开始运行程序,该地址一般是0x2000 0000。
同理,设置该启动模式前,程序应该被保存在内部SRAM的最前面,并且由于SRAM掉电后不会保留数据,因此不能进行复位等操作,一般该启动模式用于调试程序。
ICP(In-Circuit Programmer)指在电路中编程,需要ST-Link、J-Link等烧录器进行烧录(此时使用JTAG、SWD等接口),使用该烧录方式时,通过烧录器连接计算机和微控制器后通过keil5进行程序烧录(听说除了keil5还有其它软件,但都需要烧录器),此时烧录文件为编译后产生的bin文件。
通过ICP进行烧录时,启动方式设置为第1种,即Boot0=0,Boot1=x,以在烧录完成并复位后直接从内部Flash开始运行。
虽然这时候在硬件设置启动方式为Boot0=0,Boot1=x,但烧录器会在下载时调整为Boot0=1,Boot1=0以进行程序烧录,但这不影响后续复位后的程序运行。
ISP(In-System Programming)指在系统编程,不需要烧录器,但同样需要将计算机和微控制器连接,用于作为程序的数据进行传输,可以使用USB、USB转串口模块(常用)等方式进行烧录,在烧录时计算机需要打开ISP烧录软件,并选择对应的模式,一般不同微控制器使用的ISP软件不同,下图分别是ST和GD使用的ISP软件。
红框中为烧录时选择模式,DTR(Data Terminal Ready)表示数据终端准备好,RTS(Request To Send)表示请求发送 。可以看到都是通过串口进行烧录的(当然还有其它的ISP软件)。
通过ISP烧录时,启动方式设置为第2种,即Boot0=1,Boot1=0,运行引导装载程序以将传输的数据作为程序存储在内部Flash最前面,烧录完成后需要修改启动方式为第1种。
通过ISP方式烧录到程序文件通常是hex文件。
当然,使用此种模式一般将Boot0接到按键上,然后不按下接高电平,按下后接低电平,此时通过ISP烧录前按住按键即可。
IAP(In-Application Programming)指在应用编程,不需要通过烧录器、USB等设备与计算机连接,但需要从SD卡等存储设备获取新程序(bin文件)进行自我更新(当然,通过USB、UART等接口获取的程序也可行)。
IAP烧录方式的烧录逻辑是:在内部Flash的最前面烧录第1个程序(称为Bootloader程序),其次在相对0800 0000具有偏移的位置存储第2个程序(称为APP程序)。在Bootloader程序中,完成APP程序的更新,以及跳转至APP程序执行。
APP程序则和上述两种烧录方式烧录的程序相同,为正式运行的程序。
IAP一般可以用于产品的程序更新,避免更新时需要对其进行收回、拆解等步骤,并且方便用于自行操作。
ICP和ISP都是将1个程序存储到内部Flash中并运行,程序在内部Flash中的存储空间分布如下图所示,最前面是栈顶地址,其次是各个中断向量,然后是各个中断向量对应的中断程序入口,最后是main函数入口。
程序运行时,栈指针指向复位中断向量,其次通过该向量跳转至复位中断程序中运行,复位中断程序一般在startupxx.s文件中定义,用于初始化系统并跳转至main函数运行,最后进入main函数执行循环。当中断请求出现时,栈指针指向对应中断向量,并通过该向量跳转至对应的中断程序中运行,运行结束后再回到原位置,继续运行循环中的内容。
IAP是从Bootloader程序中跳转至APP程序中运行,因此在内部Flash中的空间分布略有不同,如下图所示,在执行Bootloader程序的main函数时,进行APP函数的跳转过程,最后执行APP程序中的main函数。当中断请求出现时,栈指针指向Bootloader程序中断向量表中对应中断向量,并通过该向量及程序偏移值跳转至对应的中断程序中运行,运行结束后再回到原位置,继续运行循环中的内容。
Bootloader程序主要完成APP程序的更新以及跳转,其中APP程序的更新即从接口或存储设备中获取后存放在内部Flash对应区域中接口(相对首地址有偏移量)。
APP程序的跳转可参考以下代码,其中& 0x2FFE0000
并与0x20000000判断是否相等,表示该地址在0x20000000 ~ 0x2001 0000之间(理论上可修改,在内部SRAM中即可)。
void GotoApp(u32 appAddr)
{
//App复位中断服务函数
void (*appResetHandler)(void);
//延时变量
u32 delay;
//检查栈顶地址是否合法.
if(0x20000000 == ((*(u32*)appAddr) & 0x2FFE0000))
{
//获取App复位中断服务函数地址,用户代码区第2个字为程序开始地址(复位地址)
appResetHandler = (void (*)(void))(*(u32*)(appAddr + 4));
//设置App主栈指针,用户代码区的第一个字用于存放栈顶地址
SetMSP(*(u32*)(appAddr));
//跳转到App
__set_FAULTMASK(1);//关闭所有中断
NVIC_SystemReset();//系统复位
}
}
其中SetMSP为汇编程序编写的函数,如下所示,其中r0为addr的地址,具体可参考ARM架构基本寄存器一文及该专栏。
__asm static void SetMSP(u32 addr)
{
MSR MSP, r0
BX r14
}
APP程序为用户程序,实际上任意程序都可以,但需要进行相应的配置,配置步骤如下:
2. 设置中断向量表偏移量
在APP程序的main函数执行硬件初始化前,加入以下代码,设置相对偏移量X为0x10000。
nvic_vector_table_set(FLASH_BASE,0x10000);
fromelf.exe路径在leil安装路径中找,bin文件存放路径任意,用户程序路径指APP程序的axf文件路径。
注意:路径可以是相对路径或绝对路径,并且需要空格隔开。
设置结束后点击编译,即可在上面填写的bin文件路径中找到对应的bin文件。