在中断服务例程中使用队列,
选择在中断回调函数中填充带有中断模式的队列,
API
xQueueSendToFrontFromISR();
xQueueSendToBackFromISR();
xQueueReceiveFromISR();
专门用于中断服务例程
信号量用于事件通信,而队列不仅可以用于事件通信,还可以用来传递数据。
parameters 队列句柄,消息指针,是否切换调度更到任务优先级的任务
由于在创建队列时设置了队列中数据单元的长度,所以会从该指针指向的空间复制对应长度的数据到队列的存储区域,所以是拷贝传递!
当队列已满而无法将数据写入时,则返回errQUEUE_FULL
所以在填充队列时需要进行检验
两种高效队列读取串口模型
- 将接收到的字符先缓存在内存中,当接收到一个传输完成消息,或是检测到传输完成后,使用信号量让某个任务解除阻塞,这个任务将对数据缓存进行处理。
- 在中断服务中直接解析收到的字符,然后通过队列将解析后的命令发送到处理任务中。
多任务系统中由于竞争存在死锁的风险,访问外设、读-改-写操作、变量的非原子访问、函数重入
更新结构体的多个成员变量,或是更新的变量的长度超过了架构体系的自然长度均是非原子操作,
每个任务都单独维护自己的栈空间以及自身在处理器内核中寄存器组中的值,保护上下文。但是如果一个函数除了访问自己栈空间上分配的数据或者处理器内核寄存器中的数据外,不能去访问其他任何内存,那么这个函数就是可重入的!!!
互斥的关键就是需要考虑这片内存是否会被多任务共享!
当访问一个被多任务共享,或是被任务与中断共享的资源时,需要采用互斥来保证数据在任何时候都要保持一致性。这样做的目的是要确保任务从开始访问资源就具有排他性,直至这个资源有恢复到完整状态。
FreeRTOS 提供了多种特性来实现互斥。
基本临界区
taskENTER_CRITICAL()、taskEXIT_CRITCAL()
之间的代码被保护成为临界区间,只有一个入口!
用法
taskENTER_CRITICAl();
xxxxxxx
xxxxxx 需要保护的操作或资源
xxxxxxx
taskEXIT_CRITCAL();
一个典型例子,如果是使用串口输出调试信息,需要保护串口重入函数。
临界区必须只具有很短的时间,临界区是可以嵌套的,因为FreeRTOS 内核维护一个嵌套深度计数器,只有当递归退出时,才可以真正的退出临界区!
挂起调度器
也可以通过挂起调度器来创建临界区,通过这种方式创建的被保护区间是不能被其他任务打断,但是可以被中断打断,
但是开关调度器需要一定开销!
API
vTaskSuspendAll(void) 挂起调度器
portBASE_TYPE vTaskResumeAll(void) 唤醒调度器
注意:
当一个中断服务在调度器挂起过程中请求上下文
切换,那么这个请求将被挂起,直到调度器被唤醒 后才会执行,在调度器处于挂起时,不能调用任何 FreeRTOS API
互斥量及二值信号量
互斥量是一种特殊的二值信号量,用于控制在两个或多个任务之间访问共享资源。一个任务只有获得了相应的互斥锁才可以合法的去访问共享资源,否则是坚决不允许去访问。
用于互斥的信号量必须归还;
用于同步的信号量通常是完成同步之后便丢弃,不用归还!
API
xSemaphoreCreateMutex()
FreeRTOS 中所有种类的信号量的句柄都保存在类型为 xSemaphoreHandle 的变量中
互斥量在使用前必须先创建。
创建失败返回
否则返回互斥量句柄
有时候我们回创建多个优先级不同的任务,所以在运行时低优先级的任务会被高优先级的任务抢占,但是使用互斥量可以保证调度器不会打断低优先级的任务访问共享资源。
优先级继承只是暂时地将互斥量持有者的优先级提升为所有等待此互斥量的任务中优先级最高的那个级别,因此被称为优先级继承。
当多个互斥量存在时,死锁风险提升!!!
守护任务
守护任务提供了一种干净利落的方法来实现互斥功能。
守护任务是对某个资源具有唯一所有权的任务,只有守护任务才可以直接访问其守护的资源---其它任务要想访问该资源只能间接地通过守护任务提供的服务。
在创建 FreeRTOS_Inti 中创建队列时,系统内核还没有运行起来,不能使用同步手段!
采用 临界区手段 来保护最顶层的 PWM 设置调用!
创建一个临界区使用系统锁关闭调度和中断!
数据传输 BUG 当第一个字节保存 01 时 没有办法拷贝 !!!内存访问的问题!