#AT32 USART IAP 快速使用方法
文章目录
前言
在很多应用场合,现场需要对固件代码进行升级但不便拆装设备,这时候可以采用485模式进行IAP升级,和usart iap升级代码部分是一样的,只是485电路要采用硬件自动切换收发,并且速率不能太快,实验19200bps下载速率正常。
下面以AT32F403AVGT7为例,以AT-START-AT32F403A 实验板的硬件条件详细介绍操作流程
一、概述
IAP(In Application Programming)即在应用编程,IAP 是用户自己的程序在运行过程中对 User
Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件
程序进行更新升级。通常实现 IAP 功能时,即用户程序运行中作自身的更新操作,需要在设计固件
程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如
USB、USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代
码。这两部分项目代码都同时烧录在 User Flash 中,当芯片上电后,首先是第一个项目代码开始运
行,它作如下操作:
- 检查是否需要对第二部分代码进行更新
- 如果不需要更新则转到4)
- 执行更新操作
- 跳转到第二部分代码执行
在上图所示流程中,MCU 复位后,还是从 0x08000004 地址取出复位中断向量的地址,并跳转到复
位中断服务程序,在运行完复位中断服务程序之后跳转到 IAP 的 main 函数,如图标号①所示;在执
行完 IAP 以后(即将新的 APP 代码写入 AT32 的 FLASH,灰底部分。新程序的复位中断向量起始地
址为 0x08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并
跳转执行新程序的复位中断服务程序,随后跳转至新程序的 main 函数,如图标号②和③所示,同样
main 函数为一个死循环,并且注意到此时 AT32 的 FLASH,在不同位置上,共有两个中断向量表。
在 main 函数执行过程中,如果 CPU 得到一个中断请求,PC 指针仍强制跳转到地址 0x08000004 中
断向量表处,而不是新程序的中断向量表,如图标号④所示;程序再根据我们设置的中断向量表偏移
量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示;在执行完中断服务程序后,程序返回
main 函数继续运行,如图标号⑥所示。
通过以上两个过程的分析,我们知道 IAP 程序必须满足两个要求: - 新程序必须在IAP程序之后的某个偏移量为x的地址开始
- 必须将新程序的中断向量表相应的移动,移动的偏移量为x
二、AT32 USART IAP 快速使用方法
1.硬件电路
单片机控制,也可以用AT32F403AVGT7代替
485硬件收发电路
三、软件资源
- tool_release IAP_Programmer.exe,PC机tool,用于演示IAP升级流程
- source_code
bootloader,bootloader源程序,运行LED2闪烁
app_led3_toggle,app1源程序,运行LED3闪烁
注:工程基于keil v5和IAR8.2建立,若用户需要在其他编译环境上使用,请参考
AT32F403A_407_Firmware_Library_V2.x.x\project\at_start_f403a\templates中各种编译环境(例如IAR6/7/8,keil 4/5,
eclipse_gcc)进行对应修改即可。
四、IAP demo 使用
- 打开bootloader工程源程序,选择对应MCU型号的target编译后下载到实验板
- 打开IAP_Programmer.exe
- 选择正确的串口、APP下载地址和bin文档,点击Download下载,如下图
- 观察LED2/3/4闪烁,LED2闪烁-bootloader工作,LED3闪烁-app1工作
图 2. IAP demo 上位机
五、AT32 USART IAP 程序设置
地址分布
表 1. 地址分布
ITEM Address and Size
app code address 2: 0x800 4000 size: 0x40000(256K Byte)
bootloader code address 1: 0x800 0000 size: 0x4000(16K Byte)
注:bootloader区域最后一个扇区,用于存放防止升级过程掉电的flag,用户编译修改bootloader时,要保证不覆盖
flag的地址。
六、执行流程
IAP 分为 Bootloader 和 App 两部分,应用在 App 中执行,升级过程在 bootloader 中执行。程序执行
整体流程框图如下: 图 3. 程序执行流程
七、bootloader project 设置
- Keil设置
- bootloader源程序修改Iap.h文件中
图 5. bootloader project 中 address 2 在程序中设置
八、app project 设置
IAP demo 提供了 2 个 app 程序供测试用,皆以 address 2(0x800 4000)为起始地址。app1 LED3
闪烁,app2 LED4 闪烁。以 app1 为例,设计步骤如下:
-
Keil工程设置
图 6. app project 中 address 2 在 Keil 设置
-
app1 源程序设置
3) 编译生成bin文件
通过 User 选项卡,设置编译后调用 fromelf.exe,根据.axf 文件生成.bin 文件,用于 IAP 更新。
通过以上 3 个步骤,我们就可以得到一个.bin 的 APP 程序,通过 bootloader 程序即可实现更新。
4) 开启debug app code功能
如果在设计 app code 过程中需要对 app project 进行单独调试,请按照以下操作。
a) 先下载bootloader工程
b) 再调试app工程
九、bootloader/app 与上位机串口通信协议
- 上位机通信协议
图 8. 上位机通信协议
- IAP 端下位机通信协议
注: ACK: 0xCCDD
NACK: 0xEEFF
Data: 0x31+ Addr + 数据 + chenksum(1byte)
Addr:4bytes,高位在前
Kbytes,下载数据,不足2K内容填充0xFF
Checksum:1byte,4bytes的Addr + 2KBytes数据的校验和的低八位
十、bootloader程序
main.c文件
#include "at32f403a_407_board.h"
#include "at32f403a_407_clock.h"
#include "tmr.h"
#include "usart.h"
#include "flash.h"
#include "iap.h"
/** @addtogroup UTILITIES_examples
* @{
*/
/** @addtogroup USART_IAP_bootloader
* @{
*/
/**
* @brief main function.
* @param none
* @retval none
*/
int main(void)
{
system_clock_config();
at32_board_init();
/* config nvic priority group */
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
/* check iap_upgrade_flag flag */
if(flash_upgrade_flag_read() == RESET)
{
/* check app starting address whether 0x08xxxxxx */
if(((*(uint32_t*)(APP_START_ADDR + 4)) & 0xFF000000) == 0x08000000)
app_load(APP_START_ADDR);
}
/* init usart used for app update */
uart_init(19200);
/* check whether need to upgrade, if yes, response ok to pc-tool */
if(flash_upgrade_flag_read() != RESET)
back_ok();
/* init tmr used for show code running state(led cycle toggle) */
tmr_init();
while(1)
{
/* upgrade handle */
iap_upgrade_app_handle();
}
}
iap.c文件
#include "iap.h"
#include "usart.h"
#include "flash.h"
#include "tmr.h"
/** @addtogroup UTILITIES_examples
* @{
*/
/** @addtogroup USART_IAP_bootloader
* @{
*/
cmd_data_step_type cmd_data_step = CMD_DATA_IDLE;
cmd_ctr_step_type cmd_ctr_step = CMD_CTR_IDLE;
cmd_data_group_type cmd_data_group_struct;
update_status_type update_status = UPDATE_PRE;
static uint8_t cmd_addr_cnt = 0;
static uint32_t cmd_data_cnt = 0;
iapfun jump_to_app;
/* app_load don't optimize */
#if defined (__CC_ARM)
#pragma O0
#elif defined (__ICCARM__)
#pragma optimize=s none
#endif
/**
* @brief app load.
* @param app_addr
* app code starting address
* @retval none
*/
void app_load(uint32_t app_addr)
{
/* check the address of stack */
if(((*(uint32_t*)app_addr) - 0x20000000) < (SRAM_SIZE * 1024))
{
/* disable periph clock */
crm_periph_clock_enable(CRM_TMR3_PERIPH_CLOCK, FALSE);
crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, FALSE);
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, FALSE);
/* disable nvic irq and clear pending */
nvic_irq_disable(USART1_IRQn);
nvic_irq_disable(TMR3_GLOBAL_IRQn);
__NVIC_ClearPendingIRQ(USART1_IRQn);
__NVIC_ClearPendingIRQ(TMR3_GLOBAL_IRQn);
jump_to_app = (iapfun)*(uint32_t*)(app_addr + 4); /* code second word is reset address */
__set_MSP(*(uint32_t*)app_addr); /* init app stack pointer(code first word is stack address) */
jump_to_app(); /* jump to user app */
}
}
/**
* @brief take data from usart buf.
* @param app_addr
* app code starting address
* @retval val
* took data
*/
uint8_t data_take(