Skip to content

Instantly share code, notes, and snippets.

@RickKimball
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RickKimball/1a860d229b2370e4e50a to your computer and use it in GitHub Desktop.
Save RickKimball/1a860d229b2370e4e50a to your computer and use it in GitHub Desktop.
another ringbuffer
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include "ringbuffer.h"
struct data_xyz {
int x;
int y;
int z;
bool bValid;
void print(void) {
printf("valid=%s, x=%d,y=%d,z=%d\n",bValid?"t":"f",x,y,z);
}
data_xyz():
x(-1),y(-1),z(-1),bValid(false)
{
}
data_xyz(int x, int y, int z)
:x(x),y(y),z(z),bValid(true)
{
}
};
typedef struct data_xyz data_xyz_t;
typedef ringbuffer_t<16, data_xyz_t > rb_data;
rb_data buff;
int main()
{
int n = 0;
int cnt = 1;
data_xyz_t item = { 1,2,3};
while(1) {
do {
item.x=cnt++;
buff.push_back(item);
} while(++n < 10);
printf("items = %ld\n", buff.size());
n = 0;
while(buff.size()) {
data_xyz_t item = buff.pop_front();
item.print();
}
data_xyz_t bogo = buff.pop_front();
printf("bogo = ");
bogo.print();
sleep(1);
}
return 0;
}
all: main
main: main.cpp
gcc -std=c++0x -Os -g -Wall -o $@ $<
clean:
rm -f main
test:
./main
/*
* ringbuffer_02.h - yet another version of ringbuffer_t optimized for msp430-gcc
*
* Desc: This template class creates a ringbuffer with arbitrary types. Provides push
* and pop methods for adding and removing items. Access function for checking
* the number of items in the buffer, capacity and full or empty methods
* provide safe access to the info.
*
* This version has been optimized for the msp430-gcc. It doesn't use disable
* or enable any interrupts. It is safe nonetheless for use when there is a single
* writer and single reader. This is common when using an interrupt and the main
* line thread working together to move data in and out of peripherals on demand.
*
* Version: 1.0.0
* Created: Jul-24-2012
* Author: rick@kimballsoftware.com
* Date: 02-28-2013
* Version: 1.0.0
*
* =========================================================================
* Copyright © 2012 Rick Kimball
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef RINGBUFFER_T_H_
#define RINGBUFFER_T_H_
#include <stdint.h>
/**
* uint16x2_t - a union containing 16 bit head and tail offsets into the ring buffer.
* The union make it simpler to grab both values with asm.
*
*/
union uint16x2_t {
// access as 32 bit
unsigned long both;
// -- or as 2 16 bit values --
struct {
unsigned head:16;
unsigned tail:16;
};
};
/**
* uint8x2_t - a union containing 2 8 bit values that can be accessed a byte
* at a time or a word at a time. The union make it more
* efficient to grab both values.
*
*/
typedef union uint8x2_t {
//--- word access
uint16_t as_u16; // access both as a word: mask is low byte, data is high byte
//--- or --- individual byte access
struct {
uint8_t as_u8[2];
};
} uint8x2_t;
/**
* ringbuffer_t - provide a circular_buffer without disabling interrupts
* expects a power of 2 container, and only one reader and one writer
* container capacity SIZE-1
*/
template <
uint16_t SIZE, /* how many elements-1 must be power of 2 */
typename T = uint8_t, /* works with any type */
typename POP_T = int16_t, /* return type of pop_front */
POP_T EMPTY_ELEM = (POP_T)-1 /* default return value when empty */
>
struct ringbuffer_t {
// --- private structure data ---
volatile uint16x2_t offsets;
T elements[SIZE];
enum { CAPACITY=SIZE-1 };
is_power_of_two<SIZE> check_buffer_size; // your SIZE is not a power of 2, if you error here
public:
// --- methods ---
inline void init() {
offsets.both=0;
}
inline void clear(void ) {
offsets.both=0;
}
ALWAYS_INLINE size_t size() {
register uint16x2_t temp = { offsets.both };
temp.both = (temp.head-temp.tail) & CAPACITY;
return temp.both;
}
ALWAYS_INLINE size_t capacity() {
return CAPACITY;
}
bool inline empty() {
return !size();
}
bool inline full() {
return size() == capacity();
}
// affects head, reads tail, element ignored if overflow ~1.68 us @16MHz
inline void push_back(const T element) {
register uint16_t temp_head = offsets.head;
elements[temp_head++] = element;
temp_head &= CAPACITY;
if ( !(temp_head == offsets.tail) ) { // !full
offsets.head = temp_head;
}
return;
}
// no bounds check version, affects head ~1.120 us @ 16MHz
inline void push_back_nc(const T element) {
register uint16_t temp_head = offsets.head;
elements[temp_head++] = element;
offsets.head = temp_head & CAPACITY;
return;
}
// affects tail, reads head ~1.80 us @ 16MHz
inline POP_T pop_front(void) {
register uint16x2_t temp = { offsets.both };
if ( (temp.head-temp.tail) & CAPACITY ) { // !empty
POP_T elem = elements[temp.tail++];
offsets.tail = temp.tail & CAPACITY;
return elem;
}
return EMPTY_ELEM; // on underflow return default element
}
// no bounds check, affects tail, reads head ~1.128 us @ 16MHz
inline POP_T pop_front_nc(void) {
register uint16_t temp_tail = offsets.tail;
POP_T elem = elements[temp_tail++];
offsets.tail = temp_tail & CAPACITY;
return elem;
}
};
template <const uint16_t CAPACITY=8, typename value_type=uint8_t >
struct ptr_ringbuf {
volatile value_type *tail;
volatile value_type *head;
value_type ring_buf[CAPACITY+1];
void init(void) {
head = tail = ring_buf;
}
// interrupt safe capacity
ALWAYS_INLINE unsigned capacity() {
return CAPACITY-size();
}
// n
ALWAYS_INLINE unsigned capacity_() {
return CAPACITY-size_();
}
ALWAYS_INLINE unsigned size() {
register unsigned cnt;
__dint();
cnt = size_();
__eint();
return cnt;
}
/*
* size() - return number of unread bytes in buffer
*/
ALWAYS_INLINE unsigned size_() { // interrupt safe version
#if 1
register unsigned rc;
__asm__ (
" mov %[head], %[cnt]\n"
" sub %[tail], %[cnt]\n"
: [cnt] "=r" (rc)
: [head] "m" (head), [tail] "m" (tail)
:
);
#else
register unsigned rc=(unsigned)head-(unsigned)tail;
#endif
return rc;
}
ALWAYS_INLINE value_type pop_front() {
if ( size() ) {
return pop_front_nc();
}
return value_type(-1);
}
ALWAYS_INLINE value_type pop_front_nc() {
value_type *next_tail=(value_type *)tail+1;
if ( next_tail >= ring_buf+CAPACITY ) next_tail=ring_buf;
int rc = *tail;
tail = next_tail;
return rc;
}
ALWAYS_INLINE void push_back(const value_type c) {
if ( capacity_() > 0 ) {
register value_type *next_head=(value_type *)head+1;
if (next_head >= ring_buf+CAPACITY) next_head = ring_buf;
*head = c;
head = next_head;
}
}
};
#endif /* RINGBUFFER_T_H_ */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment