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); //拿走信号量