数据传输的方法 - 目录
参考《FreeRTOS入门与工程实践(基于DshanMCU-103).pdf》
1. 数据传输的方法
1.1 任务之间如何传输数据
数据传输的多种方法比较:
数据个数 | 互斥措施 | 阻塞-唤醒 | 使用场景 | |
---|---|---|---|---|
全局变量 | 1 | 无 | 无 | 一读一写 |
环形缓冲区 | 多个 | 无 | 无 | 一读一写 |
队列 | 多个 | 有 | 有 | 多读多写 |
1.2 队列的本质
队列中,数据的读写本质就是环形缓冲区,在这个基础上增加了互斥措施、阻塞-唤醒机制。
- 如果这个队列不传输数据,只调整"数据个数",它就是信号量(semaphore)。
- 如果信号量中,限定"数据个数"最大值为1,它就是互斥量(mutex)。
画图演示队列的"阻塞-唤醒"机制。
1.3 说明
全局变量就只能传递一个数据,或者一个结构体,还有可能传递错误数据
- 假设有两个坐标x,y;x更新了,但是y未更新,现在要进行传参,现在就是错误的数据
- 如果我们不适用阻塞和唤醒机制的话,CPU资源的利用率比较低
全局变量就只能传递一个数据,或者传递一个结构体;
所以使用全局变量有很多隐患,效率低,没有互斥,也没有阻塞和唤醒
2 环形buffer 读写数据
环形缓冲区:在两个任务之间传递参数
环形缓冲区就是一个数组,最开始是空的,从0开始写,从0开始读, r=0,w=0
- 怎么写入数据,怎么读取数据呢?
- 首先得判断里面有没有数据,是否空,或者是否满(不能再写入了)
2.1 如何表示空?
- r==w,读位置 = 写位置就表示空
2.2 如何表示满?
- 先知道如何写入数据,才知道怎么表示满
2.3 写数据:
if(未满)
{
buf[w] = value;
w++; //下次写入的位置
if(w == 8) //溢出
w = 0; //清零
}
写到第8个了(溢出),赋值0
2.4 读数据
读取操作:
if(r != w) //如果读位置不等于写位置,表示有数据
{
value = buf[r];
r ++; //下次读取的位置
if(r == 8) //越界
r = 0; //清零
}
所以:
- 变量r和w表示:下次读写的位置
- 在这个场景里面,假设我一直都不读取,r一直=0,不断的写,w指向7,如果我把最后的7给写了,那w=0,现在就是r=0,w=0,表示满,那不就和“空”冲突了吗!
2.5 满
所以满的表示方法如下:下一个写位置=读位置
- 现在w=7,指向了7这个地方,但是现在不写,本来可以写8个数据 ,现在这里面只有7个数据,下一个写位置就是0,等于读位置就表示满!
2.6伪代码
用一段伪代码来演示,在写函数里判断满或者未满~
int next_w = w+1; //下一个写位置
if (next_w == 8)
next_w = 0;
//这个时候就可以判断是否满了
if(next_w == r) //如果满(下一个写位置等于读位置)
{}
else if (next_w != r) //如果不满(下一个写位置不等于读位置)
}
2.7 有缺陷的环形buffer
(新增一个)写的数量num
int buf[8]; //
int r=0,w=0; //读写位置
int num; //(新增一个)写的数量
读写操作
这个方法是有缺陷的!~
缺陷:num是全局变量,两个任务都想修改它,这样就会有问题
假设num = 10
num++需要的操作
- 读入某个寄存器 R0
- 寄存器R0++
- 把新的值写入num所在的内存
- 任务A是num++,执行到圈1的时候就被切换了
- 任务B成功读取数据,让num–,从10变成9了
- 假设这时任务又切换了,执行圈2,数据++之后变成了11 !!!
这样就乱套了
所以不需要加这个num变量,
用 r 和 w ,就没有一个变量同时被改变,没有一个变量同时被改变,没有一个变量同时被改变,就不会出现上述问题了,不用考虑阻塞和唤醒,也不用考虑效率的问题。
3 学习视频
【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS(FreeRTOS教程 基于STM32,以实际项目为导向)】 【精准空降到 01:31】 https://www.bilibili.com/video/BV1Jw411i7Fz/?p=29&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933&t=91