1.任务的基本特性

1.1 任务的定义与结构     

• 每个任务是一个独立的函数,任务函数的格式为: void TaskFunction(void *pvParameters) 的原型,通常以无限循环形式运行,通过 vTaskDelay() 等函数主动释放 CPU 资源。任务由任务控制块(TCB描述,包含栈指针、优先级、状态等信息。
堆栈分配:每个任务需预分配独立的栈空间(无论是静态创建还是动态创建),例如可以在STM32中的configMINIAL_STACK_SIZE设置为128个字(即512字节)
优先级范围:0(最低)至configMAX_PRIORITIES-1(最高),优先级越高越容易抢占到CPU(数值越大优先级就越高)。

1.2 任务类型 

动态任务:通过 xTaskCreate() 创建,由内核自动分配内存,适用于灵活性要求高的场景。
静态任务:使用 xTaskCreateStatic() 创建,需要手动预分配TCB和栈内存,适合资源受限系统。

2.任务状态与转换

FreeRTOS 的任务包含五种状态,需要通过调度器动态切换:

1. 运行态 (Running):当前占用CPU的任务,单核系统中同一时刻只能有一个任务处于运行态。
2. 就绪态(Ready):已准备就绪,等待调度器分配 CPU 时间。
3. 阻塞态(Blocked):等待事件(如信号量、队列消息或延时)触发,例如调用vTaskDelay(1000)延时函数,进入 1 秒阻塞,此时的任务状态会变成阻塞态。
4. 挂起态(Suspended):通过 vTaskSuspend() 显式挂起,需调用 vTaskResume() 恢复,期间任务不会参与调度。
5. 终止态/僵尸态(Deleted):任务被删除后进入终止态,由空闲任务清理资源。
转换图,如下
        

补充:

1、仅就绪态可转变成运行态。
2、其他状态的任务想运行,必须先转变成就绪态。

3. 任务调度机制

3.1 调度概述

1. 抢占式调度
高优先级抢占:当高优先级任务就绪时,会立即抢占当前任务(如优先级 2 的任务抢占优先级 1 的任务)。
优先级继承:低优先级任务持有互斥锁时,会临时继承高优先级任务的优先级,避免优先级翻转的问题。(在互斥量那里使用,mutex)
2. 时间片轮转调度
同优先级任务按时间片轮流执行,每个时间片长度由 configTICK_RATE_HZ 定义(如 1ms TICK中断)
示例:例如任务 A B 优先级相同,各自运行 1 个时间片后会进行任务的切换。


3.2 抢占式调度的详解

3.2.1 如图        

3.2.2 运行条件:

        •  依次创建三个任务:task1task2task3优先级分别为123

3.2.3 运行过程如下:

1. 当task1是第一个被创建时,然后会优先进入运行态,当task2被创建之后,也会进入就绪态时,此时,由于优先级比task1高,则会抢占task1进入运行态。
2. task3被创建并进入就绪态时,由于优先级比task2高,也会抢占task2进入运行态。
3. 此时,task3在运行过程中被阻塞了(如调用了vTaskDelay()这一类的延时函数),task3就会进入阻塞态,此时task2由就绪态转为运行态(由于task2任务优先级比task1高)。
4. task3解除了阻塞(如延时时间到),首先会由阻塞态转为就绪态,由于优先级比task2高,则会抢占task2转为运行态。

3.3 时间片轮转详解

        • 任务运行固定时间片(由 configTICK_RATE_HZ 定义,如1ms的时间片)后,会自动切换至同优先级的下一个任务。

3.3.1 如图

        

3.3.2 运行条件

        依次创建三个任务:task1task2task3优先级均为1

3.3.3. 运行过程

1. 当task1第一个被创建,则会优先进入运行态。然后执行完一个时间片后,转为就绪态,此时,会切换task2,task2进入运行态(由就绪态变为运行态)。
2. 当task2运行完一个时间片后,转为就绪态,此时task3进入运行态。
3. task3在运行过程中被阻塞了(如调用了vTaskDelay()函数),则会进入阻塞态,此时未用完的时间片被丢弃。(注意没有用完的时间片就不会再使用,下次执行任务Task3时,还是会按照一个时间片的时钟节拍运行。)
4. 调度器会直接调度task1进入运行态,task1运行完一个时间片后,切换至task2运行。

4. 任务创建函数

          • 任务创建函数主要有两种:动态创建静态创建主要有以下的区别:

          • 动态创建:通过 xTaskCreate() 函数实现,任务控制块(TCB)和任务堆栈内存由 FreeRTOS 从系统堆(Heap)中动分配,需依赖内存管理算法(如 heap_4.c )。
                  • 优点:开发者无需手动管理内存,适合频繁创建/删除任务的场景。
                  • 缺点:可能因内存碎片导致长期运行后分配失败。
          • 静态创建:通过 xTaskCreateStatic() 函数实现,开发者需预先分配 TCB 和堆栈内存(如全局数组),并通过参数传递给函数。
                   • 优点:内存分配在编译时完成,无需动态内存开销,适合对内存确定性要求高的系统(如汽车电子)。
                   • 缺点:需手动管理内存生命周期,灵活性较低。

         4.1 动态创建任务(自动分配):xTaskCreat。

                • 函数原型:

BaseType_t xTaskCreate(
    TaskFunction_t pvTaskCode, // 任务函数指针(函数原型:void task(void *pvParams))
    const char *const pcName, // 任务名称(调试用,长度≤configMAX_TASK_NAME_LEN)
    configSTACK_DEPTH_TYPE usStackDepth, // 堆栈深度(单位:字,如1024字=4KB for ARM)
    void *const pvParameters, // 任务参数指针(传递给任务函数)
    UBaseType_t uxPriority, // 优先级(0最低,configMAX_PRIORITIES-1最高)
    TaskHandle_t *const pxCreatedTask // 任务句柄(用于后续操作如删除、挂起)
);
//此函数创建的任务会立刻进入就绪态,由任务调度器调度运行

        • 返回值:

                • pdPASS :任务创建成功。

                • errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY :堆内存不足,创建失败。

        • 注意: 需要将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1。

        4.2 动态创建任务函数的内部实现

                 • 申请堆栈内存和任务控制块(TCB)内存。

                 • 给TCB结构体成员赋值。

                 • 添加新任务到就绪列表。

                 • 如图,任务控制块(TCB)的部分成员

typedef struct tskTaskControlBlock
{
    volatile StackType_t * pxTopOfStack; /* 任务栈栈顶,必须为 TCB 的第一个成员 */
    ListItem_t xStateListItem; /* 任务状态列表项 */
    ListItem_t xEventListItem; /* 任务事件列表项 */
    UBaseType_t uxPriority; /* 任务优先级,数值越大,优先级越大 */
    StackType_t * pxStack; /* 任务栈起始地址 */
    char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务名字 */
    //…省略很多条件编译的成员
} tskTCB;
//任务栈栈顶指针,在任务切换时的任务上下文保存、任务恢复息息相关。每个任务都有属于自己的任务控制块,类似身份证。

4.3 静态(内存分配)创建任务:xTaskCreateStatic。

        4.3.1 函数原型

TaskHandle_t xTaskCreateStatic(
    TaskFunction_t pvTaskCode,
    const char *const pcName,
    uint32_t ulStackDepth,
    void *const pvParameters,
    UBaseType_t uxPriority,
    StackType_t *const puxStackBuffer, // 用户预分配的堆栈内存
    StaticTask_t *const pxTaskBuffer // 用户预分配的TCB内存
);

        4.3.2 返回值

        • 成功:返回任务句柄。
        • 失败:返回 NULL (参数错误或内存未对齐)。

        4.3.3 使用流程

        1. 配置宏与预分配内存

        • FreeRTOSConfig.h 中启用静态内存分配:

                #define configSUPPORT_STATIC_ALLOCATION 1 // 启用静态内存分配

        预分配 TCB 和堆栈内存

                StaticTask_t xTaskTCB; // 任务TCB
                StackType_t xTaskStack[128]; // 任务堆栈
         
        2. 实现空闲任务和定时器任务的函数
        定义空闲任务内存(否则编译报错):
        
//空闲任务配置
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];

//软件定时器任务配置
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];

//空闲任务内存分配
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
                                    StackType_t ** ppxIdleTaskStackBuffer,
                                    configSTACK_DEPTH_TYPE * pulIdleTaskStackSize )
{
        * ppxIdleTaskTCBBuffer = &idle_task_tcb;
        * ppxIdleTaskStackBuffer = idle_task_stack;
        * pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}

//软件定时器内存分配
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
                                     StackType_t ** ppxTimerTaskStackBuffer,
                                     configSTACK_DEPTH_TYPE * pulTimerTaskStackSize)
{
        * ppxTimerTaskTCBBuffer = &timer_task_tcb;
        * ppxTimerTaskStackBuffer = timer_task_stack;
        * pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}

     3. 调用xTaskCreateStatic创建任务

TaskHandle_t xStaticTaskHandle = xTaskCreateStatic(
    vTaskDemo, // 任务函数指针
    "StaticTask", // 任务名称
    1024, // 堆栈深度
    NULL, // 任务参数
    3, // 优先级
    xTaskStack, // 预分配的堆栈地址
    &xTaskTCB // 预分配的 TCB 地址
);
//返回值检查:若 xStaticTaskHandle != NULL 表示成功。

4.3.4 静态创建内部实现

        1. TCB结构体成员赋值。

        2. 添加新任务到就绪列表。

5. 任务删除

        5.1 任务删除函数

void vTaskDelete(TaskHandle_t xTaskToDelete);
//参数:xTaskToDelete :任务句柄。若为 NULL ,删除当前任务自身(当前正在运行的任务)。


//前提:使用删除任务函数,需将宏 INCLUDE_vTaskDelete 配置为 1

//描述:
//该函数用于删除已被创建的任务,被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表。

//需要注意的是,空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存,则需要由用户在任务被删除前提前释放,否则将导致内存泄露

        5.2 行为        

            • 删除任务后,任务进入僵尸态,其TCB和堆栈由 空闲任务Idle Task)自动回收。
            • 若任务持有资源(如互斥锁、队列),需先手动释放,否则可能引发内存泄漏。

        5.3 内部实现过程

               1. 获取所要删除任务的控制块:通过传入的任务句柄,判断所需要删除哪个任务,NULL 代表删除自身。
                2. 把被删除任务所在列表移除,包括:就绪、阻塞、挂起等列表。
                3. 判断所需要删除的任务:如果删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行;如果删除其他任务,释放内存,任务数量--
                4. 更新下个任务的阻塞时间:更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务。(如果当前删除的任务是正在超时列表里面的下一个超时任务,此时删除完成之后内核会更新下一个超时任务)。
                                      

        5.4 什么是空闲任务

           5.4.1 空闲任务的概述

           • 空闲任务(Idle Task)是 FreeRTOS 调度器启动时自动创建的后台任务,具有以下特性:

            1. 最低优先级:固定为 0 优先级(时刻都可以抢占),仅在所有其他任务阻塞或挂起时运行。
            2. 必要性:确保系统始终有任务可运行,防止处理器空转
            3. 资源占用:默认堆栈大小为 configMINIMAL_STACK_SIZE (通常较小,如 128 字)。
                

          5.4.2 空闲任务的核心功能

                • 自删除任务的内存释放:当任务调用 vTaskDelete(NULL) 删除自身时,其任务控制块
                                                         (TCB)和堆栈内存由空闲任务自动回收。
                • 直接删除任务的资源处理:若任务 A 删除任务 B,则任务 B 的资源立即释放,无需空
                                                              闲任务介入。

6. 任务的挂起与恢复

        6.1 函数介绍

           6.1.1 vTaskSuspend() - 任务挂起函数

void vTaskSuspend() - 任务挂起函数
功能:将指定任务置为挂起态,该任务将不再参与调度,直到被显式恢复。
参数xTaskToSuspend :目标任务的句柄(传 NULL 表示挂起自身)。
源码行为:
    从就绪/阻塞列表中移除任务,并插入挂起列表 xSuspendedTaskList 。
    若挂起自身,立即触发上下文切换 portYIELD_WITHIN_API() 。
配置依赖:需在FreeRTOSConfig.h 中启用宏 INCLUDE_vTaskSuspend=1 。

        6.1.2 vTaskResume() - 任务恢复函数

void vTaskResume() - 任务恢复函数

功能:将挂起的任务恢复到就绪态。
参数:xTaskToResume :目标任务的句柄。
限制:仅能恢复通过 vTaskSuspend() 挂起的任务,不支持恢复因事件阻塞的任务。
无返回值:直接修改任务状态至就绪列表。

       6.1.3 xTaskResumeFromISR() - 中断中任务恢复函数

xTaskResumeFromISR() - 中断中任务恢复函数

功能:在中断服务程序(ISR)中恢复挂起的任务。
参数:xTaskToResume:目标任务的句柄。
返回值:
     pdTRUE :恢复的任务优先级≥当前任务,需调用 portYIELD_FROM_ISR() 触发上下文切换。
     pdFALSE :恢复的任务优先级较低,无需立即切换。
配置依赖:需启用宏 INCLUDE_xTaskResumeFromISR=1 。
注意:
     NVIC中断优先级分组需设置为分组4;
     中断服务程序中要调用freeRTOS的API函数则中断优先级不能高于FreeRTOS所管理的最高优先级(只能在这个范围内5-15)

  6.2 注意

        6.2.1 非嵌套性

               • 无论调用多少次 vTaskSuspend() 挂起任务,只需调用一次 vTaskResume() 即可恢复。

        6.2.2 中断安全性

        • 挂起限制:禁止在 ISR 中调用 vTaskSuspend() ,否则会导致程序异常。
        • 恢复操作:在 ISR 中恢复任务时,必须处理返回值并决定是否触发上下文切换。

7. 实验

         7.1 实验一

1. 动态创建三个任务:task1、task2、task3,三个任务作用如下:
    task1:以1000ms频率闪烁LED1;
    task2:以500ms频率闪烁LED2;
    task3:检测按键
2. task2创建后即自我删除;
3. 检测到按键1按下,则删除task1任务。

           7.1.1 代码

#include "FreeRTOS.h"
#include "task.h"
#include "led.h"
#include "freertos_test.h"
#include "key.h"

TaskHandle_t            task1_handle;   /* 任务句柄 */
TaskHandle_t            task2_handle;   /* 任务句柄 */
TaskHandle_t            task3_handle;   /* 任务句柄 */


void task1(void *pvParameters)
{
    printf("task1创建成功,准备运行...\r\n");
    while(1)
    {
        led1_on();
        vTaskDelay(1000);
        led1_off();
        vTaskDelay(1000);
    }
}

void task2(void *pvParameters)
{
    printf("task2创建成功,准备自我删除...\r\n");
    vTaskDelete(NULL);
    while(1)
    {
        led2_on();
        vTaskDelay(500);
        led2_off();
        vTaskDelay(500);
    }
}

void task3(void *pvParameters)
{
    printf("task3创建成功,检测按键...\r\n");
    uint8_t key_num = 0;
    while(1)
    {
        key_num = key_scan();
        if(key_num == 1){
            if(task1_handle != NULL){
                printf("task1被删除\r\n");
                vTaskDelete(task1_handle);//led的亮灭取决于最后的状态。
                task1_handle = NULL;
            }
        }
        vTaskDelay(10);
    }
}

void freertos_test(void)
{
    xTaskCreate(task1,                  /* 任务函数 */
                "task1",                /* 任务名称 */
                128,                    /* 任务堆栈大小 */
                NULL,                   /* 传入给任务函数的参数 */
                1,                      /* 任务优先级 */
                &task1_handle);         /* 任务句柄 */

    xTaskCreate(task2,                  /* 任务函数 */
                "task2",                /* 任务名称 */
                128,                    /* 任务堆栈大小 */
                NULL,                   /* 传入给任务函数的参数 */
                1,                      /* 任务优先级 */
                &task2_handle);         /* 任务句柄 */
    
    
    
    xTaskCreate(task3,                  /* 任务函数 */
                "task3",                /* 任务名称 */
                128,                    /* 任务堆栈大小 */
                NULL,                   /* 传入给任务函数的参数 */
                1,                      /* 任务优先级 */
                &task3_handle);         /* 任务句柄 */
}


    7.2 实验二

        7.2.1  使用静态任务创建方法完成小实验1

#include "FreeRTOS.h"
#include "task.h"
#include "led.h"
#include "freertos_test.h"
#include "key.h"

//定义静态创建任务函数要预分配的TCB和堆栈空间

TaskHandle_t            task1_handle;   /* 任务句柄 */
StackType_t task1_Stack[configMINIMAL_STACK_SIZE];
StaticTask_t task1_TCB;


TaskHandle_t            task2_handle;   /* 任务句柄 */
StackType_t task2_Stack[configMINIMAL_STACK_SIZE];
StaticTask_t task2_TCB;

TaskHandle_t            task3_handle;   /* 任务句柄 */
StackType_t task3_Stack[configMINIMAL_STACK_SIZE];
StaticTask_t task3_TCB;




//定义空闲任务需要的空间
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];

//实验空闲任务
 void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
                                        StackType_t ** ppxIdleTaskStackBuffer,
                                        configSTACK_DEPTH_TYPE * puxIdleTaskStackSize )
{
                                        *ppxIdleTaskTCBBuffer = &idle_task_tcb;
                                        *ppxIdleTaskStackBuffer = idle_task_stack;
                                        *puxIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}

//定义空闲任务需要的空间
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];

//实现定时器任务   
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
                                         StackType_t ** ppxTimerTaskStackBuffer,
                                         configSTACK_DEPTH_TYPE * puxTimerTaskStackSize )
{
                                        * ppxTimerTaskTCBBuffer = &timer_task_tcb;
                                        * ppxTimerTaskStackBuffer = timer_task_stack;
                                        * puxTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;

}

void task1(void *pvParameters)
{
    printf("task1创建成功,准备运行...\r\n");
    while(1)
    {
        led1_on();
        vTaskDelay(1000);
        led1_off();
        vTaskDelay(1000);
    }
}

void task2(void *pvParameters)
{
    printf("task2创建成功,准备自我删除...\r\n");
    vTaskDelete(NULL);
    while(1)
    {
        led2_on();
        vTaskDelay(500);
        led2_off();
        vTaskDelay(500);
    }
}

void task3(void *pvParameters)
{
    printf("task3创建成功,检测按键...\r\n");
    uint8_t key_num = 0;
    while(1)
    {
        key_num = key_scan();
        if(key_num == 1){
            if(task1_handle != NULL){
                printf("task1在用静态创建任务的函数中被删除\r\n");
                vTaskDelete(task1_handle);//led的亮灭取决于最后的状态。
                task1_handle = NULL;
            }
        }
        vTaskDelay(10);
    }
}

void freertos_test(void)
{
    printf("使用静态创建任务的方法\r\n");
    task1_handle = xTaskCreateStatic(task1,                  /* 任务函数 */
                "task1",                /* 任务名称 */
                128,                    /* 任务堆栈大小 */
                NULL,                   /* 传入给任务函数的参数 */
                1,                      /* 任务优先级 */
                task1_Stack,              /* 堆栈空间 */
                &task1_TCB);              /* TCB */        

    task2_handle = xTaskCreateStatic(task2,                  /* 任务函数 */
                "task2",                /* 任务名称 */
                128,                    /* 任务堆栈大小 */
                NULL,                   /* 传入给任务函数的参数 */
                1,                      /* 任务优先级 */
                task2_Stack,              /* 堆栈空间 */
                &task2_TCB);              /* TCB */ 
    
    
    
    task3_handle = xTaskCreateStatic(task3,                  /* 任务函数 */
                "task3",                                    /* 任务名称 */
                128,                                    /* 任务堆栈大小 */
                NULL,                               /* 传入给任务函数的参数 */
                1,                                  /* 任务优先级 */
                task3_Stack,                          /* 堆栈空间 */
                &task3_TCB);                          /* TCB */ 
}


   7.3 实验三

        7.3.1 描述

                基于实验一,实现以下功能:
                1. 按下按键1,挂起task1。
                2. 按下按键2,在任务中恢复task1。

        7.3.2 代码

#include "FreeRTOS.h"
#include "task.h"
#include "led.h"
#include "freertos_test.h"
#include "key.h"

TaskHandle_t            task1_handle;   /* 任务句柄 */
TaskHandle_t            task2_handle;   /* 任务句柄 */
TaskHandle_t            task3_handle;   /* 任务句柄 */


void task1(void *pvParameters)
{
    printf("task1创建成功,准备运行...\r\n");
    while(1)
    {
        led1_on();
        vTaskDelay(1000);
        led1_off();
        vTaskDelay(1000);
    }
}

void task2(void *pvParameters)
{
    printf("task2创建成功,准备自我删除...\r\n");
    vTaskDelete(NULL);
    while(1)
    {
        led2_on();
        vTaskDelay(500);
        led2_off();
        vTaskDelay(500);
    }
}

void task3(void *pvParameters)
{
    printf("task3创建成功,检测按键...\r\n");
    uint8_t key_num = 0;
    while(1)
    {
        key_num = key_scan();
        if(key_num == 1){
            printf("按下了按键1,此时任务一被挂起\r\n");
            vTaskSuspend(task1_handle);
        }else if(key_num == 2){
            printf("按下了按键2,此时任务一被恢复\r\n");
            vTaskResume(task1_handle);
        }
        vTaskDelay(10);
    }
}

void freertos_test(void)
{
    xTaskCreate(task1,                  /* 任务函数 */
                "task1",                /* 任务名称 */
                128,                    /* 任务堆栈大小 */
                NULL,                   /* 传入给任务函数的参数 */
                1,                      /* 任务优先级 */
                &task1_handle);         /* 任务句柄 */

    xTaskCreate(task2,                  /* 任务函数 */
                "task2",                /* 任务名称 */
                128,                    /* 任务堆栈大小 */
                NULL,                   /* 传入给任务函数的参数 */
                1,                      /* 任务优先级 */
                &task2_handle);         /* 任务句柄 */
    
    
    
    xTaskCreate(task3,                  /* 任务函数 */
                "task3",                /* 任务名称 */
                128,                    /* 任务堆栈大小 */
                NULL,                   /* 传入给任务函数的参数 */
                1,                      /* 任务优先级 */
                &task3_handle);         /* 任务句柄 */
}


7.4 实验四

        7.4.1 描述

        基于小实验1,实现以下功能:
        1. 按下按键1,挂起task1
        2. 按下按键2,在中断中恢复task1

        7.4.2 代码

        

#include "key.h"
#include "delay.h"
#include "FreeRTOS.h"
#include "task.h"

extern TaskHandle_t            task1_handle;

//初始化GPIO
void key_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();                           // 使能GPIOA时钟
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_0;          // 两个KEY对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_INPUT;                 // 输入
    gpio_initstruct.Pull = GPIO_PULLUP;                     // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;           // 高速
    HAL_GPIO_Init(GPIOA, &gpio_initstruct);
    
    
    gpio_initstruct.Pin = GPIO_PIN_1;
    gpio_initstruct.Mode = GPIO_MODE_IT_FALLING;
    gpio_initstruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &gpio_initstruct);
     HAL_NVIC_SetPriority(EXTI1_IRQn, 6, 0);                 // 设置EXTI0中断线的优先级,并且中断优先级不能高于FreeRTOS管理的范围 5-15
    HAL_NVIC_EnableIRQ(EXTI1_IRQn);                         // 使能中断
}

//按键扫描函数
uint8_t key_scan(void)
{
    //检测按键是否按下
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
    {
        //消抖
        delay_ms(10);
        //再次判断按键是否按下
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
        {
            //如果确实是按下的状态,等待按键松开
            while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
            //返回按键值
            return 1;
        }
    }
    
//    //检测按键是否按下
//    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
//    {
//        //消抖
//        delay_ms(10);
//        //再次判断按键是否按下
//        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
//        {
//            //如果确实是按下的状态,等待按键松开
//            while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);
//            //返回按键值
//            return 2;
//        }
//    }
    //返回默认值
    return 0;
}



void EXTI1_IRQHandler(){
    
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){

    if(GPIO_Pin == GPIO_PIN_1){
        BaseType_t ret = xTaskResumeFromISR(task1_handle);
        if(ret == pdTRUE){
            portYIELD_FROM_ISR(pdTRUE);
        }
    }

    /*delay_ms(20);//说白了现在是在数数,不是真正的"延时了"
    BaseType_t ret;
    if (GPIO_Pin == GPIO_PIN_1)
    {
        
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){
                printf("在中断中恢复task1\r\n");
                ret = xTaskResumeFromISR(task1_handle);
                if(ret == pdTRUE){
                    portYIELD_FROM_ISR(ret);
                }
        }
            
    }*/

}
#include "FreeRTOS.h"
#include "task.h"
#include "led.h"
#include "freertos_test.h"
#include "key.h"

TaskHandle_t            task1_handle;   /* 任务句柄 */
TaskHandle_t            task2_handle;   /* 任务句柄 */
TaskHandle_t            task3_handle;   /* 任务句柄 */


void task1(void *pvParameters)
{
    printf("task1创建成功,准备运行...\r\n");
    while(1)
    {
        led1_on();
        vTaskDelay(1000);
        led1_off();
        vTaskDelay(1000);
    }
}

void task2(void *pvParameters)
{
    printf("task2创建成功,准备自我删除...\r\n");
    vTaskDelete(NULL);
    while(1)
    {
        led2_on();
        vTaskDelay(500);
        led2_off();
        vTaskDelay(500);
    }
}

void task3(void *pvParameters)
{
    printf("task3创建成功,检测按键...\r\n");
    uint8_t key_num = 0;
    while(1)
    {
        key_num = key_scan();
        if(key_num == 1){
            printf("按下了按键1,此时任务一被挂起\r\n");
            vTaskSuspend(task1_handle);
        }
        vTaskDelay(10);
    }
}

void freertos_test(void)
{
    xTaskCreate(task1,                  /* 任务函数 */
                "task1",                /* 任务名称 */
                128,                    /* 任务堆栈大小 */
                NULL,                   /* 传入给任务函数的参数 */
                1,                      /* 任务优先级 */
                &task1_handle);         /* 任务句柄 */

    xTaskCreate(task2,                  /* 任务函数 */
                "task2",                /* 任务名称 */
                128,                    /* 任务堆栈大小 */
                NULL,                   /* 传入给任务函数的参数 */
                1,                      /* 任务优先级 */
                &task2_handle);         /* 任务句柄 */
    
    
    
    xTaskCreate(task3,                  /* 任务函数 */
                "task3",                /* 任务名称 */
                128,                    /* 任务堆栈大小 */
                NULL,                   /* 传入给任务函数的参数 */
                1,                      /* 任务优先级 */
                &task3_handle);         /* 任务句柄 */
}


          
              
        
             
    
        

        

                

        

        

        

        

                

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐