views:

159

answers:

4

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. :)

+7  A: 

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.

Paul R
+1 for 5 bytes concept. One practical problem. The device just sends a sequence of values. How do I recognize the blocks of 5 bytes?
Chathuranga Chandrasekara
That's why I said you will need to be careful about synchronization ! ;-) There are various ways of doing this but one possibility would be to set your UARTs to use 8 data bits + 1 manual parity bit and then set that manual parity bit to 1 for the first byte of each sequence of 5 bytes, and 0 for the rest. If your UARTs don't support this mode then you may have to consider building up a longer frame, with a special start/stop character, and/or perhaps only use 7 data bits out of every 8.
Paul R
+1 for mentioning that sending in ASCII has advantages - I think that they shouldn't be underestimated if it can be fast enough.
Michael Burr
@Chathuranga - One suggestion for a delimiter character(s) to separate blocks of data would be to define an 'invalid' data value. In your case since you have 10-bit data, then 0xFFFF since its obviously outside the possible values of your data. When you parse the data stream, every time you encounter 0xFFFF you'll know that its the start of a new data block.
spade78
@Paul R - +1 for an example of how I can use existing RS-232 field for data packet meta purposes. Never thought that it could be used like that. I always thought of introducing extra bytes for meta info but then that's always overhead.
spade78
+13  A: 

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!

Nick Fortescue
Very very helpful. +1
Chathuranga Chandrasekara
@ Nick : We just increase the probability of detecting 2 bit errors by using 2 bit parity?? Or are there any other advantages?
Chathuranga Chandrasekara
One extra advantage was you then only need to find the parity of three bits, so only need a size 8 table! However you are right there isn't much advantage. I had a spare bit so just wanted to find something vaguely useful to do with it. I couldn't see any good 2bit error correcting schemes.
Nick Fortescue
@ Nick: I am thinking something like this. Instead of 2 bit detection I suggest a one correction bit. And instead of using a single bit for first byte/second byte mechanism, we use 2 bits. So instead of marking 1,2 now we can mark 1,2,3,4 - 1,2 is relevant to a one byte and 3,4 is for the consecutive byte. The advantage is we can just keep a 4 byte buffer in PIC (a rotating one) and we can implement a repeat request process. The PC needs to tell, hey!! give me "3".. Hope you get my idea :)
Chathuranga Chandrasekara
@Chathuranga - sounds good
Nick Fortescue
+3  A: 

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;
}
Peer Stritzinger
If you like this aproach, I can post code that calculates the parity as well
Peer Stritzinger
+1 -- simple and efficient.
Jason S
I'd probably use "1defghij","0nopqrst","0abc_klm" so that you minimize the #s of shifts on encoding.
Jason S
Yes if minimising work done encoding is top priority (which it might well be) it would be better to encode like "1defghij","0nopqrst","0abc_klm". I thought about it when I already hat done the example code. One advantage that remains in my encoding is the lag until the first value can be seen by the receiver is lower. But this is very probably totaly not a problem here. But lag should always be considered if there is more blocking and interleaving going on.
Peer Stritzinger
A: 

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.

dwelch
in this case with only 10 bits, you could put 5 bits each per byte in the lower bytes and mark the upper half and lower half in the upper bits say 0x123 (0b0100100011) becomes 0x89,0x03 for example. The receiver knowing which half is marked as the upper and which half comes first upper or lower can always put the pairs back together....Ahh, Peer's answer basically, a parity bit is good too.
dwelch