STM32的boot启动freertos教程(进阶的小白)
BOOT启动FreeRtos
工作以后发现躺平是真舒服,但是如果不学点东西总觉得过意不去。
Boot启动是当下很多产品都具备的功能,接下来介绍一下在STM32里是如何实现的。
在网上搜到了很多零零散散的教程才成功实现,由此再一次总结给大家,共同进步。
由于本教程适用于进阶小白,默认都会使用STM32
本教程将会把boot烧写到0x8000000,app烧写到0x8008000。
软件工具准备
1.stm32cubeMX
2.stm32cubeIDE
3.hexview
4.烧录程序(FlyMcu)
如有使用STMLINK的同学可以不用hexview将两个bin文件进行合并,可直接将两个bin文件分别写到对应的STM32地址内。
本教程适用于大部分STM32单片机。
本人使用的是STM32F103RCT6单片机作为示例。
关键点概览
1.boot跳转前关中断
2.boot跳转前关滴答定时器中断(若不启动freertos可省略此步骤)
3.app进入前重载hal外设
4.app进入前重载rcc时钟
5.开中断
6.app进入前设定VECT_TAB_OFFSET
7.app进入前设定.id地址文件
开始
stm32cubeMX来配置基本的信息,输出给stm32cubeIDE编辑。
hexview是用来将两个bin文件合成一个hex文件,然后通过FlyMcu烧写到STM32。
本人用的STM32有两个led灯可以用来显示是否跳转成功,在boot中时闪LED1,在app时闪LED2,当然哈,如果大家也想有个明确的外设来让自己确定app跳转是否成功的话,建议大家先把两个固件单独验证成功再合并到一起。单独验证时就先不要执行此教程中上一个标题:关键点概览 的操作了。
1.MX生成boot
我的板子使用的是外部8MB晶振,所以要进行如下设置,设置完成后生成boot工程
由于我的板子有两个led,所以把两个led引脚也配置成输出了,对于mini板的同学要查看app是否跳入成功的话就用串口吧
时钟
输出文件路径和格式
2.MX生成app
生成freertos的步骤与boot相比只多三个:sys的配置,rtos的配置,uart1的配置
sys的配置
uart1的配置(默认115200的速率)
freertos的配置
3.boot跳转代码
跳转代码:
void boot_jump_to_run(uint32_t addr)
{
uint32_t jmp_addr;
/* Check if user code is programmed starting from address addr */
if (((*(uint32_t *)addr) & 0x2FFE0000) == 0x20000000) {
/* Jump to user application */
jmp_addr = *(uint32_t *)(addr + 4);
/* Initialize user application's Stack Pointer */
__set_MSP(*(uint32_t *)addr);
((void (*)(void))jmp_addr)();
}
}
main代码(就不全贴上来了):
我设置的app写入地址是0x8008000,就是说0x8000000到0x8008000的32KB空间都可以用来存储boot。
由于我是使用LED显示是否跳转成功的,所以要在跳转前加入LED闪烁
如果读者没有led的话,可以用串口打印。
跳转前需要进行两步操作:
1.关SysTick
2.关中断
while (1)
{
/* USER CODE END WHILE */
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, 0);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, 1);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, 0);
HAL_Delay(1000);
//最主要的代码如下
SysTick->CTRL = 0X00;//禁止SysTick
SysTick->LOAD = 0;
SysTick->VAL = 0;
__disable_irq();
boot_jump_to_run(0x8008000);
// jump_to_app();
/* USER CODE BEGIN 3 */
}
4.app代码
app进入成功后首先要:
3.app进入前重载hal外设
4.app进入前重载rcc时钟
5.开中断
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
HAL_DeInit(); //hal外设
HAL_RCC_DeInit(); //rcc时钟
__enable_irq(); //开启中断
/* 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 */
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, 1);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, 1);
/* USER CODE END 2 */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* definition and creation of defaultTask */
osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
由于app使用的是freertos,所以直接将闪灯程序写在task任务里了
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
/* USER CODE BEGIN 5 */
printf("start app!");
/* Infinite loop */
for(;;)
{
osDelay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, 1);
osDelay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, 0);
}
/* USER CODE END 5 */
}
5.app设置
代码加入完成后,需要修改app的相关配置才能正常跳转app
1.重设向量表
进入app时还需要配置向量表偏移:
6.app进入前设定VECT_TAB_OFFSET
2.重设起始地址
起始地址设定为app的起始地址0x8008000
7.app进入前设定.id地址文件
6.设置工程输出.bin文件
至此,我们已经完成了大部分操作,接下来就需要将编译生成的文件烧录到单片机了。如果读者采用的烧写方式支持地址编辑的话,可以忽略后续操作,并且将boot文件烧写至0x8000000,app文件烧写到0x8008000。
将两个工程都设置成输出bin格式
7.生成hex文件
bin文件是只含有编译结果的文件,hex文件是将烧写目标地址与编译结果文件合在一起了,所以有了hex文件就可以直接烧写进去。
使用hexview将生成的两个bin文件合并并且生成一个hex文件便于烧写。
1.将boot生成的bin文件拖拽到hexview,然后双击地址对其进行修改。
2.file——>merge将app地址载入进去
刚开始界面不会显示bin文件的,需要将有下角类型筛选宣城All Files全部才行
并且将载入地址设置为0x8008000
3.输出hex文件
8.烧写
结束
注明:读者也可以试着使用freertos作为boot启动freertos,我用此方法也成功启动了。
参考资料:
STM32IAP升级 bootoader跳转到freeRTOS死机
STM32单片机实现Bootloader跳转的关键步骤
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律