Каков наилучший способ отправки данных на последовательный порт? - PullRequest
7 голосов
/ 19 октября 2010

Это связано с микроконтроллерами, но мы решили опубликовать это здесь, потому что это проблема алгоритмов и типов данных, а не каких-либо аппаратных средств. Я объясню проблему так, чтобы кто-то, у кого нет никаких знаний об оборудовании, все еще мог участвовать:)

  1. В микроконтроллере есть аналого-цифровой преобразователь с 10 битовое разрешение. (Это выведет значение от 0 до 1023)

  2. Мне нужно отправить это значение на ПК через последовательный порт.

  3. Но вы можете записать только 8 битов одновременно. (Вам нужно написать байтов). это ограничение в микроконтроллере.

  4. Так что в приведенном выше случае, по крайней мере, мне нужно отправить 2 байта.

  5. Приложение на моем ПК просто читает последовательность чисел для построения. Так это должно захватить два подряд байт и построить номер обратно. Но здесь нам понадобится разделитель характер также. но все же символ разделителя имеет значение ascii в диапазоне от 0 до 255, тогда он запутает процесс.

Так каков самый простой способ сделать это? Должен ли я отправить значения в виде последовательности символов?

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

Итак, мне нужно как можно быстрее отправить последовательность из 10-битных чисел через последовательный порт. :)

Ответы [ 4 ]

15 голосов
/ 19 октября 2010

Вам нужно отправить 10 битов, и поскольку вы отправляете байт за раз, вы должны отправить 16 битов. Большой вопрос - насколько скорость является приоритетом и насколько синхронизированы отправитель и получатель? Я могу придумать 3 ответа, в зависимости от этих условий.

Регулярная выборка, неизвестная точка соединения

Если устройство работает все время, вы не уверены, когда собираетесь подключаться (вы можете присоединиться в любое время в последовательности), но частота дискретизации ниже скорости связи, поэтому вам не важен размер Я думаю, что я, вероятно, сделал бы это следующим образом. Предположим, вы пытаетесь отправить десять бит abcdefghij (каждая буква один бит).

Я бы отправил pq0abcde, затем pq1fghij, где p и q являются битами проверки ошибок . Таким образом:

  • разделитель не требуется (вы можете сказать, какой байт вы читаете по 0 или 1)
  • вы определенно можете обнаружить любую 1-битную ошибку, чтобы вы знали о неверных данных

Я изо всех сил пытаюсь найти хороший двухбитный код с исправлением ошибок, поэтому, я думаю, я бы просто сделал бит четности pa для битов 2,3 и 4 (0, ab выше) и бит четности q для 5 6 и 7 (с, д, е выше). Это может быть понятнее с примером.

  1. Предположим, я хочу отправить 714 = 1011001010.
  2. Разделить на 2 10110, 01010
  3. Добавить биты для обозначения первого и второго байта 010110, 101010
  4. вычисление четности для каждой половины: p0 = par (010) = 1, q0 = par (110) = 0, p1 = par (101) = 0, q1 = par (010) = 1
  5. байтов равны 10010110, 01101010

Затем вы можете обнаружить множество различных состояний ошибки, быстро проверить, какой байт вы отправляете, если вы потеряете синхронизацию, и ни одна из операций не займет очень много времени в микроконтроллере (я бы выполнил паритет с поиском 8 записей) таблицу).

Плотные данные, известная точка соединения

Если вы знаете, что считыватель запускается одновременно с записывающим устройством, просто отправьте 4 десятибитные значения в виде 5 байтов. Если вы всегда читаете 5 байтов за раз, тогда никаких проблем. Если вы хотите еще больше сэкономить место и уже имеете хороший пример данных, я бы сжал, используя код Хаффмана .

Плотные данные, неизвестная точка соединения

В 7 байтах вы можете отправить 5 десятибитных значений с 6 запасными битами. Отправьте 5 значений следующим образом:

  • байт 0: 0 (7 бит)
  • байт 1: 1 (7 бит)
  • байт 2: 1 (7 бит)
  • байт 3: 1 (7 бит)
  • байт 4: 0 (7 бит)
  • байт 5: 0 (7 бит)
  • байт 6: (8 бит)

Тогда всякий раз, когда вы видите 3 1 в ряду для самого значимого бита, вы знаете, что у вас есть байты 1, 2 и 3. Эта идея тратит 1 бит из 56, поэтому ее можно сделать еще более эффективной, но вы бы чтобы отправить больше данных одновременно. Например (5 последовательных, 120 бит отправлено в 16 байтах):

  • байт 0: 0 (7 бит) 7
  • байт 1: 1 (7 бит), 14
  • байт 2: 1 (7 бит) 21
  • байт 3: 1 (7 бит) 28
  • байт 4: 1 (7 бит) 35
  • байт 5: 1 (7 бит) 42
  • байт 6: 0 (7 бит) 49
  • байт 7: (8 бит) 57
  • байт 8: (8 бит) 65
  • байт 9: (8 битов) 73
  • байт 10: (8 бит) 81
  • байт 11: 0 (7 бит) 88
  • байт 12: (8 бит) 96
  • байт 13: (8 бит) 104
  • байт 14: (8 бит) 112
  • байт 15: (8 бит) 120

Это довольно забавная проблема!

7 голосов
/ 19 октября 2010

Лучший способ - преобразовать данные в строку ASCII и отправить их таким образом - это значительно упрощает отладку и позволяет избежать различных проблем со связью (особое значение определенных управляющих символов и т. Д.).

Есливам действительно нужно использовать всю доступную полосу пропускания, хотя тогда вы можете упаковать 4 10-битных значения в 5 последовательных 8-битных байтов.Вам нужно быть осторожным с синхронизацией.

4 голосов
/ 19 октября 2010

Поскольку вы указали "самый быстрый способ", я думаю, что расширение чисел до ASCII исключено.

На мой взгляд, хороший компромисс в простоте и производительности кода можно получить с помощью следующей кодировки:

Два 10-битных значения будут закодированы в 3 байта, как это.

первые 10-битные биты значения: = abcdefghij

вторые 10-битные биты значения: = klmnopqrst

байтов для кодирования:

1abcdefg
0hijklmn
0_opqrst

Существует еще один бит (_), который можно использовать для проверки четности на всех 20 битах для проверки ошибок или просто установить фиксированное значение.

Пример кода (ставит 0 в позицию _):

#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;
}
0 голосов
/ 19 октября 2010

Я обычно использую стартовый байт и контрольную сумму, и в этом случае фиксированную длину, поэтому отправьте 4 байта, получатель может найти начальный байт, и если следующие три сложения составят известное количество, тогда это хороший пакет, который можно извлечьсредние два байта, если не продолжать искать.Приемник всегда может выполнить повторную синхронизацию, и это не приводит к потере пропускной способности ascii.Ascii - ваш другой вариант, начальный байт, который не является числом и, возможно, четырьмя числами для десятичного числа.Десятичная дробь в микроконтроллере определенно неинтересна, поэтому начните с чего-то не шестнадцатеричного типа, например, X, а затем с трех байтов значениями шестнадцатеричной ascii для вашего числа.Ищите x, изучите следующие три байта, надейтесь на лучшее.

...