BootLoader和App1和App2三个程序之间的跳转
目录:
1 、说明:
三个程序是
(1)BootLoader 亮两个LED灯,1秒后进入App1.
(2)app1 LED1闪烁,长按按键后进入App2.
(3)app2 LED2闪烁,长按按键后进入App1.
2、跳转程序:
三个程序的公共部分,包括boot和LED程序
//boot.c
#include "main.h"
#include "boot.h"
/* 开关全局中断的宏 */
#define ENABLE_INT() __set_PRIMASK(0) /* 使能全局中断 */
#define DISABLE_INT() __set_PRIMASK(1) /* 禁止全局中断 */
#if 1
//uint32_t ApplicationAddress = 0x08000000+0x2800;
typedef void (*pFunction)(void);
pFunction Jump_To_Application;//做为全局变量,函数内局部时跳转失败。
void jumpToApplication(uint32_t addr)
{
/* 关闭全局中断 */
DISABLE_INT();
/* 关闭滴答定时器,复位到默认值 */
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
/* 设置所有时钟到默认状态, 使用 HSI 时钟 */
LL_RCC_DeInit();
/* 关闭所有中断,清除所有中断挂起标志 */
for (uint8_t i = 0; i < 8; i++)
{
NVIC->ICER[i]=0xFFFFFFFF;
NVIC->ICPR[i]=0xFFFFFFFF;
}
/* 使能全局中断 */
ENABLE_INT();
uint32_t JumpAddress;
// /* 跳转,首地址是 MSP,地址+4 是复位中断服务程序地址 */
JumpAddress = *(__IO uint32_t *)(addr + 4);
Jump_To_Application = (pFunction)JumpAddress;
/* 设置主堆栈指针 */
__set_MSP(*(__IO uint32_t *)addr);
Jump_To_Application();
/* 跳转成功的话,不会执行到这里,用户可以在这里添加代码 */
while (1)
{
}
}
#endif
//boot.h
#ifndef _BOOT_H_
#define _BOOT_H_
void jumpToApplication(uint32_t addr);
#endif
LED部分:
#define GPIO_PORT_LED1 GPIOB
#define GPIO_PIN_LED1 LL_GPIO_PIN_3
#define GPIO_PORT_LED2 GPIOD
#define GPIO_PIN_LED2 LL_GPIO_PIN_2
#ifndef __BSP_LED_H
#define __BSP_LED_H
/* 供外部调用的函数声明 */
void bsp_InitLed(void);
void bsp_LedOn(uint8_t _no);
void bsp_LedOff(uint8_t _no);
void bsp_LedToggle(uint8_t _no);
uint8_t bsp_IsLedOn(uint8_t _no);
#endif
3、BootLoader程序
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "bsp_led.h"
#include "bsp_key.h"
#include "boot.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
uint8_t Itimer_f=0;
uint8_t Itimer=0;
static uint8_t s_count = 0;
#define BOOT_APP 1
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* 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 */
void sIncTick();
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void KeyProc();
/* 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_USART2_UART_Init();
/* USER CODE BEGIN 2 */
#if (!BOOT_APP)
bsp_Init();
#endif
// SerialPutString("\r\n======================================================================");
// SerialPutString("\r\n= boot =");
// SerialPutString("\r\n= =");
// SerialPutString("\r\n======================================================================");
// SerialPutString("\r\n\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
#if BOOT_APP
HAL_Delay(1000);
jumpToApplication((uint32_t) 0x08002800);
#else
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
KeyProc();
if(READ_BIT(Itimer_f,1<<0))
{
CLEAR_BIT(Itimer_f,1<<0);
if(++s_count %10 !=0) continue;
if(s_count ==250) s_count = 0;
bsp_KeyScan();
if (++Itimer % 50 !=0)continue;
if (Itimer == 250 )
{
Itimer=0;
// while(1);
}
bsp_LedToggle(2);
// bsp_LedToggle(1);
}
#endif
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
LL_FLASH_SetLatency(LL_FLASH_LATENCY_2);
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_2)
{
Error_Handler();
}
LL_RCC_HSE_Enable();
/* Wait till HSE is ready */
while(LL_RCC_HSE_IsReady() != 1)
{
}
LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE_DIV_1, LL_RCC_PLL_MUL_9);
LL_RCC_PLL_Enable();
/* Wait till PLL is ready */
while(LL_RCC_PLL_IsReady() != 1)
{
}
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_2);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
/* Wait till System clock is ready */
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
{
}
LL_SetSystemCoreClock(72000000);
/* Update the time base */
if (HAL_InitTick (TICK_INT_PRIORITY) != HAL_OK)
{
Error_Handler();
};
}
/* USER CODE BEGIN 4 */
void sIncTick()
{
SET_BIT(Itimer_f,1<<0);
}
void KeyProc()
{
uint8_t ucKeyCode; /* 按键代码 */
int GetKey;
/* 按键滤波和检测由后台systick中断服务程序实现,我们只要调用bsp_GetKey读取键即可 */
ucKeyCode = bsp_GetKey(); /* 读取键 , 无键按下时返 KEY_NONE = 0 */
if (ucKeyCode != KEY_NONE)
{
switch (ucKeyCode)
{
case KEY_DOWN_K1: /* K1键按 */
break;
case KEY_UP_K1:
{
}break;
case KEY_LONG_K1:
{
}break;
default:
/* 其它的键值不处理 */
break;
}
}
}
/* 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 */
/* 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,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
有用的只有两行
(1)配置地址和大小
占用flash大小为10k,也可不用配置
4、App1程序
(1)程序
程序和BootLoader相同,
复制一份BootLoader程序,需要改的是
#define BOOT_APP 0
在main里最前面加入中断向量表,就是APP1的开始地址:
SCB->VTOR =0x2800;
在按键长按里加入跳转地址:
jumpToApplication(0x08002800+0xD800);//这里是APP2的地址
如下图:
(2)配置地址和大小
开始地址是0x08002800 大小是0xD800
连接器里面要勾选左边,不然上面的地址和大小就不起用了,就会按照右边填写的。
不勾选也行,把上面那步的数据填到右边。
擦除,要扇区擦除,
调试在.S 程序开头打一个断点,然后运行,能停在这里说明跳转成功:
向上翻,可以看从0x2800开始放的是向量表:
可以看到地址0x2800以前是全是0xFFFFFFFF,这是BootLoader没用到的剩余空间,分配了10k的空间其实只用了一半多。
(3)程序现象
全速运行,蓝色LED2闪烁。
5、APP2程序
(1)程序
程序和APP1相同,
复制一份app1程序,需要改的是
在main里最前面的中断向量表,就是App2的开始地址:
SCB->VTOR =0X2800+0xD800;
在按键长按里加入跳转地址:
jumpToApplication(0x08002800);//这里是APP1的地址和BootLoader里面相同
改动部分如下图所示:
(2)配置地址和大小
开始地址是0X2800+0xD800=0x10000,即从0x08010000开始,剩下的空间大小全部给APP2。
其它的连接器,擦除方法和App1相同。
调试,发现程序一直在运行,打开汇编窗口,可以看到在0x08010000开始是向量表了。
长按按键程序停在断点处,说明跳转成功,继续全速运行绿色LED1闪烁。
(3)程序现象
断电,上电,全速运行。蓝色LED2闪烁,长按按键绿色LED1闪烁。再按一次蓝色LED2闪烁,如此反复。
6、综述
三个程序,每个里面都要有不一样的开始地、程序所占空间大小、中断向量表,跳转地址。
上电顺序是BootLoader,App1,按键App,按键App2。
也可以改一下地址,按照App1,按键BootLoader,App2的顺序。
也可以很多个程序一起玩。
可以用STM32CubeProgrammer来烧写hex文件或者bin文件,hex文件里面带有地址,开始地址这里不用填写,bin文件需要填写地址。
烧写无顺序。