IAP在编程升级

news2024/9/24 23:21:48

以STM32F103ZET6为例讲解,
FLASH 512KB,SRAM64KB.
让APP程序加载在FLASH里运行,在SRAM运行的先不讲解。

IAP执行流程

当加入 IAP 程序之后,程序运行流程如图。在这里插入图片描述

APP程序的生成步骤

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

我们设置起始地址(Start)为 0X08010000,即偏移量为 0x10000(64K 字节,
即留给 BootLoader 的空间),因而,留给 APP 用的 FLASH 空间(Size)为 0x80000-
0x10000=0x70000(448K 字节)大小了。
注意:需要确保 APP 起始地址在 Bootloader 程序结束位置之后,并且偏移量为 0X200 的倍数即可
在这里插入图片描述

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

VTOR 寄存器存放的是中断向量表的起始地址。默认的情况它由 BOOT 的启动模式决定,
对于 F103 来说就是指向 0x0800 0000 这个位置,也就是从默认的启动位置加载中断向量等信息,不过 ST 允许重定向这个位置,这样就可以从 Flash 区域的任意位置启动我们的代码了。
我们可以通过调用 sys.c 里面的 sys_nvic_set_vector_table 函数实现,该函数定义如下:

/**
* @brief 设置中断向量表偏移地址
* @param baseaddr: 基址
* @param offset: 偏移量
* @retval 无
*/
void sys_nvic_set_vector_table(uint32_t baseaddr, uint32_t offset)
{
	/* 设置 NVIC 的向量表偏移寄存器,VTOR 低 9 位保留,即[8:0]保留 */
	SCB->VTOR = baseaddr | (offset & (uint32_t)0xFFFFFE00);
}

该函数用于设置中断向量偏移,baseaddr 为基地址(即 APP 程序首地址),Offset 为偏移
量,需要根据自己的实际情况进行设置。比如 FLASH APP 设置中断向量表偏移量为 0x10000,调用情况如下:

/* 设置中断向量表偏移量为 0X10000 */
sys_nvic_set_vector_table(FLASH_BASE,0x10000);

3.设置MDK编译后运行fromelf.exe,生成.bin

因为使用MDK编译器默认生成的代码是.hex文件,并不方便我们用作 IAP更新,我们希望生成的文件是.bin 文件,这样可以方便进行 IAP 升级,这里我们通过 MDK 自带的格式转换工具 fromelf.exe来实现.axf 文件到.bin 文件的转换。该工具在 MDK 的安装\ARM\ARMCC\bin 文件夹里面。
本实验,我们可以通过在 MDK 点击 Options for Target→User 选项卡,在 AfterBuild/Rebuild一栏中,勾选 Run #1,我们推荐使用相对地址,在勾选的同一行后的输入框并写入命令行:fromelf --bin -o …\Output@L.bin …\Output%L
设置生成编译结果文件名
在这里插入图片描述
MDK 生成.bin 文件设置方法
在这里插入图片描述通过这一步设置,我们就可以在 MDK 编译成功之后,调用 fromelf.exe,…\Output%L 表
示当前编译的链接文件(…\是相对路径,表示上级目录,编译器默认从工程文件*.uvprojx 开始查找,根据我的工程文件 Output 的位置就能明白路径的含义),指令–bin –o …\Output@L.bin表示在 Output 目录下生成一个.bin 文件,@L 在 Keil 的下表示 Output 选项卡下的 Name ofExecutable 后面的字符串,即在 Output 文件夹下生成一个 atk_f103.bin 文件。在得到.bin 文件之后,我们只需要将这个 bin 文件传送给单片机,即可执行 IAP 升级。

实操

归纳一下,使用IAP的操作流程。
1.确定一下所写的bootload程序的大小 (bootload工程生成的.map文件查看)
2.根据bootload程序的大小,确定存放APP程序的flash地址。(bootload程序要预留合适的内存大小,且flash应用程序的起始地址要为0x200的整数倍)。
3.app程序里要设置下中断向量表偏移。设置存放app地址-0x0800 0000 等于多少,就要偏移多少。
4.将app程序生成.bin文件
5.将bootload程序下载进单片机
6.使用工具将app的.bin文件发送给bootload,然后bootload会将这些数据填充到第2步指定的flash地址。
7.按下按键跳转到app程序执行。

bootload代码讲解

main.c程序的代码

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/STMFLASH/stmflash.h"
#include "./IAP/iap.h"


int main(void)
{
    uint8_t t;
    uint8_t key;
    uint32_t oldcount = 0;      /* 老的串口接收数据值 */
    uint32_t applenth = 0;      /* 接收到的app代码长度 */
    uint8_t clearflag = 0;

    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */
    delay_init(72);                             /* 延时初始化 */
    usart_init(115200);                         /* 串口初始化为115200 */
    led_init();                                 /* 初始化LED */
    lcd_init();                                 /* 初始化LCD */
    key_init();                                 /* 初始化按键 */

    lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30,  70, 200, 16, 16, "IAP TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "KEY_UP: Copy APP2FLASH!", RED);
    lcd_show_string(30, 130, 200, 16, 16, "KEY1: Run FLASH APP", RED);
    lcd_show_string(30, 150, 200, 16, 16, "KEY0: Run SRAM APP", RED);

    while (1)
    {
        if (g_usart_rx_cnt)
        {
            if (oldcount == g_usart_rx_cnt)   /* 新周期内,没有收到任何数据,认为本次数据接收完成 */
            {
                applenth = g_usart_rx_cnt;
                oldcount = 0;
                g_usart_rx_cnt = 0;
                printf("用户程序接收完成!\r\n");
                printf("代码长度:%dBytes\r\n", applenth);
            }
            else oldcount = g_usart_rx_cnt;
        }

        t++;
        delay_ms(100);

        if (t == 3)
        {
            LED0_TOGGLE();
            t = 0;

            if (clearflag)
            {
                clearflag--;

                if (clearflag == 0)
                {
                    lcd_fill(30, 190, 240, 210 + 16, WHITE);    /* 清除显示 */
                }
            }
        }

        key = key_scan(0);

        if (key == WKUP_PRES)   /* WKUP按下,更新固件到FLASH */
        {
            if (applenth)
            {
                printf("开始更新固件...\r\n");
                lcd_show_string(30, 190, 200, 16, 16, "Copying APP2FLASH...", BLUE);

                if (((*(volatile uint32_t *)(0X20001000 + 4)) & 0xFF000000) == 0x08000000)  /* 判断是否为0X08XXXXXX */
                {
                    iap_write_appbin(FLASH_APP1_ADDR, g_usart_rx_buf, applenth);            /* 更新FLASH代码 */
                    lcd_show_string(30, 190, 200, 16, 16, "Copy APP Successed!!", BLUE);
                    printf("固件更新完成!\r\n");
                }
                else
                {
                    lcd_show_string(30, 190, 200, 16, 16, "Illegal FLASH APP!  ", BLUE);
                    printf("非FLASH应用程序!\r\n");
                }
            }
            else
            {
                printf("没有可以更新的固件!\r\n");
                lcd_show_string(30, 190, 200, 16, 16, "No APP!", BLUE);
            }

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

        if (key == KEY1_PRES)   /* KEY1按键按下, 运行FLASH APP代码 */
        {
            if (((*(volatile uint32_t *)(FLASH_APP1_ADDR + 4)) & 0xFF000000) == 0x08000000) /* 判断FLASH里面是否有APP,有的话执行 */
            {
                printf("开始执行FLASH用户代码!!\r\n\r\n");
                delay_ms(10);
                iap_load_app(FLASH_APP1_ADDR);/* 执行FLASH APP代码 */

            }
            else
            {
                printf("没有可以运行的固件!\r\n");
                lcd_show_string(30, 190, 200, 16, 16, "No APP!", BLUE);
            }

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

        if (key == KEY0_PRES)   /* KEY0按下 */
        {
            printf("开始执行SRAM用户代码!!\r\n\r\n");
            delay_ms(10);

            if (((*(volatile uint32_t *)(0x20001000 + 4)) & 0xFF000000) == 0x20000000)   /* 判断是否为0X20XXXXXX */
            {
                iap_load_app(0x20001000);   /* SRAM地址 */
            }
            else
            {
                printf("非SRAM应用程序,无法执行!\r\n");
                lcd_show_string(30, 190, 200, 16, 16, "Illegal SRAM APP!", BLUE);
            }

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

    }
}

usart.c
在这里插入图片描述
讲解一下逻辑,当使用串口助手将,文本数据发送给单片机时,文本数据会从地址0X20001000(SRAM)存放,全局变量。当按下KEY_UP的时候。会根据0X20001000+4处地址进行判断是否为FLASH应用程序(该程序兼容SRAM应用程序)。如果是,那么将数据写入FLASH区,这里FLASH应用程序开始的存放地址为0X08010000。
在这里插入图片描述
这是因为这里的bootload程序大概为34.11KB,0x08010000-0x08000000 = 0x10000,为64KB。这样就避免了bootload程序被误改。
在这里插入图片描述

当按下KEY1时,判断下FLASH里面是否有APP代码,如果,就跳转过去执行。
重点函数iap_load_app()
因为FLASH应用程序的起始地址存放的是栈顶的地址,取出来看下,栈顶地址是不是合法(SRAM的起始地址)。合法的话,初始化一下MSP指针(让MSP指针指向栈顶),然后跳转去执行app程序。

/**
 * @brief       跳转到应用程序段(执行APP)
 * @param       appxaddr : 应用程序的起始地址

 * @retval      无
 */
void iap_load_app(uint32_t appxaddr)
{
    if (((*(volatile  uint32_t *)appxaddr) & 0x2FFE0000) == 0x20000000)     /* 检查栈顶地址是否合法.可以放在内部SRAM共64KB(0x20000000) */
    {
        /* 用户代码区第二个字为程序开始地址(复位地址) */
        jump2app = (iapfun) * (volatile uint32_t *)(appxaddr + 4);
        
        /* 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) */
        sys_msr_msp(*(volatile uint32_t *)appxaddr);
        
        /* 跳转到APP */
        jump2app();
    }
}

APP代码讲解

app工程里得设置一下程序起始地址,这里程序起始地址为0X0810 000,距离0X0800 0000 为0X10000。大概64KB,这时因为bootload代码大小为32.11KB,64KB远大于32.11KB,防止bootload代码被误改。

设置了程序起始地址,也要设置对应的中断向量表偏移。这里设置偏移为0X10000,FLASH基地址为0X0800 0000.
在这里插入图片描述

0x080001dd
0x0801022d

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1334878.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浮点数的转换--IEEE 754

IEEE754标准是一种浮点数表示标准,一般分为 单精度(32位的二进制数);双精度(64位的二进制数) 根据国际标准IEEE754,任意一个二进制浮点数V可以表示为下面形式: V (-1)^s *&#…

JavaWeb后门(webshell)基础

0x00 基础 JSP JSP全称为JavaServer Pages&#xff0c;是一种用于开发支持动态内容的Web页面的技术。它有助于开发人员通过使用特殊的JSP标记在HTML页面中插入Java代码&#xff0c;其中大多数以<&#xff05;开头&#xff0c;以&#xff05;>结尾。Java是一种通用的计算…

互联网+建筑工地:技术革新引领建筑行业的未来

随着科技的飞速发展&#xff0c;互联网正日益渗透到建筑工地的方方面面。从设计、施工到管理&#xff0c;互联网建筑工地的深度融合不仅推动了建筑行业的数字化转型&#xff0c;还为工地管理、信息交流、安全监控等带来了全新的解决方案。本文将介绍互联网建筑工地的几个关键技…

【c++】入门2

函数重载 函数重载&#xff1a;是函数的一种特殊情况&#xff0c;C允许在同一作用域中声明几个功能类似的同名函数&#xff0c;这 些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同&#xff0c;常用来处理实现功能类似数据类型 不同的问题。 c区分重载函数是根据参数…

golang的jwt学习笔记

文章目录 初始化项目加密一步一步编写程序另一个参数--加密方式关于StandardClaims 解密解析出来的怎么用关于`MapClaims`上面使用结构体的全代码实战项目关于验证这个项目的前端初始化项目 自然第一步是暗转jwt-go的依赖啦 #go get github.com/golang-jwt/jwt/v5 go get githu…

AAAI 2024录用论文合集,包含图神经网络、时间序列、多模态、异常检测等热门研究方向

AAAI是国际顶级人工智能学术会议&#xff0c;属于CCF A类&#xff0c;在人工智能领域享有盛誉。今年的AAAI 会议投稿量突破了历史记录&#xff0c;共有12100篇投稿&#xff08;主赛道&#xff09;&#xff0c;最终录用2342篇&#xff0c;录用率为23.75%。对比前几年有了很大的提…

人工智能_机器学习073_SVM支持向量机_人脸识别模型建模_预测可视化_网格搜索交叉验证最优化参数对比---人工智能工作笔记0113

接着上一节来说,可以看到我们已经找到了合适的参数,然后 我们可以看一下这里 gc.best_params_ 就可以打印出最合适的参数 然后我们把最合适串按说填入到代码中,然后进行计算,看看得分 可以看到得分,训练数据是1.0 然后测试数据得分是0.7857...对吧

unity HoloLens2开发,使用Vuforia识别实体 触发交互(二)(有dome)

提示&#xff1a;文章有错误的地方&#xff0c;还望诸位大神不吝指教&#xff01; 文章目录 前言一、打包到HoloLens二、Vuforia相关1.配置识别框2.制作一个半透明识别框&#xff1a;3.设置如下4.问题 四 HoloLens2 问题总结 前言 我使用的utniy 版本&#xff1a;Unity 2021.3…

Day70力扣打卡

打卡记录 收集足够苹果的最小花园周长&#xff08;找规律 二分&#xff09; 链接 class Solution:def minimumPerimeter(self, neededApples: int) -> int:l, r 1, 10 ** 5while l < r:mid (l r) >> 1if 2 * (2 * (mid ** 3) 3 * (mid ** 2) mid) > nee…

防止synplify优化ILA模块或信号

摘要&#xff1a;synplify综合的时候会优化掉没有用到的信号或者模块&#xff0c;为防止synplify优化ILA或信号特记录此方法&#xff1b; 我使用的是synplify_pro compile完成之后&#xff0c;可以打开synplify图形化的网表 1.防止synplify优化wire信号 在vivado里面一般可以…

【各种**问题系列】Java 数组集合之间的相互转换

&#x1f4cc; 问题点&#xff1a; 在 Coding 过程中经常会遇到数组、List、Set、Map 之间的相互转换......这里记录一下转换的几种方式。&#x1f636;&#x1f636;&#x1f636; 目录 &#x1f4cc; 集合转换 1.数组 转 List&#xff1a; 2.List 转 数组&#xff1a; 3…

2024年软考电子商务设计师如何备考?考什么?

一、电子商务设计师概述&#xff1f; 电子商务设计师属于软考中级资格考试&#xff0c;软考是由国家人力资源和社会保障部&#xff08;原人事部&#xff09;、工业和信息化部&#xff08;原信息产业部&#xff09;领导的国家级考试&#xff0c;其目的是&#xff0c;科学、公正…

Python中Pandas详解之数据结构

文章目录 Pandas 数据分析Pandas 简介Pandas 安装Series 类型数据Series的创建Series的访问Series 中向量化操作与布尔索引Series的切片Series的缺失值Series的增与删Series的name DataFrame 数据类型DataFrame的创建DataFrame的访问DataFrame的删除DataFrame的添加添加行添加列…

【WPF.NET开发】数据绑定应用场景

目录 1、实现属性更改通知 示例 2、双向绑定​​​更新源 示例 3、对分层数据使用主-从模式 示例 4、对分层 XML 数据使用主-从模式 示例 5、绑定两个控件的属性 示例 6、创建和绑定到 ObservableCollection 示例 7、使用 XMLDataProvider 和 XPath 查询绑定到 XML…

Nginx快速入门:return、rewrite重定向、重写详解(六)

0. 引言 我们在日常的生产过程中&#xff0c;常常有需要重定向转发的需求&#xff0c;比如企业更换了域名&#xff0c;但又要保证之前的域名能访问&#xff0c;这就需要做重定向的跳转。 我们在之前的章节中学习了Nginx的负载均衡、各类转发代理配置&#xff0c;今天继续来补…

本地部署Jellyfin影音服务器并实现远程访问内网影音库

文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及&#xff0c;各种各样的使用需求也被开发出来&…

Python基础入门第六节课笔记

while循环 for循环用于针对序列中的每个元素的一个代码块。 while循环是不断的运行&#xff0c;直到指定的条件不满足为止。 while 条件&#xff1a; 条件成立重复执行的代码1 条件成立重复执行的代码2 …….. 当条件成立时&#xff0c;执行下方缩…

Nature | 大型语言模型(LLM)能够产生和发现新知识吗?

大型语言模型&#xff08;LLM&#xff09;是基于大量数据进行预训练的超大型深度学习模型。底层转换器是一组神经网络&#xff0c;这些神经网络由具有自注意力功能的编码器和解码器组成。编码器和解码器从一系列文本中提取含义&#xff0c;并理解其中的单词和短语之间的关系。通…

制作TikTok获客脚本必备源代码!

在这个数字时代&#xff0c;TikTok已成为全球最受欢迎的社交媒体平台之一&#xff0c;无数的品牌和企业都试图通过这个平台吸引潜在客户。 但是&#xff0c;要想在TikTok上获得更多关注和粉丝&#xff0c;除了制作有趣、有创意的内容外&#xff0c;还需要借助一些自动化工具来…

vmware虚拟机不显示网络图标没有网络解决办法

1、虚拟机终端执行命令 sudo service network-manager stop sudo rm /var/lib/NetworkManager/NetworkManager.state sudo service network-manager start即可出现网络图标并正常连接到网络 2、设置网络适配器为NAT 先移除网络适配器再添加网络适配器网络连接选择NAT模式