Thread: Char Buffer

  1. #1
    Registered User
    Join Date
    Apr 2013
    Posts
    56

    Char Buffer

    Hi,


    I need some advise on how to implement a circular buffer, I do not really understand why it is used. I want to read a string from a 8 bit, 1 byte port on a micro. So if I read hello in on this port, this would be 6 bytes including the null char. At this point I think why cant I write this into a standard char array of a fixed size. Why is it recommended to use a circular buffer?

    Thanks,

    RocketMan46

  2. #2
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    A circular buffer would allow you to "reuse" your array, by overwritting earlier portions that are (presumably) not being used anymore. It is a good way to make an "infinite" buffer. They are especially useful if it's possible to receive a new command while a current one is processing (otherwise, you could just clear the buffer and wait for a new command).

    Let's say you have a buffer 12 bytes long, and you receive commands that consist of four bytes, plus one terminating byte.

    You read the first command "ABCDt":

    Code:
    .
     0   1   2   3   4   5   6   7   8   9   10  11
    [A] [B] [C] [D] [t] [ ] [ ] [ ] [ ] [ ] [ ] [ ]
     ^
    While you're processing this command, another is received "EFGHt":

    Code:
    .
     0   1   2   3   4   5   6   7   8   9   10  11
    [A] [B] [C] [D] [t] [E] [F] [G] [H] [t] [ ] [ ]
                         ^
    You finish processing the first command, and start on the second command. At the same time, a third command "IJKLt" comes in:

    Code:
    .
     0   1   2   3   4   5   6   7   8   9   10  11
    [K] [L] [t] [D] [t] [E] [F] [G] [H] [t] [I] [J]
                                             ^
    The buffer wraps around and stores the new data over the (now "used up") old data.

    Obviously, it's important that the buffer is long enough to not allow un-processed commands to be overwritten.
    Last edited by Matticus; 04-28-2016 at 02:25 PM.

  3. #3
    Registered User
    Join Date
    Apr 2013
    Posts
    56
    Hi Matticus,

    Thanks for your detailed reply, that was very helpful.

    My next problem is, after reading numerous examples, I do not understand how to implement a circular buffer. Most examples have a buffer, push and pop functions.

    So I understand main should be something like below, but I do not really understand. I want to read in hello, put in buffer, then print out buffer contents. I have tried to assemble an example I have found on another site but it is not working.

    * read char
    * push char into buffer
    * pop last char out of buffert
    * print out buffer

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    
    #define CIRCBUF_DEF(x,y) unsigned char x##_space[y]; circBuf_t x = { x##_space,0,0,y}
    
    
    
    
    typedef struct
    {
        unsigned char * const buffer;
        int head;
        int tail;
        const int maxLen;
    }circBuf_t;
    
    
    CIRCBUF_DEF(cb, 6);
    
    
    int circBufPush(circBuf_t *c, unsigned char data)
    {
        int next = c->head + 1;
        if (next >= c->maxLen)
            next = 0;
    
    
        // Cicular buffer is full
        if (next == c->tail)
            return -1;  // quit with an error
    
    
        c->buffer[c->head] = data;
        c->head = next;
        return 0;
    }
    
    
    int circBufPop(circBuf_t *c, unsigned char *data)
    {
        // if the head isn't ahead of the tail, we don't have any characters
        if (c->head == c->tail)
            return -1;  // quit with an error
    
    
        *data = c->buffer[c->tail];
        c->buffer[c->tail] = 0;  // clear the data (optional)
    
    
        int next = c->tail + 1;
        if(next >= c->maxLen)
            next = 0;
    
    
        c->tail = next;
    
    
        return 0;
    }
    
    
    unsigned char buffer(unsigned char data)
    {
        if (circBufPush(&cb, data))
        {
                printf("Out of space in CB\n");
        }
    
    
        if (circBufPop(&cb, &data))
        {
                printf("CB is empty\n");
        }
    
    
    return data;
    }
    
    
    int main()
    {
        char i;
        unsigned char text[6] = "hello";
        unsigned char data;
    
    
        for(i = 0; i < 5; i++)
        {
            printf("%c", text[i]);
            
            // I cant push data into the buffer
            circBufPush(1,text[i]);
        }
    
        // print buffer contents
    
    
        return 0;
    }

  4. #4
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    If you are still trying to understand the basics of this concept, I suggest dispensing with any push/pop functionality for the time being. I also suggest you avoid finding pre-existing code, and try to express the ideas yourself in C.

    The whole concept relies on the idea that, once you reach the end of your buffer, you start back at the beginning.

    Here are a few exercises for you to try.



    Exercise 1. Create a circular char buffer with 8 elements, and load the following sequence of characters into it: ABCDEFGHIJK

    At this phase, it is perfectly okay to overwrite previous data (that is actually the point of this exercise).

    I recommend you create a simple function that receives one character and adds it to the circular buffer. Call this function for each of the above characters to be added.

    The trick here is to understand what happens when you reach the end of the array. Once you fill in the last element, you need to go back to the beginning.

    This will illustrate the importance of keeping track of the "next available" index in the circular array, and what to do when it reaches the end of the array.

    Be sure to print out the contents of your array at the end to ensure it contains the data you expect.



    Exercise 2. Using an 8-char circular buffer, load it with the following string: "ABC\nDEF\nGHI\nJKL\n".

    Assume that a newline character ('\n') terminates a command.

    This time, you want to ensure data is printed ("processed") before it is overwritten. So when you receive a newline, you need to print the preceding command.

    This will illustrate the importance of keeping track of the "start" index for a given command. When a command is "processed", the "start" position must be updated to indicate the starting point of the next command.

    Example:

    Code:
    // caret (^) is the current "start" position
    
    // 'A' is received
     0   1   2   3   4   5   6   7
    [A] [ ] [ ] [ ] [ ] [ ] [ ] [ ]
     ^
    
    // 'B' is received
     0   1   2   3   4   5   6   7
    [A] [B] [ ] [ ] [ ] [ ] [ ] [ ]
     ^
    
    // 'C' is received
     0   1   2   3   4   5   6   7
    [A] [B] [C] [ ] [ ] [ ] [ ] [ ]
     ^ 
    
    // '\n' is received
     0   1   2   3   4   5   6   7
    [A] [B] [C] [n] [ ] [ ] [ ] [ ]
     ^ 
    
    // Command terminated, "ABC" is printed
    
    // 'D' is received
     0   1   2   3   4   5   6   7
    [A] [B] [C] [n] [D] [ ] [ ] [ ]
                     ^
    
    // ... and so on
    If you get these two exercises working, you will be well on your way to implementing a fully functional circular buffer, simple enough for use on an 8-bit MCU.

    If you need any clarification, or have any questions along the way, feel free to ask.
    Last edited by Matticus; 04-29-2016 at 04:05 PM.

  5. #5
    Registered User
    Join Date
    May 2015
    Posts
    228
    Wouldn't the modulus operator be a very nice way to iterate through a circular buffer Matticus? The reason I'm mentioning the modulus operator is because the very nature of the modulus operator is circular. This means if we have a buffer that can hold 8 characters and the buffer is completely full, the modulus operator will take us back to the beginning of the buffer and from there we can continue to overwrite characters.

  6. #6
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    Quote Originally Posted by deathslice View Post
    Wouldn't the modulus operator be a very nice way to iterate through a circular buffer Matticus? The reason I'm mentioning the modulus operator is because the very nature of the modulus operator is circular. This means if we have a buffer that can hold 8 characters and the buffer is completely full, the modulus operator will take us back to the beginning of the buffer and from there we can continue to overwrite characters.
    Generally speaking, yes, the modulus operator works very well in an indexing expression for a circular array. In fact, such an expression is very straight-forward and succinct.

    However, if one is programming a simpler microcontroller (as the OP appears to be doing), they should be aware that modulus can be an expensive operation. While this may have minimal impact on your average desktop application, it can be very costly in a memory-limited device such as this (depending upon the specific device in question, of course).

    I recently had an application where I had to squeeze out every byte I possible could to meet design constraints. I originally reset my state machine with a modulus in this manner - switching to a "if max, then zero" operation reduced my code size by almost a hundred bytes (which was significant for this application).

    But again, yes, modulus works well for this purpose in general.

  7. #7
    Registered User
    Join Date
    May 2015
    Posts
    228
    I see, sounds to me like embedded programming is not only challenging but very brutal if even a modulus operation is considered to be an expensive operation. In that situation, it makes sense to have an if statement that simply reset the index to zero whenever you reach the end of the buffer.

  8. #8
    Registered User
    Join Date
    Apr 2013
    Posts
    56
    OK, I will try and write my own buffer and come back with my solution in steps.

    Currently I never use a debugger only print to screen in both windows and embedded. Would you say this is a major disadvantage to the progression in my development?

    Thanks,

    Rocketman46.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. XOR on char buffer fails?
    By heatblazer in forum C Programming
    Replies: 9
    Last Post: 01-12-2015, 10:41 AM
  2. Can not get valid value from Char* buffer
    By jeffwang in forum C Programming
    Replies: 6
    Last Post: 01-05-2012, 05:00 PM
  3. Char Buffer filtering
    By TGM76 in forum C Programming
    Replies: 3
    Last Post: 08-03-2010, 11:42 PM
  4. how to know if there is some char in the buffer..
    By transgalactic2 in forum C Programming
    Replies: 30
    Last Post: 01-30-2009, 03:27 PM
  5. Using a *char Buffer
    By Hankyaku in forum C++ Programming
    Replies: 2
    Last Post: 04-13-2003, 01:49 AM