STM32单片机的bootloader运行流程
直接上代码
照抄都可以
main.c
//全局变量的定义
uint8_t iap_buff[iap_len];
uint32_t Iap_CNt=0;
uint32_t Iap_C1=0;
uint8_t Iap_sign[1];
uint8_t T_sign[1]={0};
HAL_Read_Flash(0x08007800,T_sign,1); //从flahs中读取T_sign的标志
__HAL_TIM_CLEAR_IT(&htim3,TIM_IT_UPDATE); //初始化完成之后以防立马进入中断,必须先清除一下
while (1)
{
if(T_sign[0]==1)
{
iap_load_app(USER_FLASH_ADDR);
}
if(Iap_sign[0]==1&&T_sign[0]==0)
{
HAL_Flash_Write(USER_FLASH_ADDR,iap_buff,Iap_C1);
//Iap_sign=0;
T_sign[0]=1;
HAL_Delay(1000);
HAL_Flash_Write(0x08007800,T_sign,4);
iap_load_app(USER_FLASH_ADDR);
__disable_irq(); //关闭总中断
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
mian.h
extern uint32_t Iap_CNt;
extern uint8_t iap_buff[iap_len];
extern uint32_t Iap_C1;
extern uint8_t Iap_sign[1];
extern uint8_t T_sign[1];
flash.c
#include "flash.h"
#include <string.h>
FLASH_EraseInitTypeDef flashEraseInit;
uint32_t pageError = 0;
iapfun jump2app;
// 读取Flash数据
void HAL_Read_Flash(uint32_t addr, uint8_t *data, uint32_t len)
{
memcpy(data, (uint8_t*)addr, len);
}
// 写入Flash数据
/*************************************************************************************
* name: ADD:开始的flash地址,DATA:数据,LEN:长度
* msg:
* Time: time
* Input: void
* Output: None
* Return: void
* param {uint32_t} addr
* param {uint8_t} *data
* param {uint32_t} len
*************************************************************************************/
void HAL_Flash_Write(uint32_t addr, uint8_t *data, uint32_t len)
{
HAL_FLASH_Unlock(); // 解锁Flash
// 擦除扇区
flashEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
flashEraseInit.PageAddress = addr;
flashEraseInit.NbPages = 1;
HAL_FLASHEx_Erase(&flashEraseInit, &pageError);
// 写入数据
for(uint32_t i = 0; i < len; i += 4)
{
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i, *(uint32_t*)(data + i));
}
HAL_FLASH_Lock(); // 锁定Flash
// Iap_sign=0;
}
//********************************IAP升级********************************//
/*************************************************************************************
* name: 设置栈顶地址
* msg: addr: 栈顶地址
* Time: time
* Input: void
* Output: None
* Return: void
* param {uint32_t} addr
*************************************************************************************/
void sys_msr_msp(uint32_t addr)
{
__set_MSP(addr); /* 设置栈顶地址 */
}
/*************************************************************************************
* name: iap升级,跳转到app用户程序
* msg: appxaddr:起始地址
* Time: time
* Input: void
* Output: None
* Return: void
* param {uint32_t} appxaddr
*************************************************************************************/
void iap_load_app(uint32_t appxaddr)
{
if (((*(volatile uint32_t *)appxaddr) & 0x2FFE0000) == 0x20000000) /* 检查栈顶地址是否合法.可以放在内部SRAM共64KB(0x20000000) */
{
/* 用户代码区第二个字为程序开始地址(复位地址) */
jump2app = (iapfun) * (volatile uint32_t *)(appxaddr + 4);
/* 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) */
sys_msr_msp(*(volatile uint32_t *)appxaddr);
/* 跳转到APP */
jump2app();
}
}
flahs.h
#include "main.h"
//定义APP的占用地址
#define iap_len 6*1024
typedef void (*iapfun)(void); /* 定义一个函数类型的参数 */
// 定义Flash地址
#define FLASH_BASE_ADDR 0x08000000
#define USER_FLASH_ADDR FLASH_BASE_ADDR + 0x2000 // 用户Flash地址,从0x0300开始
// 定义Flash大小
#define FLASH_SIZE 32 // 单位为KB
// 定义Flash扇区大小
#define FLASH_SECTOR_SIZE 1024 // 单位为字节
// 定义Flash扇区数量
#define FLASH_SECTOR_NUM FLASH_SIZE * 1024 / FLASH_SECTOR_SIZE
// 定义Flash写操作相关变量
#define FLASH_TIMEOUT 5000 // Flash写入超时时间,单位为毫秒
uint32_t endian_swap(uint32_t x);
void HAL_Read_Flash(uint32_t addr, uint8_t *data, uint32_t len);
void HAL_Flash_Write(uint32_t addr, uint8_t *data, uint32_t len);
void sys_msr_msp(uint32_t addr);
void iap_load_app(uint32_t appxaddr);
中断
/中断
//此bootloader中使用了 串口的接收中断和TIM的定时中断
void TIM3_IRQHandler(void)
{
/* USER CODE BEGIN TIM3_IRQn 0 */
Iap_C1=Iap_CNt;
Iap_CNt=0;
HAL_TIM_Base_Stop_IT(&htim3);//停止使能 TIM3 中断。这个 TIM3_IRQn 被中断后不会再
__HAL_TIM_CLEAR_IT(&htim3,TIM_IT_UPDATE);//清除TIM3上的上一次增值中断。
Iap_sign[0]=1;
T_sign[0]=0;
/* USER CODE END TIM3_IRQn 0 */
HAL_TIM_IRQHandler(&htim3);
/* USER CODE BEGIN TIM3_IRQn 1 */
/* USER CODE END TIM3_IRQn 1 */
}
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
uint8_t BUFF;
// 接收到数据,将数据存入接收缓存区
BUFF=USART1->DR;
//HAL_UART_Receive(&huart1,&BUFF,1,0x1000); //读取串口信息到BUFF中
if(Iap_CNt<=iap_len) //设定长度大于接收长度
{
iap_buff[Iap_CNt]=BUFF; //把当前接收到的字节赋值给iap_buff数组进行存储起来
Iap_CNt++;//对接收长度进行计数
__HAL_TIM_CLEAR_IT(&htim3,TIM_IT_UPDATE);//清除定时器溢出中断
__HAL_TIM_SET_COUNTER(&htim3,0); //当接收到第一个字节,就清空定时器(相当于喂狗,若没有产生接收中断,此数据就不会清空)
HAL_TIM_Base_Start_IT(&htim3);//开始计时
}
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}