How to run multiple tasks simultaneously on STM32 chip

In embedded systems, the need to handle multiple tasks simultaneously is very common. This article will introduce how to implement multitasking on the STM32 chip, through reasonable task scheduling and management, to give full play to the performance of the chip, and improve the flexibility and efficiency of the system. Here are two ways to implement multitasking

1. Time slice round-robin scheduling mechanism

The time slice round-robin scheduling mechanism is realized by using timer interrupt. Set a timer, when the timer interrupt occurs, switch to the execution of the next task. The following is a sample code for a simple time slice round-robin scheduling mechanism

  • Define different tasks: define task priority, stack size, maintain a task list, and select the next task to execute at the right time by writing scheduler code.
#include "stm32fxxx.h"

// 定义任务的优先级
#define TASK1_PRIORITY 1
#define TASK2_PRIORITY 2

// 定义任务的堆栈大小
#define TASK_STACK_SIZE 128

// 定义任务堆栈空间
uint32_t task1_stack[TASK_STACK_SIZE];
uint32_t task2_stack[TASK_STACK_SIZE];

// 定义任务函数
void task1(void);
void task2(void);

// 定义任务控制块结构
typedef struct {
    
    
  uint32_t* stack_ptr;
} TaskControlBlock;

// 定义任务控制块实例
TaskControlBlock tcb1;
TaskControlBlock tcb2;

// 定义当前任务指针
TaskControlBlock* current_task;

// 任务1的函数
void task1(void) {
    
    
  while (1) {
    
    
    // 任务1的处理逻辑

    // 切换任务
    __asm volatile("yield");
  }
}

// 任务2的函数
void task2(void) {
    
    
  while (1) {
    
    
    // 任务2的处理逻辑

    // 切换任务
    __asm volatile("yield");
  }
}
  • Timer interrupt: switch tasks in the interrupt handler, save the context of the current task (including registers, stacks, etc.), and then load the context of the next task to start execution.

// 定义定时器中断处理函数
void TIM_IRQHandler(void) {
    
    
  // 切换到下一个任务
  if (current_task == &tcb1) {
    
    
    current_task = &tcb2;
  } else {
    
    
    current_task = &tcb1;
  }

  // 加载下一个任务的堆栈指针
  __asm volatile("mov sp, %0" ::"r"(current_task->stack_ptr));
}
  • Multiple tasks may need to communicate and share resources. Data transfer and resource sharing between tasks can be achieved using global variables or other synchronization mechanisms.
int main() {
    
    
  // 初始化任务控制块
  tcb1.stack_ptr = task1_stack + TASK_STACK_SIZE - 1;
  tcb2.stack_ptr = task2_stack + TASK_STACK_SIZE - 1;

  // 初始化定时器,设置定时器中断
  // 这里使用TIM3作为定时器,具体配置请根据实际情况进行修改
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
  TIM_TimeBaseInitTypeDef TIM_InitStruct;
  TIM_InitStruct.TIM_Prescaler = 1000;
  TIM_InitStruct.TIM_Period = 1000;
  TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_TimeBaseInit(TIM3, &TIM_InitStruct);
  TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
  NVIC_EnableIRQ(TIM3_IRQn);
  TIM_Cmd(TIM3, ENABLE);

  // 初始化当前任务指针
  current_task = &tcb1;

  // 启动任务1
  task1();

  while (1) {
    
    
    // 主循环,任务在定时器中断中切换
  }
}

This simple multitasking method is suitable for simpler application scenarios, but for complex multitasking applications, it is recommended to use RTOS to provide better task management and scheduling mechanisms.

2. Using RTOS (Real Time Operating System)

RTOS is a commonly used multitasking solution, which provides task scheduling and management mechanisms to simplify the development of multitasking applications. For STM32 chips, common RTOS options include FreeRTOS, uC/OS, etc. Here are the basic steps to achieve multitasking:

  • Create tasks: use the RTOS API to create multiple tasks in the application. Each task has its own code and priority
void Task1(void* pvParameters)
{
    
    
  while (1)
  {
    
    
    // Task1处理代码
  }
}

void Task2(void* pvParameters)
{
    
    
  while (1)
  {
    
    
    // Task2处理代码
  }
}

int main()
{
    
    
  // 硬件初始化和其他配置

  // 创建任务
  xTaskCreate(Task1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
  xTaskCreate(Task2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, NULL);

  // 启动调度器
  vTaskStartScheduler();

  // 代码永远不会执行到这里
  while (1)
  {
    
    
  }
}
  • Kernel parameters: Configure some parameters of the RTOS kernel, such as clock beat and priority.
int main()
{
    
    
  // 硬件初始化和其他配置

  // 配置FreeRTOS内核
  // 设置时钟节拍
  TickType_t tickRate = 1000 / configTICK_RATE_HZ;
  TickTypeSet(tickRate);

  // 配置优先级分组
  NVIC_SetPriorityGrouping(0);

  // 创建任务和启动调度器
  // ...

  // 代码永远不会执行到这里
  while (1)
  {
    
    
  }
}

  • Task processing code: In the task processing function, write the actual processing code of the task. Since FreeRTOS adopts preemptive scheduling, the processing function of each task should be an infinite loop to ensure that the task will not end.
void Task1(void* pvParameters)
{
    
    
  while (1)
  {
    
    
    // Task1处理代码

    // 任务挂起一段时间,以便给其他任务执行机会
    vTaskDelay(pdMS_TO_TICKS(100));
  }
}

void Task2(void* pvParameters)
{
    
    
  while (1)
  {
    
    
    // Task2处理代码

    // 任务挂起一段时间,以便给其他任务执行机会
    vTaskDelay(pdMS_TO_TICKS(50));
  }
}

This is a simple sample code that implements two tasks (Task1 and Task2), each task executes its own processing code in an infinite loop, and uses the vTaskDelay() function to suspend for a period of time to allow other tasks to execute Chance.

Using RTOS can provide high reliability and flexibility, and is suitable for complex multi-task application scenarios.

Guess you like

Origin blog.csdn.net/Chuangke_Andy/article/details/130945075