嵌入式 Linux C ARM 单片机学习C语言&嵌入式

freeRTOS学习笔记

2018-08-10  本文已影响39人  8326bab41c32

第一节 Task管理

(一)Task管理常用API

创建Task API
    static BaseType_t xTaskCreate(TaskFunction_t pvTaskCode, 
                                const char *constpcName, 
                                const uint32_t usStackDepth, 
                                void *constpvParameters, 
                                UBaseType_t uxPriority, 
                                TaskHandle_t *constpvCreatedTask)

功能

参数描述

返回值


删除Task API
void vTaskDelete(TaskHandle_t xTaskToDelete)

功能

参数描述

返回值

NULL


开始调度Task API
void vTaskStartScheduler()

功能

参数描述

NULL

返回值

NULL


Task延时阻塞API
void vTaskDelay(const TickType_t xTicksToDelay)

功能

参数描述

返回值

NULL


将Task阻塞到指定时间API
void vTaskDelayUntil(TickType_t *constpxPreviousWakeTime, const TickType_t xTimeIncrement)

功能

参数描述

返回值

NULL


获取Task优先级API
UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask)

功能

参数描述

返回值


设置Task优先级API
void vTaskPrioritySet(TaskHandle_t xTask, UBaseType_t uxNewPriority)

功能

参数描述

返回值

NULL


获取Task状态API
eTaskStateeTaskGetState(TaskHandle_t xTask)

功能

参数描述

返回值


挂起TaskAPI
void vTaskSuspend(TaskHandle_t xTaskToSuspend)

功能

参数描述

返回值

NULL


取消Task挂起API
void vTaskResume(TaskHandle_t xTaskToResume)

功能

参数描述

-xTaskToResume:需要重新回到可运行状态的任务的句柄。

返回值

NULL


挂起调度器API
void vTaskSuspendAll(void)

功能

参数描述

NULL

返回值

NULL


取消挂起调度器API
BaseType_t xTaskResumeAll(void)

功能

参数描述

NULL

返回值


(二)Task使用流程

新建一个Task
  1. 定义一个任务句柄 TaskHandle_t xTask
  2. 创建一个任务执行函数,函数的返回值类型必须为 void,形参为 void *data
  3. 在任务执行函数内配置任务阻塞时间。
  4. 调用 xTaskCreate() API创建一个新任务,分配好栈深度,设置好优先级。
  5. 调用 vTaskStartScheduler() API,开始任务调度。
修改Task优先级
  1. 调用 vTaskPrioritySet()API
删除Task

1.调用 vTaskDelete()API


第二节 队列管理

基于FreeRTOS 的应用程序由一组独立的任务构成——每个任务都是具有独立权
限的小程序。这些独立的任务之间很可能会通过相互通信以提供有用的系统功能。
FreeRTOS 中所有的通信与同步机制都是基于队列实现的。

(一)队列管理常用的API

创建一个新队列的宏
xQueueCreate(uxQueueLength, uxItemSize)

功能

参数描述

返回值


删除一个队列的函数
void vQueueDelete(QueueHandle_t xQueue)

功能

参数描述

返回值

NULL


向队列写入数据
  1. 向队列写入数据(写到队列尾部)

    xQueueSend(xQueue, pvItemToQueue, xTicksToWait)
    
  2. 向队列头部写入数据

    xQueueSendToFront(xQueue, pvItemToQueue, xTicksToWait)
    
  3. 向队列尾部写入数据(等同于 xQueueSend(xQueue, pvItemToQueue, xTicksToWait)

    xQueueSendToBack(xQueue, pvItemToQueue, xTicksToWait)
    

以上三个API都不能用于中断中,中断中要使用以下中断安全的API

  1. 向队列写入数据( xQueueSend(xQueue, pvItemToQueue, xTicksToWait) 的中断安全API)

    xQueueSendFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken)
    
  2. 向队头写入数据(xQueueSendToFront(xQueue, pvItemToQueue, xTicksToWait) 的中断安全API)

    xQueueSendToFrontFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken)
    
  3. 向队尾写入数据(xQueueSendToBack(xQueue, pvItemToQueue, xTicksToWait) 的中断安全API )

    xQueueSendToBackFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken)
    

参数描述

返回值


读取队列中的数据
  1. 读取队列中的数据但不删除队列中的内容,也不会改变队列中的数据存储顺序

    xQueuePeek(xQueue, pvBuffer, xTicksToWait)
    
  2. 读取队列中的数据,并且读取完后把数据从队列中删除

    xQueueReceive(xQueue, pvBuffer, xTicksToWait)
    

以上两个API不能用于中断中,中断要使用以下中断安全的API

  1. 中断中读取队列中断数据,并把数据从队列中删除

    xQueueReceiveFromISR(xQueue, pvBuffer, pxHigherPriorityTaskWoken)
    
  2. 中断中读取队列数据,但不删除队列中的数据,也不会改变队列中数据的顺序

     xQueuePeekFromISR(xQueue, pvBuffer, pxHigherPriorityTaskWoken)
    

参数描述

返回值


清空队列中的数据
xQueueReset(xQueue)

参数描述

返回值

-永远都是返回 pdPASS


(二)队列使用流程

  1. 声明一个xQueueHandle类型变量,用于存放队列句柄。
  2. 调用 xQueueCreate(uxQueueLength, uxItemSize)创建一个新队列。
  3. 根据需要调用队列的读、写、复位等API。

第三节 信号量操作

(一)常用的信号量操作API

创建二值信号量
xSemaphoreCreateBinary()

功能

参数描述

NULL

返回值


创建计数信号量
xSemaphoreCreateCounting(uxMaxCount, uxInitialCount)

功能

参数说明

返回值


创建互斥信号量
xSemaphoreCreateMutex()

功能

参数描述

NULL

返回值


Take 信号量
  1. 非中断安全

    xSemaphoreTake(xSemaphore, xBlockTime)
    
  2. 中断安全

    xSemaphoreTakeFromISR(xSemaphore, pxHigherPriorityTaskWoken)

功能

参数

返回值


Give 信号量
  1. 非中断安全

    xSemaphoreGive(xSemaphore)
    
  2. 中断安全

    xSemaphoreGiveFromISR(xSemaphore, pxHigherPriorityTaskWoken)
    

功能

参数


删除信号量
vSemaphoreDelete(xSemaphore)

参数描述


获取信号量的当前值
uxSemaphoreGetCount(xSemaphore)

参数描述

返回值


获取互斥锁持有者
xSemaphoreGetMutexHolder(xSemaphore)

参数描述

返回值


(二)信号量的运用

  1. 延迟中断处理中,使用二值信号量进行同步。使用步骤
  1. 多值信号量的使用
  1. 互斥信号量的使用

    互斥信号量用于多个任务共享资源使用。使用步骤如下:

    1. 定义一个 SemaphoreHandle_t类型的变量,用于存放信号量句柄。
    2. 使用 xSemaphoreCreateMutex()创建一个互斥信号量。
    3. 使用共享资源之前使用xSemaphoreTake获取令牌。
    4. 使用完共享资源之后,使用 xSemaphoreGive(xSemaphore)把令牌还回去。一定要还回去。

第四节 内存管理

(一)概述

每当任务,队列或是信号量被创建时,内核需要进行动态内存分配。虽然可以调用
标准的malloc()free()库函数,但必须承担以下若干问题:

  1. 这两个函数在小型嵌入式系统中可能不可用。
  2. 这两个函数的具体实现可能会相对较大,会占用较多宝贵的代码空间。
  3. 这两个函数通常不具备线程安全特性。
  4. 这两个函数具有不确定性。每次调用时的时间开销都可能不同。
  5. 这两个函数会产生内存碎片。
  6. 这两个函数会使得链接器配置得复杂。

不同的嵌入式系统具有不同的内存配置和时间要求。所以单一的内存分配算法只可
能适合部分应用程序。因此,FreeRTOS 将内存分配作为可移植层面(相对于基本的内
核代码部分而言)。这使得不同的应用程序可以提供适合自身的具体实现。
当内核请求内存时,其调用pvPortMalloc()而不是直接调用malloc();当释放内存
时,调用vPortFree()而不是直接调用free()pvPortMalloc()具有与malloc()相同的函
数原型;vPortFree()也具有与free()相同的函数原型。
FreeRTOS 自带有三种pvPortMalloc()vPortFree()实现范例。
这三种方式都会在本节描述。FreeRTOS 的用户可以选用其中一种,也可以采用自己的内存管理方式。
这三个范例对应三个源文件:heap_1.cheap_2.cheap_3.c

(二)内存分配案例

Heap_1.c

Heap_1.c 实现了一个非常基本的pvPortMalloc()版本,而且没有实现vPortFree()。
如果应用程序不需要删除任务,队列或者信号量,则具有使用heap_1 的潜质。Heap_1
总是具有确定性。
这种分配方案是将FreeRTOS 的内存堆空间看作一个简单的数组。当调用
pvPortMalloc()时,则将数组又简单地细分为更小的内存块。
数组的总大小(字节为单位)在FreeRTOSConfig.h 中由configTOTAL_HEAP_SIZE
定义。以这种方式定义一个巨型数组会让整个应用程序看起来耗费了许多内存——即使
是在数组没有进行任何实际分配之前。
需要为每个创建的任务在堆空间上分配一个任务控制块(TCB)和一个栈空间。

Heap_2.c

Heap_2.c 也是使用了一个由configTOTAL_HEAP_SIZE 定义大小的简单数组。不
同于heap_1 的是,heap_2 采用了一个最佳匹配算法来分配内存,并且支持内存释放。
由于声明了一个静态数组,所以会让整个应用程序看起来耗费了许多内存——即使是在
数组没有进行任何实际分配之前。
最佳匹配算法保证pvPortMalloc()会使用最接近请求大小的空闲内存块。比如,考
虑以下情形:

  • 堆空间中包含了三个空闲内存块,分别为5 字节,25 字节和100 字节大小。
  • pvPortMalloc()被调用以请求分配20 字节大小的内存空间。
    匹配请求字节数的最小空闲内存块是具有25字节大小的内存块——所以pvPortMalloc()
    会将这个25 字节块再分为一个20 字节块和一个5 字节块3,然后返回一个指向20 字
    节块的指针。剩下的5 字节块则保留下来,留待以后调用pvPortMalloc()时使用。
    Heap_2.c 并不会把相邻的空闲块合并成一个更大的内存块,所以会产生内存碎片
    ——如果分配和释放的总是相同大小的内存块,则内存碎片就不会成为一个问题。
    Heap_2.c 适合用于那些重复创建与删除具有相同栈空间任务的应用程序。
    Heap_2.c 虽然不具备确定性,但是比大多数标准库实现的malloc()与free()更有效率。
Heap_3.c

Heap_3.c 简单地调用了标准库函数malloc()free(),但是通过暂时挂起调度器使
得函数调用备线程安全特性。其实现代码参如下。
此时的内存堆空间大小不受configTOTAL_HEAP_SIZE 影响,而是由链接器配置
决定。

void *pvPortMalloc( size_t xWantedSize )
{
    void *pvReturn;
    vTaskSuspendAll();
    {
        pvReturn = malloc( xWantedSize );
    }
    xTaskResumeAll();
    return pvReturn;
}

void vPortFree( void *pv )
{
    if( pv != NULL )
    {
        vTaskSuspendAll();
        {
            free( pv );
        }
        xTaskResumeAll();
    }
 }
上一篇 下一篇

猜你喜欢

热点阅读