STM32-OTA升级-基于STM32CubeMX+STM32F103(二)代码实现

这篇具有很好参考价值的文章主要介绍了STM32-OTA升级-基于STM32CubeMX+STM32F103(二)代码实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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(最后一页的开始)。
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

本实验的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串口(并开启全局中断)即可:
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

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

在烧录程序的时候一定要注意:
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

然后烧录进去,bootloader的程序则设计实现完成,下面进行app1程序的设计,其中要用到Ymodem协议,同时需要进行串口的DMA接收。

在串口调试助手中的运行结果如下:

stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

3 APP1程序设计

在app1中主要实现的功能为:新bin文件的接收与中断向量表的偏移。我们需要在STM32CubeMx中新建程序:

3.1 STM32CubeMX设计:

需要开启两个串口,一个用于打印,另一个用于接收(DMA接收)
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

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 */

stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

其运行结果为:
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

4 APP2设计:

此程序可以设计的比较简单:直接输出app2即可。我们还是需要新建工程,并将它转为bin文件:

4.1 STM32CubeMX设计:

对于APP2程序,只需要开一路串口用于打印信息即可:
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

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文件:

stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

将同名的文件转为bin文件:

C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o “$L@L.bin” “#L”

生成的bin文件可在下面的文件夹中找到:
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

5 总体实验:

1、在Xshell中,配置如下图所示:
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

右键按下,按下面地步骤进行选择想传输的文件:
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

XShell正在使用Ymodelm协议传输数据:
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

bin文件传输完成
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

开始运行新程序
stm32f103 ota,1-STM32单片机,stm32,网络,嵌入式硬件

5. 总结

至此,STM32-OTA部分就全部完成了,当然Ymodelm协议后面可以换成任意一个协议,我们只要能获取到,然后将其存到Flash中即可。
今天也不早了,下班狗要小班了,我们下期再见!!!文章来源地址https://www.toymoban.com/news/detail-766928.html

到了这里,关于STM32-OTA升级-基于STM32CubeMX+STM32F103(二)代码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 基于STM32F103ZET6使用STM32CubeMX配置FSMC模块驱动LCD屏(基于正点原子历程)

    在学习STM32的过程中,刚好学到了LCD屏,我使用的是STM32F103ZET6,屏幕是正点原子的。但是在我自己新建工程点亮显示LCD屏时遇到了很多问题。解决之后分享在此,希望能帮助到遇到此困惑的朋友。 想要快速驱动LCD屏请直接跳转到CubeMX配置 FSMC全名叫可变静态存储控制器(Fle

    2024年02月15日
    浏览(53)
  • STM32 OTA远程升级

    前言:OTA全称是over the air,主要应用于物联网设备作为更新代码使用,其原理在不同芯片上相通,应用较为广泛。 一、OTA硬件组成 ​ 对于OTA硬件来讲,常用的硬件组成是无线芯片+MCU,常用的无线模块有WIFI、4G、LORA甚至是蓝牙等具有无线传输功能的设备,MCU则是例如51,STM3

    2024年02月11日
    浏览(43)
  • 使用stm32进行ota升级

    主要方案: 1、硬件方案:只使用mcu内部flash,没有外置flash。 2、数据传输协议:ymodem,如果不了解ymodem值得去了解下。 3、bootloader和app存放方案:将mcu内部flash分为两块内存,分别存放bootloader和app。 4、ota流程:使用uart进行数据更新,并运行新的程序。 实施: 1、下载“S

    2024年02月13日
    浏览(36)
  • 使用STM32F103的串口实现IAP程序升级功能

    🎬IAP程序烧录全过程演示: ✨这几天折腾IAP升级功能,狂补了很多相关BootLoader相关的知识。本来最想实现IAP升级程序的方式是,基于SPI通讯的SD卡,借助挂载的FatFS文件系统,来实现对目标stm32芯片的自身程序的升级,奈何没有实现,只能求其次,先来通过官方现有的串口实

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

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

    2024年02月11日
    浏览(47)
  • STM32CubeMX生成C代码及时钟树配置(基于stm32f407)

    近来对于stm32单片机编程中,HAL库逐渐取代标准库成为主流的库。标准库支持的芯片型号有限,而且目前已经停止支持,而HAL库支持所有类型的芯片,可移植性也很高,再加上有神器STM32Cube可以生成工程模板,越来越多的编程开始从使用标准库转到使用HAL库。 新建工程后,在

    2024年02月15日
    浏览(56)
  • STM32_通过Ymodem协议进行蓝牙OTA升级固件教程

    作为单片机进阶能力,IAP升级固件的学习是非常重要的。 想直接看如何操作的从第三条开始看。 蓝牙OTA(Over-The-Air)升级是指通过蓝牙无线技术,对设备中的固件或软件进行远程升级和更新的过程。蓝牙OTA升级在现代物联网和智能设备领域有着重要的应用和意义。 重要性:

    2024年02月04日
    浏览(42)
  • STM32CubeMX配置--STM32F103C8T6最小系统板

    首先是新建工程选择左上角的 File 然后点击 NewProject ,或者直接使用快捷键 Ctrl+N 新建工程 然后选择开发板型号,在 Commercial Part Number 处输入 STM32F103C8T6 然后在下方会有选择 直接双击型号 就会进入配置界面 PINoutConfiguration (1)RCC修改 首先点击左上角的 System Core 然后点击

    2024年02月04日
    浏览(61)
  • stm32 esp8266 ota升级-qt bin文件处理工具

    stm32 esp8266 ota系列文章: stm32 esp8266 ota-快速搭建web服务器之docker安装openresty stm32 esp8266 ota升级-tcp模拟http stm32 esp8266 ota升级-hex合并-烧录-bin生成 stm32 esp8266 ota升级-qt bin文件处理工具 stm32 esp8266 ota升级-自建mqtt和文件服务器动态AB面方式 stm32 esp8266 ota升级-自建mqtt和文件服务器全

    2024年02月05日
    浏览(78)
  • STM32F103利用CubeMX配置开启定时中断

    1、外部晶振8MHz,下载方式SWD模式,需求配置定时器1,产生每100ms一次中断 新建工程、配置晶振、选择下载方式等略 2、查阅资料,STM32F103的时钟树分配  3、配置CubeMX的时钟树  4、配置定时器-开启定时中断  5、配置定时时间 ( 定时周期 = (Prescaler + 1) × (Period + 1) ÷ 时钟频率

    2024年02月15日
    浏览(42)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包