組み込みシステムでは、複数のタスクを同時に処理する必要があることが非常によくあります。この記事では、合理的なタスクのスケジューリングと管理を通じて STM32 チップにマルチタスクを実装し、チップのパフォーマンスを最大限に発揮し、システムの柔軟性と効率を向上させる方法を紹介します。マルチタスクを実装する 2 つの方法は次のとおりです
1. タイムスライスラウンドロビンスケジューリングメカニズム
タイムスライスラウンドロビンスケジューリング機構は、タイマー割り込みを使用して実現されます。タイマーを設定し、タイマー割り込みが発生すると次のタスクの実行に切り替えます。以下は、単純なタイム スライス ラウンドロビン スケジューリング メカニズムのサンプル コードです。
- さまざまなタスクを定義します。スケジューラー コードを記述して、タスクの優先順位、スタック サイズを定義し、タスク リストを維持し、適切なタイミングで実行する次のタスクを選択します。
#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");
}
}
- タイマー割り込み: 割り込みハンドラーでタスクを切り替え、現在のタスクのコンテキスト (レジスター、スタックなどを含む) を保存し、次のタスクのコンテキストをロードして実行を開始します。
// 定义定时器中断处理函数
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));
}
- 複数のタスクが通信してリソースを共有する必要がある場合があります。タスク間のデータ転送とリソース共有は、グローバル変数またはその他の同期メカニズムを使用して実現できます。
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) {
// 主循环,任务在定时器中断中切换
}
}
この単純なマルチタスク方法は、より単純なアプリケーション シナリオに適していますが、複雑なマルチタスク アプリケーションの場合は、より優れたタスク管理およびスケジューリング メカニズムを提供するために RTOS を使用することをお勧めします。
2. RTOS (リアルタイム オペレーティング システム) の使用
RTOS は一般的に使用されるマルチタスク ソリューションであり、マルチタスク アプリケーションの開発を簡素化するタスクのスケジュール設定および管理メカニズムを提供します。STM32 チップの場合、一般的な RTOS オプションには FreeRTOS、uC/OS などが含まれます。マルチタスクを実現するための基本的な手順は次のとおりです。
- タスクの作成: RTOS API を使用して、アプリケーション内に複数のタスクを作成します。各タスクには独自のコードと優先度があります
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)
{
}
}
- カーネル パラメータ: クロック ビートや優先順位など、RTOS カーネルのいくつかのパラメータを設定します。
int main()
{
// 硬件初始化和其他配置
// 配置FreeRTOS内核
// 设置时钟节拍
TickType_t tickRate = 1000 / configTICK_RATE_HZ;
TickTypeSet(tickRate);
// 配置优先级分组
NVIC_SetPriorityGrouping(0);
// 创建任务和启动调度器
// ...
// 代码永远不会执行到这里
while (1)
{
}
}
- タスク処理コード:タスク処理関数には、タスクの実際の処理コードを記述します。FreeRTOS はプリエンプティブ スケジューリングを採用しているため、タスクが終了しないように各タスクの処理機能を無限ループにする必要があります。
void Task1(void* pvParameters)
{
while (1)
{
// Task1处理代码
// 任务挂起一段时间,以便给其他任务执行机会
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void Task2(void* pvParameters)
{
while (1)
{
// Task2处理代码
// 任务挂起一段时间,以便给其他任务执行机会
vTaskDelay(pdMS_TO_TICKS(50));
}
}
これは 2 つのタスク (Task1 と Task2) を実装する単純なサンプル コードです。各タスクは無限ループで独自の処理コードを実行し、vTaskDelay() 関数を使用して一定期間一時停止し、他のタスクが Chance を実行できるようにします。
RTOS を使用すると、高い信頼性と柔軟性が得られ、複雑なマルチタスク アプリケーション シナリオに適しています。