Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way of sending the data to serial port?

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

like image 609
Chathuranga Chandrasekara Avatar asked Oct 19 '10 07:10

Chathuranga Chandrasekara


People also ask

How do you transfer data to a serial port?

To transmit a data byte, the serial device driver program sends a data to the serial port I/O address. This data gets into a 1-byte "transmit shift register" in the serial port. From this shift register bits are taken from the data one-by-one byte and sent out bit-by-bit on the serial line.

How the data is transferred in serial communication?

In serial communication, data is sent one bit at a time using one signal line, so in order for the receiving side to accurately receive the data, the sending side must know at what speed it is sending each bit.In RS-232C, synchronous communication and asynchronous communication standards have been defined.

Which standard format is used for sending data on the serial port?

As the name suggests, the serial port sends and receives bytes of information in a serial fashion—one bit at a time. These bytes are transmitted using either a binary format or a text (ASCII) format.

What are the two methods used in serial data communication?

A serial interface where both devices may send and receive data is either full-duplex or half-duplex. Full-duplex means both devices can send and receive simultaneously. Half-duplex communication means serial devices must take turns sending and receiving.


3 Answers

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!

like image 53
Nick Fortescue Avatar answered Oct 24 '22 17:10

Nick Fortescue


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.

like image 41
Paul R Avatar answered Oct 24 '22 18:10

Paul R


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;
}
like image 30
Peer Stritzinger Avatar answered Oct 24 '22 18:10

Peer Stritzinger