2017-02-14 20 views
0

долгое время читатель, впервые постер.Надежный последовательный протокол с автоответчиком вопросы

У меня есть последовательная связь между датчиком и базовой станцией через bluetooth. Ссылка bluetooth невероятно ненадежна и отбрасывает пакеты, как вы не поверите. Я использую это как положительное и собираюсь разработать надежный последовательный протокол, который может пережить связь с дерьмом.

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

Планирование использования байтовой начинки для создания пакетов с байтами начала (STX) и конца (ETX), номера индекса и CRC. Я планирую использовать escape-символ (DLE), когда появляются символы STX и ETX и DLE. Эта часть все довольно ясно, и здесь сказано код, который должен сделать

static void SendCommand(struct usart_module * const usart_module, uint16_t cmd, 
     uint8_t *data, uint8_t len) 
{ 
    //[STX] ({ [IDX] [CMD_H] [CMD_L] [LEN] ...[DATA]... } [CRC]) [ETX] // Data in {} brackets is XORed together for the CRC. // Data in() is the packet length 
    static uint8_t idx; 
    uint8_t packet[256]; 
    uint8_t crc, packetLength, i; 

    if (len > 250) 
     return; 

    packetLength = len + 5; 

    packet[0] = idx++; 
    packet[1] = (cmd >> 8); 
    packet[2] = (cmd & 0x00FF); 
    packet[3] = packetLength; 

    crc = 0; 
    for (i = 0; i <= 3; i++) 
    { 
     crc ^= packet[i]; 
    } 

    for (i = 0; i < len; i++) 
    { 
     packet[4 + i] = data[i]; 
     crc ^= data[i]; 
    } 

    packet[4 + len] = crc; 

    // Send Packet 
    uart_putc(usart_module, STX); 
    for (i = 0; i < packetLength; i++) 
    { 
     if ((packet[i] == STX) || (packet[i] == ETX) || (packet[i] == DLE)) 
     { 
      uart_putc(usart_module, DLE); 
     } 
     uart_putc(usart_module, packet[i]); 
    } 
    uart_putc(usart_module, ETX); 
} 

Так что пошлет пакет, но теперь мне нужно добавить код для отслеживания пакетов и автоматически ретранслировать неисправные из них и Вот где мне нужна помощь с некоторыми идеями.

Варианты пар у меня были; -Easiest, назначьте гигантский массив из 256 пакетов, каждый из которых имеет достаточно места для хранения пакета и после передачи пакета, поместите его в буфер, и если я не получу ACK через х количество времени, отправьте его снова. Тогда, если я действительно получаю ACK, удалите эту запись из массива, чтобы запись была пустой, поэтому я знаю, что она получена просто отлично.

Проблема с этим состоит в том, что если я использую худший размер случае пакета 256 байт, и 256 экземпляров из них, тот 64К оперативной памяти и я не имею, что (и даже если я сделал, вот страшная мысль)

-Следующая идея, создайте связанный список всех пакетов и динамически назначайте память с помощью команд malloc и т. Д. И удалите те, которые получили подтверждение, и сохраните те, которые есть, поэтому я знаю, что для повторной передачи через x время.

Вопросы, которые у меня есть, это идея всего malloc. Честно говоря, я никогда не использовал его, и мне не нравится идея его использования во встроенной среде с ограниченной памятью. Может быть, это только я, будучи глупым, но я чувствую, что это открывает дверь для загрузки ковша других проблем, которые мне не нужны.

-Потенциальное решение, создайте связанный список для всех пакетов, как указано выше, но создайте их в fifo и переместите все записи вокруг, чтобы сохранить их в fifo.

например. отправить пакет 1, положить пакет в в ФИФО
отправки пакета 2, положить пакет в в ФИФО
получить NACK для пакета 1, ничего не делать
отправить пакет 3, положить пакет в в ФИФО
получить ACK для пакет 2, MemSet пакет 2 до 0х00 в FIFO
приема ACK для пакета 3, MemSet пакетов от 2 до 0х00 в Fifo
отправки пакета 4, поместите пакет в в FIFO
отправить пакет 5, положить пакет в в FIFO
больше нет места в FIFO, пройдите и перетасуйте все на фронт, потому что там, где пакеты 2 и 3 были пусты, теперь.

Не могли бы они их перетасовать в реальном времени, но тогда нам нужно перетасовать целую фило после каждого полученного пакета, и это похоже на ненужную работу?

Что я ищу для одного из вас, чтобы сказать «Jeez Ned, это неплохо, но если вы просто делаете xyz, то это экономит ваши кучи работы или оперативной памяти или сложности» или что-то в этом роде.

Просто хочу, чтобы пара людей отказывалась от идей действительно так, как вы обычно получаете лучшее решение. Мне нравится мое решение, но я чувствую, что я что-то пропустил, или, может быть, усложнил его? не уверен ... я просто не чувствую себя на 100% довольной этой идеей, я не думаю, но просто не могу думать о лучшем пути.

+0

Вам действительно нужно жонглировать сразу несколькими пакетами? Почему бы просто не повторить один пакет, пока вы не получите ACK до передачи следующего? – kkrambo

+0

Я тоже с этим боролся, но если я могу делать сразу несколько пакетов, тогда мне не нужно ждать «длинного» времени между пакетами, чтобы убедиться, что они туда попали, и вместо этого загружать кучу данных, которые нужно отправить быстро. – Ned

+0

У меня есть некоторые сценарии, в которых я отправляю 100 пакетов в короткой последовательности, так что возможность иметь приемник обрабатывать их, пока передатчик будет отправлять сообщения, будет замечательным. Это значительно ускорит передачу данных – Ned

ответ

0

Вам не нужно использовать malloc. Просто статически выделяйте все свои пакеты, возможно, как массив структуры. Но не используйте массив для итерации через пакеты во время выполнения. Скорее, расширьте свою идею связанного списка. Создайте два списка: один для пакетов, ожидающих ACK, и другой для бесплатных (то есть доступных) пакетов. При запуске добавьте каждый пакет в свободный список. Когда вы отправляете пакет, удалите его из бесплатного списка и добавьте его в список ожидающих ACK. Сделайте список ожидающих ACK дважды связанным, чтобы вы могли удалить пакет из середины списка и вернуть его в свободный список.

Если размер вашего пакета сильно варьируется, и вы хотите поддерживать больше пакетов с меньшим объемом памяти, вы можете создать несколько бесплатных списков для пакетов разного размера. Например, в max-size-free-list хранятся самые большие пакеты, в то время как список без размера эконом-класса содержит меньшие пакеты. Список ожидающих ACK может содержать оба размера, если известен, какой бесплатный список возвращает пакеты. И это можно узнать, добавив флаг в struct struct.

typedef enum PacketSizeType 
    PACKET_SIZE_MAX = 0, 
    PACKET_SIZE_ECONOMY 
} PacketSizeType; 

typedef struct PacketBase{ 
    PacketBase * next; 
    PacketBase * prev; 
    PacketSizeType type; 
    uint8_t data[1]; // a place holder (must be last) 
} PacketBase; 

typedef struct PacketMax 
{ 
    PacketBase base; // inherit from PacketBase (must be first) 
    uint8_t data[255]; 
} PacketMax; 

typedef struct PacketEconomy 
{ 
    PacketBase base; // inherit from PacketBase (must be first) 
    uint8_t data[30]; 
} PacketEconomy; 

PacketMax MaxSizedPackets[100]; 
PacketEconomy EconomySizedPackets[100]; 
Packet *free_list_max; 
Packet *free_list_economy; 
Packet *awaiting_ack_list; 

Код инициализация должен перебирать оба массивов, установите base.type элемент в любом MAX или ECONOMY, и добавить пакет к соответствующему свободному списку. Код передачи получает пакет из бесплатного списка соответствующего размера и перемещает его в список ожидающих ACK. Список ожидающих ACK может содержать оба типа пакетов, поскольку они оба наследуются от PacketBase. Код обработчика ACK должен проверить base.type, чтобы вернуть пакет в соответствующий свободный список.

+0

Спасибо, kkrambo. Не уверен, что вы имеете в виду со свободным списком, но остальная идея хорошая. вы имеете в виду со свободным списком список всех возможных команд? потому что это просто сойдет с ума от количества команд, которые у меня есть. Я могу легко различать большие и малые пакеты на основе команды, если я поместил все большие вместе в перечисление, поэтому я знаю, что все команды после CMD_BIG или что-то все идут в большом массиве и т. Д. Спасибо за ваши идеи, высоко оценили – Ned

+0

@Ned. Свободный список - это связанный список, в котором хранятся пакеты, которые в настоящее время не используются (т. Е. Доступные пакеты). Удаление пакета из бесплатного списка аналогично malloc. Возврат пакета к свободному списку аналогичен его освобождению. Пакеты всегда находятся в списке свободных или ожидающих ACK. Другой способ подумать о том, что бесплатный список - это пул пустых пакетов, ожидающих использования. – kkrambo

+0

@Ned Сохранение неиспользуемых пакетов в свободном списке исключает необходимость стирания пакетов после их использования и итерации по массиву для поиска пустого пакета. Вместо этого вы просто вытаскиваете следующий пакет из бесплатного списка и перезаписываете все, что было раньше. – kkrambo

0

Несколько заметок.

  • Вам необходимо рассмотреть вопрос о заказе. Допустимо ли иметь доставку вне заказа?
  • Вы заботитесь о дублирующих пакетах?
  • Утерянные ACK должны быть обнаружены с таймаутом.

Я предполагаю, что вы заботитесь о заказе доставки пакетов и не хотите дублировать пакеты.

Основной подход - отправить пакет с порядковым номером, дождаться ACK в течение таймфрейма. Если вы получите ACK, отправьте следующий пакет последовательно. Если вы не получите ACK (учитывайте тайм-аут и эквивалент NAK), повторите передачу. Повторение.

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

Если вы хотите попасть в несколько непогашенных пакетов, вам нужна более сложная структура данных и способ выполнения выборочных ACK. Там много литературы, но хорошим местом для начала является «Компьютерные сети» Танненбаума.

+0

Спасибо за это. Я не беспокоюсь о том, что не удалось заказать пакеты или дубликаты. Было бы неплохо, если бы этого не произошло, конечно, но мне действительно нужна высокая пропускная способность данных, а обработка пакетов без заказов - это не большая проблема. У меня есть порядковые номера, в основном для определения того, когда пакет отсутствует, а не для сохранения порядка. Эта книга похожа на интересное чтение и наверняка посмотрит на это. Спасибо, что нашли время ответить на мой вопрос. – Ned