Skip to content

Instantly share code, notes, and snippets.

@RickKimball RickKimball/example.ino
Last active Nov 9, 2018

Embed
What would you like to do?
#ifndef FABOOH
#include <Streaming.h>
#include "ringbuffer_simple.h"
#define sizeofs(a) sizeof((a))/sizeof((a[0]))
#else
serial_usart_isr_t<115200, CPU::frequency, TX1_PIN, RX1_PIN> Serial1;
USART_IRQHandler(1, Serial1)
#endif
struct debug_buffer_t : ringbuffer_t<16> {
void toString( const char *msg ) {
Serial1 << msg << ": "
<< "empty=" << (empty() ? "Y" : "N" )
<< ", full=" << (full() ? "Y" : "N" )
<< ", {tail=" << tail() << ", head=" << head() << "}"
<< ", size=" << size()
<< endl;
}
};
void setup() {
Serial1.begin(115200);
}
debug_buffer_t dbg;
void loop() {
const uint8_t values[] = { 2, 3 };
dbg.toString("\n"
">initial state");
dbg.push(1);
dbg.toString("after 1 push ");
dbg.push(values,sizeofs(values));
dbg.toString("after 2 pushes");
unsigned cnt = sizeofs(values)+1;
do {
Serial1 << "pop()=" << dbg.pop() << " " ;
dbg.toString("");
} while(--cnt);
Serial1 << endl;
delay(1000);
}
/*
ringbuffer.h - yet another version of ringbuffer_t c++ template
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 and stm32. It
doesn't 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.
See also:
https://en.wikipedia.org/wiki/Circular_buffer
Version: 1.0.3
Created: Jul-24-2012
Author: rick@kimballsoftware.com
Jul-14-2016: rick@kimballsoftware.com removed extra stuff not needed
for stm32 formatted using auto format in arduino ide
*/
/*
Copyright © 2012-2018 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/>.
*/
#pragma once
#include <inttypes.h>
#include <type_traits>
/**
ringbuffer_t - provide a circular_buffer without disabling interrupts.
Expects a power of 2 container. Assumes one reader and
one writer. Container capacity is SIZE-1 as an open slot
is used to indicate full state.
*/
template<
uint16_t SIZE, // # elements must be power of 2
typename T = uint8_t, // works with any type
typename POP_T = int, // return type of pop_front
POP_T const EMPTY_ELEM = (POP_T)-1 // default return value when empty
>
class ringbuffer_t {
#if 0 /* flip this to 1 to force smaller code generation */
static_assert( IS_POWER_OF_2(SIZE),
"ringbuffer_t SIZE must be power of 2" );
#endif
// --- class specific data type ---
/**
uint16x2_t - a union containing 16 bit head and tail offsets into
the ring buffer. The union allows the c code to
grab both values atomically with just one assembler
instruction.
*/
union uint16x2_t {
// -- access as 32 bit value --
volatile uint32_t both;
// -- or as 2 16 bit values --
struct {
volatile uint16_t head;
volatile uint16_t tail;
};
};
private:
// --- private structure data ---
uint16x2_t offsets; // head and tail pointers with 32/16 bit access
T elements[SIZE]; // element container, usable capacity is SIZE-1
// --- private functions ---
// indx_wrap_() - make sure head & tail index values are within bounds
// wrap around on array index overflow
static const unsigned indx_wrap_(const unsigned indx) {
return indx % (SIZE);
}
public:
// --- public methods ---
// accessor functions
uint16_t head() {
return offsets.head;
}
uint16_t tail() {
return offsets.tail;
}
// constructor
ringbuffer_t(void) : offsets{0} {
}
// capacity - max elements that can be stored in elements array
// as we don't malloc any memory, same as max_size()
const size_t capacity(void) const {
return SIZE-1;
}
// clear() - zero out head and tail
void clear(void) {
offsets.both = 0;
}
// empty() - checks whether the elements container is empty
bool empty(void) const {
uint16x2_t temp = {offsets.both};
return (temp.head == temp.tail);
}
// full() - returns true when all slots used
bool full(void) const {
return size() == capacity();
}
// max_size() - maximum number of elements that can be held
size_t max_size(void) const {
return SIZE-1;
}
// push() - inserts element at the end of the queue
// Note: affects head, reads tail, element ignored if overflow ~300 ns @72MHz
void push(const T element) {
uint16x2_t temp = {offsets.both};
if ( (indx_wrap_(temp.head+1)) != temp.tail ) {
elements[temp.head++] = element; // use next empty slot
offsets.head = indx_wrap_(temp.head);
}
return;
}
// push_nc() - no bounds check push()
// Note: affects head, user responsible for making sure there is enough room
void push_nc(const T element) {
uint16_t temp_head = offsets.head;
elements[temp_head++] = element; // use the empty slot
offsets.head = indx_wrap_(temp_head); // don't check assume user knows
return;
}
// push(src, cnt) - insert multiple elements
// Note: affects head, reads tail, all elements ignored if not room
void push(const T * src, const size_t cnt) {
uint16x2_t temp = {offsets.both};
if ( cnt < capacity() && indx_wrap_(temp.head+cnt) != temp.tail ) {
uint16_t head=temp.head;
for (size_t x = 0; x < cnt; ++x) {
elements[head++] = src[x];
head = indx_wrap_(head);
}
offsets.head = head;
}
}
// push_nc(src, cnt) - no bounds insert multiple elements
// Note: affects head, user responisble for making sure there is enough room
void push_nc(const T * src, const size_t cnt) {
uint16_t temp = offsets.head;
for (size_t x = 0; x < cnt; ++x) {
elements[temp++] = src[x];
temp = indx_wrap_(temp);
}
offsets.head = temp;
}
// pop() - removes the first element, affects tail, reads head
POP_T pop(void) {
uint16x2_t temp = {offsets.both};
if (!(temp.head == temp.tail)) { // !empty
POP_T elem = elements[temp.tail++];
offsets.tail = indx_wrap_(temp.tail);
return elem;
}
return EMPTY_ELEM; // on underflow return default element
}
// pop_nc() - no bounds check pop(), affects tail
POP_T pop_nc(void) {
uint16x2_t temp = {offsets.both};
POP_T elem = elements[temp.tail++];
offsets.tail = indx_wrap_(temp.tail);
return elem;
}
// size() - return number of unread elements
size_t size(void) const {
uint16x2_t temp = { offsets.both };
uint32_t cnt_unread = indx_wrap_(temp.head-temp.tail);
return cnt_unread;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.