串口缓冲区管理分析

来源:互联网 发布:mac怎么查看激活时间 编辑:程序博客网 时间:2024/04/27 20:52


 

一、 概述:

串口使用时一般包含两个缓冲区,即发送缓冲区和接收缓冲区。发送数据时,先将数据存在发送缓冲区,然后通过串口发送;接收数据时,先将接收的数据存在接收缓冲区,然后再进行读取。

 

合理恰当的使用缓冲区,不仅可以使不同设备间正常通信,而且还有助于节约内存,提高效率。

 

二、缓冲区分配管理:

方法一:

通过内存池实现

 

1、数据结构:

 

struct _CHN_POOL_MGR

{

    u8 buffer[BUF_SZ];

    u32 free_bitmap;

};

 

参数含义:struct _CHN_POOL_MGR:内存池的数据类型

 

buffer:缓冲区,大小为BUF_SZ  192

 

free_bitmap:标志位。

 

注意:缓冲区又分为若干块,每块大小BLK_SZ

 

 

 

free_bitmap标志缓冲区块中的空闲块和被使用块,1表示空闲,0表示被使用

 

 

 

注:阴影部分表示存放着数据

 

free_bitmap初始化为(1 << (sizeof(chn_pool_mgr.buffer) / BLK_SZ)) -1即(1 << (2^10/2^6)) -1,即二进制数11111...11111b,共16个1,缓冲区块全部空闲

 

alloc_a_slot()函数分配缓冲区块:检测free_bitmap值,将空闲的缓冲区块标号较小的块分配,返回分配的缓冲区块的标号

 

free_bitmap值

分配的缓冲区块标号

alloc_a_slot()返回值

1111...111111

0

0

1111...111100

2

2

1111...110010

1

1

1111...000100

2

2

//不是太明白????

 

 

struct _CHN_SLOT

{

    s16 tx; 

    s16 rx;

    /*the current count of this channel */

    s16 data_cnt; 

    s16 data_max;

};

 

参数含义:struct _CHN_SLOT:记录缓冲区读写状态的结构体

 

tx:记录缓冲区块标号和数据的写入位置(具体存放如图所示)

rx:记录缓冲区块标号和数据的读取位置(具体存放如图所示)

 

data_cnt:记录缓冲区中未读取的数据量

data_max:向缓冲区中写入数据时,缓冲区中允许存在的最大数据量

 

 

tx,rx数据含义:

 

 

 

 

 

2,实例分析

 

1)向缓冲区中写数据:

 

向缓冲区中写数据,每次写90个字节,写两次。

 

初始状态:

假设struct _CHN_SLOT结构体中各参数均为初始状态:tx = rx = INVALID_PTR,即(INVALID_BLK_NO << BLK_NO_SHIFT),data_cnt为0,data_max为UART_MAX_LEN

 

内存池的状态如下:

 

 

写数据:

 

alloc_a_slot()分配缓冲区块:

 

检测free_bitmap,分配缓冲区块标号为2的块;

tx记录缓冲区块标号及写入数据位置(0x80);

缓冲区块最后一个字节置为INVALID_BLK_NO;

 

变为如下状态:

 

 

写入90个数据:

由于90 > BLK_SZ-1(一个块存放数据的最大字节数),所以再次调用alloc_a_slot()分配缓冲区块

 

检测free_bitmap,分配缓冲区块标号为4的块;

tx记录缓冲区块标号及写入数据位置(0x100);

缓冲区块最后一个字节置为INVALID_BLK_NO;

此外,还要使标号为2的块的最后一个字节记录下一块的标号(4),最后将剩余的数据写入,tx记录数据位置(0x11B)

 

 

写入完成后,各参数状态如下:

 

 

第二次写入数据:

 

与上面类似,根据tx记录的缓冲区块标号及数据位置继续向后写。

 

 

最终变为如下状态:

 

 

 

 

 

3)从缓冲区中读数据

从缓冲区中读数据,每次读40个,读完为止。

 

假设此时缓冲区状态,及各参数如下:

 

 

 

 

data_cnt为180

 

 

此时开始读取数据,rx记录缓冲区标号及数据位置,成功读取40个数据后变为:

 

 

继续读数据,标号为2的缓冲区块内的数据读完,由该块内最后一个字节得知下一缓冲区块的标号,rx记录,此时,各参数状态如下:

 

 

 

 

继续读取,data_cnt变为0,读取结束。

 

 

小结:

由以上分析可知,使用内存池的方法,通过检测free_bitmap可使缓冲区被多个任务共同使用,节约空间。

 

 

 

方法二:

利用循环队列实现

 

1、数据类型:

struct _CHN_SLOT

{

int tx, rx;

u8 buf[BUFFER_LEN];

};

 

参数含义:

tx:记录缓冲区写入位置

rx:记录缓冲区读取位置

buf:缓冲区

 

 

2、方法实现:

 

每存入一个字节,tx后移一位,每取走一个字节,rx后移一位

 

 

 

当tx移至缓冲区结尾时,若缓冲区头部已读取,则tx会继续在头部存放数据,如下:

 

 

当(rx + 1) % BUFFER_LEN == tx时,缓冲区存满

 

 

 

三、两种方法的比较:

比较内容

方法一

方法二(循环队列式)

使用缓冲区的任务数

允许多任务

只能单任务

空间利用率

缓冲区的使用顺序

优先使用低地址处的缓冲区块

由低地址到高地址循环使用

不能存放数据的字节数

缓冲区块数

1B

编程复杂度

略微复杂

简单

 

注:方法一不能存放数据的字节数用于记录下一缓冲区块的位置;循环队列式,不能存放数据的字节数用于循环使用缓冲区

 

 

方法一最大的好处在于可同时被多个任务共同使用,互不影响,有助于节约内存;而且每次分配空间时,会优先使用低地址处的空闲块,数据集中,有利于减少内存池的占用;某一任务释放的空间可被另一任务使用,提高了利用率,但编程略微复杂。

 

循环队列式编程简单,容易理解,特别适合单任务的使用,但缓冲区的利用率不是很高,且无法多任务使用。

 

 

0 0
原创粉丝点击