From e4bc7f9946a5998da9791660865b3af0ea22116e Mon Sep 17 00:00:00 2001 From: Penguin Date: Wed, 13 Apr 2022 15:27:22 -0500 Subject: [PATCH] cbuffer and queue working --- .../index/p_buffer.h.9A92E5E417D76D1D.idx | Bin 0 -> 872 bytes .../index/p_queue.h.8C2CF32F4831B384.idx | Bin 0 -> 536 bytes .../p_queue_example.c.43EE21D32765822A.idx | Bin 0 -> 2210 bytes .clang-format | 178 ++++++++++++++++++ .dir-locals.el | 6 - CircularBuffer/p_cbuffer.c | 77 -------- CircularBuffer/p_cbuffer.h | 64 ------- Makefile | 32 ++++ bin/p_cbuffer_example | Bin 0 -> 15872 bytes bin/p_queue_example | Bin 0 -> 15960 bytes compile_commands.json | 16 ++ examples/p_cbuffer_example.c | 90 +++++++++ Queue/queue.c => examples/p_queue_example.c | 9 +- .../build/Makefile => old_cbuffer_Makefile | 0 p_buffer.h | 22 +++ p_cbuffer.h | 133 +++++++++++++ Queue/queue.h => p_queue.h | 46 ++--- p_queue_example | Bin 0 -> 15960 bytes 18 files changed, 497 insertions(+), 176 deletions(-) create mode 100644 .cache/clangd/index/p_buffer.h.9A92E5E417D76D1D.idx create mode 100644 .cache/clangd/index/p_queue.h.8C2CF32F4831B384.idx create mode 100644 .cache/clangd/index/p_queue_example.c.43EE21D32765822A.idx create mode 100644 .clang-format delete mode 100644 .dir-locals.el delete mode 100644 CircularBuffer/p_cbuffer.c delete mode 100644 CircularBuffer/p_cbuffer.h create mode 100644 Makefile create mode 100644 bin/p_cbuffer_example create mode 100644 bin/p_queue_example create mode 100644 compile_commands.json create mode 100644 examples/p_cbuffer_example.c rename Queue/queue.c => examples/p_queue_example.c (83%) rename CircularBuffer/build/Makefile => old_cbuffer_Makefile (100%) create mode 100644 p_buffer.h create mode 100644 p_cbuffer.h rename Queue/queue.h => p_queue.h (89%) create mode 100644 p_queue_example diff --git a/.cache/clangd/index/p_buffer.h.9A92E5E417D76D1D.idx b/.cache/clangd/index/p_buffer.h.9A92E5E417D76D1D.idx new file mode 100644 index 0000000000000000000000000000000000000000..89b71f8e685f374fa7d0f82f1e6e4665e02f8ae8 GIT binary patch literal 872 zcmWIYbaP8!W?*nm@vO*AElFfyU|So&My+oYgme{8X^_(2eK@Hx=K>+`8YkABjAvsK5S; zkC4&nDJeI@-})`zSzhuk=+6lY9cT3}BOT9&8{gh@&TPMWJ!|IyU4}1N4ibT7)4V^l zCQW6y94_T(_k-b3&y@vr3(O9S$lE+?wG%4O{U+~u3=J|khi0_GB-&R=qFx2 z!E2G*rM~bp0Da6Oz$CyACKxz@e&*t2lqI}0~1gI4-=0Fm_R5nu7BFe zF*zWSm4O4OfRmR~3`~F(Ffag32x7nb=}f!ba()J&eLUPu+#Fy6VM6N0DS!TzZ`jSx zzzS5r#l*!4CJ+jmpO>!`w_^3+X8^_!4?h#X0GL21n4a}6dd5HBD1HVupaNbdUI8$H zP_QuP=_!u9zqk1r*ntZ8nE3d>1VX{`{;vlLoN{@&!M+e=66EFq5nu&iUoaG)7ML(B yJb=RBaDch6C^aV$#4avMF4hMz_&@{%aKjkDAXa=H*gHFP@or{rUOon1gaiO;Q0r*` literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/p_queue.h.8C2CF32F4831B384.idx b/.cache/clangd/index/p_queue.h.8C2CF32F4831B384.idx new file mode 100644 index 0000000000000000000000000000000000000000..871723e7bb8e912b08718eb4385d413bf9d44e3f GIT binary patch literal 536 zcmWIYbaNA6VqkDi@vO*AElFfyU|i+m{6z7P zM~o9Q&3Xf8O*>+D;I-pJ);nceq~2b#`oim#EA|scSjT*t~hMsWIQx zN=W~2MZ(sD+_#?HS~t)5*8}5w>AR-)mGB-(_k%2+|p7NV7204X%3@qFX>>`{Zd|(3TE-V_*VG^KkI+fC-p%mps4XuC<+4_!yXh(!zYgqF@3h-IqN(!<@Zy z8y^D?P@0d8j|)t|q}Tr|vG!80jpbwD07~<+@p6F)nDoBH`i)y0R2cafSb@?)d_tmN z0w(>o(N(glyH<*iffFb#$|))UCScP4_9^$&`&91cV_*bI3$O`rg9(^4Ls4p4u^y18 ze~>RDI7{;&BNsa-2d4;3J|*q^gCpLL&H{xQIT(du!bPb$i6C}yQF5_3h{3M;p*wfa Pm$MCwYz%B50SEv9t9pDT literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/p_queue_example.c.43EE21D32765822A.idx b/.cache/clangd/index/p_queue_example.c.43EE21D32765822A.idx new file mode 100644 index 0000000000000000000000000000000000000000..f8f1c7610abecdac4384b0434e69b5bcd93d9b29 GIT binary patch literal 2210 zcmWIYbaR`Ungpt$TGxu=<3Eod47Q{+-6-)mU|L!95*g4R)<&EtQ?3 z?x)&|j%d0cntADllhfIok!~|{6n(rqP8dlq+!P>Kl5_dRe&O~y?Tyt}*lQV7ugV|4 zvWw5;TllSi)^Ao0gybx0Cnq)#uxbD;}S^=V5(7 zEp_jN%FY|MYrglKbKL)~iQj1R-U9+N<_Y{i__2S9w1dAuuE&Nh-B}Y>ecbXsV9x8O zam)X@cr3iYo+z^@Cqhn~NgPQ|kx`Ky=3ZFpV?s@J zJbbbevT`s*_PjH8D6wSpF+;*1<}36F6txz0fY}L)NHH#ygA71)q? zdhb9z`PP8T0Im|2cCjWK zSh`EyIOWg3@(sJu(tA<#)%H2xBkQro6)X|)@QJF5YQo~G`FZ(DaVu62s7bKg)+Nuc zxNB|a6#}VWlUG!f5P0x!i7>` z@$jiKs&XK#(_z#>;u$a+zLJLD+0%Vkh2lVp(uLla8a(SVa!*V3dZD@K?^ZojN zCDvZ*wXp;OT1Zex6y_P2`E1zo5zN6nd`j#}@G#`zQ)X92;;FE!Ao0}L)!@9hjjoba z-L+C!LmQT&U?IoIg;G@i+o#-9?^C&(6`R9gMI{d(HxIWU%r88A!m`3j2%e&>q7uw> zV9B482r9;ki;|0hH7WxGKZpPWM!pw45`X^6+<3~!&ybv(k^`*u7&y7OS(w?GSUK1j Fxd0P-xM=_Y literal 0 HcmV?d00001 diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..311ee05 --- /dev/null +++ b/.clang-format @@ -0,0 +1,178 @@ +--- +Language: Cpp +# BasedOnStyle: Microsoft +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +#DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseLabels: true +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequires: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 1000 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +PPIndentWidth: -1 +ReferenceAlignment: Pointer +ReflowComments: true +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseCRLF: false +UseTab: Always +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +... + diff --git a/.dir-locals.el b/.dir-locals.el deleted file mode 100644 index 33388b3..0000000 --- a/.dir-locals.el +++ /dev/null @@ -1,6 +0,0 @@ -((c-mode . ((lsp-clients-clangd-args . ("--compile-commands-dir=build/" - "--pch-storage=memory" - "--background-index" - "-j=4" - )) - ))) diff --git a/CircularBuffer/p_cbuffer.c b/CircularBuffer/p_cbuffer.c deleted file mode 100644 index b7b6b31..0000000 --- a/CircularBuffer/p_cbuffer.c +++ /dev/null @@ -1,77 +0,0 @@ -#include - -// Error handler used for debugging only - -// Circular Buffer Prototypes -- Double -static PB_CB_STATUS p_cb_double_push(p_cb_double *cbuffer, double value); -static PB_CB_STATUS p_cb_double_empty(p_cb_double *cbuffer); - -// Circular Buffer Definitions -- double -PB_CB_STATUS p_cb_double_init(p_cb_double *circ_buffer, double *buff, uint32_t max_length) -{ - PB_CB_STATUS ret = PB_CB_GOOD; - do - { - // Make sure the buffer isn't bad (null) - if (!buff) - { - ret = PB_CB_NULL_BUFFER; - break; - } - - // Make sure the max buffer is a useable size - if (max_length > PB_CB_MAX_BUFFER_SIZE || max_length <= 0) - { - ret = PB_CB_BAD_BUFFER_SIZE; - break; - } - circ_buffer->buffer = buff; - circ_buffer->max_len = (uint16_t)max_length; - circ_buffer->head = 0; - circ_buffer->push = p_cb_double_push; - circ_buffer->empty = p_cb_double_empty; - circ_buffer->empty(circ_buffer); - } while (0); - - return ret; -} - -PB_CB_STATUS p_cb_double_push(p_cb_double *cbuffer, double value) -{ - PB_CB_STATUS ret = PB_CB_GOOD; - - if (!cbuffer) - { - ret = PB_CB_NULL_CBUFFER; - } - else - { - cbuffer->buffer[cbuffer->head] = value; - cbuffer->head = (cbuffer->head + 1) % cbuffer->max_len; - } - - return ret; -} -PB_CB_STATUS p_cb_double_empty(p_cb_double *cbuffer) -{ - PB_CB_STATUS ret = PB_CB_GOOD; - do - { - if (!cbuffer) - { - ret = PB_CB_NULL_CBUFFER; - break; - } - - if (!cbuffer->buffer) - { - ret = PB_CB_NULL_BUFFER; - break; - } - memset(cbuffer->buffer, 0, sizeof(double) * cbuffer->max_len); - cbuffer->head = 0; - cbuffer->b_empty = true; - cbuffer->b_filled = false; - } while (0); - return ret; -} diff --git a/CircularBuffer/p_cbuffer.h b/CircularBuffer/p_cbuffer.h deleted file mode 100644 index 41ba807..0000000 --- a/CircularBuffer/p_cbuffer.h +++ /dev/null @@ -1,64 +0,0 @@ -// Resource/Inspiration: https://embedjournal.com/implementing-circular-buffer-embedded-c/ - -/* - Penguin's Circular Buffer -- a simple floating queue designed for low memory usage (mainly for embedded) - - This is a ring buffer with limited capabilities. It is meant as a container for moving data. - Normally included features such as checks to see if the buffer is full or empty have been omitted - because this type of buffer is being implemented mainly for sensor data usage. Data is almost never read - individually, and even if it is, it isn't meant to be cleared on read. It is a simple moving buffer that - automatically writes over old data for the purpose of keeping track of the most up to date data. - - Notes: - - Initialization is mandatory using p_cb__init. - - If a circular buffer is not initialized, you will run into a LOT of hard-to-debug problems. Just initialize it. - - By default, all cb types should be defined as disabled. This is to save on size. Enable the ones you want to use. - - Behavior: - - Oldest data is overwritten - - popping of data isn't implemented. If this feature is required, use a ring buffer. - - Acronyms: - - p: penguin - - pb: penguin buffer - - cb: circular buffer - - uX, where X is bit size: unsigned X bit integer - - iX, where X is bit size: signed X bit integer -*/ - -#ifndef _PCIRCULARBUFFER_H_ -#define _PCIRCULARBUFFER_H_ - -#include -#include - -typedef enum PB_CB_STATUS -{ - PB_CB_GOOD = 0, - PB_CB_BAD = 1, - PB_CB_BAD_BUFFER_SIZE = 2, - PB_CB_NULL_BUFFER = 3, - PB_CB_NULL_CBUFFER = 4 -} PB_CB_STATUS; - -#if PB_CB_DOUBLE -typedef struct p_cb_double -{ - double *buffer; - uint16_t head; - uint16_t max_len; - // Signifies the buffer being filled at least once. - // Useful for initializing sensor data - bool b_filled; - // Signifies the buffer being empty - // Useful for knowing if data is being received - bool b_empty; - - PB_CB_STATUS (*push)(struct p_cb_double *cbuffer, double value); - PB_CB_STATUS (*empty)(struct p_cb_double *cbuffer); -} p_cb_double; - -PB_CB_STATUS p_cb_double_init(p_cb_double *circ_buffer, double *buff, uint32_t max_length); -#endif - -#endif diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..272ffd6 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +CC=gcc + +CFLAGS=\ +-Wall \ +-std=gnu11 \ + +ifdef $(DEBUG) + CFLAGS+=\ + -DDEBUG\ + -ffunctions-sections\ + -O3\ + -g3 +endif + + +MK_DIR=mkdir -p + +.PHONY: clean examples tests + +all: examples + +clean: + rm -rf bin + + +examples: clean + $(MK_DIR) bin/ + $(CC) $(CFLAGS) -I. ./examples/p_queue_example.c -o ./bin/p_queue_example + $(CC) $(CFLAGS) -I. ./examples/p_cbuffer_example.c -o ./bin/p_cbuffer_example + + + diff --git a/bin/p_cbuffer_example b/bin/p_cbuffer_example new file mode 100644 index 0000000000000000000000000000000000000000..cb6fcaa596aef371e37e52ae51408a38848c97cc GIT binary patch literal 15872 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 MWiZEK;9$kS0YxJ!M*si- literal 0 HcmV?d00001 diff --git a/compile_commands.json b/compile_commands.json new file mode 100644 index 0000000..7872854 --- /dev/null +++ b/compile_commands.json @@ -0,0 +1,16 @@ +[ + { + "arguments": [ + "gcc", + "-c", + "-Wall", + "-std=gnu11", + "-I.", + "-o", + "./bin/p_queue_example", + "examples/p_queue_example.c" + ], + "directory": "/storage/Shared/Projects/PenguinBuffers", + "file": "examples/p_queue_example.c" + } +] \ No newline at end of file diff --git a/examples/p_cbuffer_example.c b/examples/p_cbuffer_example.c new file mode 100644 index 0000000..b480145 --- /dev/null +++ b/examples/p_cbuffer_example.c @@ -0,0 +1,90 @@ +/* + (WORK IN PROGRESS) + Penguin's Circular Buffer Example -- Turning a noisy bell curve into a less + noisy bell curve + + Here's an example of using the circular buffer library for sensor data: + + Let's say I have a sensor that gives me temperature at 1khz (using ideals so + it is exactly 1khz) If this sensor was only giving me raw analog data, I + might want to do some processing on these values so that I can make sure the + values are as clean as possible. The more samples we have, the closer we are + to the actual value. The faster we gather samples, the closer we get to + representing our data in realtime. + + Using the sample rate, I can decide on a sampling window (in seconds) and an + OSR (Oversampling Rate). The larger the buffer size, the more memory I need, + so it is useful to find a happy medium between a large buffer and clean + values. + + A sampling window is the window of time in which we can accept values for an + average value. Depending on your application, a sampling window may need to + be extremely small or maybe not so small. A rocket going extremely fast using + some sensor for real time controls will want an extremely small sampling + window as well as a lot of measurements for both clean, near noiseless data + and as close to realtime data as possible. + + The sampling windows is usually an engineering requirement given that can + match the sampling rate of the sensor with the following: sampling window (in + seconds) = 1 / frequency Here, the OSR is simply 1. + + So at 1khz sample rate, let's say I decide I only need a 0.125 second sample + window and I want to clean up some noisy data. We can now use this equation: + OSR = frequency * sample window (in seconds) + + All of these have ignored real world slowdowns like the time it takes to do + math on lower end hardware, interrupts slightly delaying the math, etc + Without wanting to do some hard analysis on whatever hardware we're using, I + usually take my frequency and half it to ensure timing requirements are met, + like so: + + OSR = frequency / 2 * sample window + + Please note: In applications that require real real-time data, this is not a + good way of doing things. + +*/ + +#include "p_cbuffer.h" + +DEFINE_CBUFFER(int); +#define CBUFFER_SIZE (6) + +void display(cbuffer_int_t* cbuffer) +{ + printf("Cbuffer:\n"); + for (int ind = 0; ind < cbuffer->msize; ind++) + { + printf("[%d]: %d\n", ind, cbuffer->buffer[ind]); + } + printf("\n"); +} + +int main(int argc, char** argv) +{ + cbuffer_int_t cbuffer; + int data[CBUFFER_SIZE]; + + cbuffer_int_init(&cbuffer, data, CBUFFER_SIZE); + cbuffer.push(&cbuffer, 1); + cbuffer.push(&cbuffer, 2); + cbuffer.push(&cbuffer, 3); + cbuffer.push(&cbuffer, 4); + cbuffer.push(&cbuffer, 5); + printf("Is cbuffer filled? %s\n", + cbuffer.is_filled(&cbuffer) ? "Yes" : "No"); + cbuffer.push(&cbuffer, 6); + printf("Is cbuffer filled? %s\n", + cbuffer.is_filled(&cbuffer) ? "Yes" : "No"); + display(&cbuffer); + cbuffer.push(&cbuffer, 7); + display(&cbuffer); + printf("Emptying cbuffer...\n"); + printf("Is cbuffer filled? %s\n", + cbuffer.is_filled(&cbuffer) ? "Yes" : "No"); + cbuffer.empty(&cbuffer); + cbuffer.push(&cbuffer, 8); + display(&cbuffer); + printf("Is cbuffer filled? %s\n", + cbuffer.is_filled(&cbuffer) ? "Yes" : "No"); +} diff --git a/Queue/queue.c b/examples/p_queue_example.c similarity index 83% rename from Queue/queue.c rename to examples/p_queue_example.c index d7c4791..9c547a7 100644 --- a/Queue/queue.c +++ b/examples/p_queue_example.c @@ -1,8 +1,11 @@ -#include "queue.h" +#include "p_queue.h" -QUEUE(int, 4); +DEFINE_QUEUE(int); + +#define SIZE_QUEUE (256) queue_int_t queue; +int buffer[SIZE_QUEUE]; void display(queue_int_t* queue) { @@ -24,7 +27,7 @@ void display(queue_int_t* queue) int main(int argv, char** argc) { - queue_int_init(&queue); + queue_int_init(&queue, buffer, SIZE_QUEUE); // queue.enqueue(&queue, 10); queue.empty(&queue); diff --git a/CircularBuffer/build/Makefile b/old_cbuffer_Makefile similarity index 100% rename from CircularBuffer/build/Makefile rename to old_cbuffer_Makefile diff --git a/p_buffer.h b/p_buffer.h new file mode 100644 index 0000000..592ca58 --- /dev/null +++ b/p_buffer.h @@ -0,0 +1,22 @@ +#ifndef __PBUFFER_H__ +#define __PBUFFER_H__ + +#define MAX_QUEUE_SIZE (512) +#define MAX_CBUFFER_SIZE (512) + +#include +#include +#include + +typedef enum PB_STATUS +{ + PB_GOOD = 0, + PB_BAD = 1, + PB_BAD_BUFFER_SIZE = 2, + PB_NULL_BUFFER = 3, + PB_NULL_DATA = 4, + PB_EMPTY_QUEUE = 5, + PB_FULL_QUEUE = 6 +} PB_STATUS; + +#endif diff --git a/p_cbuffer.h b/p_cbuffer.h new file mode 100644 index 0000000..7b0afed --- /dev/null +++ b/p_cbuffer.h @@ -0,0 +1,133 @@ +// Resource/Inspiration: +// https://embedjournal.com/implementing-circular-buffer-embedded-c/ + +/* + Penguin's Circular Buffer -- a simple floating queue designed for low memory + usage (mainly for embedded) + + This is a ring buffer with limited capabilities. It is meant as a container + for moving data. Normally included features such as checks to see if the + buffer is full or empty have been omitted because this type of buffer is + being implemented mainly for sensor data usage. Data is almost never read + individually, and even if it is, it isn't meant to be cleared on read. It is + a simple moving buffer that automatically writes over old data for the + purpose of keeping track of the most up to date data. + + Notes: + - Initialization is mandatory using p_cb__init. + - If a circular buffer is not initialized, you will run into a LOT of + hard-to-debug problems. Just initialize it. + - By default, all cb types should be defined as disabled. This is to save + on size. Enable the ones you want to use. + + Behavior: + - Oldest data is overwritten + - popping of data isn't implemented. If this feature is required, use a + ring buffer. + + Acronyms: + - p: penguin + - pb: penguin buffer + - cb: circular buffer + - uX, where X is bit size: unsigned X bit integer + - iX, where X is bit size: signed X bit integer +*/ + +#ifndef _PCIRCULARBUFFER_H_ +#define _PCIRCULARBUFFER_H_ + +#include "p_buffer.h" + +#define DEFINE_CBUFFER(type) \ + typedef struct cbuffer_##type##_t \ + { \ + type* buffer; \ + int head; \ + int csize; \ + int msize; \ + \ + PB_STATUS (*push)(struct cbuffer_##type##_t * cbuffer, type value); \ + PB_STATUS (*empty)(struct cbuffer_##type##_t * cbuffer); \ + PB_STATUS (*is_filled)(struct cbuffer_##type##_t * cbuffer); \ + } cbuffer_##type##_t; \ + \ + PB_STATUS cbuffer_##type##_is_filled(cbuffer_##type##_t* cbuffer) \ + { \ + return (cbuffer->csize == cbuffer->msize); \ + } \ + \ + PB_STATUS cbuffer_##type##_push(cbuffer_##type##_t* cbuffer, type value) \ + { \ + PB_STATUS ret = PB_GOOD; \ + \ + if (!cbuffer) \ + { \ + ret = PB_NULL_BUFFER; \ + } \ + else \ + { \ + cbuffer->csize = \ + (cbuffer->csize >= cbuffer->msize ? cbuffer->msize \ + : cbuffer->csize + 1); \ + cbuffer->buffer[cbuffer->head] = value; \ + cbuffer->head = (cbuffer->head + 1) % cbuffer->msize; \ + } \ + \ + return ret; \ + } \ + PB_STATUS cbuffer_##type##_empty(cbuffer_##type##_t* cbuffer) \ + { \ + PB_STATUS ret = PB_GOOD; \ + do \ + { \ + if (!cbuffer) \ + { \ + ret = PB_NULL_BUFFER; \ + break; \ + } \ + \ + if (!cbuffer->buffer) \ + { \ + ret = PB_NULL_DATA; \ + break; \ + } \ + memset(cbuffer->buffer, 0, sizeof(type) * cbuffer->msize); \ + cbuffer->head = 0; \ + cbuffer->csize = 0; \ + } while (0); \ + return ret; \ + } \ + \ + PB_STATUS cbuffer_##type##_init(cbuffer_##type##_t* circ_buffer, \ + type* buff, int total_length) \ + { \ + PB_STATUS ret = PB_GOOD; \ + do \ + { \ + if (!buff) \ + { \ + ret = PB_NULL_DATA; \ + break; \ + } \ + \ + if (total_length > MAX_CBUFFER_SIZE || total_length <= 0) \ + { \ + ret = PB_BAD_BUFFER_SIZE; \ + break; \ + } \ + memset(circ_buffer, 0, sizeof(cbuffer_##type##_t)); \ + memset(buff, 0, sizeof(type) * total_length); \ + circ_buffer->buffer = buff; \ + circ_buffer->msize = total_length; \ + circ_buffer->csize = 0; \ + circ_buffer->head = 0; \ + circ_buffer->push = cbuffer_##type##_push; \ + circ_buffer->empty = cbuffer_##type##_empty; \ + circ_buffer->is_filled = cbuffer_##type##_is_filled; \ + circ_buffer->empty(circ_buffer); \ + } while (0); \ + \ + return ret; \ + } + +#endif diff --git a/Queue/queue.h b/p_queue.h similarity index 89% rename from Queue/queue.h rename to p_queue.h index 4976972..b726a3a 100644 --- a/Queue/queue.h +++ b/p_queue.h @@ -1,31 +1,16 @@ #ifndef __QUEUE_H__ #define __QUEUE_H__ -#include -#include -#include - -#define MAX_QUEUE_SIZE (512) - -typedef enum PB_STATUS -{ - PB_GOOD = 0, - PB_BAD = 1, - PB_BAD_BUFFER_SIZE = 2, - PB_NULL_BUFFER = 3, - PB_NULL_DATA = 4, - PB_EMPTY_QUEUE = 5, - PB_FULL_QUEUE = 6 -} PB_STATUS; +#include "p_buffer.h" // This macro defines a queue type. It doesn't make a queue for you. // See below for an example on how to make a queue. // Creates a type of queue_${type}_t of size ${size}. // the size will be fixed for all instances of that type unfortunately. -#define QUEUE(type, size) \ +#define DEFINE_QUEUE(type) \ typedef struct queue_##type##_t \ { \ - int data[size]; \ + type* data; \ int head; \ int tail; \ int msize; \ @@ -88,7 +73,8 @@ typedef enum PB_STATUS } \ return ret; \ } \ - PB_STATUS queue_##type##_init(queue_##type##_t* queue) \ + PB_STATUS queue_##type##_init(queue_##type##_t* queue, type* buffer, \ + int total_length) \ { \ PB_STATUS ret = PB_GOOD; \ do \ @@ -99,13 +85,16 @@ typedef enum PB_STATUS break; \ } \ \ - if (size > MAX_QUEUE_SIZE || size <= 0) \ + if (total_length > MAX_QUEUE_SIZE || total_length <= 0) \ { \ ret = PB_BAD_BUFFER_SIZE; \ break; \ } \ } while (0); \ - queue->msize = size; \ + memset(queue, 0, sizeof(queue_##type##_t)); \ + memset(buffer, 0, sizeof(type) * total_length); \ + queue->data = buffer; \ + queue->msize = total_length; \ queue->head = -1; \ queue->tail = -1; \ queue->csize = 0; \ @@ -116,10 +105,15 @@ typedef enum PB_STATUS return ret; \ } -// Example: -// QUEUE(int, 4); -// queue_int_t queue; -// // in .c -// queue_int_init(&queue); +/* + Example: + DEFINE_QUEUE(int); + // in .c + queue_int_t queue; + const int SIZE_QUEUE = 256; + int buffer[SIZE_QUEUE]; + queue_int_init(&queue, buffer, SIZE_QUEUE); + queue_int_enqueue(&queue, 12); +*/ #endif diff --git a/p_queue_example b/p_queue_example new file mode 100644 index 0000000000000000000000000000000000000000..aaffed61e9372b02ae303a86dd292551ddebf536 GIT binary patch literal 15960 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 MWiZEK;9$kS0YxJ!M*si- literal 0 HcmV?d00001