FreeRTOS笔记(更新中...)

1 任务管理

1.1 静态、动态创建任务

xTaskCreate(task1, "task1", 100, NULL, 1, &xHandleTask1); //动态
xTaskCreateStatic(task2, "task2", 100, NULL, 1, xTask3Stask, &xTask3TCB); //静态

一般都是静态创建任务,动态创建任务需要指定栈位置和存放位置,不同任务的任务指针函数可以相同。

//动态任务
BaseType_t xTaskCreate(
    (TaskFunction_t)                pxTaskCode, //任务函数指针,指向要调用的函数
    (const char *const)             pcName, //任务名字
    (const configSTACK_DEPTH_TYPE)  usStackDepth, //栈大小(100字为400字节)
    (void *const)                   pvParameters, //任务函数的指针参数
    (UBaseType_t)                   uxPriority, //优先级(越小越先执行)
    (TaskHandle_t *const)           pxCreatedTask //任务句柄指针(获取返回值)
)
//静态任务
TaskHandle_t xTaskCreateStatic(
    (TaskFunction_t)pxTaskCode, //任务函数指针,指向要调用的函数
    (const char *const)pcName, //任务名字
    (const uint32_t)ulStackDepth, //栈大小
    (UBaseType_t)uxPriority, //优先级
    (StackType_t *const)puxStackBuffer, //栈缓冲区
    (StackType_t *const)pxTaskBuffer, //任务缓冲区
)

1.2 删除任务

vTaskDelete(NULL); //自杀
vTaskDelete(xHandleTask1); //杀死句柄为xHandleTask1任务

1.3 任务状态

任务状态:运行(Runing)和非运行(Not Runing)
非运行又分为:
阻塞态(Blocked):任务在等待某个事件,不消耗CPU资源
等待时间(相对和绝对手机)或者同步事件(别的任务或者中断产生的事件)
暂停态(Suspended):

//调用下面的函数进入暂停状态:
void vTaskSuspend(TaskHandle_t xTaskToSuspend); //句柄为NULl,则表示暂停自己
void vTaskSuspendAll(void); //暂停所有任务
//要退出暂停状态
void vTaskResume(TaskHandle_t xTaskToResume); //句柄为NULl,则表示恢复自己
void xTaskResumeAll(void); //恢复所有任务
void vTaskResumeFromISR(TaskHandle_t xTaskToResume); //中断恢复

就绪态(Ready):
任务已经完全准备好了,随时可以运行,只是现在还没轮到它。

1.4 Delay函数

vTaskDelay(30); //等待30毫秒
xTaskDelayUntil(&xLastWakeTime, 30); //所在任务执行30ms
xTaskAbortDelay(TaskHandle_t xTask); //终止延时 xTask是任务句柄

相对延时:任务执行完后必须等待
绝对延时:任务加空闲的时间等于指定的绝对时刻

//相对延时
void vTaskDelay(const TickType_t xTicksToDelay); //延时时间
vTaskDelay(30); //等待30毫秒
//绝对延时
BaseType_t xTaskDelayUntil(TickType_t *const pxpreviousWakeTime, const TickType_t xTimeIncrement); //延时时间
//一般xLastWakeTime = xTaskGetTickCount(); //获得当前的Tick Count
xTaskDelayUntil(&xLastWakeTime, 30); //等待30毫秒

1.5 空闲任务、钩子函数

一般空闲任务的优先级为0,用于执行一些低优先级、后台函数、释放被删除的内存,空闲任务每执行一次都调用一次钩子函数。
空闲任务的钩子函数限制:不能导致空闲任务进入阻塞状态、暂停状态(即不能在其内部写死循环等代码)

//configUSE_IDLE_HOOK 1 这个宏置为1就可以调用下面的函数
void vAPPlicationIdleHook(void); //空闲任务的钩子函数

1.6 任务调度

#define configUSE_PREEMPTION //1抢占式 
#define configUSE_TIME_SLICING //1时间片轮转
#define configIDLE_SHOULD_YIELE //1空闲任务让步

2 同步与互斥

下面代码就是一个同步的案例:
任务1和任务2同时执行,任务2一直判断flag_end变量是否为1,然后打印cnt的值,这个过程中任务2一直在等待任务1完成。缺点显而易见比较耗时间。

volatile int cnt; //不同线程访问的变量,用volatile仿真被编译器优化
volatile int flag_end;

//任务1执行完for循环后,删除自身是为了保证不再次执行任务1,不然cnt就不会输出1000000,而是输出比1000000更大的值,并且一直累加。
void vtask1( void *pvParameters )
{
	volatile int i = 0;
    while (1)
    {
		for (i = 0; i < 100000; i++) //keil仿真示波器大约运行0.4秒左右,flag_end变量变为1
			cnt++;
		flag_end = 1;
		vTaskDelete(NULL); //删除自身
    }
}

void vtask2( void *pvParameters )
{
    while (1)
    {
		if (flag_end == 1)
		{
			printf("%d\r\n", cnt);
		}
    }
}

//主函数中创建任务
xTaskCreate(vtask1, "task1", 100, NULL, 1, NULL);
xTaskCreate(vtask2, "task2", 100, NULL, 1, NULL);

下面的代码是一个互斥的案例:
任务3和任务4同时通过vTask_general函数打印传参值,使用flag_uart来充当标志位,这样来确保任务3执行的时候,任务4不会执行。但这个代码有个隐患就是,当任务3和任务4都处于if判断和“flag_uart = 1”语句之间时,任务3往下执行打印到一半可能任务4抢断打印,导致程序输出异常。

volatile int flag_uart;

void vtask_general(void *str)
{
	while(1)
	{
		if (!flag_uart)
		{
			flag_uart = 1;
			printf("%s\r\n", (char *)str);
			flag_uart = 0;
			vTaskDelay(1);
		}
	}
}

//主函数中创建任务
xTaskCreate(vtask_general, "task3", 100, "task3 runing", 1, NULL);
xTaskCreate(vtask_general, "task4", 100, "task4 runing", 1, NULL);

3 队列(queue)

3.1 静态、动态创建队列

//动态创建
xQueueCreate(4, sizeof(int)); //创建一个长度为4,数据大小为int的队列
//静态创建
uint8_t QueueStaticArray[4 * sizeof(int)]; //“4*sizeof(int)”是这个数组的最小长度
StaticQueue_t xQueueBuffer;
xQueueCreateStatic(4, sizeof(int), QueueStaticArray, &xQueueBuffer);
//动态创建队列(创建成功返回句柄用来操作队列,失败返回0)
QueueHandle_t xQueueCreate( 
    UBaseType_t uxQueueLength, //队列长度
    UBaseType_t uxItemSize //数据大小,如果是int,则写sizeof(int)
); 
//静态创建队列
QueueHandle_t xQueueCreateStatic( 
    UBaseType_t uxQueueLength, 
    UBaseType_t uxItemSize,
    uint8_t *pucQueueStorageBuffer, //队列缓冲区(数组大小至少是(uxQueueLength * uxItemSize)字节)
    StaticQueue_t *pxQueueBuffer    //队列结构体,保存队列的数据结构
);

3.2 写队列

1.返回值:
pdPASS:数据成功写入队列 errQUEUE_FULL:写入失败,因为队列满了
2.xTicksToWait(等待时间):
一般为0~(portMAX_DELAY-1)
portMAX_DELAY是一直阻塞到有数据可写才返回,不是阻塞到最大时间
0是无法写入数据则立即返回,不是0秒不写就返回

xQueueSend(handle_1, &dat, portMAX_DELAY); //发送数据dat到handle_1队列,阻塞等待
//返回值:pdPASS:数据成功写入队列   errQUEUE_FULL:写入失败,因为队列满了
BaseType_t xQueueSend(...); //向队列尾部写数据
BaseType_t xQueueSendToBack(...); //向队列尾部写数据
BaseType_t xQueueSendToBackFromISR(....); //中断中向队列尾部写数据
BaseType_t xQueueSendToFront(...); //向队列头部写数据
BaseType_t xQueueSendToFrontFromISR(....); //中断中向队列头部写数据

内部的具体函数如下所示:

BaseType_t xQueueSendToFront(
    QueueHandle_t xQueue, //队列句柄
    const void *pvItemToQueue, //要发送的数据
    TickType_t xTicksToWait //阻塞等待
);
BaseType_t xQueueSendToFrontFromISR( //中断中向队列头部写数据
    QueueHandle_t xQueue, //队列句柄
    const void *pvItemToQueue, //要发送的数据
    BaseType_t *pxHigherPriorityTaskWoken //中断服务程序中使用
);

3.3 读队列

1.返回值:
pdPASS:数据成功写入队列 errQUEUE_FULL:写入失败,因为队列满了
2.xTicksToWait(等待时间):
一般为0~(portMAX_DELAY-1)
portMAX_DELAY是一直阻塞到有数据可写才返回,不是阻塞到最大时间
0是无法写入数据则立即返回,不是0秒不写就返回

xQueueReceive(handle_1, &val, portMAX_DELAY); //从handle_1队列读数据到val,阻塞等待
BaseType_t xQueueReceive(...); //从队列头部读数据
BaseType_t xQueueReceiveFromISR(....); //中断中从队列头部读数据

内部的具体函数如下

BaseType_t xQueueReceive(
    QueueHandle_t xQueue, //队列句柄
    void *const pvBuffer, //复制队列的数据
    TickType_t xTicksToWait //阻塞等待
);
BaseType_t xQueueReceiveFromISR( //中断中从队列头部读数据
    QueueHandle_t xQueue, //队列句柄
    void *const pvBuffer, //复制队列的数据
    BaseType_t *pxHigherPriorityTaskWoken //中断服务程序中使用
);

3.4 队列邮箱

队列邮箱长度是1,写是覆盖(即不需要等待时间),读是偷看(即不会删除)

//BaseType_t
xQueueOverwrite(handle_1, &dat); //发送数据dat到handle_1邮箱,如果邮箱满了,会覆盖原有数据
xQueueOverwriteFromISR(handle_1, &dat, &xHigherPriorityTaskWoken); //中断中发送数据dat到handle_1邮箱
xQueuePeek(handle_1, &dat, portMAX_DELAY); //从handle_1邮箱读数据到dat,不删除数据
xQueuePeekFromISR(handle_1, &dat); //中断中从handle_1邮箱读数据到dat,不删除数据

3.5 队列集

【FreeRTOS基础入门】队列集_freertos如何使用队列集-CSDN博客

//创建队列集  添加队列搭配队列集  读取队列集
QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength );
BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet );
QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, TickType_t const xTicksToWait );
// 声明队列句柄
QueueHandle_t queue1, queue2;
// Task1,向队列1发送数据
void Task1(void *pvParameters) {
    int data = 1;
    while (1) {
        // 向队列1发送数据
        xQueueSend(queue1, &data, portMAX_DELAY);
        data++; // 更新数据
        vTaskDelay(pdMS_TO_TICKS(1000)); // 1秒延迟
    }
}

// Task2,向队列2发送数据
void Task2(void *pvParameters) {
    int data = 100;
    while (1) {
        // 向队列2发送数据
        xQueueSend(queue2, &data, portMAX_DELAY);
        data += 100; // 更新数据
        vTaskDelay(pdMS_TO_TICKS(2000)); // 2秒延迟
    }
}

// Task3,从队列集中读取数据
void Task3(void *pvParameters) {
    QueueSetHandle_t queueSet;
    QueueHandle_t member;
    BaseType_t xResult;
    int received_data;
    // 创建队列集
    queueSet = xQueueCreateSet(QUEUE_LENGTH * 2);
    // 将队列1和队列2添加到队列集中
    xQueueAddToSet(queue1, queueSet);
    xQueueAddToSet(queue2, queueSet);
    while (1) {
        // 等待从队列集中读取数据
        member = xQueueSelectFromSet(queueSet, portMAX_DELAY);
        // 判断是哪个队列有可读数据
        if (member == queue1) {
            // 从队列1中接收数据
            xQueueReceive(queue1, &received_data, 0);
            printf("Task3 received data from queue1: %d\n", received_data);
        } else if (member == queue2) {
            // 从队列2中接收数据
            xQueueReceive(queue2, &received_data, 0);
            printf("Task3 received data from queue2: %d\n", received_data);
        }
    }
}

//主函数中代码---创建两个队列放数据
queue1 = xQueueCreate(QUEUE_LENGTH, sizeof(int));
queue2 = xQueueCreate(QUEUE_LENGTH, sizeof(int));
xTaskCreate(Task1, "Task1", 128, NULL, 1, NULL);
xTaskCreate(Task2, "Task2", 128, NULL, 1, NULL);
xTaskCreate(Task3, "Task3", 128, NULL, 2, NULL);

4 信号量(semaphore)

4.1 二进制、计数型信息量创建

SemaphoreHandle_t sem1;
SemaphoreHandle_t sem2;
sem1 = xSemaphoreCreateCounting(10, 0); //创建计数值信号量 (最大计数值,初值)
sem2 = xSemaphoreCreateBinary(); //创建二进制信号量,初值为0
//动态
SemaphoreHandle_t xSemaphoreCreateBinary(void); //二值信号量
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount); //计数信号量(最大计数值,初值)
//静态创建
xSemaphoreCreateBinaryStatic(SemaphoreHandle_t xSemaphoreBuffer); //二值信号量
xSemaphoreCreateCountingStatic(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, SemaphoreHandle_t xSemaphoreBuffer); //计数信号量

4.2 获取和拿走

xSemaphoreTake(sem2, portMAX_DELAY);
xSemaphoreGive(sem2);
//在任务中
xSemaphoreGive(xSemaphore); //获取信号量
xSemaphoreTake(xSemaphore, portMAX_DELAY); //拿走信号量
//在中断中
xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken); //获取信号量
xSemaphoreTakeFromISR(xSemaphore, &xHigherPriorityTaskWoken); //拿走信号量

5 互斥量(mutex)

  • 24
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值