Pull to refresh

Comments 30

Во-первых, там нет возможности задать именно стартовый бит, чтобы в непрерывном потоке байт определить начало пакета. Во-вторых, там на каждую единицу данных идет дополнительный байт, который определяет тип данных, соответственно это сильно увеличивает размер пакета, что критично в системах с ограниченной физической памятью, как в моем примере.
У вас в каждом байте первый бит стартовый, но в UARTе и так так первый бит стартовый, и их там 11. Для такой задачи как у вас достаточно было немного модифицировать модбас, ведь в нем уже есть и адресация, и контрольная сумма.
Этот «стартовый» бит указывает на начало пакета, а не байта. Протокол Modbus работает по принципу запрос-ответ (что снижает скорость передачи данных), а здесь организована непрерывная потоковая передача данных. Еще в структуре протокола Modbus передается много лишней для нас информации, а тут каждый бит на счету.
Какая скорость то?
UFO just landed and posted this here
Реализация чего-то другого выходит дороже и по аппаратным средствам, и по затратам на ПО. Создание чего-то вроде USB, но без всей этой чепухи с PIDом и VIDом, было бы весьма кстати.
UFO just landed and posted this here
Здесь главный плюс – это компактность данных и не важно по какому интерфейсу вы работаете с этим протоколом хоть USB, хоть UART, хоть беспроводная сеть или записываете в файл.
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Каждый начинающий программист должен написать свой протокол :)
Читайте про 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% больше по объему.
Так это другой расклад! Если так критичен объем и невозможно поставить более объемную микросхему — тогда используйте алгоритмы сжатия, даже простейший даст существенную прибавку и ресурсы потребуются небольшие.
А контрольную сумму при записи во флэш обязательно используйте.
UFO just landed and posted this here
Сжимать блочно, по 2К блоками. Каждый блок имеет какой-то идентификатор, метку времени чтоли — выбираем сначала приблизительно блок, быстро раскрываем его и ищем нужную метку времени. Просто доступ к конкретной записи будет происходить в два этапа — сначала грубый, потом уточняющий, с распаковкой.
Контрольная сумма это несколько другой уровень работы с флеш-памятью, этим должен заниматься контроллер флеш-памяти или соответствующий модуль записи, вплоть до использования корректирующих кодов, для которых есть специально предусмотренное место в чипах памяти. Протокол передачи/хранения данных на уровне приложения этим не должен заниматься, это удел более нижнего уровня абстракции.
Да, почему-то у меня в голове засело что автора простая флэш (да и вы пишите «флеш-памятью»), а сейчас присмотрелся — SD-карта.
Протокол вроде называется потоковый, а значит должна быть возможность «синхронизации» когда слушать начали с произвольного места. И жесткая структура блока данных этому никак не помогает. Признак в один бит на байт — компромисс между надёжностью и простотой реализации. Можно было бы этот признак вынести как 9-й бит, но это актуально для физического уровня, тут же уже используется логический уровень где данные поделены на байты с которыми микроконтроллеру работать гораздо удобнее чем с 9-битовыми значениями. Компромиссы…
Контрольная сумма, даже элементарная, повысит требования к ресурсоемкости алгоритма, пока нет синхронизации нам надо считать контрольную сумму для всего блока при приёме КАЖДОГО байта, и успевать это делать. Поэтому жертва 1 бита из 8 кажется довольно гуманной альтернативой.
Автор пытается усидеть на «двух стульях» — и хранить и передавать одинаково, но требования-то разные. Естественно получился велосипед с тремя колесами и двумя рулями.
Хранить надо в формате хранения, т.е. минимальный вариант — известен размер блока данных, вот и писать последовательно, для удобства записи можно выровнять на минимальный размер слова флэш. Чтобы не терять биты на выравнивании, можно группировать блоки. Смысл перемежать данные нулями я так и не уловил.
А передавать — в формате канала передачи, уже описывал.
У контроллера ресурсы ограничены, где-то может и можно сделать более сложные способы синхронизации потока всякие там специальные метки начала пакета, контрольные суммы… но тут выигрывает простота способа — смотрим на 8-й бит, если он «1» бросаем всё и начинаем заполнять структуру с нуля, никакой арифметики — простое и понятное условие. Так же и программа поступит, которая будет отображать данные… ты ей тыкнул куда-то внутрь массива данных, дескать хочу посмотреть что здесь, она нашла этот бит, и с этой позиции начинает интерпретировать данные. Это и называется поток. В каком бы месте не начали его принимать, всегда есть способ узнать где начало структуры и по крайней мере последующие данные интерпретировать правильно. Даже сжатие данных тут только вредит, для качественного отлова начала структуры поток надо будет разбивать на чанки, которые будут сжиматься независимо а это потеря эффективности сжатия, укрупнения минимальных блоков которые можно начать интерпретировать, требование к размеру используемой оперативной памяти, производительности ядра и т.д. а если вместо SD-карты(по техническим требованиям, например условия эксплуатации в широком диапазоне температур где SD-карточки чувствуют себя фигово) в другом экземпляре устройства поток надо будет направить через радиомодуль и он будет сохраняться уже на другом конце… потоковый протокол хорош не только для передачи, но и для хранения.
Кгхм, а почему «1» в фразе «смотрим на 8-й бит, если он «1» бросаем всё»? Вроде как в протоколе он «0», ну пусть будет так. Да, пропустил что у вас вначале пакета «1».
Т.е. если вам попалась «1» вы бросаете всё и начинаете заполнять структуру с нуля, и не важно может быть это заработал сотовый и внёс помеху на канала передачи?
На приемном конце идет проверка, если пришел байт со «стартовым» битом «1», то считывается следующий байт, там в «стартовом» должен быть «0» и так все оставшиеся байты в пакете (количество байт в пакете фиксировано). Если до конца пакета вдруг встречается опять байт со стартовым битом «1», то все начинается с начала и предыдущие данные считаются ошибочными.
Помеха такого уровня испортит данные в любом случае, доверять им нельзя. Поэтому да — ситуация «сбой» и начинаем всё с начала, потеряв всего один(два?) пакет а не синхронизацию в случае непрерывного потока, после чего все последующие данные будут кашей, но контроллеру этого не понять.
0 — для данных, а 1 означает начало пакета, за него и надо цепляться чтобы раскрутить последующие данные в структуру.
Если этого не делать… то помехи подобного рода могут привести к перманентному сбою синхронизации и простейший автомат будет раскладывать данные начиная с середины пакета считая что делает всё правильно. Опять же помочь понять что происходит что-то не то могут контрольные суммы, но это резко потребует наличия вычислительного ресурса которого может не быть. Поэтому такая синхронизация очень проста и позволяет минимизировать потери данных при сбоях. Причем синхронизация настолько проста что используется во многих протоколах в том или ином виде.
А не страшно передавать данные без контрольной суммы?
Sign up to leave a comment.

Articles