05-2_创建任务函数的进一步实验
- 创建的任务传入handle,之后通过handle引用任务或者删除任务。
- 空闲任务释放堆和栈
- 同一个函数可以创建不同的任务,因为他们对应的栈不同,每个任务有自己的栈,互不影响
- 如何确定任务需要多大的栈空间需要你研究
直播四小时讲解栈
- 任务--定义
- 运行起来的函数
- 不仅仅是一个函数或者代码,
- 运行的位置
- 运行的环境
- arm架构汇编
- Flash中存取着指令和汇编码
- cpu读Flash得到指令,然后再执行指令
- SP就是指的栈
- LR表示返回地址
- PC表示当前指令地址
- CPU虽然强大,但是还是要靠指令取指挥他,指令保存在Flash上,即机器码
- 由汇编码得到机器码
- PUSH入栈,入栈的本质就是写内存
- POP是读内存
- 硬件中断
- 硬件保存一部分reg
- 软件要保存一部分用到的reg
- 任务切换中是保存全部reg
- 句柄---结构体指针
- 每个任务都有自己的栈
- 栈的大小依赖
- 局部变量
- 调用深度
- 栈的分配
- FreeRTOS划分出一个巨大的数组给栈分配
- 栈的大小依赖
- 高优先级不执行完,低优先级任务永远无法执行
- 同等优先级任务轮流执行:时间片轮转
- TICK中断进行任务调度
- 间隔可以配置,一般为1ms
- 产生中断就调用tick中断函数
- 取出下一个任务
- 切换任务
- 保存当前任务
- 回复新Task
- 同是0优先级,空闲任务会礼让其他任务
06-1_任务状态理论讲解
06-3_vTaskDelay和vTaskDelayUntil
- vTaskDelay是任务暂停的时间是固定的
- vTaskDelayUntil让任务周期性执行,总的运行暂停时长是固定的
自杀与他杀
- 自杀不能清理尸体,需要空闲任务清理并释放栈
- 他杀,凶手处理尸体,释放栈
- 空闲任务只能是running或者ready状态
任务调度算法
- 时间片轮转可以配置
- 是否支持抢占也可以配置
- 空闲任务是否yield别人
同步与互斥
- 同步比较浪费CPU资源
- 再等待的时候让当前任务进入阻塞状态
- 全局变量实现互斥的变量切换时间太长了
08-1_队列的理论讲解
- 队列---传送带
- 队列要有存放数据的缓冲区
- 指针就是buffer
- 等待数据的写入(没有空间)和读取(没有数据)
- 优先级高的先读取
- 同优先级等待时间长的先读取
08-2_队列的常规使用
- 队列实现同步,读取数据,最后一千万计数为
1.3s
- 队列可以理解为一个容器,你放进去东西,别人能取到东西,就代表能使用,取不到东西就代表不能使用
- 释放这个队列就把东西放到这个容器里,让别人能取到。
- 创建队列可以指定
- 多少个元素
- 每个元素的大小
- 队列实现互斥
- 向你创建的互斥队列中写入数据
- 读取到数据就代表获取了这个东西,不需要考虑写入了啥数据
task1
是ready,但是task2
是running- 可以通过
vTaskDelay
让task2
主动放弃 - 或者通过
taskYIELD()
,这个更优 - 或者设置不同的优先级
- 可以通过
- 传输的时候可以选择
结构体struct
加入ID
,然后区分数据源谁传入的。 - 数据量过大直接传输地址最快。
- 队列中可以传入数据,数据要么是值要么是地址
08-3_队列集(Queue Set)
-
队列集中放的是队列,每个队列中放的是其分组的数据。
-
队列集实际上也是个队列。
-
每个队列的
handle
指向队列集。 -
读一次queue set,就会读一次queue
-
例子
static QueueHandle_t xQueueHandle1; static QueueHandle_t xQueueHandle2; static QueueSetHandle_t QueueSet; /*-----------------------------------------------------------*/ void xTask1Function(void * param) { int i = 0; while(1) { xQueueSend(xQueueHandle1, &i, portMAX_DELAY); i++; vTaskDelay(10); } } /*-----------------------------------------------------------*/ void xTask2Function(void * param) { int i = -1; while(1) { xQueueSend(xQueueHandle2, &i, portMAX_DELAY); i--; vTaskDelay(20); } } /*-----------------------------------------------------------*/ void xTask3Function(void * param) { QueueSetMemberHandle_t handle; int i; while(1) { /* 1. read queue set: which queue has data */ handle = xQueueSelectFromSet(xQueueSet1, portMAX_DELAY); /* 2. read queue */ xQueueReceive(handle, &i, 0); /* 3. print */ printf("get data : %d\r\n", i); } } /*-----------------------------------------------------------*/ int main( void ) { #ifdef DEBUG debug(); #endif prvSetupHardware(); /* 1. 创建2个queue */ xQueueHandle1 = xQueueCreate(2, sizeof(int)); if(xQueueHandle1) { printf("create queue1 fail\r\n"); }else { } xQueueHandle2 = xQueueCreate(2, sizeof(int)); if(xQueueHandle2) { printf("create queue2 fail\r\n"); }else { } /* 2. 创建queue set */ xQueueSet1 = xQueueCreateSet(4); /* 3. 把2个queue添加进queue set */ xQueueAddToSet(xQueueHandle1, xQueueSet1); xQueueAddToSet(xQueueHandle2, xQueueSet1); /* 4. 创建三个任务 */ xTaskCreate(xTask1Function, "Task 1", 100, NULL, 1, NULL); xTaskCreate(xTask2Function, "Task 2", 100, NULL, 1, NULL); xTaskCreate(xTask3Function, "Task 3", 100, NULL, 1, NULL); /* 启用任务调度器 */ vTaskStartScheduler(); /* Will only get here if there was not enough heap space to create the idle task. */ return 0; }
-
队列集能让你统筹的管理队列,不用一个个的去处理每个队列。让你能随时从每个队列中获取数据,同一件事情的不同处理方式,那个方式输入进来,相应那个方式。
09-1_信号量的理论讲解
- 信号量不能传输数值,只能表示资源的数量。
- 信号量为正数,且能限制最大值
- 步骤
- 创建一个结构体
Semaphpre
- give/take函数构建
- 创建一个结构体
- 二进制信号量和技术型信号量