尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

FreeRTOS 学习笔记 队列管理 中断管理 资源管理

FreeRTOS 学习笔记 队列管理 中断管理 资源管理 第二章 队列管理一、队列是什么队列是任务间通信的主要手段任务之间通过队列相互传递消息任务与中断之间也可以通过队列通信。队列的本质是一块固定大小的数据缓冲区数据项按先进先出的方式排队发送方把数据放入队尾接收方从队头取出数据。二、队列的两大特性第一队列能存储有限个长度确定的数据项创建队列时必须同时指定队列深度和每个数据项的字节数。第二数据以复制的方式进出队列发送方把数据复制进队列接收方把数据复制出队列发送完成后原变量可以放心复用。三、队列的阻塞读写机制任务读取空队列时可以指定超时时间阻塞等待数据到来写入满队列时也可以指定超时时间阻塞等待空间空出超过超时时间后任务自动恢复就绪。多个任务同时等待同一个队列时优先级最高的任务先被唤醒优先级相同时等待时间最长的任务先被唤醒。超时时间以系统节拍为计数单位portTICK_RATE_MS 可以把毫秒换算成节拍数portMAX_DELAY 表示无限等待使用它需要先在配置文件中把 INCLUDE_vTaskSuspend 设为 1。四、队列的核心 API 函数xQueueCreate 负责创建队列第一个参数是队列深度第二个参数是每个数据项的字节数成功返回队列句柄内存不足时返回空指针。xQueueSendToBack 把数据放入队尾xQueueSendToFront 把数据放入队头xQueueSend 与 xQueueSendToBack 完全等价发送成功返回 pdPASS队列已满返回 errQUEUE_FULL。xQueueReceive 从队列取走一个数据项xQueuePeek 只查看队首数据而不取走接收成功返回 pdPASS队列为空返回 errQUEUE_EMPTY。这些函数的参数除队列句柄外还有数据指针和超时时间接收缓冲区必须足够大能容纳完整的数据项。五、使用队列的注意事项数据在队列中按值复制传递这一点相对安全但若队列传递的是指针则必须保证指针指向的数据在通信期间始终有效。应安排一个任务专门管理这块数据的生命周期避免多个任务同时读写同一块数据造成竞争缓冲区一旦释放任何任务和中断都不应再访问它。书中示例由两个发送任务向一个接收任务发送结构体数据接收任务优先级较低队列满时发送任务会阻塞等待直观展示了队列的阻塞特性。第三章 中断管理一、中断概述实时系统必须响应外部事件中断处理程序应尽量简短把耗时的处理工作放到任务中完成这种模式称为延迟处理。只有以 FromISR 结尾的 API 函数才允许在中断服务例程中调用普通 API 函数不能用于中断。二、二值信号量二值信号量常用于任务与中断之间的同步可以理解成深度为 1 的队列。任务调用 xSemaphoreTake 获取信号量获取不到时阻塞等待获取成功返回 pdPASS超时未取到返回 pdFALSE中断调用 xSemaphoreGiveFromISR 给出信号量。vSemaphoreCreateBinary 负责创建二值信号量。二值信号量不累积事件任务取走信号量之前再次发生的中断不会被记录事件可能丢失这是它的固有局限。三、pxHigherPriorityTaskWoken 参数xSemaphoreGiveFromISR 的第二个参数是指向该变量的指针调用前应把变量初始化为 pdFALSE若给出信号量唤醒了更高优先级的任务该变量会被置为 pdTRUE。此时应在中断退出前执行上下文切换让被唤醒的高优先级任务立即运行从而保证系统响应及时。四、计数信号量计数信号量可以理解成深度大于 1 的队列能避免二值信号量丢失事件的问题。xSemaphoreCreateCounting 负责创建计数信号量第一个参数是最大计数值第二个参数是初始计数值。计数信号量有典型两种用途。其一是事件计数初始计数值为 0中断每给出一次信号量计数值加一任务每取走一次计数值减一计数值就是待处理事件的个数连续发生的中断不会丢失。其二是资源管理初始计数值设为可用资源总数任务使用资源前取走信号量用完资源后归还信号量计数值代表当前剩余的可用资源数。五、在中断中使用队列队列的中断安全版本是 xQueueSendToFrontFromISRxQueueSendToBackFromISR 和 xQueueReceiveFromISR它们比普通版本多一个 pxHigherPriorityTaskWoken 参数用法与信号量版本一致。书中示例是生成任务向队列发送整数后触发中断中断从队列取出整数并转换成字符串再转发到打印队列打印任务阻塞接收并打印完整展示了中断与任务通过队列协作的流程。中断中传递的字符串以静态常量方式保存保证中断未运行时数据依然有效。六、中断嵌套的配置中断嵌套需要在 FreeRTOSConfig 配置文件中定义两个常量。configKERNEL_INTERRUPT_PRIORITY 设置内核节拍中断的优先级。configMAX_SYSCALL_INTERRUPT_PRIORITY 规定允许调用中断安全 API 的中断的优先级界限优先级数值不大于它的中断才能调用 API超出界限的中断禁止调用任何 FreeRTOS API。书中示例把两个常量分别设为 3 和 1此时优先级 1 到 3 的中断可以调用 API优先级 4 及以上的中断不能调用。在 ARM Cortex M3 上优先级数值越小实际优先级越高例如 STM32 把 0 定为最高优先级把 15 定为最低优先级。因此不要给中断分配 0 号优先级或比 configMAX_SYSCALL_INTERRUPT_PRIORITY 更高的优先级否则调度器将无法管理该中断。第四章 资源管理一、为什么要进行资源管理多任务系统中多个任务可能同时访问同一个共享资源比如显示器或寄存器这会造成数据混乱。书中示例是两个任务同时向 LCD 写字符串最终屏幕上显示出两段拼接错乱的文字。另一个示例是读改写操作一句简单的 PORTA 赋值语句会被编译成读取、修改、写回三步任务可能在中间被切换走导致修改结果丢失。变量的更新也不是原子的比如在 16 位机器上更新 32 位变量就可能被中断拆散。因此访问共享资源需要互斥机制确保任务从开始访问资源到完成访问的整个过程拥有排他性。二、可重入函数可重入函数是安全的函数它的参数通过栈或寄存器传递局部变量也分配在栈上每个任务都有自己的栈和寄存器副本所以多个任务可以同时调用它。不可重入函数使用全局变量或静态变量所有任务访问的是同一份数据这样的函数被多个任务同时调用就会出问题。判断函数是否可重入就看它是否只依赖参数和局部变量而不依赖全局或静态数据。三、临界区临界区用 taskENTER_CRITICAL 和 taskEXIT_CRITICAL 这对函数包围起来进入后中断被屏蔽代码执行不会被切换打断。临界区通过屏蔽中断实现互斥会直接影响系统的中断响应所以临界区内的代码必须尽量简短并尽快退出。临界区支持嵌套内核用嵌套深度计数只有所有嵌套都退出后中断才恢复。四、挂起调度器挂起调度器是另一种建立互斥的方式用 vTaskSuspendAll 和 xTaskResumeAll 包围需要保护的代码段。挂起调度器不屏蔽中断只是禁止任务切换中断仍然可以运行但中断触发的切换请求会被推迟到调度器恢复后执行。调度器恢复时xTaskResumeAll 返回 pdTRUE 表示有被推迟的切换请求否则返回 pdFALSE。挂起调度器同样支持嵌套嵌套计数归零后才真正恢复调度。挂起期间不能调用任何可能导致任务阻塞的 FreeRTOS API打印函数用挂起调度器保护就是典型的应用场景。临界区适合保护很短的代码挂起调度器适合保护较长的代码段因为它不会关闭中断。五、互斥量互斥量是特殊的二值信号量专门用于互斥访问创建函数是 xSemaphoreCreateMutex。互斥量与普通二值信号量的区别有两点。第一互斥量必须由获得它的那个任务释放其他任务不能替它释放而同步用的二值信号量没有这种归属约束。第二互斥量内置优先级继承机制二值信号量没有。使用互斥量的典型模式是先调用 xSemaphoreTake 获取互斥量访问共享资源再调用 xSemaphoreGive 释放互斥量获取时一般使用无限等待。六、优先级翻转与优先级继承优先级翻转是指高优先级任务等待低优先级任务持有的互斥量时反而被中等优先级任务抢占导致高优先级任务的执行被拖慢这是一种不理想的行为。互斥量的优先级继承机制可以缓解这个问题当低优先级任务持有互斥量时它会临时继承所有等待该互斥量的任务中的最高优先级。经过继承后低优先级任务以高优先级运行就不会被中等优先级任务打断从而尽快释放互斥量。优先级继承只能最小化优先级翻转的影响并不能完全消除它所以设计中仍要尽量避免长时间的互斥持锁。七、死锁死锁又叫僵局是指两个任务各自持有一个资源同时又都在等待对方持有的资源结果谁都无法继续执行。书中示例是任务 A 持有互斥量 X 并等待互斥量 Y任务 B 持有互斥量 Y 并等待互斥量 X两个任务都进入阻塞永远无法推进。避免死锁的关键是良好的设计比如保证所有任务按相同的顺序获取互斥量尽量避免持有一个互斥量时再去获取另一个。八、门卫任务门卫任务是一种用队列代替互斥量的资源管理方式只有门卫任务能直接访问共享资源其他任务要使用资源时把请求通过队列发送给门卫任务由它统一处理。书中的例子是打印资源两个打印任务把要打印的字符串指针发送到队列门卫任务从队列接收并完成实际打印任何任务和中断都无法绕过队列直接访问打印输出。节拍钩子函数也能通过中断安全的队列发送函数向门卫任务发送消息因为消息在队列中排队所以中断上下文同样可以安全地请求资源。门卫任务的缺点是它运行在最低优先级一旦它被阻塞所有等待资源的高优先级任务都会跟着被延迟所以门卫任务要尽量简单只做最少的处理。
返回列表