STM32G473 固件升级IAP(BootLoader)CAN/USART。(详细步骤)

这篇具有很好参考价值的文章主要介绍了STM32G473 固件升级IAP(BootLoader)CAN/USART。(详细步骤)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本例程仅供参考(个人学习总结_有需要文中有的封装好的跳转函数可私信),

例程可举一反三完成FDCAN通信和USART通信。

目录

简介

1.APP程序配置步骤

APP 程序起始地址设置方法

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

KEIL5生成bin文件步骤

2.IAP(BootLoader 程序)配置(HAL库,Cubemax)

2.1RCC配置

2.2时钟树配置

2.3CAN配置(版本例程CAN接收数据和发送数据为普通模式,配合TIM2定时器使用)

2.4TIM2定时器配置

2.5USART配置

3.IAP(BootLoader)代码程序配置

        3.1CAN过滤器,发送,接收函数配置

3.2CAN发送配置

3.3CAN.h函数声明

3.4CAN测试函数

3.5CAN测试效果

3.6USART接收

3.7Printf重映射函

3.8USART主函数测试

3.9USART测试效果

4.Flash写入数据

5.跳转至APP函数(即IAP)

6. Bootloader 程序

7.Main程序代码

8.升级效果

8.1CAN升级效果

8.2USART升级效果

9.注意事项


简介

IAP 是用户自己的程序在运行过程中对 User Flash 的部分区域进行烧写,目的是为了在产 品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。通常实现 IAP 功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,

第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如 USB、USART)接收程序或数据,执行对第二部分代码的更新;

第二个项目代码才是真正的功能代码。这两部分项目代码都同时烧录在 User Flash 中,当芯片上电后,首先是第一个项目代码开始运行,它做如下操作:

1)检查是否需要对第二部分代码进行更新

2)如果不需要更新则转到 4)

3)执行更新操作

4)跳转到第二部分代码执行

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

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

STM32G4 的 APP 程序不仅可以放到 FLASH 里面运行,也可以放到 SRAM 里面运行,我们选用在 FLASH 运行。Flash存储器是一种非易失性存储器,可以在掉电之后保存数据,通常用于存储程序代码。Flash存储器的可写入次数有限,且需要执行擦除操作才能写入新的数据,因此,在使用过程中需要注意擦写周期和数据备份问题。SRAM存储器则是一种易失性存储器,具有相对较快的读写速度和无限的读写次数,但掉电时将会丢失所有内容。SRAM存储器主要用于暂存数据和临时变量,读写操作由CPU直接完成,访问速度较快。

单片机的Flash存储器和SRAM存储器都嵌入在单片机芯片内部,能够方便的实现对程序和数据、变量的读写操作。通常,编译器会把程序烧录在Flash存储器,并使用SRAM存储器来存储变量、函数堆栈以及其他的临时数据

1.APP程序配置步骤

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

    图中 IROM1 的起始地址(Start)为 0x08000000,大小(Size)为 0x80000,即从 0x08000000 开始的 512K 空间为我们的程序存储区。

    APP程序将设置起始地址(Start)为 0x08010000,即偏移量为 0x10000(64K 字节,即留给 BootLoader 的空间),因而,留给 APP 用的 FLASH 空间(Size)只有 0x80000- 0x10000=0x70000(448K 字节)大小了。设置好 Start 和 Size,就完成 APP 程序的起始地址设置。IRAM 是内存的地址,APP 可以独占这些内存,不需要修改。

    注意:需要确保 APP 起始地址在 Bootloader 程序结束位置之后,并且偏移量为 0x200 的倍数即可

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

    VTOR 寄存器存放的是中断向量表的起始地址。默认的情况它由 BOOT 的启动模式决定,对于 STM32G4 来说就是指向 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);

    通过以上两个步骤的设置,我们就可以生成 APP 程序了,只要 APP 程序的 FLASH大小不超过我们的设置即可   MDK 默认生成的文件是.hex 文件,并不方便我们用作 IAP更新,工程直接生成.bin 文件,可以方便进行 IAP 升级。can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

  3. KEIL5生成bin文件步骤

通过 MDK 自带的格式转换工具 fromelf.exe 来生成 bin 文件。注意:如果用户安装 MDK 在 C 盘的默认路径,那它的位置一般C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe。

其他位置可点击鼠标右键 KEIL 软件 打开文件所在目录 ,然后跳转到上一级文件目录的ARM ->>ARMCC ->>获得绝对路径。

点击Options for Target→User,在 After Build/Rebuild 一栏中,勾选 Run #1,输入转换语句

D:\A_A_ProgramFiles\A_Keil5\ARM\ARMCC\bin\fromelf --bin -o ..\..\..\..\..\A_ADesktop\Program\Stm32_00X\Can_IAP_003\Output\@L.bin ..\..\..\..\..\A_ADesktop\Program\Stm32_00X\Can_IAP_003\MDK-ARM\Can_IAP_003\%L

推荐相对地址,(不同的工程文件地址可能不同,需要根据自己的工程来定,文件路径较深推荐用绝对路径。)can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

转换语句的组成解释如下:

首先是MDK 自带的格式转换工具 fromelf.exe的绝对路径 + 命令fromelf --bin -o 路径(生成bin文件保存路径)\@L.bin  路径(生.axf的文件路径)%L。

D:\A_A_ProgramFiles\A_Keil5\ARM\ARMCC\bin\fromelf --bin -o ..\..\..\..\..\A_ADesktop\Program\Stm32_00X\Can_IAP_003\Output\@L.bin ..\..\..\..\..\A_ADesktop\Program\Stm32_00X\Can_IAP_003\MDK-ARM\Can_IAP_003\%L

总结APP 程序的生成步骤

  1. 设置 APP 程序的起始地址和存储空间大小
  2. 设置中断向量表偏移量
  3. 设置编译后运行 fromelf.exe,生成.bin 文件

以上 3 个步骤,就可以得到一个.bin 的 APP 程序,通过 Bootlader 程序即可实现更新

APP程序中加入can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言文件夹中的can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言.c和.h文件在工程中添加包含路径,并在Main函数最开始部分添加语句can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

2.IAP(BootLoader 程序)配置(HAL库,Cubemax)

2.1RCC配置

can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

2.2时钟树配置

can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

2.3CAN配置(版本例程CAN接收数据和发送数据为普通模式,配合TIM2定时器使用)

can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

2.4TIM2定时器配置

can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

2.5USART配置

can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

生成工程

can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

3.IAP(BootLoader)代码程序配置

        3.1CAN过滤器,发送,接收函数配置

/* USER CODE BEGIN 0 */
FDCAN_FilterTypeDef   sFilterConfig;	//接收过滤器类型定义结构体
/* USER CODE END 0 */

 /* USER CODE BEGIN FDCAN1_Init 2 *///配置接收过滤	
sFilterConfig.IdType = FDCAN_STANDARD_ID;       
//  配置为过滤标准帧
  sFilterConfig.FilterIndex = 0;                 
  // 过滤器的索引号  如果有2组标准帧过滤,那么就是通过index区别,0  1   2这种
  sFilterConfig.FilterType = FDCAN_FILTER_RANGE;  
//  过滤方式为范围,即从FilterID1~FilterID2之间的值
  sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  sFilterConfig.FilterID1 = 0x0000;
  sFilterConfig.FilterID2 = 0x07ff;         		  
//标准帧为11位ID,即0x7ff,本例配置为接收所有帧
/*设置接收过滤器,根据FDCAN_FilterTypeDef结构中指定的参数配置FDCAN接收筛选器。*/
if(HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)	
{
    Error_Handler();
}
  /*配置FDCAN全局过滤器*/
if(HAL_FDCAN_ConfigGlobalFilter(&hfdcan1,FDCAN_REJECT,FDCAN_REJECT,DCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK)
  {
    Error_Handler();
  }
if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK)
  {
    Error_Handler();
  }
/* USER CODE END FDCAN1_Init 2 */

3.2CAN发送配置

/*发送函数*/
void CAN_Send_Msg(FDCAN_HandleTypeDef hcan,uint32_t can_id,uint8_t tx_buff[])
{	
	FDCAN_TxHeaderTypeDef TxHeader;	
	TxHeader.Identifier = can_id;                         /* 32位ID */         
TxHeader.IdType = FDCAN_STANDARD_ID;					  /* 标准ID */
  TxHeader.TxFrameType = FDCAN_DATA_FRAME;		     	  /* 数据帧 */
  TxHeader.DataLength = FDCAN_DLC_BYTES_8;				 	/* 数据长度 */
  TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
  TxHeader.BitRateSwitch = FDCAN_BRS_ON;			   //用于设置发送是否波特率可变。
  TxHeader.FDFormat = FDCAN_CLASSIC_CAN;				//普通can还是FDcan
  TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;		
//是用于设置发送事件FIFO控制事件是否保存
  TxHeader.MessageMarker = 0;											
//marker++;	//用于设置复制到TX EVENT FIFO的消息Maker,来识别消息状态,范围0到0xFF
  HAL_FDCAN_AddMessageToTxFifoQ(&hcan, &TxHeader, tx_buff); //发送
}	
void CAN_Receive_Msg(void)
{  /*函数的作用是获取CAN接收FIFO的填充级别。*/
  if(HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1,FDCAN_RX_FIFO0) >= 1)
{
			/*从Rx FIFO 收取一个 CAN 帧*/
if(HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &CANRec_msg, CANRec_buff) == HAL_OK )	{
		  CANRecFlag = 1;//接收完成标志位
		}				
}					
}
在数函数中全局变量
uint8_t CANRecFlag ;     //CAN接收表示,完成 1 
uint8_t CANRec_buff[8];  //CAN接受到的数据位
FDCAN_RxHeaderTypeDef CANRec_msg;   
在Main.h中extern
extern uint8_t CANRecFlag ;
extern uint8_t CANRec_buff[8];
extern FDCAN_RxHeaderTypeDef CANRec_msg;   

3.3CAN.h函数声明

void CAN_Send_Msg(FDCAN_HandleTypeDef hcan,uint32_t can_id,uint8_t tx_buff[]);
void CAN_Receive_Msg(void);
void Power_ON_Start_TX(void);

3.4CAN测试函数

void Power_ON_Start_TX(void)
{
  FDCAN_TxHeaderTypeDef TxHeader;
  uint8_t tx_data[8] = {0x20, 0x23, 0x11, 0x09, 0x12, 0x20, 0x00, 0x00};
  uint32_t can_id = 0x601;  
TxHeader.Identifier = can_id;
TxHeader.IdType = FDCAN_STANDARD_ID;
  TxHeader.TxFrameType = FDCAN_DATA_FRAME;
  TxHeader.DataLength = FDCAN_DLC_BYTES_8;
  TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
  TxHeader.BitRateSwitch = FDCAN_BRS_ON; 				//用于设置发送是否波特率可变。
  TxHeader.FDFormat = FDCAN_CLASSIC_CAN;				//普通can还是FDcan
  TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;		
//是用于设置发送事件FIFO控制事件是否保存
  TxHeader.MessageMarker = 0;											
//marker++;	//用于设置复制到TX EVENT FIFO的消息Maker,来识别消息状态,范围0到0xFF
HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, tx_data);//向 Tx 邮箱中增加一个消息,并且激活对应的传输请求
  }

在#include "stm32g4xx_it.c文件中添加CAN头文件和TIM2中断函数中添加接收函数

#include "fdcan.h"
int TickMs = 0;
void TIM2_IRQHandler(void)
{
  /* USER CODE BEGIN TIM2_IRQn 0 */
  /* USER CODE END TIM2_IRQn 0 */
  HAL_TIM_IRQHandler(&htim2);
  /* USER CODE BEGIN TIM2_IRQn 1 */
CAN_Receive_Msg();//FDCAN接收函数
  TickMs++;//定时器计数
  /* USER CODE END TIM2_IRQn 1 */
}

主函数Main.c中while中添加如下代码

//while前
HAL_TIM_Base_Start_IT(&htim2);	          //定时器 中断
Power_ON_Start_TX();						//上电后发送一串数据	
//While中
if (CANRecFlag)
{
CANRecFlag = 0;
CAN_Send_Msg(hfdcan1,CANRec_msg.Identifier,CANRec_buff);
//接收到数据网CAN上位机反馈一下
}

3.5CAN测试效果

上电程序运行,会发送一组数据 20 23 11 09 12 20 00 00,然后上位机发送数据后会将发送的数据进行反馈给上位机,表示数据接收成功。

can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

3.6USART接收

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART1)             /* 如果是串口1 */
    {
        if ( UART_RX_Cont  < USART1_MAX_REC_LENGTH)
        {
            UART1_Rx_Buff[UART_RX_Cont] = UART1_Rx_Temp[0];
            UART_RX_Cont++;
        }
        /* 接收完成后在开启写一次中断接收 */
        HAL_UART_Receive_IT(&huart1, (uint8_t *)UART1_Rx_Temp, RXBUFF_ERSIZE);
    }
}



串口相关定义
/* USER CODE BEGIN 0 */
uint16_t UART1_Rx_Sta=0;               					/* 接收状态 */
uint8_t UART1_Rx_Buff[USART1_MAX_REC_LENGTH];           /* HAL库使用的串口接收缓冲 */
uint8_t UART1_Rx_Temp[RXBUFF_ERSIZE];         			/* 接收字节 */      
uint32_t UART_RX_Cont = 0;
UART_HandleTypeDef huart1;
/* USER CODE END 0 */



usart.h声明
#include "stdio.h"
int fputc(int ch, FILE *f);
#define RXBUFF_ERSIZE    1 /* 缓存大小 */
#define USART1_MAX_REC_LENGTH   64*1024          /* 定义最大接收字节数 200 */
extern uint8_t  UART1_Rx_Buff[USART1_MAX_REC_LENGTH];  	
/* 接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 */
extern uint16_t UART1_Rx_Sta;   							/* 接收状态标记 */  
extern uint8_t UART1_Rx_Temp[RXBUFF_ERSIZE];    			/* HAL库USART接收Buffer */
extern uint32_t UART_RX_Cont ;            /* 接收到的app代码长度 */



Main.c
uint32_t lastcount = 0;                     /* 上一次串口接收数据值 */
uint32_t applenth = 0;                      /* 接收到的app代码长度 */
printf("STM32_CAN_UASRT_IAP_End\n");	  //程序串口运行发送STM32_CAN_UASRT_IAP_End



 在usart.c中MX_USART1_UART_Init函数中,打开中断接收
 /* USER CODE BEGIN USART1_Init 2 */
	HAL_UART_Receive_IT(&huart1, (uint8_t *)UART1_Rx_Temp, RXBUFF_ERSIZE);//开启中断
  /* USER CODE END USART1_Init 2 */

3.7Printf重映射函

int fputc(int ch, FILE *f)
{
 uint8_t temp[1] = {ch};
 HAL_UART_Transmit(&huart1, temp, 1, 0xffff);
return ch;
}

3.8USART主函数测试

/*----------串口接收1--------------------------------------*/		
 if (UART_RX_Cont)
    {
       if (lastcount == UART_RX_Cont)   /* 新周期内,没有收到任何数据,认为本次数据接收完成 */
         {
            applenth = UART_RX_Cont;
            lastcount = 0;
            UART_RX_Cont = 0;
          for(int i = 0; i<applenth;i++)
			{
printf("UART1_Rx_Buff[%d] == %x \r\n",i,UART1_Rx_Buff[i]);
			}
                printf("用户程序接收完成!\r\n");
                printf("代码长度:%dBytes\r\n", applenth);
            }
            else lastcount = UART_RX_Cont;
        }

3.9USART测试效果

上电发送STM32_CAN_UASRT_IAP_End,通过上位机发送数据,将数据每一位转化成ascll码后16进制进行打印,发送的bin文件的所有数据都存放在UART1_Rx_Buff的数组中can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

4.Flash写入数据

FLASH写入数据是引用封装后的函数,在文件夹can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言文件中的can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言文件可以直接添加到工程中,并添加头文件的路径。使用时直接引用相关函数。

5.跳转至APP函数(即IAP)

跳转函数同样也是引用的封装好的函数,在跳转函数中做了一些改进,可以直接添加工程中直接进行使用can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

注意:由于是因引用的封装后的库,建议将can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

中的所有源文件(.c)和头文件(.h)同时添加到工程文件中。

到此部分Bootloader 程序的CAN通讯和usart通讯已经完成(接收升级固件包数据)

下面程序的升级逻辑介绍

6. Bootloader 程序

升级程序流程图如下

can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

7.Main程序代码

函数声明部分
/* USER CODE BEGIN Includes */
#include "sys.h"
#include "delay.h"
#include "iap.h"
uint8_t CANRecFlag ;     //CAN接收表示,完成 1 
uint8_t CANRec_buff[8];  //CAN接受到的数据位
FDCAN_RxHeaderTypeDef CANRec_msg;   

#define APP_LEN_MAX 60*1024
uint8_t APP_RX_BUF[APP_LEN_MAX] = {0};  //接收缓冲,最大64kb.
uint32_t APPLength = 0;									//升级程序数据长度
extern int TickMs;
/* USER CODE END Includes */


Bootloader程序升级或跳转选择while 
 /* USER CODE BEGIN 2 */
	 //delay_init(170);                //若有报错打开注释
	 uint32_t lastcount = 0;                      /* 上一次串口接收数据值 */
      uint32_t applenth = 0;                      /* 接收到的app代码长度 */
	 HAL_TIM_Base_Start_IT(&htim2);	           //定时器 中断
	 Power_ON_Start_TX();				      //上电后发送一串数据	
	 printf("STM32_IAP_02\n");	 
	while(1)
    {    
	  if(TickMs>5000)//5秒后跳转
	  {
		if (((*(volatile uint32_t *)(FLASH_APP1_ADDR + 4)) & 0xFF000000) == 0x08000000)
 /* 判断FLASH里面是否有APP,有的话执行 */
		{
		 printf("开始执行FLASH用户代码!!\r\n\r\n");
		 printf("RUN addr :%x \r\n",(*(volatile uint32_t *)(FLASH_APP1_ADDR )));
		 iap_load_app(FLASH_APP1_ADDR);       /* 执行FLASH APP代码 */					}else{
			printf("非APP———FLASH应用程序,无法执行!\r\n");
			printf("进入主程序等待升级!!\r\n");
			break;
			 }
	 }
	if(CANRecFlag)//升级命令CAN口有接收任意消息
	{
		CANRecFlag = 0;
		printf("进入主程序等待升级!!\r\n");
	break;
	  }
    }
  /* USER CODE END 2 */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
/*-----------串口接收数据处理--------------------------------------*/		
if (UART_RX_Cont)
     {
        if (lastcount == UART_RX_Cont)   /* 新周期内,没有收到任何数据,认为本次数据接收完成 */
        {
                applenth = UART_RX_Cont;
                lastcount = 0;
                UART_RX_Cont = 0;
//		for(int i = 0; i<applenth;i++)
//		{
//		  printf("UART1_Rx_Buff[%d] == %x \r\n",i,UART1_Rx_Buff[i]);
//	     }
          printf("用户程序接收完成!\r\n");
          printf("代码长度:%dBytes\r\n", applenth);
        }
            else lastcount = UART_RX_Cont;
     }
/*----------CAN升级命令------------------------------*/
if (CANRecFlag)
	{
	  CANRecFlag = 0;
//	CAN_Send_Msg(hfdcan1,CANRec_msg.Identifier,CANRec_buff);
//接收到数据网CAN上位机反馈一下
	  if (CANRec_msg.Identifier ==0x123) //升级指令包
	{
		  if (applenth < APP_LEN_MAX)
			{
		     	for(int i=0 ;i < 8;i++)
				{
			     	APP_RX_BUF[applenth] = CANRec_buff[i];
					applenth++;
				}
			 }else{	
				printf("缓冲区容量不足...\r\n");
				}
			}
	 if (CANRec_msg.Identifier ==0x111) 
		{
	     	printf("APP_RX_BUF[%d] \n",applenth);//APPLength
//		  for(int i = 0; i<applenth;i++)
//			{
//		     	printf("APP_RX_BUF[%d] = %x\n",i,APP_RX_BUF[i]);
//			}
	 if(applenth)
		{
/*-------------串口升级----------------------------------------------------------------------*/			  if (((*(volatile uint32_t *)(UART1_Rx_Buff + 4)) & 0xFF000000) == 0x08000000)  
/* 判断是否为0X08XXXXXX */
		{
		  printf(" \n串口升级开始更新固件...\r\n");
		printf("updata addr :%x \r\n",(*(volatile uint32_t *)(UART1_Rx_Buff + 4)) & 0xFF000000);		iap_write_appbin(FLASH_APP1_ADDR, UART1_Rx_Buff, applenth);/* 更新FLASH代码 */		printf("UPdata addr :%x \r\n",(*(volatile uint32_t *)(FLASH_APP1_ADDR )));         			printf("\n!!!串口升级固件更新完成!!!\r\n");						
		 }
/*---------------CAN升级-------------------------------------------------------------------*/		
		else if (((*(volatile uint32_t *)(APP_RX_BUF + 4)) & 0xFF000000) == 0x08000000)  
/* 判断是否为0X08XXXXXX */
		{					
		printf(" \nCAN升级开始更新固件...\r\n");										
		printf("updata addr :%x \r\n",(*(volatile uint32_t *)(APP_RX_BUF + 4)) & 0xFF000000);		iap_write_appbin(FLASH_APP1_ADDR, APP_RX_BUF, applenth);	/* 更新FLASH代码 */		printf("UPdata addr :%x \r\n",(*(volatile uint32_t *)(FLASH_APP1_ADDR )));         			printf("\n!!!固件更新完成!!!\r\n");
		}else{		
			printf("\n!!!非FLASH应用程序!!!\r\n");
	     }
		}else{
            printf("没有可以更新的固件!\r\n");
            }
		}
	if (CANRec_msg.Identifier == 0x222)   /* KEY1按键按下, 运行FLASH APP代码 */
	{
	  printf("flash Run addr :%x \r\n",(*(volatile uint32_t *)(FLASH_APP1_ADDR + 4)) & 0xFF000000);
	   if (((*(volatile uint32_t *)(FLASH_APP1_ADDR + 4)) & 0xFF000000) == 0x08000000) /* 判断FLASH里面是否有APP,有的话执行 */
		{
		  printf("开始执行FLASH用户代码!!\r\n\r\n");
			printf("RUN addr :%x \r\n",(*(volatile uint32_t *)(FLASH_APP1_ADDR )));
			iap_load_app(FLASH_APP1_ADDR);/* 执行FLASH APP代码 */						}else	{
			printf("没有可以运行的固件!\r\n");
			}
		}
	}
/*-------------------Bootloader LED反馈--------------------------------------------------*/
		HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_1|GPIO_PIN_0);
		HAL_Delay(500);
  }
  /* USER CODE END 3 */

8.升级效果

8.1CAN升级效果

can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言

8.2USART升级效果

通过Ecantools发送更新,升级指令实现升级跳转程序

can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言文章来源地址https://www.toymoban.com/news/detail-777211.html

9.注意事项

  1. CAN接受数据时不可有延时出现,否则会影响数据接收
  2. CAN通过上位机发送bin文件数据或其他方式发送文件数据时,需要注意发送每一帧数据的时间间隔的控制,否则会出现数据丢帧,不能接收完整的数据包。
  3. 配置APP程序和Bootloader程序时,需要注意起始地址和空间大小,具体看程序的大小从而进行配置。本程序的起始控件大小如下can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言can 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言
  4. 程序中有Printf重映射相关的函数,需要将上图中的           use MicroLIBcan 升级 固件 stm32,stm32,单片机,嵌入式硬件,c语言勾选上

到了这里,关于STM32G473 固件升级IAP(BootLoader)CAN/USART。(详细步骤)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【STM32】IAP升级01 bootloader实现以及APP配置(主要)

    通过之前的了解 之前的了解,我们知道实现IAP升级需要两个条件: 1.APP程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始; 2.APP程序的中断向量表相应的移动,移动的偏移量为 x; 默认条件下的起始地址 默认的条件下,图中 IROM1 的起始地址(Start)一般为 0x08000000,大小

    2024年02月03日
    浏览(44)
  • 基于STM32单片机BOOTLOADER通过串口升级程序IAP——APP方案

                            此方法前提是你得有一个EEPROM         我用的单片机是STM32F103ZET6 , 此单片机FLASH容量为512KB; 在此单片机里面FLASH的起始地址是0X8000000,BOOT作为引导加载程序一般都是从这个地址开始,单片机一上点默认会从这个地址开始运行,所以将自己

    2024年02月04日
    浏览(56)
  • STM32F0实现IAP升级固件

    好几年前写过一篇关于 STM32 bootloader 升级固件的博客,但是使用的芯片是 STM32 F4 系列,升级固件的方式是在外部 flash 的 fat32 文件系统中存入固件文件,reset 后通过特定按键进入 IAP 程序。 最近需要在 STM32 上实现同样的 IAP 功能,但是方式不太一样,也发现一些芯片的差别,

    2024年02月14日
    浏览(42)
  • STM32单片机实现固件在线升级(IAP)

    单片机的固件升级方式有很多种, 1、ICP:In Circuit Programing,简单说就是在单片机开发时使用烧录器升级程序,比如使用J-Link烧录单片机程序。 2、ISP:In System Programing,在单片机内部实现了基于通信接口(如串口、I2C、SPI等等)的FLASH引导程序,配合厂家提供的烧录软件工具

    2024年02月13日
    浏览(52)
  • bootloader编写——MCU固件升级系列2(STM32)

    本系列将从升级流程、boot代码编写、APP代码编写以及固件打包来介绍,硬件选用STM32F407ZGT6(手里只有),来完成这系列教程。 开发STM32固件升级并编写Bootloader时,需要注意以下几个关键点: 熟悉硬件和数据手册:在开发过程中,确保充分理解STM32微控制器的特性和功能。阅

    2024年02月03日
    浏览(53)
  • STM32 IAP应用开发——通过USB实现固件升级

    什么是IAP? IAP(In-Application Programming) 指MCU可以在系统中获取新代码并对自己重新编程,即可用程序来改变程序。在应用编程(IAP)是用户的应用代码对片内Flash存储器进行擦除/编程的方法。这种方式的典型应用就是用一小段代码来实现程序的下载,实际上单片机的ISP功能就

    2024年02月12日
    浏览(44)
  • 调试笔记-stm32的OTA/IAP 通过485升级固件

    背景:最近需要在stm32上实现通过rs485升级固件功能。经过几天搜索和调试,实现了功能。 目标:使用cubeIDE实现stm32F407VGT6,通过RS485升级固件 调试记录: 步骤1. 在keil环境下的rs485升级固件(含源码):STM32 OTA应用开发——通过串口/RS485实现OTA升级(方式2)_stm32串口升级_柒壹漆

    2024年02月11日
    浏览(48)
  • STM32 IAP应用开发——通过串口/RS485实现固件升级(方式1)

    什么是IAP? IAP(In-Application Programming) 指MCU可以在系统中获取新代码并对自己重新编程,即可用程序来改变程序。在应用编程(IAP)是用户的应用代码对片内Flash存储器进行擦除/编程的方法。这种方式的典型应用就是用一小段代码来实现程序的下载,实际上单片机的ISP功能就

    2024年02月10日
    浏览(40)
  • STM32 IAP应用开发——通过串口/RS485实现固件升级(方式2)

    什么是IAP? IAP(In-Application Programming) 指MCU可以在系统中获取新代码并对自己重新编程,即可用程序来改变程序。在应用编程(IAP)是用户的应用代码对片内Flash存储器进行擦除/编程的方法。这种方式的典型应用就是用一小段代码来实现程序的下载,实际上单片机的ISP功能就

    2024年02月14日
    浏览(45)
  • STM32 usart bootloader 源代码 STM32 usart bootloader 源代码 STM32 usart bootloader 原代源码

    STM32 usart bootloader 源代码   STM32 usart bootloader 源代码  STM32 usart bootloader 原代源码,上位机C#,下位机c。 简单修改可以支持stm32全系列芯片。 支持串口升级 该版本为优化过的版本, 1.支持代码段保护; 2.支持烧写失败重置; 3.兼容我公司生产的配套wifi模块和w5500模块远程更新

    2024年01月23日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包