1

I need to create a byte array that is needed to be stream to another device through UART. There are some fixed parameters that I can fill in before hand but variables such as string is dynamically sized. Right up till now, I've been doing:

unsigned char buffer[255];
unsigned char wr_head = 0;
buffer[wr_head++] = 0x01; // and so on
memcpy(&buffer[wr_head], &some_chararray, sizeof(some_chararray));
wr_head += some_chararray;

I've experimented with other methods like std::string and std::vector but I felt that there is much manageable way of writing byte array for streams. Suggestions?

edit: Please advice on performance as well because is threaded.

edit2: Sorry for lacking of details the first time around. The device is indeed an embedded device. Though some suggested some solution, its not really what I want. Maybe a snippet of my current implementation will clear some confusion:

unsigned char buffer[255];
unsigned char wr_head = 0;

buffer[wr_head++] = 0x01; // Set message type
buffer[wr_head++] = 0x30; // message length
memcpy(&buffer[wr_head], &some_chararray, sizeof(some_chararray));
wr_head += some_chararray;
buffer[wr_head++] = CalChecksum;
UartSend(&buffer, wr_head); // Send array to stream out from UART

The configuration and setting value is known before hand, provided by the device documentation. This question is related to what I've asked in here

Thanks for the effort so far.

Community
  • 1
  • 1
freonix
  • 1,605
  • 3
  • 22
  • 35

2 Answers2

3

A ring buffer is a typical solution for problems like these.

I have no idea what kind of device you're on, but I'll just suppose that you're writing for some kind of embedded device. Let's assume that there's some interrupt moving data from the ring buffer to the UART. This interrupt will call getc, other code will call putc and puts.

class RingBuffer {
private:
    static unsigned BUFSZ = 256;
    volatile unsigned char buf[BUFSZ];
    volatile unsigned char read, write;

public:
    RingBuffer() : read(0), write(0) { }

    // Blocks until space is available
    void putc(unsigned int c) {
        while (((write - read) & (BUFSZ - 1)) == 1)
            sleep();
        buf[write++ & (BUFSZ - 1)] = c;
    }

    // Returns -1 if empty
    int getc() {
        if (read == write)
            return -1;
        return buf[read++ & (BUFSZ - 1)];
    }

    // There are faster ways to write this.
    void puts(char *str) {
        for (; *str; ++str)
            putc(*str);
    }
};

Typically, you don't want to make the buffer dynamically grow for something like this. There's lots of room for improvement in the above code, and there are also libraries available for this kind of thing.

This particular implementation also never lets you fill the buffer completely, but the code is simpler as a result. I probably wouldn't put this code in production, but hopefully it's a step in the right direction.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • `putc()` and `getc()` are called from different threads otherwise it makes no sense. If they called from different threads then the code should have proper sync primitives. Without them all this will produce "interesting" results. – c-smile May 19 '11 at 02:46
  • Are they on different threads or different processors? This code will work with multithreading but not on multiprocessor systems. You haven't told us what kind of device it is. – Dietrich Epp May 19 '11 at 02:49
  • @ Dietrich: Thanks for the explanation, I've updated the question to be, hopefully, much clearer. – freonix May 19 '11 at 02:54
  • @Dietrich Epp: how this "different threads or different processors" is related to the need of using critical sections here or the like? This code will crash in both cases. Just with different frequencies. – c-smile May 19 '11 at 03:05
  • @c-smile: How would it crash on a uniprocessor system? Can you give a scenario? (Assuming one reader, one writer.) – Dietrich Epp May 19 '11 at 03:14
  • @Dietrich Epp: it is "threaded" as author told us. In your getc() function the value of `write` can be changed after `if (read == write)` and before `read++`. In general special treatment needs to be made in such cases, see: http://www.codeproject.com/KB/threads/LockFree.aspx for example. – c-smile May 19 '11 at 04:53
  • @c-smile: I don't see how such a scenario would give incorrect results. The linked article seems to be geared towards multiprocessor systems. In particular, on a uniprocessor system, I/O reordering is never an issue. – Dietrich Epp May 19 '11 at 05:04
  • If getc() is called from an interrupt then you'd probably want to disable the interrupt while updating the buffer in putc(). But like you said, you wouldn't put this code into production without a more complete implementation. – Sean May 19 '11 at 05:11
  • @Sean: Why is it a problem if `getc` is called in an interrupt? – Dietrich Epp May 19 '11 at 05:15
  • 1
    @Dietrich: Let's say read == write == 0. You call putc() from your main code which puts the character in the buffer. If you don't disable interrupts it's possible for getc() to be called after the character is inserted but before write is incremented. If that happends, getc() will return -1 instead of returning the character. – Sean May 19 '11 at 06:03
  • @Sean: That is correct behavior. The `putc` function hasn't returned yet. It doesn't violate atomicity of the `getc` and `putc` functions. – Dietrich Epp May 19 '11 at 06:35
0

If UartSend is a blocking function then you can do just this:

void UartSend(byte b) { UartSend(&b, 1); } // sends one byte

UartSend(0x01); // Set message type
UartSend(0x30); // message length
UartSend(some_chararray,sizeof(some_chararray));
c-smile
  • 26,734
  • 7
  • 59
  • 86