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


    一、环形缓冲区

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

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

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

    请添加图片描述

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

    请添加图片描述

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

    1.1 写操作

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

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

    需要注意的是,当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)
    
    • 1
    • 2

    下面我来画图举例:

    请添加图片描述

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

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

    val等于8时,val & 7 = 0

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

    if (w++ >= 7)
    {
    	w=0;
    }
    
    • 1
    • 2
    • 3
    • 4

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

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

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

    比如,比如读写位置都用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

    1.2 读操作

    我们再来讲读的操作。

    请添加图片描述

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

    读的时候怎么读:

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

    去读出一个数据之后,要更新一下读的位置,读位置的更新,也要 % 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的可能性还大吗?大家都说在可靠性要求高的工业应用场合尽量使用裸机?

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

  • 相关阅读:
    PaadleInference源码编译操作流程
    Java并发(十二)----线程应用之多线程解决烧水泡茶问题
    【深入理解设计模式】模板方法模式
    【计算机网络】传输层协议——TCP(中)
    mysql 索引
    three.js点滴yan(整理后)
    模拟问题(中)
    2024最新版JavaScript逆向爬虫教程-------基础篇之JavaScript密码学以及CryptoJS各种常用算法的实现
    P4_toturial练习1问题:ModuleNotFoundError: No module named ‘p4.tmp‘
    基于python的火车票售票系统/基于django火车票务网站/火车购票系统
  • 原文地址:https://blog.csdn.net/thisway_diy/article/details/125917450