Creating Circular Buffer lib

nick_p:
Bill, could you recommend any debug tools? I have Linux at home and Windows at work. No Mac though.

For debugging I write the code on a platform with the best debug tools.
It is worth the time to get source level debugging up and working.

Currently I use linux to get the logic debugged then move it to the AVR.
I use command-line gdb on linux for source level debugging.
gdb is nice because it works on pretty much everything including
embedded environments once you have the proper gdb interfaces set up.
I like gdb because it means only one environment to learn. (been using it for about 25 years)
There are some GUI wrappers and full IDEs like eclipse but I still use the command-line
as the command-line works the same everywhere.

From what I understand from your post, I will need to consider a trade-off between using a handful of big functions (slower execution), or to use lots of small functions (lots of memory).

No. The trade off I was referring to was between RAM usage for Q entries
and function call latency. i.e. the amount of time it takes to figure out
what function to call once the entry is pulled from the Q not the functions themselves.
The code you have now is using a case statement to figure out what function to call based on a "char" token.
As the number of functions increases, this method becomes less efficient, requiring more code and taking more time.
If the Q entries contained a function pointer instead, then there is no additional code to figure out what function
to call, and the code size and latency is a constant no matter how many functions are being used.
In the situations where I needed to do this in the past,
I also needed some context information, so my Q entries contained a function pointer and an argument.
Each function then received a single argument.
This allows doing things like scheduling the same function but with a different argument to be able to deal with say
multiple instances of something.
It would use the Q mechanism to Q tasks from the ISRs.
I've also had systems that had multiple Qs (usually 2) so that there could be a high priority Q and a low priority Q.

When using this mechanism, the idle loop did nothing more than process the Qs.
Everything else was scheduled through the Qs.
Essentially it is callback Q. And the idle loop runs the Q(s) and processes
all the callbacks.

--- bill