1.参考内容
参考连接地址:https://github.com/MaJerle/lwrb
参考文档地址:LwRB latest-develop documentation — LwRB documentation
2.接口部分:
/**
* \file lwrb.h
* \brief LwRB - Lightweight ring buffer
*/
/*
* Copyright (c) 2020 Tilen MAJERLE
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* This file is part of LwRB - Lightweight ring buffer library.
*
* Author: Tilen MAJERLE <tilen@majerle.eu>
* Version: v2.0.0
*/
#ifndef LWRB_HDR_H
#define LWRB_HDR_H
#include <string.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* \defgroup LWRB Lightweight ring buffer manager
* \brief Lightweight ring buffer manager
* \{
*/
/**
* \brief Enable buffer structure pointer parameter as volatile
* To use this feature, uncomment keyword below
*/
#define LWRB_VOLATILE /* volatile */
/**
* \brief Adds 2 magic words to make sure if memory is corrupted
* application can detect wrong data pointer and maximum size
*/
#define LWRB_USE_MAGIC 1
/**
* \brief Event type for buffer operations
*/
typedef enum {
LWRB_EVT_READ, /*!< Read event */
LWRB_EVT_WRITE, /*!< Write event */
LWRB_EVT_RESET, /*!< Reset event */
} lwrb_evt_type_t;
/**
* \brief Buffer structure forward declaration
*/
struct lwrb;
/**
* \brief Event callback function type
* \param[in] buff: Buffer handle for event
* \param[in] evt: Event type
* \param[in] bp: Number of bytes written or read (when used), depends on event type
*/
typedef void (*lwrb_evt_fn)(LWRB_VOLATILE struct lwrb* buff, lwrb_evt_type_t evt, size_t bp);
/**
* \brief Buffer structure
*/
typedef struct lwrb {
#if LWRB_USE_MAGIC
uint32_t magic1; /*!< Magic 1 word */
#endif /* LWRB_USE_MAGIC */
uint8_t* buff; /*!< Pointer to buffer data.
Buffer is considered initialized when `buff != NULL` and `size > 0` */
size_t size; /*!< Size of buffer data. Size of actual buffer is `1` byte less than value holds */
size_t r; /*!< Next read pointer. Buffer is considered empty when `r == w` and full when `w == r - 1` */
size_t w; /*!< Next write pointer. Buffer is considered empty when `r == w` and full when `w == r - 1` */
lwrb_evt_fn evt_fn; /*!< Pointer to event callback function */
#if LWRB_USE_MAGIC
uint32_t magic2; /*!< Magic 2 word */
#endif /* LWRB_USE_MAGIC */
} lwrb_t;
uint8_t lwrb_init(LWRB_VOLATILE lwrb_t* buff, void* buffdata, size_t size);
uint8_t lwrb_is_ready(LWRB_VOLATILE lwrb_t* buff);
void lwrb_free(LWRB_VOLATILE lwrb_t* buff);
void lwrb_reset(LWRB_VOLATILE lwrb_t* buff);
void lwrb_set_evt_fn(LWRB_VOLATILE lwrb_t* buff, lwrb_evt_fn fn);
/* Read/Write functions */
size_t lwrb_write(LWRB_VOLATILE lwrb_t* buff, const void* data, size_t btw);
size_t lwrb_read(LWRB_VOLATILE lwrb_t* buff, void* data, size_t btr);
size_t lwrb_peek(LWRB_VOLATILE lwrb_t* buff, size_t skip_count, void* data, size_t btp);
/* Buffer size information */
size_t lwrb_get_free(LWRB_VOLATILE lwrb_t* buff);
size_t lwrb_get_full(LWRB_VOLATILE lwrb_t* buff);
/* Read data block management */
void* lwrb_get_linear_block_read_address(LWRB_VOLATILE lwrb_t* buff);
size_t lwrb_get_linear_block_read_length(LWRB_VOLATILE lwrb_t* buff);
size_t lwrb_skip(LWRB_VOLATILE lwrb_t* buff, size_t len);
/* Write data block management */
void* lwrb_get_linear_block_write_address(LWRB_VOLATILE lwrb_t* buff);
size_t lwrb_get_linear_block_write_length(LWRB_VOLATILE lwrb_t* buff);
size_t lwrb_advance(LWRB_VOLATILE lwrb_t* buff, size_t len);
/**
* \}
*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* LWRB_HDR_H */
3.库的接口实现部分:
/**
* \file lwrb.c
* \brief Lightweight ring buffer
*/
/*
* Copyright (c) 2020 Tilen MAJERLE
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* This file is part of LwRB - Lightweight ring buffer library.
*
* Author: Tilen MAJERLE <tilen@majerle.eu>
* Version: v2.0.0
*/
#include "lwrb.h"
/* Memory set and copy functions */
#define BUF_MEMSET memset
#define BUF_MEMCPY memcpy
#if LWRB_USE_MAGIC
#define BUF_IS_VALID(b) ((b) != NULL && (b)->magic1 == 0xDEADBEEF && (b)->magic2 == ~0xDEADBEEF && (b)->buff != NULL && (b)->size > 0)
#else
#define BUF_IS_VALID(b) ((b) != NULL && (b)->buff != NULL && (b)->size > 0)
#endif /* LWRB_USE_MAGIC */
#define BUF_MIN(x, y) ((x) < (y) ? (x) : (y))
#define BUF_MAX(x, y) ((x) > (y) ? (x) : (y))
#define BUF_SEND_EVT(b, type, bp) do { if ((b)->evt_fn != NULL) { (b)->evt_fn((b), (type), (bp)); } } while (0)
/**
* \brief Initialize buffer handle to default values with size and buffer data array
* \param[in] buff: Buffer handle
* \param[in] buffdata: Pointer to memory to use as buffer data
* \param[in] size: Size of `buffdata` in units of bytes
* Maximum number of bytes buffer can hold is `size - 1`
* \return `1` on success, `0` otherwise
*/
uint8_t
lwrb_init(LWRB_VOLATILE lwrb_t* buff, void* buffdata, size_t size) {
if (buff == NULL || buffdata == NULL || size == 0) {
return 0;
}
BUF_MEMSET((void*)buff, 0x00, sizeof(*buff));
buff->size = size;
buff->buff = buffdata;
#if LWRB_USE_MAGIC
buff->magic1 = 0xDEADBEEF;
buff->magic2 = ~0xDEADBEEF;
#endif /* LWRB_USE_MAGIC */
return 1;
}
/**
* \brief Check if buff is initialized and ready to use
* \param[in] buff: Buffer handle
* \return `1` if ready, `0` otherwise
*/
uint8_t
lwrb_is_ready(LWRB_VOLATILE lwrb_t* buff) {
return BUF_IS_VALID(buff);
}
/**
* \brief Free buffer memory
* \note Since implementation does not use dynamic allocation,
* it just sets buffer handle to `NULL`
* \param[in] buff: Buffer handle
*/
void
lwrb_free(LWRB_VOLATILE lwrb_t* buff) {
if (BUF_IS_VALID(buff)) {
buff->buff = NULL;
}
}
/**
* \brief Set event function callback for different buffer operations
* \param[in] buff: Buffer handle
* \param[in] evt_fn: Callback function
*/
void
lwrb_set_evt_fn(LWRB_VOLATILE lwrb_t* buff, lwrb_evt_fn evt_fn) {
if (BUF_IS_VALID(buff)) {
buff->evt_fn = evt_fn;
}
}
/**
* \brief Write data to buffer.
* Copies data from `data` array to buffer and marks buffer as full for maximum `btw` number of bytes
*
* \param[in] buff: Buffer handle
* \param[in] data: Pointer to data to write into buffer
* \param[in] btw: Number of bytes to write
* \return Number of bytes written to buffer.
* When returned value is less than `btw`, there was no enough memory available
* to copy full data array
*/
size_t
lwrb_write(LWRB_VOLATILE lwrb_t* buff, const void* data, size_t btw) {
size_t tocopy, free;
const uint8_t* d = data;
if (!BUF_IS_VALID(buff) || data == NULL || btw == 0) {
return 0;
}
/* Calculate maximum number of bytes available to write */
free = lwrb_get_free(buff);
btw = BUF_MIN(free, btw);
if (btw == 0) {
return 0;
}
/* Step 1: Write data to linear part of buffer */
tocopy = BUF_MIN(buff->size - buff->w, btw);
BUF_MEMCPY(&buff->buff[buff->w], d, tocopy);
buff->w += tocopy;
btw -= tocopy;
/* Step 2: Write data to beginning of buffer (overflow part) */
if (btw > 0) {
BUF_MEMCPY(buff->buff, &d[tocopy], btw);
buff->w = btw;
}
/* Step 3: Check end of buffer */
if (buff->w >= buff->size) {
buff->w = 0;
}
BUF_SEND_EVT(buff, LWRB_EVT_WRITE, tocopy + btw);
return tocopy + btw;
}
/**
* \brief Read data from buffer.
* Copies data from buffer to `data` array and marks buffer as free for maximum `btr` number of bytes
*
* \param[in] buff: Buffer handle
* \param[out] data: Pointer to output memory to copy buffer data to
* \param[in] btr: Number of bytes to read
* \return Number of bytes read and copied to data array
*/
size_t
lwrb_read(LWRB_VOLATILE lwrb_t* buff, void* data, size_t btr) {
size_t tocopy, full;
uint8_t* d = data;
if (!BUF_IS_VALID(buff) || data == NULL || btr == 0) {
return 0;
}
/* Calculate maximum number of bytes available to read */
full = lwrb_get_full(buff);
btr = BUF_MIN(full, btr);
if (btr == 0) {
return 0;
}
/* Step 1: Read data from linear part of buffer */
tocopy = BUF_MIN(buff->size - buff->r, btr);
BUF_MEMCPY(d, &buff->buff[buff->r], tocopy);
buff->r += tocopy;
btr -= tocopy;
/* Step 2: Read data from beginning of buffer (overflow part) */
if (btr > 0) {
BUF_MEMCPY(&d[tocopy], buff->buff, btr);
buff->r = btr;
}
/* Step 3: Check end of buffer */
if (buff->r >= buff->size) {
buff->r = 0;
}
BUF_SEND_EVT(buff, LWRB_EVT_READ, tocopy + btr);
return tocopy + btr;
}
/**
* \brief Read from buffer without changing read pointer (peek only)
* \param[in] buff: Buffer handle
* \param[in] skip_count: Number of bytes to skip before reading data
* \param[out] data: Pointer to output memory to copy buffer data to
* \param[in] btp: Number of bytes to peek
* \return Number of bytes peeked and written to output array
*/
size_t
lwrb_peek(LWRB_VOLATILE lwrb_t* buff, size_t skip_count, void* data, size_t btp) {
size_t full, tocopy, r;
uint8_t* d = data;
if (!BUF_IS_VALID(buff) || data == NULL || btp == 0) {
return 0;
}
r = buff->r;
/* Calculate maximum number of bytes available to read */
full = lwrb_get_full(buff);
/* Skip beginning of buffer */
if (skip_count >= full) {
return 0;
}
r += skip_count;
full -= skip_count;
if (r >= buff->size) {
r -= buff->size;
}
/* Check maximum number of bytes available to read after skip */
btp = BUF_MIN(full, btp);
if (btp == 0) {
return 0;
}
/* Step 1: Read data from linear part of buffer */
tocopy = BUF_MIN(buff->size - r, btp);
BUF_MEMCPY(d, &buff->buff[r], tocopy);
btp -= tocopy;
/* Step 2: Read data from beginning of buffer (overflow part) */
if (btp > 0) {
BUF_MEMCPY(&d[tocopy], buff->buff, btp);
}
return tocopy + btp;
}
/**
* \brief Get available size in buffer for write operation
* \param[in] buff: Buffer handle
* \return Number of free bytes in memory
*/
size_t
lwrb_get_free(LWRB_VOLATILE lwrb_t* buff) {
size_t size, w, r;
if (!BUF_IS_VALID(buff)) {
return 0;
}
/* Use temporary values in case they are changed during operations */
w = buff->w;
r = buff->r;
if (w == r) {
size = buff->size;
} else if (r > w) {
size = r - w;
} else {
size = buff->size - (w - r);
}
/* Buffer free size is always 1 less than actual size */
return size - 1;
}
/**
* \brief Get number of bytes currently available in buffer
* \param[in] buff: Buffer handle
* \return Number of bytes ready to be read
*/
size_t
lwrb_get_full(LWRB_VOLATILE lwrb_t* buff) {
size_t w, r, size;
if (!BUF_IS_VALID(buff)) {
return 0;
}
/* Use temporary values in case they are changed during operations */
w = buff->w;
r = buff->r;
if (w == r) {
size = 0;
} else if (w > r) {
size = w - r;
} else {
size = buff->size - (r - w);
}
return size;
}
/**
* \brief Resets buffer to default values. Buffer size is not modified
* \param[in] buff: Buffer handle
*/
void
lwrb_reset(LWRB_VOLATILE lwrb_t* buff) {
if (BUF_IS_VALID(buff)) {
buff->w = 0;
buff->r = 0;
BUF_SEND_EVT(buff, LWRB_EVT_RESET, 0);
}
}
/**
* \brief Get linear address for buffer for fast read
* \param[in] buff: Buffer handle
* \return Linear buffer start address
*/
void*
lwrb_get_linear_block_read_address(LWRB_VOLATILE lwrb_t* buff) {
if (!BUF_IS_VALID(buff)) {
return NULL;
}
return &buff->buff[buff->r];
}
/**
* \brief Get length of linear block address before it overflows for read operation
* \param[in] buff: Buffer handle
* \return Linear buffer size in units of bytes for read operation
*/
size_t
lwrb_get_linear_block_read_length(LWRB_VOLATILE lwrb_t* buff) {
size_t w, r, len;
if (!BUF_IS_VALID(buff)) {
return 0;
}
/* Use temporary values in case they are changed during operations */
w = buff->w;
r = buff->r;
if (w > r) {
len = w - r;
} else if (r > w) {
len = buff->size - r;
} else {
len = 0;
}
return len;
}
/**
* \brief Skip (ignore; advance read pointer) buffer data
* Marks data as read in the buffer and increases free memory for up to `len` bytes
*
* \note Useful at the end of streaming transfer such as DMA
* \param[in] buff: Buffer handle
* \param[in] len: Number of bytes to skip and mark as read
* \return Number of bytes skipped
*/
size_t
lwrb_skip(LWRB_VOLATILE lwrb_t* buff, size_t len) {
size_t full;
if (!BUF_IS_VALID(buff) || len == 0) {
return 0;
}
full = lwrb_get_full(buff);
len = BUF_MIN(len, full);
buff->r += len;
if (buff->r >= buff->size) {
buff->r -= buff->size;
}
BUF_SEND_EVT(buff, LWRB_EVT_READ, len);
return len;
}
/**
* \brief Get linear address for buffer for fast read
* \param[in] buff: Buffer handle
* \return Linear buffer start address
*/
void*
lwrb_get_linear_block_write_address(LWRB_VOLATILE lwrb_t* buff) {
if (!BUF_IS_VALID(buff)) {
return NULL;
}
return &buff->buff[buff->w];
}
/**
* \brief Get length of linear block address before it overflows for write operation
* \param[in] buff: Buffer handle
* \return Linear buffer size in units of bytes for write operation
*/
size_t
lwrb_get_linear_block_write_length(LWRB_VOLATILE lwrb_t* buff) {
size_t w, r, len;
if (!BUF_IS_VALID(buff)) {
return 0;
}
/* Use temporary values in case they are changed during operations */
w = buff->w;
r = buff->r;
if (w >= r) {
len = buff->size - w;
/*
* When read pointer is 0,
* maximal length is one less as if too many bytes
* are written, buffer would be considered empty again (r == w)
*/
if (r == 0) {
/*
* Cannot overflow:
* - If r is not 0, statement does not get called
* - buff->size cannot be 0 and if r is 0, len is greater 0
*/
--len;
}
} else {
len = r - w - 1;
}
return len;
}
/**
* \brief Advance write pointer in the buffer.
* Similar to skip function but modifies write pointer instead of read
*
* \note Useful when hardware is writing to buffer and application needs to increase number
* of bytes written to buffer by hardware
* \param[in] buff: Buffer handle
* \param[in] len: Number of bytes to advance
* \return Number of bytes advanced for write operation
*/
size_t
lwrb_advance(LWRB_VOLATILE lwrb_t* buff, size_t len) {
size_t free;
if (!BUF_IS_VALID(buff) || len == 0) {
return 0;
}
free = lwrb_get_free(buff);
len = BUF_MIN(len, free);
buff->w += len;
if (buff->w >= buff->size) {
buff->w -= buff->size;
}
BUF_SEND_EVT(buff, LWRB_EVT_WRITE, len);
return len;
}
4.单片机进行测试部分
#if 1
#include <stdio.h>
#include <string.h>
#include "lwrb.h"
#endif
/* Externs -------------------------------------------------------------------*/
#if 1
/* Create data array and buffer */
uint8_t lwrb_data[8 + 1];
lwrb_t buff;
uint8_t tmp[8];
void my_buff_evt_fn(lwrb_t* buff, lwrb_evt_type_t type, size_t len)
{
(void)buff;
switch (type) {
case LWRB_EVT_RESET:
printf("[EVT] Buffer reset event **********!\r\n");
break;
case LWRB_EVT_READ:
printf("[EVT] Buffer read event @@@@@@@@@@@: %d byte(s)!\r\n", (int)len);
break;
case LWRB_EVT_WRITE:
printf("[EVT] Buffer write event =========: %d byte(s)!\r\n", (int)len);
break;
default: break;
}
}
#endif
// 在一个任务函数中进行测试
{
// 2023-07-21 测试lwrb 库模块
#if 1
size_t len;
/* Init buffer */
lwrb_init(&buff, lwrb_data, sizeof(lwrb_data));
lwrb_set_evt_fn(&buff, my_buff_evt_fn);
lwrb_write(&buff, "abc", 3);
lwrb_write(&buff, "abc", 3);
lwrb_write(&buff, "abc", 3);
len = lwrb_read(&buff, tmp, 9);
buff.r = 0;
buff.w = 0;
memset(lwrb_get_linear_block_write_address(&buff), 'A',
lwrb_get_linear_block_write_length(&buff));
lwrb_advance(&buff, lwrb_get_linear_block_write_length(&buff));
buff.r = 2;
buff.w = 0;
memset(lwrb_get_linear_block_write_address(&buff), 'B',
lwrb_get_linear_block_write_length(&buff));
lwrb_advance(&buff, lwrb_get_linear_block_write_length(&buff));
buff.r = 3;
buff.w = 3;
memset(lwrb_get_linear_block_write_address(&buff), 'C',
lwrb_get_linear_block_write_length(&buff));
lwrb_advance(&buff, lwrb_get_linear_block_write_length(&buff));
lwrb_reset(&buff);
(void)len;
#endif
}