Question

This is related with microcontrollers but thought to post it here because it is a problem with algorithms and data types and not with any hardware stuff. I'll explain the problem so that someone that doesn't have any hardware knowledge can still participate :)

  1. In Microcontroller there is an Analog to Digital converter with 10 bit resolution. (It will output a value between 0 and 1023)

  2. I need to send this value to PC using the serial port.

  3. But you can only write 8 bits at once. (You need to write bytes). It is a limitation in micro controller.

  4. So in the above case at least I need to send 2 bytes.

  5. My PC application just reads a sequence of numbers for plotting. So it should capture two consecutive bytes and build the number back. But here we will need a delimiter character as well. but still the delimiter character has an ascii value between 0 - 255 then it will mixup the process.

So what is a simplest way to do this? Should I send the values as a sequence of chars?

Ex : 1023 = "1""0""2""3" Vs "Char(255)Char(4)"

In summary I need to send a sequence of 10 bit numbers over Serial in fastest way. :)

Was it helpful?

Solution

You need to send 10 bits, and because you send a byte at a time, you have to send 16 bits. The big question is how much is speed a priority, and how synchronised are the sender and receiver? I can think of 3 answers, depending on these conditions.

Regular sampling, unknown join point

If the device is running all the time, you aren't sure when you are going to connect (you could join at any time in the sequence) but sampling rate is slower than communication speed so you don't care about size I think I'd probably do it as following. Suppose you are trying to send the ten bits abcdefghij (each letter one bit).

I'd send pq0abcde then pq1fghij, where p and q are error checking bits. This way:

  • no delimiter is needed (you can tell which byte you are reading by the 0 or 1)
  • you can definitely spot any 1 bit error, so you know about bad data

I'm struggling to find a good two bit error correcting code, so I guess I'd just make p a parity bit for bits 2,3 and 4 (0, a b above) and q a parity bit for 5 6 and 7 (c,d,e above). This might be clearer with an example.

  1. Suppose I want to send 714 = 1011001010.
  2. Split in 2 10110 , 01010
  3. Add bits to indicate first and second byte 010110, 101010
  4. calculate parity for each half: p0=par(010)=1, q0=par(110)=0, p1=par(101)=0, q1=par(010)=1
  5. bytes are then 10010110, 01101010

You then can detect a lot of different error conditions, quickly check which byte you are being sent if you lose synchronisation, and none of the operations take very long in a microcontroller (I'd do the parity with an 8 entry lookup table).

Dense data, known join point

If you know that the reader starts at the same time as the writer, just send the 4 ten bit values as 5 bytes. If you always read 5 bytes at a time then no problems. If you want even more space saving, and have good sample data already, I'd compress using a huffman coding.

Dense data, unknown join point

In 7 bytes you can send 5 ten bit values with 6 spare bits. Send 5 values like this:

  • byte 0: 0 (7 bits)
  • byte 1: 1 (7 bits)
  • byte 2: 1 (7 bits)
  • byte 3: 1 (7 bits)
  • byte 4: 0 (7 bits)
  • byte 5: 0 (7 bits)
  • byte 6: (8 bits)

Then whenever you see 3 1's in a row for the most significant bit, you know you have bytes 1, 2 and 3. This idea wastes 1 bit in 56, so could be made even more efficient, but you'd have to send more data at a time. Eg (5 consecutive ones, 120 bits sent in 16 bytes):

  • byte 0: 0 (7 bits) 7
  • byte 1: 1 (7 bits) 14
  • byte 2: 1 (7 bits) 21
  • byte 3: 1 (7 bits) 28
  • byte 4: 1 (7 bits) 35
  • byte 5: 1 (7 bits) 42
  • byte 6: 0 (7 bits) 49
  • byte 7: (8 bits) 57
  • byte 8: (8 bits) 65
  • byte 9: (8 bits) 73
  • byte 10: (8 bits) 81
  • byte 11: 0 (7 bits) 88
  • byte 12: (8 bits) 96
  • byte 13: (8 bits) 104
  • byte 14: (8 bits) 112
  • byte 15: (8 bits) 120

This is quite a fun problem!

OTHER TIPS

The best method is to convert the data to an ASCII string and send it that way - it makes debugging a lot easier and it avoids various communication issues (special meaning of certain control characters etc).

If you really need to use all the available bandwidth though then you can pack 4 10 bit values into 5 consecutive 8 bit bytes. You will need to be careful about synchronization.

Since you specified "the fastest way" I think expanding the numbers to ASCII is ruled out.

In my opinion a good compromise of code simplicity and performance can be obtained by the following encoding:

Two 10bit values will be encoded in 3 bytes like this.

first 10bit value bits := abcdefghij

second 10bit value bits := klmnopqrst

Bytes to encode:

1abcdefg
0hijklmn
0_opqrst

There is one bit more (_) available that could be used for a parity over all 20bits for error checking or just set to a fixed value.

Some example code (puts 0 at the position _):

#include <assert.h>
#include <inttypes.h>

void
write_byte(uint8_t byte);    /* writes byte to serial */

void
encode(uint16_t a, uint16_t b)
{
  write_byte(((a >> 3) & 0x7f) | 0x80);
  write_byte(((a & 3) << 4) | ((b >> 6) & 0x7f));
  write_byte(b & 0x3f);
}

uint8_t
read_byte(void);  /* read a byte from serial */

void
decode(uint16_t *a, uint16_t *b)
{
  uint16_t x;

  while (((x = read_byte()) & 0x80) == 0)  {}  /* sync */
  *a = x << 3;

  x = read_byte();
  assert ((x & 0x80) == 0); /* put better error handling here */

  *a |= (x >> 4) & 3;
  *b = x << 6;

  x = read_byte();
  assert ((x & 0xc0) == 0); /* put better error handling here */

  *b |= x;
}

I normally use a start byte and checksum and in this case fixed length, so send 4 bytes, the receiver can look for the start byte and if the next three add up to a know quantity then it is a good packet take out the middle two bytes, if not keep looking. The receiver can always re-sync and it doesnt waste the bandwidth of ascii. Ascii is your other option, a start byte that is not a number and perhaps four numbers for decimal. Decimal is definitely not fun in a microcontroller, so start with something non-hex like X for example and then three bytes with the hex ascii values for your number. Search for the x examine the next three bytes, hope for the best.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top