Как стать автором
Обновить

Комментарии 30

Чем не устроил MsgPack?
Во-первых, там нет возможности задать именно стартовый бит, чтобы в непрерывном потоке байт определить начало пакета. Во-вторых, там на каждую единицу данных идет дополнительный байт, который определяет тип данных, соответственно это сильно увеличивает размер пакета, что критично в системах с ограниченной физической памятью, как в моем примере.
У вас в каждом байте первый бит стартовый, но в UARTе и так так первый бит стартовый, и их там 11. Для такой задачи как у вас достаточно было немного модифицировать модбас, ведь в нем уже есть и адресация, и контрольная сумма.
Этот «стартовый» бит указывает на начало пакета, а не байта. Протокол Modbus работает по принципу запрос-ответ (что снижает скорость передачи данных), а здесь организована непрерывная потоковая передача данных. Еще в структуре протокола Modbus передается много лишней для нас информации, а тут каждый бит на счету.
Какая скорость то?
НЛО прилетело и опубликовало эту надпись здесь
Реализация чего-то другого выходит дороже и по аппаратным средствам, и по затратам на ПО. Создание чего-то вроде USB, но без всей этой чепухи с PIDом и VIDом, было бы весьма кстати.
НЛО прилетело и опубликовало эту надпись здесь
Здесь главный плюс – это компактность данных и не важно по какому интерфейсу вы работаете с этим протоколом хоть USB, хоть UART, хоть беспроводная сеть или записываете в файл.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Каждый начинающий программист должен написать свой протокол :)
Читайте про byte stuffing (например протокол SLIP).
И нулями не надо разделять, у вас уже есть жесткая структура блока данных — достаточно получить начало блока, а в конец блока данных вставить его контрольную сумму.

При размере данных до 56 бит включительно (до 7 байт), выгодней использовать PSP1N (экономим 1 байт на пакет), при размере от 57 до 112 бит включительно (до 14 байт), размеры итоговых пакетов будут одинаковы что у PSP что и при использовании byte stuffing, при размере данных от 113 бит ( от 15 байт) и выше выгодней использовать byte stuffing. Пожалуй добавлю этот алгоритм для данных свыше 14 байт.

Абстрактно говоря, вы зачем-то выравниваете данные на 8 бит, а потом 14+6 бит =2,5 байта просто забиваете нулями — где здесь экономия? :)
Получился какой-то алгоритм с неизвестными целями, т.е. контрольной суммы нет — у вас сверхнадежный канал передачи данных или допустимо потерять 1-2 пакета данных? При увеличении размера пакета вы уже собрались переходить на другой алгоритм, а при увеличении скорости у вас еще что-нибудь приключится :)
По-хорошему на таких скоростях, как у вас 14кбит, рисуется структура:
struct __packet {
uint32_t utime;
uint16_t adcdata1;
uint16_t adcdata2;

uin32_t crc32;
}
Заворачивается в byte stuffing и передается в канал, на приемнике она принимается, проверяется crc32 и просто разбирается, без сложных манипуляций со сдвигами битов.
Контрольная сумма была не важна, так как данные пишутся во флэш память, которая расположена на одной плате с микроконтроллером. К UART подключение происходит эпизодически кабелем длиной не более метра для кратковременного анализа передаваемой информации. Здесь критическим фактором выступал именно размер пакета. Если использовать предложенную вами структуру, то соответственно растет объем: 4 байта + 2 байта * 6 + 2 байта на byte stuffing = 18 байт против 14, что на 22,2% больше по объему.
Так это другой расклад! Если так критичен объем и невозможно поставить более объемную микросхему — тогда используйте алгоритмы сжатия, даже простейший даст существенную прибавку и ресурсы потребуются небольшие.
А контрольную сумму при записи во флэш обязательно используйте.
НЛО прилетело и опубликовало эту надпись здесь
Сжимать блочно, по 2К блоками. Каждый блок имеет какой-то идентификатор, метку времени чтоли — выбираем сначала приблизительно блок, быстро раскрываем его и ищем нужную метку времени. Просто доступ к конкретной записи будет происходить в два этапа — сначала грубый, потом уточняющий, с распаковкой.
Контрольная сумма это несколько другой уровень работы с флеш-памятью, этим должен заниматься контроллер флеш-памяти или соответствующий модуль записи, вплоть до использования корректирующих кодов, для которых есть специально предусмотренное место в чипах памяти. Протокол передачи/хранения данных на уровне приложения этим не должен заниматься, это удел более нижнего уровня абстракции.
Да, почему-то у меня в голове засело что автора простая флэш (да и вы пишите «флеш-памятью»), а сейчас присмотрелся — SD-карта.
Протокол вроде называется потоковый, а значит должна быть возможность «синхронизации» когда слушать начали с произвольного места. И жесткая структура блока данных этому никак не помогает. Признак в один бит на байт — компромисс между надёжностью и простотой реализации. Можно было бы этот признак вынести как 9-й бит, но это актуально для физического уровня, тут же уже используется логический уровень где данные поделены на байты с которыми микроконтроллеру работать гораздо удобнее чем с 9-битовыми значениями. Компромиссы…
Контрольная сумма, даже элементарная, повысит требования к ресурсоемкости алгоритма, пока нет синхронизации нам надо считать контрольную сумму для всего блока при приёме КАЖДОГО байта, и успевать это делать. Поэтому жертва 1 бита из 8 кажется довольно гуманной альтернативой.
Автор пытается усидеть на «двух стульях» — и хранить и передавать одинаково, но требования-то разные. Естественно получился велосипед с тремя колесами и двумя рулями.
Хранить надо в формате хранения, т.е. минимальный вариант — известен размер блока данных, вот и писать последовательно, для удобства записи можно выровнять на минимальный размер слова флэш. Чтобы не терять биты на выравнивании, можно группировать блоки. Смысл перемежать данные нулями я так и не уловил.
А передавать — в формате канала передачи, уже описывал.
У контроллера ресурсы ограничены, где-то может и можно сделать более сложные способы синхронизации потока всякие там специальные метки начала пакета, контрольные суммы… но тут выигрывает простота способа — смотрим на 8-й бит, если он «1» бросаем всё и начинаем заполнять структуру с нуля, никакой арифметики — простое и понятное условие. Так же и программа поступит, которая будет отображать данные… ты ей тыкнул куда-то внутрь массива данных, дескать хочу посмотреть что здесь, она нашла этот бит, и с этой позиции начинает интерпретировать данные. Это и называется поток. В каком бы месте не начали его принимать, всегда есть способ узнать где начало структуры и по крайней мере последующие данные интерпретировать правильно. Даже сжатие данных тут только вредит, для качественного отлова начала структуры поток надо будет разбивать на чанки, которые будут сжиматься независимо а это потеря эффективности сжатия, укрупнения минимальных блоков которые можно начать интерпретировать, требование к размеру используемой оперативной памяти, производительности ядра и т.д. а если вместо SD-карты(по техническим требованиям, например условия эксплуатации в широком диапазоне температур где SD-карточки чувствуют себя фигово) в другом экземпляре устройства поток надо будет направить через радиомодуль и он будет сохраняться уже на другом конце… потоковый протокол хорош не только для передачи, но и для хранения.
Кгхм, а почему «1» в фразе «смотрим на 8-й бит, если он «1» бросаем всё»? Вроде как в протоколе он «0», ну пусть будет так. Да, пропустил что у вас вначале пакета «1».
Т.е. если вам попалась «1» вы бросаете всё и начинаете заполнять структуру с нуля, и не важно может быть это заработал сотовый и внёс помеху на канала передачи?
На приемном конце идет проверка, если пришел байт со «стартовым» битом «1», то считывается следующий байт, там в «стартовом» должен быть «0» и так все оставшиеся байты в пакете (количество байт в пакете фиксировано). Если до конца пакета вдруг встречается опять байт со стартовым битом «1», то все начинается с начала и предыдущие данные считаются ошибочными.
Помеха такого уровня испортит данные в любом случае, доверять им нельзя. Поэтому да — ситуация «сбой» и начинаем всё с начала, потеряв всего один(два?) пакет а не синхронизацию в случае непрерывного потока, после чего все последующие данные будут кашей, но контроллеру этого не понять.
0 — для данных, а 1 означает начало пакета, за него и надо цепляться чтобы раскрутить последующие данные в структуру.
Если этого не делать… то помехи подобного рода могут привести к перманентному сбою синхронизации и простейший автомат будет раскладывать данные начиная с середины пакета считая что делает всё правильно. Опять же помочь понять что происходит что-то не то могут контрольные суммы, но это резко потребует наличия вычислительного ресурса которого может не быть. Поэтому такая синхронизация очень проста и позволяет минимизировать потери данных при сбоях. Причем синхронизация настолько проста что используется во многих протоколах в том или ином виде.
А не страшно передавать данные без контрольной суммы?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории