STM32笔记(串口IAP升级)

news2024/11/28 6:24:36

一、IAP简介

IAP(In Application Programming)即在应用编程, IAP 是用户自己的程序在运行过程中对
User Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产
品中的固件程序进行更新升级。

通常实现 IAP 功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两部分代码,第一部分程序不执行正常的功能操作,而只是通过某种通信方式(如 USB、 USART)接收程序或数据,执行对第二部分代码的更新;第二部分代码才是真正的功能代码。

两部分代码都同时烧录在 User Flash 中,当芯片上电后,首先是第一部分代码开始运行,它作如下操作:

  1. 检查是否需要对第二部分代码进行更新;
  2. 如果不需要更新则转到第二部分代码执行;
  3. 执行更新操作;
  4. 跳转到第二部分代码执行;

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

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

STM32F4 的 APP 程序不仅可以放到 FLASH 里面运行,也可以放到 SRAM 里面运行。

二、正常的程序运行流程

内部闪存(FLASH)地址起始于 0x08000000,一般情况下,程序文件就从此地址开始写入。

程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而这张“中断向量表”的起始地址是 0x08000004,当中断来临, 内部硬件机制亦会自动将 PC 指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。

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

三、IAP 程序的运行流程

程序还是从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到 IAP 的 main 函数

在执行完 IAP 以后(即将新的 APP 代码写入 FLASH,灰底部分。新程序的复位中断向量起始地址为 0X08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的 main 函数,如图标号②和③所示,同样 main 函数为一个死循环,但在不同位置上,共有两个中断向量表。

通俗讲:其实就是在正常运行的程序(APP程序)之前加上一个程序(bootloader程序), bootloader程序运行完之后再运行APP程序,两个是独立的个体,都需要有自己的中断。所以bootloader程序运行完之后就需将中断向量表位置移到APP程序的位置,不然APP程序发生中断响应回去执行bootloader程序的中断程序。

IAP 程序必须满足两个要求:

  1. 新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始;
  2. 必须将新程序的中断向量表相应的移动,移动的偏移量为 x;

四、运用实例

bootloader程序开机的时候先显示提示信息,然后等待串口输入接收 APP 程序(无校验,一次性接收),在串口接收到 APP 程序之后,即可执行 IAP。

按下 KEY1 按键,将串口接收到的 APP 程序存放到 STM32F103 的 FLASH,之后再按 KEY2 既可以跳转执行这个 FLASH APP 程序。

Bootloader 程序:

main.c

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_key.h" 
#include "bsp_TiMbase.h"
#include "bsp_usart.h"
    	
/* 用于获取接收数据的计数 */
uint32_t USART_RX_CNT; 

/* 串口接收的数据保存在ram的0X20001000地址中 */
uint8_t  USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));

/* 接收数据函数处理 */
void Receive_App_Data(void)
{
  static uint16_t oldcount = 0;	
	static uint16_t applenth = 0;
  static uint16_t com_delay = 0;

  if (com_delay < 1000)
  {
    com_delay++;
  }
  else
  {
    if (USART_RX_CNT) 
    {
      if (oldcount == USART_RX_CNT)
      {
        applenth = USART_RX_CNT;
        printf("Receiving APP data succeeded.\r\n");
        printf("Code len: %dBytes\r\n",applenth);
        oldcount = 0;
        USART_RX_CNT = 0;
      }
      else
      {
        oldcount = USART_RX_CNT;
      }
    }
  }
  /* 扫描Key1, 将接收的app数据写进内部flash中 */
  if(Key_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == KEY_ON)
  {
    if(applenth)
    {
      printf("Start updating firmware...\r\n");	
      /* 判断APP程序的复位地址是否在范围内 */
      if(((*(volatile uint32_t *)(0x20001000 + 4)) & 0xFF000000) == 0x08000000)
      {	 
        Iap_Write_Appbin(FLASH_APP1_ADDR, USART_RX_BUF, applenth);  
        printf("Firmware update complete!\r\n");	
      }
      else 
      { 
        printf("Non-flash applications!\r\n");
      }
    }
    else 
    {
      printf("There is no firmware to update!\r\n");
    }	
  } 	
}

int main(void)
{
  USART_Config();
	LED_GPIO_Config();
	Key_GPIO_Config();
	BASIC_TIM_Init();
  
  printf("IAP Test !\n");
	
  while(1)
  {
     if( Key_Scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN) == KEY_ON)
    {
        printf("Start APP code!\r\n");
        if(((*(volatile uint32_t *)(FLASH_APP1_ADDR + 4)) & 0xFF000000)==0x08000000)
        {	 
          printf("success\n");
          /* 跳转到App程序中,最好放在主循环里,不要放在定时器里 */
          Iap_Load_App(FLASH_APP1_ADDR);
        }
        else 
        {
          printf("Non-flash applications cannot be executed!\r\n");
        }
    } 
  }
}

主要运行跳转到app的函数。

iap.c

#include "iap.h"   

uint16_t iapbuf[1024]; 	//2K字节缓存
iapfun jump_to_app;

/* 以每2k字节进行写入 */
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 = (u16)dfu[1] << 8;
		temp += (u16)dfu[0];	  
		dfu += 2;//偏移2个字节
		iapbuf[i++] = temp;	    
		if(i == 1024)
		{
			i = 0;
			Flash_Write(fwaddr, iapbuf, 1024);	
			fwaddr += 2048;//偏移2048  16=2*8.所以要乘以2.
		}
	}
	if (i)
    Flash_Write(fwaddr, iapbuf, i);//将最后的一些内容字节写进去. 
}

void Iap_Load_App(uint32_t appxaddr)
{
  /* 检查栈顶地址是否合法 */
	if(((*(volatile uint32_t *)appxaddr) & 0x2FFE0000) == 0x20000000)	
	{ 
    /* 关中断 */
    __disable_irq(); 
    
    /* 关闭外设时钟 */
    RCC->APB1ENR = 0;
    RCC->APB2ENR = 0;
    SysTick->CTRL = 0; 
  
    /* 设置主堆栈指针 */
    __set_MSP(*(volatile uint32_t *)appxaddr);

    /* APP程序的复位地址 */
		jump_to_app = (iapfun) * (volatile uint32_t *)(appxaddr + 4);
    jump_to_app();
	}
}	

/*********************************************END OF FILE**********************/

主要是将app程序写入flash,和跳转到app操作。

注意:一定要关中断和关闭外设(由于本程序用了TIM,所以关了中断,没用也可以不关,但最好是关)

串口中断-:

// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
  uint8_t ucTemp;
	if(USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE)!=RESET)
	{		
		ucTemp = USART_ReceiveData(DEBUG_USARTx);
        USART_SendData(DEBUG_USARTx,ucTemp);    
        if(USART_RX_CNT < USART_REC_LEN)
		{
			USART_RX_BUF[USART_RX_CNT] = ucTemp;
			USART_RX_CNT++;			 									     
		}
	}	 
}

主要是接收app程序。

定时器中断-bsp_TiMbase.c:

void  BASIC_TIM_IRQHandler (void)
{
	if (TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET ) 
	{	
    LED_Test();
    Receive_App_Data();
		TIM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update);  		 
	}		 	
}

运行LED闪烁程序和接收app数据并写入到flash里。

bootloader程序大小的确定:

双击工程得到.map文件看到该程序占用10.99kb大小。

使用11kb进行计算得出:2c00,但我使用了5000,为了方便扩展,当然你可以设置该大小。

APP 程序:

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_TiMbase.h"

volatile uint32_t time = 0; // ms 计时变量 


int main(void)
{
  
  SCB->VTOR = 0x08005000;
  __enable_irq();
  
	LED_GPIO_Config();
	BASIC_TIM_Init();
	
  while(1)
  {
    if ( time == 1000 ) /* 1000 * 1 ms = 1s 时间到 */
    {
      time = 0; 
	  LED1_TOGGLE; 
    }        
  }
}

主要使1s LED闪烁一次

/* 设置中断向量表的偏移量 */ 
SCB->VTOR = 0x08005000;
/* 使能中断 */
__enable_irq();

这两个必须要加上,否则无法跳转。

用XCOM进行app文件的发送:

按下KEY1:

按下KEY2:

五、源代码地址

https://gitee.com/xu-fuyong/stm32f103-iap

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

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

相关文章

VsCode 插件推荐(个人常用)

VsCode 插件推荐&#xff08;个人常用&#xff09;

解决`-bash: ./configure:/bin/sh^M:解释器错误: 没有那个文件或目录`的问题

解决`-bash: ./configure:/bin/sh^M:解释器错误: 没有那个文件或目录`的问题 一、错误原因分析二、解决方法方法一:使用`dos2unix`工具方法二:使用`sed`命令方法三:使用`tr`命令方法四:在文本编辑器中转换方法五:在Windows系统中使用适当的工具三、预防措施四、总结在使…

Excel如何设置超出单元格的内容不显示?

如图&#xff0c;在使用EXCEL时经常出现超出单元格显示的情况&#xff1a; 如果想要把超出单元格的部分隐藏&#xff0c;需要进行以下设置&#xff1a; 选中想要设置的单元格&#xff0c;然后点击对齐方式右边的按钮&#xff0c;对齐设置&#xff0c;选择“对齐”选项卡&#…

【干货分享】Boosting算法简单案例

Boosting算法是一种集成学习方法&#xff0c;通过逐步迭代训练弱分类器&#xff0c;并通过加权组合它们的预测结果来构建一个强分类器。 下面是Boosting算法&#xff08;以AdaBoost为例&#xff09;的详细过程和一个案例&#xff1a; 1. 数据准备&#xff1a;首先&#xff0c;将…

如何搭建高效的实时美颜直播APP?美颜SDK与美颜API的开发详

时下&#xff0c;搭建一款高效的实时美颜直播APP&#xff0c;尤其是集成美颜SDK与美颜API&#xff0c;已成为开发者们的技术难题。本篇文章&#xff0c;小编将详细探讨如何利用美颜SDK与美颜API来搭建一款高效的实时美颜直播APP&#xff0c;提升产品的竞争力。 一、实时美颜技…

Spring Boot教程之十一:获取Request 请求 和 Put请求

如何在 Spring Boot 中获取Request Body&#xff1f; Java 语言是所有编程语言中最流行的语言之一。使用 Java 编程语言有几个优点&#xff0c;无论是出于安全目的还是构建大型分发项目。使用 Java 的优点之一是 Java 试图借助类、继承、多态等概念将语言中的每个概念与现实世…

【JAVA进阶篇教学】第二十篇:如何高效处理List集合数据及明细数据

博主打算从0-1讲解下java进阶篇教学&#xff0c;今天教学第二十篇&#xff1a;如何高效处理List集合数据及明细数据。 Java 8 Stream API 助力高效处理集合数据&#xff08;订单明细查询优化案例&#xff09; 目录 一、前言 二、问题回顾 三、优化思路与 Stream API 的运用…

Linux的介绍及虚拟机centOS系统的下载与应用

1、什么是Linux Linux 是一种类 Unix 操作系统&#xff0c;它的内核&#xff08;Kernel&#xff09;由 Linus Torvalds 于 1991 年首次发布。作为一个开源、免费的操作系统&#xff0c;Linux 被广泛用于服务器、桌面计算机、嵌入式设备、移动设备等各种场景。 1、操作系统 操…

ORACLE数据库直接取出数据库字段JSON串中的 VALUE内容

字段内容类似这种&#xff1a; 如果是12c以上版本可以使用 SELECT JSON_VALUE(MEMO, $.supplyExercisePrice) AS supplyExercisePrice FROM your_table;如果是11g版本可以使用 SELECT REGEXP_SUBSTR(MEMO, "supplyExercisePrice":"([^"])", 1, 1, …

业务分组:流量隔离

RPC中常用的保护手段“熔断限流”&#xff0c;熔断是调用方为了避免在调用过程中&#xff0c;服务提供方出现问题的时候&#xff0c;自身资源被耗尽的一种保护行为&#xff1b;而限流则是服务提供方为防止自己被突发流量打垮的一种保护行为。虽然这两种手段作用的对象不同&…

数据结构——排序算法第二幕(交换排序:冒泡排序、快速排序(三种版本) 归并排序:归并排序(分治))超详细!!!!

文章目录 前言一、交换排序1.1 冒泡排序1.2 快速排序1.2.1 hoare版本 快排1.2.2 挖坑法 快排1.2.3 lomuto前后指针 快排 二、归并排序总结 前言 继上篇学习了排序的前面两个部分:直接插入排序和选择排序 今天我们来学习排序中常用的交换排序以及非常稳定的归并排序 快排可是有多…

【JavaEE初阶 — 网络编程】Socket 套接字 & UDP数据报套接字编程

1. Socket套接字 1.1 概念 Socket 套接字&#xff0c;是由系统提供用于网络通信的技术&#xff0c;是基于TCP / IP协议的网络通信的基本操作单元。基于 Socket 套接字的网络程序开发就是网络编程。 1.2 分类 Socket套接字主要针对传输层协议划分为如下三类&#x…

熔断限流:业务实现自我保护

服务端-限流 服务端主要是通过限流来进行自我保护&#xff0c;实现限流时要考虑到应用和IP级别&#xff0c;方便在服务治理的时候&#xff0c;对部分访问量特别大的应用进行合理的限流&#xff1b;服务端的限流阈值配置都是作用于单机的&#xff0c;而在有些场景下&#xff0c…

linux系统误操作,设置nofile值超过限制,导致无法登录,permission denied

1.问题描述&#xff08;虚拟机复现&#xff09; 在k8s集群运行某些服务时&#xff0c;对文件描述符要求比较大&#xff0c;在提高这个值前未查询这个值的限制&#xff0c;最后设置了一个超过限制的值导致登录被拒绝 [roottest4 ~]# tail -3 /etc/security/limits.conf * sof…

从零开始配置Qt+VsCode环境

从零开始配置QtVsCode环境 文章目录 从零开始配置QtVsCode环境写在前面扩展安装及配置Qt Configure配置 VsCode创建Qt工程VsCodeQMakeMinGwVsCodeQMakeMsvcVsCodeCMakeMinGwVsCodeCMakeMsvcQtCreatorQMakeMinGw->VsCodeQtCreatorQMakeMsvc->VsCodeQtCreatorCMakeMinGw-&g…

如何借助AI生成PPT,让创作轻松又高效

PPT是现代职场中不可或缺的表达工具&#xff0c;但同时也可能是令人抓狂的时间杀手。几页幻灯片的制作&#xff0c;常常需要花费数小时调整字体、配色与排版。AI的飞速发展为我们带来了革新——AI生成PPT的技术不仅让制作流程大大简化&#xff0c;还重新定义了效率与创意的关系…

【Linux】Make/Makefile

这个3/4行的语法和1/2行是一样的。也是依赖关系和依赖方法。 make命令扫描makefile文件时&#xff0c;从上向下扫描&#xff0c;默认形成一个目标文件。 指定make clean的时候才回去执行对应的清除。 为什么要给我们的clean.PHONY:clean声明它是伪目标呢&#xff1f; PHONY类…

HarmonyOS:@Provide装饰器和@Consume装饰器:与后代组件双向同步

一、前言 Provide和Consume&#xff0c;应用于与后代组件的双向数据同步&#xff0c;应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递&#xff0c;Provide和Consume摆脱参数传递机制的束缚&#xff0c;实现跨层级传递。 其中Provi…

如何做好一份技术文档?

打造出色技术文档的艺术 在当今技术驱动的世界中&#xff0c;技术文档扮演着至关重要的角色。它不仅是工程师和开发人员之间交流的桥梁&#xff0c;更是产品和技术成功的隐形推手。一份优秀的技术文档宛如一张精准的航海图&#xff0c;能够引导读者穿越技术的迷雾&#xff0c;…

泰山众筹怎样吸引用户参与

泰山众筹项目要吸引用户参与&#xff0c;需要采取一系列策略来增强项目的吸引力、提高用户信任度&#xff0c;并激发用户的参与热情。以下是一些建议&#xff1a; 1. 明确项目价值与愿景 展示独特性&#xff1a;明确泰山众筹项目的独特卖点&#xff0c;如创新性、社会影响力或…