Last active
November 9, 2018 22:14
-
-
Save RickKimball/8f6ba66e7d52727161f46e44b83d96d0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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