【RTOS训练营】环形缓冲区、AT指令、预习安排和晚课提问

一、环形缓冲区

在上一次课中,只讲了UART的硬件协议,没有讲环形缓冲区。

本节课就讲解环形缓冲区。

环形缓冲区它就是一个数组,是一个长条形的缓冲区。

开始的时候读写位置都指向0:r = w = 0 ,所谓读写位置就是数组的下标。

想想看,一开始的时候就是空的,那空是怎么判断的? if (r == w) 就是空

1.1 写操作

那么怎么写?写一个数据:

buf[w] = val;
w = w+1;

需要注意的是,当w到达数组的最右边时:你要防止w越界。

w = 6时,下一个位置是多少?那下一个位置应该是绕回来,变成0。

用数学表达式就这样:w = (w+1) % len,即w = (6+1) %7 = 0

上图里面长度是7(0~6共7个),当w=6的时候,下一个w就是7(即0)。

这个计算长度的方法,有没有改进的方案?

在单片机里面,除法运算是非常消耗CPU资源的,并且还得添加除法库,太浪费flash了。

所以能不用除法,就不要用除法。

我们怎么做呢?

我们可以让长度, len = 2的n次方,比如2、4、8、16、32。

这时候模的运算,就可以变成与的操作:

len = 8;
w % 8 就是 w & (8-1)

下面我来画图举例:

大家要用位运算来看这个问题,8-1=7,在二进制里面就是三个1,

val = 0~7时, val &7 都等于 val

val等于8时,val & 7 = 0

当然也可以使用if来判断,比如:

if (w++ >= 7)
{
	w=0;
}

从效率上来说:他先判断,判断之后再去跳转,指令有多条,
如果使用位的清除操作的话,只需要一条指令,

但是现在芯片的运行速度都那么高,浪费一两条指令问题不大,主要还是使用位清除代码更漂亮。

另外,如果是直接溢出,也是可行,不过只能是指定长度,不实用。

比如,比如读写位置都用unsigned char类型:unsigned char r, w;

w =255; w++后就是0,这个限制就很明显了,长度必须是256,

如果是unsigned int w的话,这个长度就必须是: 2^32。

缓冲区空的时候是:r == w

满的时候呢?如果真正地满了:

来看这图,假设w等于6时候, 还要写入一个数据:
写入数据之后,W就指向下一个位置,就是0

这个时候,即缓冲区满的时候,也是 r== w,空和满都是:r == w。

这样的话我们不好写程序判断,所以就退一步,满:下一个写位置 == r

我在写数据之前先判断一下: 下一个写的位置, 是不是等于读的位置,

如果下一个写的位置等于读的位置的话, 我就假设满了,

我宁愿空出一个空位, 就是为了方便写程序。

空: r==w
满 : (w+1)%len == r

1.2 读操作

我们再来讲读的操作。

请添加图片描述

这个图里面,w的位置已经绕了一圈。

读的时候怎么读:

val = buf[r];
r = (r + 1) %len

去读出一个数据之后,要更新一下读的位置,读位置的更新,也要 % len,r位置也会绕圈

请添加图片描述

这图里面,r的位置也要绕圈了.

所以什么叫环形缓冲区,你不断的写,不断的读,不断写不断的读,r,w会绕着跑好几圈。

二、AT指令

对于at指令,我们也只是使用at指令,来使用外接的WiFi模块。

并不涉及WiFi模块里面深层次的知识,后面我们我们会编写串口程序来操作WiFi模块,就会用到环形缓冲区。

但比如说我接收到数据之后,我会马上就处理完,马上清空整个buffer,我自然就不需要环形缓冲区那么复杂。

AT指令在视频中已经讲解很详细了,有问题的学员,可以去论坛提问:百问网官网:点击答疑论坛进入

三、预习安排

布置一下预习的视频和文档:

请添加图片描述

四、晚课学员提问

1. 问: %也是使用除法吗?

答: 是的,求模也是除法。

2. 问: 环形缓冲区操作中,不用做互斥吗?只能一对一?

答: 对环形缓冲区,如果说只有一个消费者(读数据)、只有一个生产者(写数据)的话,就不需要做互斥操作。如果有多个人写数据、或者有多个人要使用数据,那就要做一些互斥的操作。

3. 问: RTT的环形缓冲区,用一个位作为方向,是不是更优美?

答: 在FreeRTOS里,是这样的:

请添加图片描述

正常来说,我们写入新的数据时应该写红色位置

写红色位置,就表示说你后面写入的数据呢,是到后面才读

先进先出的关系:FIFO( First in first out)

那如果说我有些数据非常紧急,我想把它写到最前面去: 就是图片上蓝色位置

这也是可以的, 这就是环形缓冲区的增强版

RTT的环形缓冲区的方向,是不是表示这个意思?我估计,我还没有去看到rtt的具体实现

4. 问: 环形数组保存的是字符,如果我的串口 接收的是字符串,如果接收的一组字符串没有 处理完。被覆盖了怎么办?

答: 环形缓冲区可以大概率的避免数据的丢失,但是如果数据一下子来很多的话,无论什么算法都没有办法避免数据的丢失。

因为你分配了100兆的空间,我就跟你说:突然要来1000兆的数据,
你分配了1000兆的空间,我就跟你说:突然要来1T的数据。

绝对的、保证数据不丢失的方法是没有的。

我们写程序的时候,根据实际的使用情况,来确定buffer的大小。

5. 问: 假设环形缓存区是5,写入15个数据,怎么判断数据的正确性?

答: 写数据的时候, 你可以判断:满的话就返回错误。

6. 问: 串口的环形缓冲区的写和读是同时进行的,还是分别进行的?

答: 在多任务系统中,读和写可以同时进行。对于多任务系统 ,本来就是任务可以同时运行嘛,假设有一个写任务、一个读任务,他们就是同时操作这个buffer。

7. 问: 环形缓冲区中,被覆盖了怎么办?

答: 增加容错处理, 或者增加环形缓冲区的长度。

8. 问: 实际应用很少单字节读写的吧?

答: 首先串口数据的来源肯定是一个字符一个字符的接收,所以最底层的环形缓冲区肯定是单字节。

9. 问: 环形缓冲区满了怎么处理,读时会R==W就认为空了?

答: 满了就丢弃,我给大家贴一下代码。

上面的代码,是GIT仓库里面的,大家更新一下这个仓库就可以看到:

10. 问: 一般工程上的容错处理是怎么做?

答: 一般出错的话,就是:

  1. 数据来的太多
  2. 处理不过来

如果数据本来就那么多, 你就只能够从处理的效率上入手

比如说:

  1. 改进处理算法
  2. 在RTOS中,提高优先级
  3. 更换频率更快的芯片

或者说:硬件设计上就要多次重传。

11. 问: “我宁愿空出一个空位, 就是为了方便写程序”,是不是意味着环形缓冲的能装的最大数据个数都是 最大长度-1个?

答: 你可以用其他办法,就比如说你在环形缓冲区中增加一个count变量。

根据这个count变量来分辨是空还是满,这样的话,这个环境缓冲区,满的时候就是真正的满了。

12. 问: 程序里面是什么条件的时候读缓冲区?

答: 比如我们的main里面,就可以一直读环形buffer,他一直读、等待你的输入,根据你的输入来操作。

13. 问: 环形缓冲区和读写一般数组有没区别?

答: 没什么区别,主要就是调整读和写的位置,可以从尾部回到头部。

14. 问: 环形缓冲区有没有什么满了触发中断之类的?

答: 基本上没有,这本来就是软件上的概念,满了之后你可以返回错误。

15. 问: 其实可以移植一个成熟的唤醒缓冲的程序,比如Linux中的kfifo或者别的?

答: 你可不要去移植Linux里面的那些kfifo,linux考虑的东西太全了,非常庞大。

16. 问: 一个mcu要用到3个UART需要几个环形缓冲区??

答: 这要看你的设计,既然是缓冲区,就用来协调双方的。
如果说我的程序要处理数据非常快,你给的数据根本就不够处理,那我干嘛还要用环形缓冲区。

17. 问: 串口数据发送为何不需要缓冲区?

答: 也可以使用,主要是对于数据的发送,我们可以控制。

对于数据的接收,我们不知道数据什么时候来,所以使用环形缓冲区来接收数据。

18. 问: 串口中断收发例程中串口发送丢数据根本原因是什么?收的不对吗。

答: 发送是丢数据?这个问题挺容易查:

  1. 确认数据是否写入硬件寄存器
  2. UART FIFO是否满了,导致写入无效。

19. 问: 那我做数据采集的时候那不就是必须要用环形缓冲区了吧?

答: 还是那句话,如果我收到一个完整的数据之后可以马上处理,马上清空buff,就不需要环形缓冲区

20. 问: esp8266 可以和手机直连互发消息么,为什么不采用这种形式,而采用连接同一个WIFI再发消息?

答: 可以,但是讲到smartconfig就偏离rtos的主线了。

21. 问: 会讲mqtt吗,之前做过这个连接阿里云?

答: MQTT不会讲,如果大家感兴趣的话,讲完RTOS时候,我们可以用mqtt来做一下实验,我们同事对mqtt了解挺多。

22. 问: static定义静态变量,定义的变量地址会不会改变?

答: 不会变,static变量保存在data段。

23. 问: 除了轮询消抖,还有其它消抖方式的吗?

答: 对于按键消除抖动,我们一般来说都会用到定时器。

在很多系统中,都是使用定时器来处理消抖。

在中断服务程序里面,他并不是马上去确定按键。

而是启动一个定时器,说:20ms后处理。

既然是抖动,就是说这个电平在不断的、快速的变化,多次产生中断。

每产生一个中断,都把定时器的时间往后推20ms。

最后一个中断产生时,他也会往后推20ms。

也就是说多次中断,他们最终只会触发一次定时器。

最后一次中断发生时,抖动已经消除。

最后一次中断,再过20毫秒,这个时候再去读按键。

24. 问: Freertos 这种操作系统,相对于单片机裸机是不是简单点?操作系统是不是调用现成的库或者函数?裸机要写到底层的驱动?

答: FreeRTOS只是提供了多任务的功能,他并没有提供底层的驱动。也就是说对硬件的操作,跟你之前开发的裸机程序是完全一样的。rt-thread跟freertos的差别在于:rtt提供了驱动程序的框架。也就是说,如果别人为rtt写了驱动程序,你就可以直接拿来用

25. 问: 按键消抖 是需要带有定时器功能的io吗 还是一般io就可以了?

答: 我说的定时器是一般的定时器,不是引脚的定时器。有些芯片的引脚,它自带防抖动的功能。但是我讲了这个是一般的定时器,跟引脚没关系。

26. 问: 讲到串口的fputc和fgetc时,视频里说一定要勾选MicroLib,是因为stdio.h这个头文件引用的东西都在MicroLib中吗?

答: printf这个函数是你写的吗?不是,那他是谁提供的?就是Microlib。所以你要用printf等函数,必须勾选上这个库

27. 问: rtos在工业应用的可靠性怎么样,经过那么多年的迭代,从内核和机制而言存在bug的可能性还大吗?大家都说在可靠性要求高的工业应用场合尽量使用裸机?

答: 可靠性很好, 单纯的内核机制都很成熟了。