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跳转的关键步骤

物联沃分享整理
物联沃-IOTWORD物联网 » STM32进阶小白教程:从boot启动到使用FreeRTOS

发表评论