• FreeRTOS学习笔记(一)


    一、基础知识思维导图

    在这里插入图片描述
    vtaskdelay函数会开启中断,所以在临界区不能用vtaskdelay

    二、任务的创建与删除

    2.1、任务的动态创建与删除
    在这里插入图片描述

    ........
    
    #define START_TASK_PRIO         1
    #define START_TASK_STACK_SIZE   128
    TaskHandle_t    start_task_handler;
    void start_task( void * pvParameters );
    
    #define TASK1_PRIO         2
    #define TASK1_STACK_SIZE   128
    TaskHandle_t    task1_handler;
    void task1( void * pvParameters );
    
    .........
    
    int main()
    {
    
    .......
    
        xTaskCreate((TaskFunction_t         )   start_task,						//任务函数名
                    (char *                 )   "start_task",
                    (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,			//任务堆栈大小
                    (void *                 )   NULL,
                    (UBaseType_t            )   START_TASK_PRIO,				//任务优先级
                    (TaskHandle_t *         )   &start_task_handler );			//任务句柄
    	vTaskStartScheduler();
    }
    
    void start_task( void * pvParameters )
    {
        taskENTER_CRITICAL();               /* 进入临界区 */
        xTaskCreate((TaskFunction_t         )   task1,
                    (char *                 )   "task1",
                    (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                    (void *                 )   NULL,
                    (UBaseType_t            )   TASK1_PRIO,
                    (TaskHandle_t *         )   &task1_handler );
        vTaskDelete(NULL);					//删除任务
        taskEXIT_CRITICAL();                /* 退出临界区 */
    }
    
    /* 任务一,实现LED0每500ms翻转一次 */
    void task1( void * pvParameters )
    {
        while(1)
        {
            printf("task1正在运行!!!\r\n");
            LED0_TOGGLE();
            vTaskDelay(500);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    在这里插入图片描述

    三、任务的恢复与挂起

    /* 任务三,判断按键KEY0,按下KEY0删除task1 */
    void task3( void * pvParameters )
    {
        uint8_t key = 0;
        while(1)
        {
            key = key_scan(0);
            if(key == KEY0_PRES)			//按键一按下
            {
                printf("挂起task1\r\n");
                vTaskSuspend(task1_handler);	//任务挂起函数,使用时需将宏 INCLUDE_vTaskSuspend  配置为1。
            }else if(key == KEY1_PRES)		//按键二按下
            {
                printf("在任务中恢复task1\r\n");
                vTaskResume(task1_handler);	
            }
            vTaskDelay(10);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    如果在中断中用恢复函数需要在函数后带FromISR后缀,vTaskResumeFromISR()

    四、freertos中断管理

    系统所管理的优先级范围:5~15,也就是说优先级在0-4以内的中断freertos不能控制它们。

    五、FreeRTOS临界段代码保护及任务调度器挂起和恢复

    5.1、临界代码段

    什么是临界段:临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段,如:iic、spi的初始化等。

    在这里插入图片描述
    在这里插入图片描述

    5.2、任务调度器的挂起与恢复

    在这里插入图片描述
    在这里插入图片描述
    1、与临界区不一样的是,挂起任务调度器,未关闭中断;
    2、它仅仅是防止了任务之间的资源争夺,中断照样可以直接响应;
    3、挂起调度器的方式,适用于临界区位于任务与任务之间;既不用去延时中断,又可以做到临界区的安全;

    六、FreeRTOS列表和列表项

    6.1、列表相关API函数
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    总结:函数vListInsert(),是将待插入列表的列表项按照列表项值升序进行排序,有序地插入到列表中
    在这里插入图片描述
    总结:函数vListInsertEnd(),是将待插入的列表项插入到列表 pxIndex 指针指向的列表项前面;它是一种无序的插入方法!!
    在这里插入图片描述
    在这里插入图片描述

    七、任务调度

    7.1、启动第一个任务

    prvStartFirstTask () 	/* 开启第一个任务 */
    vPortSVCHandler () 	/* SVC中断服务函数 */
    
    • 1
    • 2

    1、中断产生时,硬件自动将xPSR,PC(R15),LR(R14),R12,R3-R0出/入栈;而R4~R11需要手动出/入栈
    2、进入中断后硬件会强制使用MSP指针 ,此时LR(R14)的值将会被自动被更新为特殊的EXC_RETURN

    7.2、prvStartFirstTask ()函数
    用于初始化启动第一个任务前的环境,主要是重新设置MSP 指针,并使能全局中断

    什么是MSP指针?
    程序在运行过程中需要一定的栈空间来保存局部变量等一些信息。当有信息保存到栈中时,MCU 会自动更新 SP 指针,ARM Cortex-M 内核提供了两个栈空间:
    在这里插入图片描述
    为什么是 0xE000ED08?
    因为需从 0xE000ED08 获取向量表的偏移,为啥要获得向量表呢?因为向量表的第一个是 MSP 指针!取 MSP 的初始值的思路是先根据向量表的位置寄存器 VTOR (0xE000ED08) 来获取向量表存储的地址;在根据向量表存储的地址,来访问第一个元素,也就是初始的 MSP

    CM3 允许向量表重定位——从其它地址处开始定位各异常向量 这个就是向量表偏移量寄存器,向量表的起始地址保存的就是主栈指针MSP 的初始值

    7.3、vPortSVCHandler ()
    当使能了全局中断,并且手动触发 SVC 中断后,就会进入到 SVC 的中断服务函数中
    在这里插入图片描述
    在这里插入图片描述
    7.4、任务切换
    任务切换的本质:就是CPU寄存器的切换。

    在这里插入图片描述
    注意:任务切换的过程在PendSV中断服务函数里边完成
    在这里插入图片描述
    PendSV中断是如何触发的?
    1、滴答定时器中断调用
    2、执行FreeRTOS提供的相关API函数:portYIELD()
    本质:通过向中断控制和状态寄存器 ICSR 的bit28 写入 1 挂起 PendSV 来启动 PendSV 中断

    在这里插入图片描述

    总结

    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    jquery加载初始化的三种方法
    pandas某一列的某一行 .loc
    LAMP平台搭建-Centos6
    MybatisX插件使用
    使用打表法找规律
    AAOS CarMediaService 服务框架
    【算法题】小红书2023秋招提前批算法真题解析
    forms组件补充与ModelForm简单使用与cookie与session
    DOTA-PEG-麦芽糖 maltose-DOTA 麦芽糖-四氮杂环十二烷四乙酸
    C++lambda表达式
  • 原文地址:https://blog.csdn.net/afddasfa/article/details/133046869