STM32补充——IAP

news2025/1/23 7:10:02

0 前置知识:

FLASH相关内容:前往STM32补充——FLASH

STM32三种烧录方式(看看就行):

1.ISP:In System Programming(在系统编程)
执行芯片厂商的 Bootloader 程序进入 ISP 模式,进入ISP 模式后,用户可选择官方提供的烧录通信接口(如:串口),并配合ISP 编程工具(如:FlyMcu)对闪存进行烧录。

2.ICP:In Circuit Programing(在线编程)
使用IDE并通过JTAG/SWD接口对闪存进行烧录。

3.IAP:In Application Programming(在应用编程)
使用用户的应用程序(也称为:Bootloader程序)对闪存进行烧录。该应用程序需要通过一种通信接口(如:IO口\USB\CAN\UART\I2C\SPI等)对闪存进行烧录(即把APP程序烧录到闪存)。IAP 通常被开发者用作远程升级的手段。


1.IAP原理介绍

1.1 程序正常运行过程:

①跳转到复位中断服务函数
②跳转到main函数
③发生中断时,会强制跳转到中断向量表
④根据中断源,跳转到对应的中断服务函数
⑤执行中断服务程序后,回到main函数原来的位置执行  

 

1.2 加入IAP后程序运行过程

①执行复位中断服务函数后,跳转到IAP程序main函数
②执行IAP过程,跳转到APP中断向量表
③跳转到APP的main函数
④发生中断时,会强制跳转到地址为0x08000000的中断向量表
⑤根据设置的中断向量表偏移量,跳转到APP对应的中断服务函数
⑥执行中断服务程序后,回到main函数原来的位置执行 


2.APP的生成步骤

  1. 设置APP程序的起始地址和存储空间大小
  2. 设置中断向量表偏移量(设置SCB->VTOR的值即可)
  3. 设置MDK编译后运行fromelf.exe,生成.bin文件(在User选项卡,设置编译后调用fromelf.exe,根据.axf文件生成.bin文件,用于IAP更新)

注意:APP程序的起始地址可在Keil上直接设置,但中断向量表偏移量需要另外手动设置


 

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

设置APP起始地址要注意的点:
1,APP要在BootLoader后面
2,内存不能出现重叠
3,偏移量是0x200的倍数


FLASH_APP:


SRAM_APP:


①Bootloader程序运行预留4KB SRAM
②存放APP程序预留48KB SRAM
③APP程序运行预留12KB SRAM
 

2.2 设置中断向量表偏移量 

APP存放在FLASH中的设置方法:

SCB->VTOR = FLASH_BASE | 0x10000;

 

APP存放在SRAM中的设置方法:

SCB->VTOR = SRAM_BASE | 0x1000;

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

D:\MDK5.36\ARM\ARMCC\bin\fromelf  --bin -o ..\..\Output\@L.bin  ..\..\Output\%L


3. 代码

iap模块:

iapfun jump2app;
uint16_t g_iapbuf[1024];       /* 2K字节缓存 */

/**
 * @brief       IAP写入APP BIN
 * @param       appxaddr : 应用程序的起始地址
 * @param       appbuf   : 应用程序CODE
 * @param       appsize  : 应用程序大小(字节)
 * @retval      无
 */
void iap_write_appbin(uint32_t appxaddr, uint8_t *appbuf, uint32_t appsize)
{
    uint16_t t;
    uint16_t i = 0;
    uint16_t temp;
    uint32_t fwaddr = appxaddr; /* 当前写入的地址 */
    uint8_t *dfu = appbuf;

    for (t = 0; t < appsize; t += 2)
    {
        temp = (uint16_t)dfu[1] << 8;
        temp |= (uint16_t)dfu[0];
        dfu += 2;               /* 偏移2个字节(FLASH每次写入两个字节) */
        g_iapbuf[i++] = temp;

        if (i == 1024)
        {
            i = 0;
            stmflash_write(fwaddr, g_iapbuf, 1024);
            fwaddr += 2048;     /* 偏移2048  16 = 2 * 8  所以要乘以2 */
        }
    }

    if (i)
    {
        stmflash_write(fwaddr, g_iapbuf, i);  /* 将最后的一些内容字节写进去 */
    }
}

/**
 * @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();
    }
}

 

main函数:

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后清除显示 */
        }

    }
}

 

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

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

相关文章

Spring Boot中选择性加载Bean的几种方式

说明&#xff1a;用过Spring框架的都知道其自动装配的特性&#xff0c;本文介绍几种选择性加载Bean的方式。Spring自动装配参考以下两篇文章&#xff1a; 基于SpringBoot的三层架构开发&统一响应结果 SpringBoot自动装配原理简单分析 ConditionalOnProperty Conditiona…

AI刷题-策略大师:小I与小W的数字猜谜挑战

问题描述 有 1, 2,..., n &#xff0c;n 个数字&#xff0c;其中有且仅有一个数字是中奖的&#xff0c;这个数字是等概率随机生成的。 Alice 和 Bob 进行一个游戏&#xff1a; 两人轮流猜一个 1 到 n 的数字&#xff0c;Alice 先猜。 每完成一次猜测&#xff0c;主持会大声…

利用Java爬虫获取eBay商品详情:代码示例与教程

在当今的电商时代&#xff0c;获取商品详情数据对于市场分析、价格监控和竞品研究至关重要。eBay作为全球最大的电商平台之一&#xff0c;拥有海量的商品信息。通过Java爬虫技术&#xff0c;我们可以高效地获取这些数据&#xff0c;为商业决策提供支持。本文将详细介绍如何使用…

编译Android平台使用的FFmpeg库

目录 前言 一、编译环境 二、搭建环境 1.安装MSYS2 2.更新系统包 2.1 打开MSYS2 MinGW 64-bit终端&#xff08;mingw64.exe&#xff09; 2.2 更新所有软件包到最新版本 2.3 安装必要的工具和库。 3. 克隆FFmpeg源码 4. 配置编译选项 5. 执行编译 总结 前言 记录学习…

30天开发操作系统 第 17 天 -- 命令行窗口

前言 今天一开始&#xff0c;请大家先回忆一下任务A的情形。在harib13e中&#xff0c;任务A下面的LEVEL中有任务因此FIFO为空时我们可以让任务A进入休眠状态。那么&#xff0c;如果我们并未启动任务B0~ B0~ B2, B2的话&#xff0c;任务A又将会如何呢&#xff1f; 首先&#xf…

阿九的python 爬虫进阶课18.3 学习笔记

文章目录 前言1. 爬取大标题2. 爬取小标题3. 证券栏下的标题4. 某篇文章里的具体内容 前言 网课链接&#xff1a;https://www.bilibili.com/video/BV1kV4y1576b/新浪财经网址&#xff1a;https://finance.sina.com.cn/需先下载库&#xff1a; conda install lxml布置爬取的一…

Qt 5.14.2 学习记录 —— 십팔 对话框

文章目录 1、Qt对话框2、自定义对话框1、代码方式2、图形化方式 3、模态对话框4、QMessageBox5、QColorDialog6、QFileDialog7、QFontDialog8、QInputDialog 1、Qt对话框 Qt的对话框用QDialog类来表示&#xff0c;可以自定义一些类来实现自定义对话框&#xff0c;但需要继承自…

web3py+flask+ganache的智能合约教育平台

最近在学习web3的接口文档&#xff0c;使用web3pyflaskganache写了一个简易的智能合约教育平台&#xff0c;语言用的是python&#xff0c;ganche直接使用的本地区块链网络&#xff0c;用web3py进行交互。 代码逻辑不难&#xff0c;可以私信或者到我的闲鱼号夏沫mds获取我的代码…

java中的String类、StringBuffer类、StringBuilder类的详细讲解(包含相互之间的比较)

文章目录 一、String 类1 String 类的介绍2 String 对象创建的两种方式3 测试题加深理解&#xff08;1&#xff09; 例题一&#xff08;2&#xff09;例题二&#xff08;3&#xff09; 例题三 4 String 类的常用方法&#xff08;1&#xff09;equals()&#xff08;2&#xff09…

外设链接与中断

外设链接与中断 PC键盘处理过程 定制键盘的输入过程

考研408笔记之数据结构(五)——图

数据结构&#xff08;五&#xff09;——图 1. 图的基本概念 1.1 图的定义 1.2 有向图和无向图 在有向图中&#xff0c;使用圆括号表示一条边&#xff0c;圆括号里元素位置互换没有影响。 在无向图中&#xff0c;使用尖括号表示一条边&#xff0c;尖括号里元素位置互换则表示…

71,【3】buuctf web [HITCON 2017]SSRFme

进入靶场 左上角是IP地址&#xff0c;下面有一堆代码 <?php // 检查是否存在 HTTP_X_FORWARDED_FOR 头部信息 if (isset($_SERVER[HTTP_X_FORWARDED_FOR])) {// 如果存在&#xff0c;将其按逗号分隔&#xff0c;并将第一个元素作为新的 REMOTE_ADDR$http_x_headers explo…

【TypeScript】模块化和命名空间、类型查找、类型缩小

模块化和命名空间 ts 在模块化中遵循 esm 规范&#xff0c;而且推荐导入类型时前面加上 type 字段&#xff0c;这些可以让一个非TypeScript编译器比如Babel、swc或者esbuild知道什么样的导入可以被安全移除。 TypeScript有它自己的模块格式&#xff0c;名为namespaces&#x…

城市生命线安全保障:技术应用与策略创新

城市生命线工程是维系城市正常运行、满足群众生产生活需要的重要基础设施。随着城市化进程的加快&#xff0c;城市基础设施生命线安全运行的复杂性日益加剧&#xff0c;保障城市居民日常生活正常运行的水、电、气、热等各类地下管线以及桥梁、市政设施、轨道交通等城市基础设施…

MVCC在MySQL中实现无锁的原理

一&#xff1a;基础知识 我们知道MySQL是多线程并发处理任务的。MySQL使用了MVCC来实现事务并发的无锁机制。 而且我们还需要知道MySQL的四种隔离级别&#xff1a;读未提交&#xff0c;读已提交&#xff08;RC&#xff09;&#xff0c;可重复读&#xff08;RR&#xff09;&am…

WPF实战案例 | C# WPF实现大学选课系统

WPF实战案例 | C# WPF实现大学选课系统 一、设计来源1.1 主界面1.2 登录界面1.3 新增课程界面1.4 修改密码界面 二、效果和源码2.1 界面设计&#xff08;XAML&#xff09;2.2 代码逻辑&#xff08;C#&#xff09; 源码下载更多优质源码分享 作者&#xff1a;xcLeigh 文章地址&a…

HTML5 Canvas和JavaScript的3D粒子星系效果

HTML部分 基本结构包括<html>, <head>, 和 <body>标签。<title>标签设置了页面标题为“优化版3D粒子星系”。<style>块定义了一些基本样式&#xff1a; body&#xff1a;无边距&#xff0c;隐藏滚动条&#xff0c;黑色背景&#xff0c;禁用触摸…

再见 Crontab!Linux 定时任务的新选择!

引言 说到 Linux 下定时执行任务&#xff0c;大多数人可能会想到 crontab&#xff1f;没错&#xff0c;它的确是 Linux 下比较通用和方便的方式&#xff0c;但是今天我来介绍一种新的方法来创建定时任务并且支持更多更强大的功能。 Systemd 很多小伙伴应该听说过 Systemd&…

Unity入门1

安装之后无法获得许可证&#xff0c;可以考虑重装 新建项目 单击空白处生成脚本 双击c#文件 会自动打开vstudio 检查引用 如果没有引用&#xff0c;重开vstu&#xff0c;或者重新加载项目 hierarchy层级 scenes场景 assets资产 inspector督察 icon图标 资源链接&…

【二叉树】遍历总结!

在很多问题中&#xff0c;熟练掌握二叉树的遍历方法&#xff0c;能够轻松解决很多问题。 新建一棵二叉树root[1,null,2,3] 1、前序遍历 前序遍历的顺序为根节点->左子树->右子树&#xff0c;按照以上二叉树&#xff0c;遍历顺序为[1&#xff0c;2&#xff0c;3]。代码为…