0 引言
在上一篇文章中,我们已经讲述了STM32的启动流程、IAP的原理和OTA的原理(最后这部分直接分享了一些博客,因为前辈们已经写的非常好了),下面这篇主要用来记录STM32-OTA的实验步骤。源码我大家自行下载即可。
链接:https://pan.baidu.com/s/1uemqEqDNI3-IjulZ4oNFlw?pwd=of3g
提取码:of3g
参考:
- STM32CubeMx开发之路—在线升级OTA
1 实验条件
1.1 大家需要准备:
- STM32F103开发板(我是采用正点的精英版)
- USB-TTL转化器(查看串口数据)
- XShell(程序中需要用Ymodem协议进行传输)
1.2 程序叙述:
我们需要编写3个程序,即新建三个keil工程(分别为bootloader,app1, app2),并把app2转成bin文件(其用作新文件)
1.3 FLASH分配:
F103的FALSH共512K,bootloader使用100K, app1:200K,app2:200K,private: 12K(主要用于存放升级标志位),升级标志位存放在 0x0807F800(最后一页的开始)。
本实验的FLASH设计:
name | 起始地址 | size |
---|---|---|
boot | 0x08000000 | 0x00019000 (100K) |
app1 | 0x08019000 | 0x00032000 (200K) |
app2 | 0x0804B000 | 0x00032000 (200K) |
private | 0x0807D000 | 0x00003000 (12K) |
2 bootloader程序设计
本实验中的bootloader主要用于判断是否有更新标志,如果有,则将app2中的程序拷贝到app1中,并实现中断向量表的跳转。
2.1 STM32CubeMX程序设计
在STM32CubeMX中进行初始化设置,外设的话只需要开USART串口(并开启全局中断)即可:
2.2 在keil中编程程序
只需要修改main函数中的程序,同时新建f103Flash.c和f103Flash.h文件即可。
2.2.1 main.c文件设计
1、 串口1重定向:
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF); //阻塞方式打印,串口1
return ch;
}
2、 升级标志位
读取FLASH中标志位的信息:
- 如果为0xAAAAAAAAAAAAAAAA,则系统有需要升级的程序,需要将FLASH中的程序从app2拷贝到app1,拷贝完成后中断向量表实现跳转app1;
- 如果没有升级,则中断向量表直接跳转至app1;
uint64_t Upgrade_Flag;//升级标志
uint64_t NotUpgrade = {0xFFFFFFFFFFFFFFFF};
uint64_t Upgrade = {0xAAAAAAAAAAAAAAAA};
中断向量表跳转的app1的程序:
#define FLASH_APP_ADDR (0x08019000)//APP存放起始地址
typedef void (*pFunction)(void);
pFunction Jump_To_Application;
void Jump_to_APP(uint32_t app_addr)
{
uint32_t JumpAddress;
printf("jump to app: %#x\r\n", FLASH_APP_ADDR);
/* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
if (((*(volatile uint32_t*)FLASH_APP_ADDR) & 0x2FFE0000 ) == 0x20000000) // 上面的值是取出FLASH_APP_ADDR里面的值来
{
__set_PRIMASK(1); //关总中断 0打开总中断
JumpAddress = *(volatile uint32_t*) (FLASH_APP_ADDR + 4);
Jump_To_Application = (pFunction) JumpAddress;
printf("jump %#x success \r\n",FLASH_APP_ADDR);
__set_MSP(*(volatile uint32_t*) FLASH_APP_ADDR); //设置SP指针,复位指针
Jump_To_Application(); //开始跳转
}
else
{
printf( "erorr [0x%08x]\r\n",(*(volatile uint32_t*)FLASH_APP_ADDR) );
}
}
3、将数据从app2拷贝到app1中:
uint64_t u64_Code_Buff[256];//2k
uint16_t Num = 0;
void Copy_APP2_to_APP1(void)
{
uint32_t APP1_Addr;
uint32_t APP2_Addr;
APP1_Addr = FLASH_APP1_START_ADDR;
APP2_Addr = FLASH_APP2_START_ADDR;
MyErase_page(FLASH_APP1_START_ADDR, APP1_PAGE_SUM);//擦除APP1所在地址
printf("Erase app1 flash\r\n");
/*APP2拷贝到APP1*/
for(int i=0;i<40;i++) //一次拷贝2048个字节, 2K字节,限制APP程序最多80KB
{
printf("APP1_Addr = %x\r\n",APP1_Addr);
printf("APP2_Addr = %x\r\n",APP2_Addr);
MyRead64Flash (APP2_Addr, u64_Code_Buff, 256); //读2k APP2 flash内容
HAL_Delay(10);
MyWrite64Flash(APP1_Addr, u64_Code_Buff, 256); //将从app2 flash读取到的2k程序写入到app1 flash中
HAL_Delay(10);
APP1_Addr += 0x800;
APP2_Addr += 0x800;
/*清空缓存*/
memset(u64_Code_Buff,0,sizeof(u64_Code_Buff));
Num = i;
if(flash_flag == 0)
{
printf("Successfully copied page %d\r\n",Num);
}
else
{
printf("Copy failed page %d\r\n",Num);
}
}
MyErase_page(FLASH_APP2_START_ADDR, APP2_PAGE_SUM);//拷贝结束,擦除APP2所在地址
InFlashWrite(UPGRADE_FLAG_ADDR, NotUpgrade); //将升级标志设置为不升级
}
4、main函数中功能为:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("bootloader running\r\n");
print_boot_message();
Upgrade_Flag = FLASH_R(UPGRADE_FLAG_ADDR);
printf("Upgrade_Flag = %llx\r\n", Upgrade_Flag);
if(NotUpgrade == Upgrade_Flag)
{
printf("------%#x", (FLASH_APP_ADDR+4)&0xFF000000);
/*跳转到APP1中运行*/
if(((FLASH_APP_ADDR+4)&0xFF000000)==0x08000000) //Judge if start at 0X08XXXXXX.
{
printf("jump APP1 running\r\n");
Jump_to_APP(FLASH_APP_ADDR); // Jump to APP
}
}
if(Upgrade == Upgrade_Flag)
{
/*将APP2拷贝到APP1中*/
Copy_APP2_to_APP1();
printf("------%#x", (FLASH_APP_ADDR+4)&0xFF000000);
if(((FLASH_APP_ADDR+4)&0xFF000000)==0x08000000) //Judge if start at 0X08XXXXXX.
{
printf("jump APP2 running\r\n");
Jump_to_APP(FLASH_APP_ADDR); // Jump to APP
}
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
5、main.c的整体代码如下所示:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "f103Flash.h"
#include "string.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF); //阻塞方式打印,串口1
return ch;
}
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint64_t Upgrade_Flag;//升级标志
uint64_t NotUpgrade = {0xFFFFFFFFFFFFFFFF};
uint64_t Upgrade = {0xAAAAAAAAAAAAAAAA};
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
// ******************************** 定义初始函数 ***********************************//
#define FLASH_APP_ADDR (0x08019000)//APP存放起始地址
typedef void (*pFunction)(void);
pFunction Jump_To_Application;
void Jump_to_APP(uint32_t app_addr)
{
uint32_t JumpAddress;
printf("jump to app: %#x\r\n", FLASH_APP_ADDR);
/* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
if (((*(volatile uint32_t*)FLASH_APP_ADDR) & 0x2FFE0000 ) == 0x20000000) // 上面的值是取出FLASH_APP_ADDR里面的值来
{
__set_PRIMASK(1); //关总中断 0打开总中断
JumpAddress = *(volatile uint32_t*) (FLASH_APP_ADDR + 4);
Jump_To_Application = (pFunction) JumpAddress;
printf("jump %#x success \r\n",FLASH_APP_ADDR);
__set_MSP(*(volatile uint32_t*) FLASH_APP_ADDR); //设置SP指针,复位指针
Jump_To_Application(); //开始跳转
}
else
{
printf( "erorr [0x%08x]\r\n",(*(volatile uint32_t*)FLASH_APP_ADDR) );
}
}
void print_boot_message(void)
{
// private中的最后一个字节存放是否升级的标志位
printf("---------- Enter BootLoader ----------\r\n");
printf("\r\n");
printf("======== flash pration table =========\r\n");
printf("| name | start addr | size |\r\n");
printf("--------------------------------------\r\n");
printf("| boot | 0x08000000 | 0x00019000 |\r\n");
printf("| app1 | 0x08019000 | 0x00032000 |\r\n");
printf("| app2 | 0x0804B000 | 0x00032000 |\r\n");
printf("| private | 0x0807D000 | 0x00003000 |\r\n");
printf("======================================\r\n");
}
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/************************************************************
*函数名:Copy_APP2_to_APP1
*功 能:APP2拷贝到APP1地址中
*参 数:无
*返回值:无
*************************************************************/
uint64_t u64_Code_Buff[256];//2k
uint16_t Num = 0;
void Copy_APP2_to_APP1(void)
{
uint32_t APP1_Addr;
uint32_t APP2_Addr;
APP1_Addr = FLASH_APP1_START_ADDR;
APP2_Addr = FLASH_APP2_START_ADDR;
MyErase_page(FLASH_APP1_START_ADDR, APP1_PAGE_SUM);//擦除APP1所在地址
printf("Erase app1 flash\r\n");
/*APP2拷贝到APP1*/
for(int i=0;i<40;i++) //一次拷贝2048个字节, 2K字节,限制APP程序最多80KB
{
printf("APP1_Addr = %x\r\n",APP1_Addr);
printf("APP2_Addr = %x\r\n",APP2_Addr);
MyRead64Flash (APP2_Addr, u64_Code_Buff, 256); //读2k APP2 flash内容
HAL_Delay(10);
MyWrite64Flash(APP1_Addr, u64_Code_Buff, 256); //将从app2 flash读取到的2k程序写入到app1 flash中
HAL_Delay(10);
APP1_Addr += 0x800;
APP2_Addr += 0x800;
/*清空缓存*/
memset(u64_Code_Buff,0,sizeof(u64_Code_Buff));
Num = i;
if(flash_flag == 0)
{
printf("Successfully copied page %d\r\n",Num);
}
else
{
printf("Copy failed page %d\r\n",Num);
}
}
MyErase_page(FLASH_APP2_START_ADDR, APP2_PAGE_SUM);//拷贝结束,擦除APP2所在地址
InFlashWrite(UPGRADE_FLAG_ADDR, NotUpgrade); //将升级标志设置为不升级
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("bootloader running\r\n");
print_boot_message();
Upgrade_Flag = FLASH_R(UPGRADE_FLAG_ADDR);
printf("Upgrade_Flag = %llx\r\n", Upgrade_Flag);
if(NotUpgrade == Upgrade_Flag)
{
printf("------%#x", (FLASH_APP_ADDR+4)&0xFF000000);
/*跳转到APP1中运行*/
if(((FLASH_APP_ADDR+4)&0xFF000000)==0x08000000) //Judge if start at 0X08XXXXXX.
{
printf("jump APP1 running\r\n");
Jump_to_APP(FLASH_APP_ADDR); // Jump to APP
}
}
if(Upgrade == Upgrade_Flag)
{
/*将APP2拷贝到APP1中*/
Copy_APP2_to_APP1();
printf("------%#x", (FLASH_APP_ADDR+4)&0xFF000000);
if(((FLASH_APP_ADDR+4)&0xFF000000)==0x08000000) //Judge if start at 0X08XXXXXX.
{
printf("jump APP2 running\r\n");
Jump_to_APP(FLASH_APP_ADDR); // Jump to APP
}
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
2.2.2 f103flash.c文件设计
此文件重要用于对FALSH操作的整合,对HAL库提供的函数进行了封装,其设计如下所示:
#include "f103Flash.h"
#include "stdint.h"
#include "main.h"
#include "stdio.h"
#include "string.h"
/*获取当前地址所在页*/
static uint32_t GetPage(uint32_t Addr)
{
return (Addr - FLASH_BASE) / FLASH_PAGE_SIZE;
}
/***********************************************
*函数名:MyErase_page
*功 能:指定擦除页数
*参 数:pageaddr:擦除的起始页
*参 数:num: 擦除的页数
*返回值:1
*************************************************/
int MyErase_page(uint32_t pageaddr, uint32_t num)
{
uint32_t FirstPage = 0;//擦除的地址首页
uint32_t PageError = 0;
printf("FirstPage = %d\r\n", FirstPage);
printf("Page num = %d\r\n", num);
HAL_FLASH_Unlock();
/* 擦除FLASH*/
FLASH_EraseInitTypeDef FlashSet;
FlashSet.TypeErase = FLASH_TYPEERASE_PAGES;
FlashSet.PageAddress = pageaddr;
FlashSet.NbPages = num;
/*设置PageError,调用擦除函数*/
if(HAL_FLASHEx_Erase(&FlashSet, &PageError) == HAL_OK)
{
printf("Erase Page Success\r\n");
}
else
{
printf("Erase Page Error\r\n");
}
HAL_FLASH_Lock();
return 1;
}
//不定长按读8字节读取flash中的值
/***********************************************
*函数名:MyRead64Flash
*功 能:指定地址读取不定长字符串
*参 数:addr :指定读取的起始地址
*参 数:buff :存储读取数据的字符串首地址
*参 数:word_size :读取的数组长度
*返回值:无
*************************************************/
void MyRead64Flash(uint32_t addr, uint64_t * buff, uint16_t word_size)
{
for(int i =0; i < word_size; i++)
{
buff[i] = *(__IO uint64_t*)(addr + 8 * i);
}
return;
}
/***************************************************************
*函数名:FLASH_R
*功 能:指定内部FLASH地址读出uint64_t数据
*参 数:64位读出FLASH地址。
*返回值:一个uint64_t数据
*******************************************************************/
uint64_t FLASH_R(uint32_t add)//参数1:32位读出FLASH地址。返回值:16位数据
{
uint64_t a;
a = *(uint64_t*)(add);//从指定页的addr地址开始读
return a;
}
//写flash
//参数:addr:写入地址;buff:单字节数组;word_size:写入数组长度
/***********************************************
*函数名:My_Write_Flash
*功 能:指定地址写入不定长uint64_t数组
*参 数:addr :指定写入的起始地址
*参 数:buff :写入的uint64_t数组首地址
*参 数:double_word_size :写入的数组长度
*返回值:无
*************************************************/
uint8_t flash_flag = 0;
void MyWrite64Flash(uint32_t addr, uint64_t * buff, int double_word_size)
{
HAL_FLASH_Unlock();
//__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);//清除标志位
for(int i = 0; i < double_word_size; i++)
{
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (uint32_t)(uint64_t *)addr + (8 * i), buff[i]) == HAL_OK)
{
flash_flag = 0;
}
else
{
printf("Flash write failed, Start rewriting \r\n");
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (uint32_t)(uint64_t *)addr + (8 * i), buff[i]) == HAL_OK)
{
}
else
{
flash_flag = 1;
printf("Flash Rewrite failed\r\n");
return ;
}
}
}
HAL_FLASH_Lock();
}
/******************************************************
*
*功能:指定地址写入一个双字(uint64_t)内部FLASH
*****************************************************/
void InFlashWrite(uint32_t Address, uint64_t data)
{
HAL_FLASH_Unlock();//开锁
uint32_t FirstPage = 0, NbOfPages = 0;
uint32_t PageError = 0;
FLASH_EraseInitTypeDef EraseInitStruct;
FirstPage = GetPage(Address);//首页地址
printf("FirstPage = %d\r\n", FirstPage);
NbOfPages = GetPage(Address+sizeof(data)) - FirstPage + 1;//页数
printf("NbOfPages = %d\r\n", NbOfPages);
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.PageAddress = Address;
EraseInitStruct.NbPages = NbOfPages;
if (HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) != HAL_OK)
{
printf("ErasePageError\r\n");
}else{
printf("ErasePageSuccess\r\n");
}
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, Address, data) == HAL_OK)
{
printf("Flash write success\r\n");
}
HAL_FLASH_Lock();//上锁
}
2.2.3 f103flash.h文件设计:
#ifndef __STM32G0FLASH_H__
#define __STM32G0FLASH_H__
#include "main.h"
#define STM32_FLASH_SIZE 512 //512k flash
#define FLASH_START_ADDR (0x08000000) //FLASH起始地址
/*APP1地址分配,大小0x32000 100页 200k*/
#define FLASH_APP1_START_ADDR (0x08019000) //APP1 FLASH起始地址,FLASH大小0x32000
#define FLASH_APP1_END_ADDR ((0x08019000)+(0x32000)) //APP1 FLASH结束地址
#define APP1_PAGE_SUM (0x32000/0x800) //APP1存储空间页数
/*APP2地址分配,大小0x32000 100页 200k*/
#define FLASH_APP2_START_ADDR (0x0804B000) //APP2 FLASH起始地址,FLASH大小0x32000
#define FLASH_APP2_END_ADDR ((0x0804B000)+(0x32000)) //APP2 FLASH结束地址
#define APP2_PAGE_SUM (0x32000/0x800) //APP2存储空间页数
/*自定义区地址分配,大小0x3000 6页 12k*/
#define FLASH_PERSONAL_START_ADDR (0x0807D000) //自定义区FLASH起始地址,FLASH大小0x3000
#define FLASH_PERSONAL_END_ADDR ((0x0807D000)+(0x3000)) //自定义区FLASH结束地址
#define PERSONAL_PAGE_SUM (0x3000/0x800) //自定义区存储空间页数
/*最后一页的第一个数据存放升级标志Upgrade_Flag 0x0807D000 + 0x2800 */
#define UPGRADE_FLAG_ADDR ((uint32_t)0x0807F800)
#define RAM_START_ADDR (0x20000000)
extern uint8_t flash_flag;
/***********************************************
*函数名:MyErase_page
*功 能:指定擦除页数
*参 数:pageaddr:擦除的起始页
*参 数:num: 擦除的页数
*返回值:1
*************************************************/
int MyErase_page(uint32_t pageaddr, uint32_t num);//
/***********************************************
*函数名:MyWrite_Flash
*功 能:指定地址写入不定长字符串
*参 数:addr :指定写入的起始地址
*参 数:buff :写入的字符串首地址
*参 数:word_size :写入的数组长度
*返回值:无
*************************************************/
//void MyWrite_Flash(uint32_t addr, unsigned char * buff, int word_size);//不定长指定地址写入flash
/***********************************************
*函数名:MyRead64Flash
*功 能:指定地址读取不定长字符串
*参 数:addr :指定读取的起始地址
*参 数:buff :存储读取数据的字符串首地址
*参 数:word_size :读取的数组长度
*返回值:无
*************************************************/
void MyRead64Flash(uint32_t addr, uint64_t * buff, uint16_t word_size);
/***************************************************************
*函数名:FLASH_R
*功 能:指定内部FLASH地址读出uint64_t数据
*参 数:64位读出FLASH地址。
*返回值:一个uint64_t数据
*******************************************************************/
uint64_t FLASH_R(uint32_t add);
//写flash
//参数:addr:写入地址;buff:单字节数组;word_size:写入数组长度
/***********************************************
*函数名:My_Write_Flash
*功 能:指定地址写入不定长uint64_t数组
*参 数:addr :指定写入的起始地址
*参 数:buff :写入的uint64_t数组首地址
*参 数:double_word_size :写入的数组长度
*返回值:无
*************************************************/
void MyWrite64Flash(uint32_t addr, uint64_t * buff, int double_word_size);
/******************************************************
*
*功能:指定地址写入一个双字(uint64_t)内部FLASH
*****************************************************/
void InFlashWrite(uint32_t Address, uint64_t data);
#endif
在烧录程序的时候一定要注意:
然后烧录进去,bootloader的程序则设计实现完成,下面进行app1程序的设计,其中要用到Ymodem协议,同时需要进行串口的DMA接收。
在串口调试助手中的运行结果如下:
3 APP1程序设计
在app1中主要实现的功能为:新bin文件的接收与中断向量表的偏移。我们需要在STM32CubeMx中新建程序:
3.1 STM32CubeMX设计:
需要开启两个串口,一个用于打印,另一个用于接收(DMA接收)
3.2 Keil程序设计:
3.2.1 main.c 函数设计
1、串口1重定向:
这个就不写了,同上。
2、中断向量表偏移:
static void VectorRemap(void)
{
__set_PRIMASK(0); // 打开全局中断
SCB->VTOR = FLASH_BASE|0x19000;
}
3、开启串口中断接收:
HAL_UART_Receive_DMA(&huart2, Rx_Buf, Rx_Max);
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
4、main.c函数整体代码:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "ymodem.h"
#define Rx_Max 2048
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch,1, 0xFFFF);
return ch;
}
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint8_t Rx_Flag = 0;
uint16_t Rx_Len = 0;
uint8_t Rx_Buf[Rx_Max] = {0};
uint8_t flag_reset = 0;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
static void VectorRemap(void)
{
__set_PRIMASK(0);
SCB->VTOR = FLASH_BASE|0x19000;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
VectorRemap();//中断向量表重映射
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_DMA(&huart2, Rx_Buf, Rx_Max);
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
printf("\r\n");
printf("***********************************\r\n");
printf("* *\r\n");
printf("* Leaf_Fruit's Application *\r\n");
printf("* *\r\n");
printf("* Version : 0.0.1 *\r\n");
printf("* *\r\n");
printf("***********************************\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
ymodem_fun();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
3.2.2 stm32f1xx_it.c设计:
这部分主要是为了配合串口接收文件设计的,在下面使用串口空闲中断接收的方法实现:
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
uint32_t temp;
if((__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE) != RESET))
{
/*清除状态寄存器和串口数据寄存器*/
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
/*失能DMA接收*/
HAL_UART_DMAStop(&huart2);
/*读取接收长度,总大小-剩余大小*/
temp = huart2.hdmarx->Instance->CNDTR;
Rx_Len = Rx_Max - temp;
/*接收标志位置1*/
Rx_Flag=1;
/*使能接收DMA接收*/
HAL_UART_Receive_DMA(&huart2,Rx_Buf,Rx_Max);
}
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
3.2.3 ymodelm.c设计
这个就是负责接收bin文件,其协议可参考文章开头的博客。
#include "ymodem.h"
#include "main.h"
#include "stdio.h"
#include "usart.h"
#include "string.h"
#define Rx_Max 2048
__STATIC_INLINE void Set_FAULTMASK(uint32_t faultMask)
{
register uint32_t __regFaultMask __ASM("faultmask");
__regFaultMask = (faultMask & (uint32_t)1U);
}
extern uint8_t Rx_Flag;
extern uint16_t Rx_Len;
extern uint8_t Rx_Buf[Rx_Max] ;
extern uint8_t flag_reset;
uint64_t NotUpgrade = {0xFFFFFFFFFFFFFFFF};
uint64_t Upgrade = {0xAAAAAAAAAAAAAAAA};
/* 发送指令 */
void send_command(unsigned char command)
{
HAL_UART_Transmit(&huart2, (uint8_t *)&command,1 , 0xFFFF);
HAL_Delay(10);
}
/**
* @bieaf 擦除页
*
* @param pageaddr 页起始地址
* @param num 擦除的页数
* @return 1
*/
static int Erase_page(uint32_t pageaddr, uint32_t num)
{
HAL_FLASH_Unlock();
/* 擦除FLASH*/
FLASH_EraseInitTypeDef FlashSet;
FlashSet.TypeErase = FLASH_TYPEERASE_PAGES;
FlashSet.PageAddress = pageaddr;
FlashSet.NbPages = num;
/*设置PageError,调用擦除函数*/
uint32_t PageError = 0;
if(HAL_FLASHEx_Erase(&FlashSet, &PageError) == HAL_OK){
printf("erase APP2 success\r\n");
}
else{
printf("erase APP2 failed\r\n");
}
HAL_FLASH_Lock();
return 1;
}
/**
* @bieaf 写若干个数据
*
* @param addr 写入的地址
* @param buff 写入数据的数组指针
* @param word_size 长度
* @return
*/
static void WriteFlash(uint32_t addr, uint32_t * buff, int word_size)
{
/* 1/4解锁FLASH*/
HAL_FLASH_Unlock();
for(int i = 0; i < word_size; i++)
{
/* 3/4对FLASH烧写*/
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + 4 * i, buff[i]);
}
/* 4/4锁住FLASH*/
HAL_FLASH_Lock();
}
/*获取当前地址所在页*/
static uint32_t GetPage(uint32_t Addr)
{
return (Addr - FLASH_BASE) / FLASH_PAGE_SIZE;
}
void InFlashWrite(uint32_t Address, uint64_t data)
{
HAL_FLASH_Unlock();//开锁
uint32_t FirstPage = 0, NbOfPages = 0;
uint32_t PageError = 0;
FLASH_EraseInitTypeDef EraseInitStruct;
FirstPage = GetPage(Address);//首页地址
printf("FirstPage = %d\r\n", FirstPage);
NbOfPages = GetPage(Address+sizeof(data)) - FirstPage + 1;//页数
printf("NbOfPages = %d\r\n", NbOfPages);
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.PageAddress = Address;
EraseInitStruct.NbPages = NbOfPages;
if (HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) != HAL_OK)
{
printf("ErasePageError\r\n");
}
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, Address, data) == HAL_OK)
{
printf("Flash write success\r\n");
}
HAL_FLASH_Lock();//上锁
}
/* 标记升级完成 */
void Set_Update_Down(void)
{
unsigned int update_flag = 0xAAAAAAAA; ///< 对应bootloader的启动步骤
WriteFlash((Application_2_Addr + Application_Size - 4), &update_flag,1 );
}
/* 临时存储的buff */
unsigned char save_buf[128] = {0};
/**
* @bieaf CRC-16 校验
*
* @param addr 开始地址
* @param num 长度
* @param num CRC
* @return crc 返回CRC的值
*/
#define POLY 0x1021
uint16_t crc16(unsigned char *addr, int num, uint16_t crc)
{
int i;
for (; num > 0; num--) /* Step through bytes in memory */
{
crc = crc ^ (*addr++ << 8); /* Fetch byte from memory, XOR into CRC top byte*/
for (i = 0; i < 8; i++) /* Prepare to rotate 8 bits */
{
if (crc & 0x8000) /* b15 is set... */
crc = (crc << 1) ^ POLY; /* rotate and XOR with polynomic */
else /* b15 is clear... */
crc <<= 1; /* just rotate */
} /* Loop for 8 bits */
crc &= 0xFFFF; /* Ensure CRC remains 16-bit value */
} /* Loop until num=0 */
return(crc); /* Return updated CRC */
}
/**
* @bieaf 获取数据包的类型, 顺便进行校验
*
* @param buf 开始地址
* @param len 长度
* @return
*/
unsigned char Check_CRC(unsigned char* buf, int len)
{
unsigned short crc = 0;
/* 进行CRC校验 */
if((buf[0]==0x00)&&(len >= 133))
{
crc = crc16(buf+3, 128, crc);
if(crc != (buf[131]<<8|buf[132]))
{
return 0;///< 没通过校验
}
/* 通过校验 */
return 1;
}
}
/* 设置升级的步骤 */
static enum UPDATE_STATE update_state = TO_START;
void Set_state(enum UPDATE_STATE state)
{
update_state = state;
}
/* 查询升级的步骤 */
unsigned char Get_state(void)
{
return update_state;
}
unsigned char temp_buf[512] = {0};
uint16_t temp_len = 0;
/**
* @bieaf YModem升级
*
* @param none
* @return none
*/
void ymodem_fun(void)
{
int i;
if(Get_state()==TO_START)
{
send_command(CCC);
HAL_Delay(1000);
}
if(Rx_Flag) // Receive flag
{
Rx_Flag=0; // clean flag
/* 拷贝 */
temp_len = Rx_Len;
printf("---Rx-len: %#x--------:\r\n", Rx_Len);
for(i = 0; i < temp_len; i++)
{
temp_buf[i] = Rx_Buf[i];
}
switch(temp_buf[0])
{
case SOH:///<数据包开始
{
static unsigned char data_state = 0;
static unsigned int app2_size = 0;
if(Check_CRC(temp_buf, temp_len)==1)///< 通过CRC16校验
{
if((Get_state()==TO_START)&&(temp_buf[1] == 0x00)&&(temp_buf[2] == (unsigned char)(~temp_buf[1])))///< 开始
{
printf("> Receive start...\r\n");
Set_state(TO_RECEIVE_DATA);
data_state = 0x01;
send_command(ACK);
send_command(CCC);
printf("1111111111\r\n");
/* 擦除App2 */
Erase_page(Application_2_Addr, 100); // 要擦除100页
printf("2222222222\r\n");
}
else if((Get_state()==TO_RECEIVE_END)&&(temp_buf[1] == 0x00)&&(temp_buf[2] == (unsigned char)(~temp_buf[1])))///< 结束
{
printf("> Receive end...\r\n");
InFlashWrite(UPGRADE_FLAG_ADDR, Upgrade); //将升级标志设置为升级
printf("33333333333\r\n");
Set_state(TO_START);
printf("44444444444\r\n");
send_command(ACK);
printf("55555555555\r\n");
HAL_NVIC_SystemReset();
}
else if((Get_state()==TO_RECEIVE_DATA)&&(temp_buf[1] == data_state)&&(temp_buf[2] == (unsigned char)(~temp_buf[1])))///< 接收数据
{
printf("> Receive data bag:%d byte\r\n",data_state * 128);
/* 烧录程序 */
WriteFlash((Application_2_Addr + (data_state-1) * 128), (uint32_t *)(&temp_buf[3]), 32);
data_state++;
send_command(ACK);
}
}
else
{
printf("> Notpass crc\r\n");
}
}break;
case EOT://数据包开始
{
if(Get_state()==TO_RECEIVE_DATA)
{
printf("> Receive EOT1...\r\n");
Set_state(TO_RECEIVE_EOT2);
send_command(NACK);
}
else if(Get_state()==TO_RECEIVE_EOT2)
{
printf("> Receive EOT2...\r\n");
Set_state(TO_RECEIVE_END);
send_command(ACK);
send_command(CCC);
}
else
{
printf("> Receive EOT, But error...\r\n");
}
}break;
}
}
}
3.2.4 ymodelm.h文件设计:
#ifndef __YMODEM_H
#define __YMODEM_H
#ifdef __cplusplus
extern "C" {
#endif
/*=====用户配置(根据自己的分区进行配置)=====*/
#define BootLoader_Size 0x19000U ///< BootLoader的大小 20K
#define Application_Size 0x32000U ///< 应用程序的大小 40K
#define Application_1_Addr 0x08019000U ///< 应用程序1的首地址
#define Application_2_Addr 0x0804B000U ///< 应用程序2的首地址
/*==========================================*/
#define UPGRADE_FLAG_ADDR ((uint32_t)0x0807F800)
#define SOH 0x01
#define STX 0x02
#define ACK 0x06
#define NACK 0x15
#define EOT 0x04
#define CCC 0x43
/* 升级的步骤 */
enum UPDATE_STATE
{
TO_START = 0x01,
TO_RECEIVE_DATA = 0x02,
TO_RECEIVE_EOT1 = 0x03,
TO_RECEIVE_EOT2 = 0x04,
TO_RECEIVE_END = 0x05
};
void ymodem_fun(void);
#ifdef __cplusplus
}
#endif
#endif /* __YMODEM_H */
其运行结果为:
4 APP2设计:
此程序可以设计的比较简单:直接输出app2即可。我们还是需要新建工程,并将它转为bin文件:
4.1 STM32CubeMX设计:
对于APP2程序,只需要开一路串口用于打印信息即可:
4.2 keil程序编程
1、串口重定向:
此部分不再叙述。
2、main函数中实现中断向量表偏移:
__set_PRIMASK(0);//开总中断 1关闭总中断
SCB->VTOR = FLASH_BASE|0x19000;
main.c文件的整体程序为:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch,1, 0xFFFF);
return ch;
}
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
__set_PRIMASK(0);//开总中断 1关闭总中断
SCB->VTOR = FLASH_BASE|0x19000;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("app2 start -----\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
printf("app2\r\n");
HAL_Delay(500);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
4.3 转为bin文件:
将同名的文件转为bin文件:
C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o “$L@L.bin” “#L”
生成的bin文件可在下面的文件夹中找到:
5 总体实验:
1、在Xshell中,配置如下图所示:
右键按下,按下面地步骤进行选择想传输的文件:
XShell正在使用Ymodelm协议传输数据:
bin文件传输完成
开始运行新程序
文章来源:https://www.toymoban.com/news/detail-766928.html
5. 总结
至此,STM32-OTA部分就全部完成了,当然Ymodelm协议后面可以换成任意一个协议,我们只要能获取到,然后将其存到Flash中即可。
今天也不早了,下班狗要小班了,我们下期再见!!!文章来源地址https://www.toymoban.com/news/detail-766928.html
到了这里,关于STM32-OTA升级-基于STM32CubeMX+STM32F103(二)代码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!