VxWorks的环形缓冲区设计
VxWorks环形缓冲模块主要定义在rngLib.c和rngLib.h中,对于数据结构比较了解的小伙伴应该知道,环形缓冲实际就是一个双向循环队列。
概述
关于循环队列,小编之前在《也没想象中那么神秘的数据结构-先来后到的"队列"(循环队列)》一文中有过详细阐述,这里就不在过多的赘述了。
开发环境:
vxWorks6.9.4,workbench3.3.5
开发板:
TLZ7x-EasyEVM-A3
另外,小编所有文章均是自己亲手编写验证,若需要小编的工程代码,访问地址 实时系统vxWorks - 环形缓冲工程文件 获取。
文件内容如下:
-
obj: 存放目标文件,包含vxWorks镜像,应用程序目标文件。
-
rng_test: vxWorks应用工程。
接口
官方接口
官方环形缓冲接口定义主要包含在rngLib.h头文件中。
环形缓冲接口定义
typedef struct /* RING - ring buffer */
{
size_t pToBuf; /* 写指针 */
size_t pFromBuf; /* 读指针 */
size_t bufSize; /* 数据长度 */
char *buf; /* 存放数据 */
} RING;
/* END_HIDDEN */
typedef RING *RING_ID;
创建/删除环形缓冲
/**
* @创建环形缓冲
* @nbytes: 缓冲尺寸
* @成功返回环形缓冲ID,失败返回NULL。
*/
extern RING_ID rngCreate (size_t nbytes);
/**
* @删除环形缓冲
* @ringId: 环形缓冲ID
*/
extern void rngDelete (RING_ID ringId);
判断缓冲是否空/满
/**
* @判断环形缓冲是否为空
* @ringId: 环形缓冲ID
* @不为空返回0,否则返回其它。
*/
extern BOOL rngIsEmpty (RING_ID ringId);
/**
* @判断环形缓冲是否为满
* @ringId: 环形缓冲ID
* @不为满返回0,否则返回其它。
*/
extern BOOL rngIsFull (RING_ID ringId);
读写数据
/**
* @从环形缓冲中读取数据
* @ringId: 环形缓冲ID buffer:数据 maxbytes:长度
* @返回实际读取到的数据长度。
*/
extern size_t rngBufGet (RING_ID rngId, char *buffer, size_t maxbytes);
/**
* @写数据到环形缓冲
* @ringId: 环形缓冲ID buffer:数据 nbytes:长度
* @返回实际写入数据长度
*/
extern size_t rngBufPut (RING_ID rngId, char *buffer, size_t nbytes);
获取缓冲区剩余空间/已占空间
/**
* @获取环形缓冲剩余空间长度
* @ringId: 环形缓冲ID
* @返回剩余空间长度。
*/
extern size_t rngFreeBytes (RING_ID ringId);
/**
* @获取环形缓冲已占空间长度
* @ringId: 环形缓冲ID
* @返回已占空间长度。
*/
extern size_t rngNBytes (RING_ID ringId);
清空缓冲
/**
* @清空环形缓冲
* @ringId: 环形缓冲ID
*/
extern void rngFlush (RING_ID ringId);
环形缓冲对象接口
属性定义
/* 环形缓冲类 */
struct t_rng {
u8_t flag; /* 创建标志,=1已创建,=0未创建 */
RING_ID id; /* 环形缓冲 */
struct t_sem msem; /* 环形缓冲互斥信号量 */
};
保存数据到环形缓冲
/**
* @保存数据到环形缓冲
* @p_rng:环形缓冲类 buf: 数据 len:长度
**/
void put_rng(struct t_rng *p_rng, s8_t *buf, s32_t len);
获取环形缓冲数据
/**
* @获取环形缓冲数据
* @p_rng:环形缓冲类 buf装载数据, len数据长度
* 成功返回实际数据长度,失败返回ERROR
**/
s32_t get_rng(struct t_rng *p_rng, s8_t *buf, s32_t len);
创建环形缓冲
/**
* @创建环形缓冲
* @p_rng:环形缓冲类 nbyte申请缓冲大小, name互斥信号名
**/
void create_rng(struct t_rng *p_rng, size_t nbytes, s8_t *name);
示例
-
示例创建两个任务(生产者/消费者),生产者向缓冲写入数据,消费者从缓冲读取数据,缓冲访问时使用互斥信号量进行互斥。
-
关于信号量和任务部分可参加小编的《实时系统vxWorks - 信号量(重要)》和《实时系统vxWorks - 任务(重要)》文章。
-
包含环形缓冲类rng.c/rng.h和演示程序main.c(已验证通过)。
rng.h
/**
* @Filename : rng.h
* @Revision : $Revision: 1.00 $
* @Author : Feng
* @Description : 环形缓冲类,在系统自带环形缓冲基础上封装互斥信号量
**/
#ifndef __RNG_CLASS_H__
#define __RNG_CLASS_H__
#include
#include
#include
#include
#include
#include "sem.h"
extern struct sem sem; /* 保存系统信号量信息 */
/* 环形缓冲类 */
struct t_rng {
u8_t flag; /* 创建标志,=1已创建,=0未创建 */
RING_ID id; /* 环形缓冲 */
struct t_sem msem; /* 环形缓冲互斥信号量 */
};
/**
* @保存数据到环形缓冲
* @p_rng:环形缓冲类 buf: 数据 len:长度
**/
void put_rng(struct t_rng *p_rng, s8_t *buf, s32_t len);
/**
* @获取环形缓冲数据
* @p_rng:环形缓冲类 buf装载数据, len数据长度
* 成功返回实际数据长度,失败返回ERROR
**/
s32_t get_rng(struct t_rng *p_rng, s8_t *buf, s32_t len);
/**
* @创建环形缓冲
* @p_rng:环形缓冲类 nbyte申请缓冲大小, name互斥信号名
**/
void create_rng(struct t_rng *p_rng, size_t nbytes, s8_t *name);
#endif
rng.c
/**
* @Filename : rng.c
* @Revision : $Revision: 1.00 $
* @Author : Feng
* @Description : 环形缓冲类,在系统自带环形缓冲基础上封装互斥信号量
**/
#include "rng.h"
/**
* @保存数据到环形缓冲
* @p_rng:环形缓冲类 buf: 数据 len:长度
**/
void put_rng(struct t_rng *p_rng, s8_t *buf, s32_t len)
{
if (p_rng->flag == 0)
return;
get_sem(&p_rng->msem);
if (!(rngIsFull(p_rng->id)))
rngBufPut(p_rng->id, buf, len);
lose_sem(&p_rng->msem);
}
/**
* @获取环形缓冲数据
* buf装载数据, len数据长度
* 成功返回实际数据长度,失败返回ERROR
**/
s32_t get_rng(struct t_rng *p_rng, s8_t *buf, s32_t len)
{
s32_t rLen = ERROR;
if (p_rng->flag == 0)
return ERROR;
get_sem(&p_rng->msem);
if (!(rngIsEmpty(p_rng->id)))
rLen = rngBufGet(p_rng->id, buf, len);
lose_sem(&p_rng->msem);
return (rLen);
}
/**
* @创建环形缓冲
* nbyte申请缓冲大小, name互斥信号名
**/
void create_rng(struct t_rng *p_rng, size_t nbytes, s8_t *name)
{
if (p_rng->flag == 1)
return;
p_rng->id = rngCreate(nbytes);
p_rng->msem.sem = NULL;
p_rng->msem.type = SEM_MUTEX;
strcpy(p_rng->msem.name, name);
resgister_sem(&sem, &p_rng->msem);
p_rng->flag = 1;
}
main.c
/**
* @Filename : main.c
* @Revision : $Revision: 1.00 $
* @Author : Feng
* @Description : 环形缓冲类使用示例
**/
#include
#include
#include "stdioLib.h"
#include "strLib.h"
#include "task.h"
#include "sem.h"
#include "rng.h"
#include "feng_type.h"
#define RNG_SIZE 100 /* 缓冲大小 */
struct sem sem; /* 保存系统信号量信息 */
struct t_task s_task, s_task1;
struct t_sem s_sem, s_sem1;
struct t_rng rng;
/**
* @生产者:定时存储数据到环形缓冲
**/
static void _thread(void)
{
static int cnt = 77;
while (1) {
get_sem(&s_sem);
put_rng(&rng, &cnt, sizeof(int));
printf("put data : %d...\n", cnt++);
}
}
/**
* @消费者:定时从环形缓冲中取出数据
**/
static void _thread1(void)
{
int cnt1 = 0;
while (1) {
get_sem(&s_sem1);
if (get_rng(&rng, &cnt1, sizeof(int)) > 0)
printf("get data : %d...\n", cnt1);
}
}
/**
* @创建任务
* @p_task:任务类 name:任务名 thread:函数
**/
void _create_task(struct t_task *p_task, char *name, FUNCPTR thread)
{
strcpy(p_task->name, name);
p_task->options = VX_FP_TASK;
p_task->stackSize = 50 * 1024;
p_task->pFunc = thread;
p_task->tid = NULL;
p_task->core = 0;
p_task->priority = 102;
create_task(p_task);
start_task(p_task);
}
/**
* @创建信号量
* @p_sem:信号量类 name:信号量名 type:类型
**/
void _create_sem(struct t_sem *p_sem, char *name, E_SEM_TYPE type)
{
strcpy(p_sem->name, name);
p_sem->period = 100;
p_sem->type = type;
p_sem->sem = SEM_ID_NULL;
resgister_sem(&sem, p_sem);
}
int main(void)
{
sysClkRateSet(100); /* 时间片设置 */
create_rng(&rng, RNG_SIZE, "my_rng");
_create_sem(&s_sem, "my_sem", SEM_BIN);
_create_sem(&s_sem1, "my_sem1", SEM_BIN);
_create_task(&s_task, "my_task", (FUNCPTR)_thread);
_create_task(&s_task1, "my_task1", (FUNCPTR)_thread1);
while (1) {
taskDelay(500); /* 5s */
lose_sem_by_name(&sem, "my_sem");
lose_sem_by_name(&sem, "my_sem1");
}
return 0;
}
验证
使用环形缓冲之前需要先添加INCLUDE_RNG_BUF组件。
打开镜像工程,选择kernel Configuration。按住Ctrl+F,输入rng,找到ring buffers,添加组件。
添加完组件后,编译镜像,将镜像拷贝到目标机加载指定目录。
创建应用工程rng_test,输入相关测试代码,运行后如下图所示。
注意:若不知道工程如何创建以及运行,可参见小编文章《实时系统vxWorks - 任务(重要)》和《实时系统vxWorks - 加载应用程序的方法》。