Мультимастер на USI

AVR
Это логическое продолжение предыдущей статьи «USI в двухпроводном режиме».
В этой статье пойдет рассказ о построении системы связи нескольких микроконтроллеров в составе одного устройства, на основе аппаратного модуля USI. Описаны «грабли» и «подводные камни» USI, а так же пути их обхода, на основе реального кода.
Все это реализовано и отлажено на двух микроконтроллерах ATtiny44A, один из которых работал на 8МГц, а другой на 1МГц. Размер кода 408 байт (204 слова).

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

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

Примером таких коллизий может служить дорожный перекресток, когда два автомобиля одновременно хотят проехать. Они должны решить кто сделает это первым, а кто вторым. На эту тему в ПДД есть куча правил и если все их соблюдают, то получаются «неразрушающие коллизии», а если хоть один участник посчитает, что он всегда прав или просто «зевнет», то происходит «разрушающая коллизия»… еще как разрушающая.

В нашем случае правила проще: каждый из претендентов в мастера выставляет свой бит на шину, у кого 0 тот и выигрывает, если у всех 0, то выставляется следующий бит и так далее, в итоге на шине остается один мастер, он-то и продолжает передачу (именно продолжает, а не начинает заново), а все остальные записывают за ним.
На таких принципах построена CAN (англ. Controller Area Network — сеть контроллеров), идеи которой собственно и вдохновили меня на создании нечто подобного, только в более бюджетном варианте.

Информация принимается и отправляется пакетами, это цепочка из двух и более байт данных, которая имеет начало и конец. Для контроля верности передачи, в пакет вводится контрольная информация, обычно CRC (англ. Cyclic redundancy check) — алгоритм нахождения контрольной суммы, предназначенный для проверки целостности данных.
Приняв пакет, микроконтроллер должен проверить его валидность и сообщить отправителю результат, тот в свою очередь, либо повторяет передачу, если пакет попортился в пути, либо успокаивается, с чувством выполненного долга. Верно переданные-принятые пакеты поступают на дальнейшую обработку, иначе — отбрасываются.

Ну вот собственно, предисловие закончено, пора приступать к описании реализации.

Аппаратура
Самое время показать упрощенную схему USI переведенного в двухпроводный режим, так как далее будут ссылки на флаги, биты и регистры его.


От предыдущих картинок, эта несколько отличается, добавлена информация полученная в процессе работы над программой, ну и раскрасил цветами… вот только тени не наложил.

Сейчас видно, что бесполезно пытаться влиять на флаг коллизии USIDC, так как он есть не что иное как «исключающее ИЛИ» от того, что присутствует на SDA и того, что мы хотели туда поместить.
Детектор фронтов окрашен в красное, что бы показать, что он корректно работает, только с импульсами большей длительностью, чем один такт CPU. Если импульс короче, то велика вероятность, что его не сосчитают. Это основное аппаратное ограничение скорости передачи.

Добавил транзисторов на выходные линии. На мой взгляд так виднее, что состояние SDA зависит от двух источников управления, а SCL — от трех.

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

И последнее: дорисовал вентиль NOR на защелке USIDR, он должен показать, что пока лини SCL=0, все что пишется в старший бит USIDR, прямиком попадает на линию SDA, этим надо пользоваться.

Описание аппаратной части USI на этом считаю законченным.

Пакетная передача

Описываемый протокол приема-передачи, не имеет ни какого отношения к I2C(TWI). Разница не только в трактовании отдельных битов(R/W, ASC и прочего), а в том, что мастер никогда не принимает данные от слейва.
В этом протоколе: Master — это тот кто сейчас говорит, а Slave — тот, кто сейчас слушает. Благодаря тому, что роли мастера или слейва действительны только в данной конкретной передаче, участники беседы в следующей передаче могут обменятся ролями и данными.

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


Передача пакета начинается с формирования мастером стартовой кондиции, он прибивает SDA(вверху) к 0 при высоком SCL(внизу), при этом у всех присутствующих на шине сработает флаг USISIF (этот флаг сам по себе не способен уложить SCL, это важно помнить). Потом мастер прижимает SCL и на линии SDA появляется бит7. Слейв (зеленый) готовится к приему и удерживает линию SCL (флагом USISIF!), не давая ей подняться, пока он не будет полностью готов к приему.

Вообще это время, когда на SCL ноль, самое оно для работы программы, ведь она может его растягивать под свой темперамент. Эти островки стабильности синхронизируют всю передачу, мастера отрабатывают каждый такой интервал, а слейвы только некоторые (показано синим).

Когда все погасят свои флаги USISIF, обнулят счетчики фронтов (USICNT0..3), линия SCL поднимется и начнется передача байта из сдвигового регистра USIDR мастера прямиком в USIDR-ы слейвов, сколько-бы их там не было. Почему именно так, а не наоборот? Да просто ключ dSDA замкнут только у мастера, а у слейвов он разомкнут и они не могут повлиять на линию SDA, только слушать. Хуже дело обстоит, когда в деле участвуют два и более мастеров, тут жди коллизии… но об этом позже.

Когда мастер отщелкает 8 импульсов, содержимое его USIDR перекочует в слейвы и они опять будут удерживать SCL, пока не прожуют принятую порцию. Мастер, не тратя время даром, готовит для них 9-й бит(Next), он подскажет слейвам, что делать далее, будет следующий байт или нет. Если очередной Next окажется нулевым, все поймут, что пакет кончился и пора посчитать его CRC, у «правильного» пакета он должен получится нулевым.
Так это или не так, мастер узнает после 10-го бита (NoErr)… Десятый он потому, что идет после 9-го… Задорнов это лучше объясняет.
Итак, на период действия 10-го бита все замыкают свой ключ dSDA, типа «я мастер», и выдают 1 на линию SDA. Если хоть один скажет «0» (у него CRC неверное), это увидят все и мастер в том числе… ну что-ж, «все» выкинут битый пакет, а мастеру придется повторить все заново.
Обычно все проходит нормально, все рапортуют «NoErr», мастер со спокойной совестью формирует кондицию stoP, и уже другой мастер может начать передачу своего «мяу».

Было бы заманчиво обойтись без 9-го (да и 10-го) бита, ведь конец пакета можно отметить stoP кондицией, но тогда пришлось бы отказаться от контроля правильности приема-передачи, выбросить CRC и уповать на то, что «все будет хорошо». Кстати в I2C(TWI) так и сделано.

Тут уже должно быть понятно чем делать и что делать (мне во всяком случае понятно), пора выяснить как это делать.
Себе в помощь я нарисовал алгоритм вот в таком виде:

Вверху состояния через которые проходит микроконтроллер в процессе приема-передачи, а внизу — как это выглядит на линиях SDA и SCL.
Начинается все после сброса (RESET), микроконтроллер инициализируется и тут-же формирует кондицию stoP, а пусть будет у всех присутствующих на данный момент поднят флаг USIPF, это с одной стороны выстраивает линии в нужное состояние (везде 1), а с другой — портит возможно уже начавшуюся передачу, ведь если вклиниться в середину пакета, то все равно возникнет ошибка CRC и пакет придется отбросить.
Потом чип (так короче чем микроконтроллер) начинает ждать стартовую кондицию или поступление данных в буфер передачи, это состояние идентифицируется по двум программным флагам f9 и fB (рыжие на картинке). Эти флаги управляют поведением протокольного автомата, если fB=1 значит принимаем байт, иначе — бит, когда f9=1 мы тоже принимаем бит, только вот 9-й или 10-й зависит от состояния fB. Чудно, но не страшно.
Дождавшись флага USISIF, выполняем подготовительные действия и выставляем fB=1, принимаем байт, ставим f9=1, принимаем 9-й бит, анализируем его, если есть продолжение, гасим f9=0 и переходим в состояние приема байта, опять добираемся до анализа 9-го бита, пусть в этот раз он окажется нулевым (больше байтов не будет), проверяем CRC, сбрасываем fB=0 и переходим на прием 10-го бита, принимает его, анализируем и выполняем действия Err или OK, потом формируем кондицию stoP, гасим f9=0 и плавно переходим в состояние WaitStart, всё, круг замкнулся. Видите как все просто.

Блок-схемы
Программа работы с USI разделена на две части, одна занимается обслуживанием аппаратуры, другая следует протоколу обмена (кстати, если ее подкорректировать, то можно приспособить и к работе с протоколом I2C, всякие там ASK и NASC, но это уже факультатив).
Ниже дана «аппаратная» часть программы.

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

Флаг USISIF обрабатывается прямо тут на месте, а вот USIOIF, требует вызова Вия, то есть протоколиста… а вот и он:

В зависимости от программных флагов f9 и fB, действия развиваются по разным сценариям-веткам.
Если мы приняли байт (f9,fB=0,1), то его нужно сохранить в буфере приема, что и пытаемся сделать, но может оказаться, что буфер кончился и «мест нет»… что-ж, ничего не трогаем и уходим бегать по делам (одним из которых возможно станет чтение ранее принятых пакетов, что освободит немного места в буфере приема) и «пусть весь мир подождет». Потом вернемся и «докуем», то есть переведем стрелки на прием 9-го бита (f9=1) и приготовимся его встречать новой ловушкой (USISR=14).

Если приняли 9-й бит (NEXT) (f9,fB=1,1), то либо идем считать CRC (next=0) и выкладываем на SDA результат, переходим на прием 10-го бита, что бы узнать как обстоят дела с CRC у остальных, либо настраиваемся на прием следующего байта пакета.

Если приняли 10-й бит (NoErr) (f9,fB=1,0), то либо принимаем пакет, то есть делаем его видимым для читателя, либо отбрасываем, но в любом случае формируем стоповую кондицию, это нужно, что бы быстрые мастера не ждали медленного слейва, пока он разрешит им сформировать эту кондицию, они прокукарекали, а там уж… пусть последний закроет дверь.

Есть еще одна ветка (f9,fB=0,0), но ей тут вообще нечего делать, ведь это состояние ожидания Start, а протокольные дела начинаются после USIOIF=1, что никак не связано со стартом передачи.

Так, со слейвом разобрались… посмотри, что там у мастера:


Видно, что работы у него побольше…
Сразу бросается в глаза, что чипу, что-бы стать мастером (покраснеть), необходимо иметь данные для передачи (разумно, не правда-ли?), но этого недостаточно, нужно также еще разрешение на начало передачи (USIPF=1) или хотя-бы повод (USISIF=1).

Когда эти условия выполнены и байт помещен в USIDR (этим занимается подпрограмма NextByte) чип начинает трудится в поте лица, не отвлекаясь на другие дела… но без фанатизма, если кто-то где-то тормозит, мастер уходит заниматься другими делами и продолжает свою передачу позже.

Мастеру приходится рулить линией SCL, что бы сформировать на ней тактирующие импульсы, её нужно отпускать, следить за подъемом и снова опускать и так по быстрому кругу (медленный круг — это с выходом в основной цикл). Что бы следить за каждым импульсом, он должен всегда устанавливать USISR=14, чтобы флаг USUOIF сработал на следующем отрицательном фронте, то есть не может использовать USICNT0..3 как счетчик битов (не то, что слейв). Поэтому он использует один из регистров CPU (stBit) для подсчета (вычита) переданных бит (или, если угодно, сформированных импульсов).
Тут не используется бит тактирования USITC из регистра USICR. Дело в том, что эти битом удобно пользоваться для перевода линии SCL в оппозитное состояние, если есть гарантия, что этим кроме тебя никто не занимается. Если в системе есть несколько мастеров и каждый дергает общую линию SCL в свою сторону, то есть вероятность, что линия никогда не поднимется: то один, то второй будет ее удерживать, оба захотят отпустить, а в третий её уже держит… так и будут играть в догоняли.
Вместо этого линия SCL управляется через portSCL, с оглядкой на её текущее состояние. Если мастер «видит», что линия поднялась, а флаги из USISR еще не сработали, то он укладывает SCL опять в 0, формируя тем самым отрицательный фронт импульса. Еще он должен учитывать, что на шине могут присутствовать более медленные устройства, и специально затягивать импульс SCL, что бы эти ленивцы могли его заметить.
Обращаю внимание на «странность»: когда USIOIF=1, первым делом мастер отпускает SCL (_/ SCL)… якобы. Дело в том, что в этот момент линия SCL удерживается внизу самим флагом и, возможно, битом portSCL, вот его-то мы и убираем, что бы быть уверенными, что впоследствии, погасив флаг USIOIF, мы реально отпустим линию SCL.

В конце концов мастер, запыхавшись, добирается до протокольных дел:

Тут мало отличий от слейва, разве что необходимость сформировать бит Next, глядя в свой пустой карман (ромбик Last)… а потом опять бить в барабан, проталкивать биты и байты. Еще можно заметить, что мастер сам не принимает переданный пакет («нафига он Нам, когда он Вам?»), вместо этого он сбрасывает переданный пакет, убирая его из буфера передачи.

Ну вот и добрались до самого интересного, прием и передачу мы уже освоили… теперь осталось все это слить в один шейкер, добавить немного коллизий, хорошенько перемешать (но не взбалтывать!) и посмотреть на коктейль под названием «Мультимастер»:

Тут и далее зеленым цветом помечены пути и действий для слейва, красным — для мастера, а черным — то где они оба топчутся.
Желтые ромбики разделяют дела мастера и слейва, то есть бит dSDA служит индикатором режима.

Обработкой коллизий занимаются только мастера, это их тёрки. «Обработка» это громком сказано, на самом-то деле это всего лишь сравнение переданного бит (b7) с принятым b0, когда они равны — коллизии нет, если-же передавали 1, а получили 0, вот тут и она — коллизия. Правила хорошего тона, требуют, что бы проигравший мастер срочно перекрасился в слейва. Это может произойти при передаче любого бита, поэтому новоиспеченный слейв, корректирует свой счетчик фронтов, таким образом, как будто он с самого начала пакета и был слейвом. Как видим это не сложно (USISR=16-2*stBit).

Тут нужно обратить внимание на то, что (b7) это специальный программный флаг, он равен содержимому 7-го бита сдвигового регистра USIDR, до того как поднялась линия SCL, то есть ДО сдвига USIDR положительным фронтом SCL.

Аппаратный флаг USIDC, который должен показывать коллизию (для этого он и сделан)… врёт, не постоянно, а иногда. Его беда в том, что он не имеет памяти, он показывает текущее состояние, «что вижу, то пою». Во время приема-передачи байта, он ведет себя правильно, так как данные на SDA появляются синхронно (по спаду SCL) и остаются неизменными на весь период SCL=0. Когда же мастера меняют байт, на линии SDA данные меняются асинхронно, сначала появятся бит7 от одного мастера (напомню, что пока SCL=0, все что пишется в бит7 USIDRа, сразу появляется на SDA), потом от второго, третьего…
Флаг USIDC, добросовестно покажет первому мастеру «все ОК», так как в этот момент коллизии действительно нет, мастер пока один влияет на SDA, потом приходит второй мастер и ставить SDA в свое положение и если у него на выхлопе 0, то и ему USIDC покажет «усе в порядке шев» и это будет правдой, вот только первый уже не узнает о том, что он проиграл (возможно) и должен уступить. В итоге два мастера могут одновременно передавать, гася своими нулями единицы другого и совершенно не замечая этого. В конце концов USIDC разрулит коллизию и один из мастеров наконец-то уступит, но если было хоть одно наложение данных, то испорченным окажется весь пакет и не факт, что в следующую попытку ситуация не повториться.

USIDC не то что-бы врет, он вводит в заблуждение, если в системе более одного мастера (хм, а если мастер всегда один, то нафига он вообще нужен? ). На мой взгляд, он должен бы запоминать свое состояние на момент положительного фронта SCL, то есть когда бит реально вдвигается в USIDR и держать его до следующего положительного фронта, тогда мастера успеют вовремя заметить коллизию и мирно разойтись, не разрушая данные и пакет.
Поскольку аппаратно это не реализовано, пришлось сделать программно.

Настал черёд сделать коктейль из протокольных автоматов:

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

Исходники
Все это реализовано и отлажено на двух чипах ATtiny44A, один из которых работал на 8МГц, а другой на 1МГц. Размер кода 408 байт (204 слова), правда без подсистемы управления буферами FIFO (еще 140 слов), но эта подсистема работает не только на USI.

Программа состоит из нескольких функций. Ниже я даю их реальные листинги, однако проблема в том, что ни один компилятор их не прожуёт (ну разумеется кроме моего), но зато они снабжены подробными комментариями и слегка похожи на С, что, надеюсь, поможет разобраться, что и как было сделано.

После рестарта чипа вызывается функция USIreset,

code USIreset \ инициация после сброса
    \ инициация буферов
    ldiW Y,USItx  ldi A,USItxSize rcall iniBufP \ инициация буфера передачи
    ldiW Y,USIrx  ldi A,USIrxSize rcall iniBufP \ инициация буфера приема
    \ двухпроводный режим с захватом SCL по старту и по переполнению
    \ без прерываний
    ldi A,{b USIWM1 USIWM0 USICS1 } out USICR,A 
    #clrSt \ начало, обнулить программные счетчики
    c; \ ---V провал вниз
code USIstoP ( --) \ инициализация аппаратной части
    #clrSR \ гасим флаги и сбрасываем счетчик в USISR
    \ установить состояние линий для кондиции stoP
    \ прижать SCL
    _/ dSCL \_ SCL 
    \ прижать SDA
        _/ dSDA \_ SDA 
    \ поднять SCL
            _/ dSCL  _/ SCL  
    \ поднять SDA
                _/ SDA \_ dSDA 
    \ USISR=b001x0000 \ установлен флаг USIPF, флаг USIDC не управляется
    \ pSDA=1  вход
    \ pSCL=1  выходной
    \ режим slave
    ret c;

она проваливается в функции USIstoP, которая формирует стоповую кондицию. Инициализация USI на этом заканчивается.

Далее из основного цикла программы регулярно вызывается функция USIdo:

code USIdo \ конечный автомат USI, должен постоянно call из основного цикла
    \ большой круг
    pushW AB pushW Y
        rcall [USI]
    popW Y  popW AB
    ret  c;

Её задача сохранить и восстановить регистры задействованные в работе основной функции [USI]:

code [USI] \ флаговый автомат USI
    ldiW Y,USItx 
    if_b USIPF  \ ожидаем поступления данных
        mov USISR,(0) \ обнуляем счетчик фронтов 
        rcall RsizeBufP 
        if \ есть данные, стартуем как мастер
           \_ SDA _/ dSDA ldi A,{b USIPF }  out USISR,A
            ret 
        then
    then
    if_b USISIF \ получена стартовая кондиция 
        #clrSt _/ fB \_ f9  \ синхронизация протокола
        \_ SCL
            rcall RsizeBufP \ есть данные для передачи?
            if0 #clrSR  \ старт слейва 
            else \ старт мастера 
                ldi B,1 rcall NextByteTx \ готовим первый байт для передачи
                _/ SDA _/ dSDA  
                14 #clrSR+   
            then
        _/ SCL 
        ret
    then    
    if_b USIOIF  \ завершена передача(прием) бита/байта  
        _/ SCL \ освободить, на случай если был захват портом
        \ линия удерживается только флагом USIOIF
        skip_b dSDA goto prtKA \ слейв уходит на обработку протокола
        \ мастер, проверка коллизии
        skip_b dr0 \ получен 0..
            if_b fUSIb7 \ .. если передавали 1
            \ коллизия
            \_ dSDA \ отобрать погремушку у проигравшего мастера
            then    
        tst cntBit \ проверить завершение тактирования
        if  \ остались биты
            if_b dSDA
                dec cntBit (b7) 14 #clrSR+ goto [USI] \ мастер идет на быстрый круг
            else \ слейв, восстановить счетчик в USISR..
                mov A,cntBit lsl A  neg A \ A=00-2*cntBit=b1111.xxxx
                mov USISR,A \ USISR=16-2*cntBit
                ret \ ..и ждать USIOIF
            then
        else goto prtKA \ мастер/слейв уходит на обработку протокола
        then
    then
    \ если мастер обнаружит 1 на pSCL ..
    skip_nb dSDA skip_b pSCL ret \ выход не мастера или SCL=0
    minCLK 3 / [IF] ldi A,minCLK 3 / for next A [THEN]
    \_ SCL goto [USI] \ .. положим SCL и заходим на быстрый круг
    c;


Это как раз обработка аппаратных флагов USI. Тут надо быть острожным в обращение с флагами в USISR.
Дело в том, что есть как минимум две модификации аппаратуры USI. Одна из них например реализована в чипе ATtiny26, там доступ к флагам USISR сделана по принципу «чтение-модификация-запись», то есть, если попытаться погасить один флаг, послав в него 1 командой SBI USISR,x, то вместе с ним погаснут и другие, ведь они тоже стоят в 1.
В другой модификации, такой как в ATtiny44A, эта бага устранена и можно свободно пользоваться битовыми командами, без опасения повлиять не на тот флаг. Тем не менее, для совместимости все операции с USISR, производятся через OUT, как это и рекомендовано в описании ATtiny26.

Функции NextByteTx и USIchskCRC зависят от реализации буферов и тут не приводятся, а протокольный автомат вот:

code prtKA \ протокольный конечный автомат
    if_b fB \ идет прием-передача пакета
        \_ SCL \ прижать линию ---------
        #clrSR \ ------- 
        if_b f9 \ принят 9-й бит
            if_b dr0 \ next=1
                \_ f9 \ продолжаем байты..
                if_b dSDA \ .. передавать
                    mov B,USIstatus andi B,stMask \ количество уже переданных
                    inc B rcall NextByteTx \ новый
                else _/ SCL ret \ .. принимать
                then
            else \ передан-принят последний байт
                \_ fB \ переход на 10-й бит
                rcall USIchskCRC  \ CRC 
                if_nt _/ dr7 else \_ dr7 _/ dSDA then  \ показать ошибку на SDA, если есть
                                            \ тут все мастера, что бы показать ошибку
                clr cntBit
            then
        else \ принят-передан очередной байт
            mov B,USIstatus andi B,stMask inc B
            mov A,USIDR ldiW Y,USIrx rcall WriteBufP \ положить в буфер
            if_T ret then \ отказ, ждать освобождение буфера
            \ ------------------
            \ байт принят
            inc USIstatus \ счетчик байтов +1
            _/ f9 \ переход на 9-й бит
            if_b dSDA
                if_b fUSIend \_ dr7  else _/ dr7 then \ задать бит NEXT
            then
            clr cntBit
        then
        ldi A,14 out USISR,A \ 1 бит
        (b7)
        _/ SCL \ отдать линию
        ret
    then    
    \ принят 10-й бит, пакет завершен ==================================
    skip_b f9 ret \ нечего делать, выход (никогда не должен сработать)
    \_ f9  
    if_b dr0
        \ пакет передан-принять верно
        if_nb dSDA 
            ldiW Y,USIrx rcall WendBufP \ слейв принимает пакет
        else    
            \ мастер не принимает свой пакет (антиЭхо), но ..
            ldiW Y,USItx rcall RendBufP \ ..скидывает переданный пакет
        then 
    then
    goto USIstoP 
    c; 


Заключение
И напоследок пара «семейных фотографий» от знакомого осциллографа:


На этой «фотографии» ATtiny44A с тактовой частотой 8МГц, передает самому себе пакет из двух байт.
Хорошо видно (слева на право): стартовую кондицию, байт 0x33, бит Next=1, байт CRC (0x17), следующий бит Next=0, бит NoErr=1 и, наконец, стоповая кондиция. Байт передается со скоростью 138кб/сек.

А вот тот же пакет, и тот же чип, только в присутствии «медленного слейва», в качестве которого используется ATtiny44A с тактовой частотой 1МГц.


Стартовая кондиция не влезла в экран, но хорошо видно, что скорость передачи байта осталась прежней, а вот паузы на обработку байтов и битов заметно выросли, тем не менее оба чипа успешно приняли пакет, о чем свиделельствует 1 в бите NoErr.
Эта картинка показывает, что на одной шине могут совместно жить и понимать друг друга чипы с 8-ми кратной разностью в скорости работы. На мой взгляд это намного «вкуснее» чем 7% допуска на несовпадение частот для UART.

Можно еще накидать осциллограмм с коллизиями, когда оба чипа начинают одновременно передачу, как они разруливают, как влияют внутренние прерывания на поток и прочее… но эти осциллограммы слишком длинные и запутанные, что бы выкладывать тут.
  • +2
  • 21 марта 2016, 12:30
  • iva

Комментарии (36)

RSS свернуть / развернуть
На мой взгляд это намного «вкуснее» чем 7% допуска на несовпадение частот для UART.
сравнение синхронного и асинхронного интерфейса даже веселее чем термин «кондиция»
+1
  • avatar
  • xar
  • 21 марта 2016, 13:11
A master indicates the start of transaction by issuing a START condition (S) on the bus.
0
и переводится это предложение «мастер индицирует старт транзации иссуицирую стартовую кондицию на басе»?
0
Если Вам так понятнее, то наверное можно и так.
0
мне непонятно зачем выдумывать слова, когда есть устоявшиеся термины стартовая/стоповая последовательность.
0
Не знаю, но на мой взгляд, последовательности (одно следует за другим) там нет, есть просто спад/подъем SDA при высоком SCL. Это скорее событие, причем «неправильное», так как «правильно» — это когда при высоком SCL, SDA стоит колом. Вот именно это «неправильное» поведение SDA и используется как индикатор начала и конца транзакции (простите, приема-передачи).
0
START condition это, в данном случае, особое состояние шины.
0
Вот так полностью согласен.
0
Такой вопрос — бывает USI (в тиньках) и бывает TWI (в мегах). Вы говорите, что TWI это больше относится к протоколу, но в документации-то описывается именно как отдельный модуль. Его можно как-то использовать вместо USI, или вы не пробовали?

Спасибо за статью, давно думал о чем-то подобном, правда смотрел в сторону RS485 и видимо зря…
0
Его можно как-то использовать вместо USI, или вы не пробовали?
TWI — это так в Atmel называют I2C (чтобы денег авторам I2C не платить). Так что можно использовать.
0
это да, но вопрос в функционировании схожем с USI
0
это да, но вопрос в функционировании схожем с USI
Использовать можно — оба модуля (TWI и USI) сделаны для работы с шиной I2C, но имеют разное внутреннее устройства.
0
Сейчас мне нужно подключить к шине ATtiny441, а у неё (вот засада) нет USI, вместо него положили «I2C Compatible, Two-Wire Slave Interface». Беглый осмотр пока оставляем надежду, что смогу запустит его на одну шину с USI.
Через некоторое время, я вероятно смогу ответить точнее.
0
буду очень признателен)

а то ведь возможен такой вариант — датчик для, например, умного дома — и в нем почему-либо приходится ставить мегу (или не хватает тиньки по возможностям, или под рукой только меги) — а там только TWI
0
I2C была разработана для связи микросхем внутри одной платы или хотя-бы одного корпуса, именно поэтому там и нет ничего от помех. USI в этом отношении не лучше, особенно если расстояния измеряются метрами.
Для дальних передач есть другие интерфейсы, тот же RS485 стреляет на сотни метров, а CAN на метры.
0
Есть еще 1-wire. Не помню где, но читал, что он до 300 метров держит.
0
I2C была разработана для связи микросхем внутри одной платы
Для дальних передач есть другие интерфейсы
Тише, тише=)) Многих аж воротит от таких громких слов! Извращаются… карежатся… но нормальную сеть не делают. Пилять, это ж додуматься умный дом пилить на I2C или юартине=)) Все что угодно лепять лишь бы не делать нормальный протокол передачи данных. ТО кои мультимастеры… то суперпупер мультимастер нужен, чтоб все и всем могло впихивать команды=)) то неведомую сеть ищут чтоб поменьше деталечек для ее организации, да по стабильнее работала лампочку включить да так чтоб в риалтайме покруче чем в ЧПУ крутых. Господа пора учиться делать правильно а не так как ваш воспаленный мосг представляет!

З.Ы. и не надо расказывать о том что контроллер не справится (при чем пилят все на хуйдуине) там с чем то… и что постоянное его сидение и слушанье сети сожрет всю производительность)) Чет у других не сжирает, а тут лично возьмет и сожрет… еще и в задачах где надо или лампочку включить или кнопочку опросить… ПФФФФ!
0
как же хочется плюсануть, однако хомячки за аналогичную позицию карму просаживают xD
+1
И спросила кроха: — Что такое хорошо и что такое плохо?
0
Ссать против ветра плохо!
0
Ёмко!
0
Господа пора учиться делать правильно а не так как ваш воспаленный мосг представляет!
Ссать против ветра плохо!


Эм, я вот так и не понял, как правильно. Какую структуру сети, какой стек протоколов Вы предлагаете?
+2
Ну если мультимастер — то CAN(не, не взлетит?), хотя я даже не знаю зачем в задачах допустим того же освещения (напомните пожалуйста какая скорость передачи данных у DMX512) и умного дома мультимастер
не ждать 2 секунды включения
? Если только пару секунд экономка включается… ну то ладно…
А если так уж хочется риалтайма то кольцо, допустим SERCOS тоже неплохо подойдет. Делал подобное с драйверами на RS422.
Но если совсем не в моготу, то можно и свой протокол возвоять.
Запилить жесткую привязку к времени для сообщений, как вариант гонять флаг между модулями с учетом приоритета и очередности (кому сколько времени на реакцию надо и кто за кем получает флаг)
ну и команды естественно не ASCII а хексом пилить. простейшую структуру по типу [адрес] + [команда и данные] + [контрольная сумма CRC8] = 0x01 0x55 0x86.
дальше можно до бес конечности разводить демагогию…
НО ИМХО и с одним мастером задача реализуется без описанных ТСом проблем
0
а еще можно впиндюрить широковещательные команды=)) с определенной оговоркой, что определенные команды расчитаны на определенные устройства. Тоже облегчит процесс обмена данными
0
расскажите, как правильно — буду очень признателен
с одним «но» — учитывая, что нужен мультимастер, чтобы, например, нажав кнопку «включить свет» не ждать 2 секунды включения этого самого света, пока мастер опросит все устройства на шине и соизволит послать команду включения управляющему устройству.
0
Хочу обратить внимание на одну не очевидную вещь. В блок-схеме мастера, есть два места, где он проверяет не пора-ли ему заговорить. Одно, это когда он замечает данные у себя в буфере передачи, а другое, когда кто-то уже начинает передачу. В последнем случае, если устройству есть что сказать, он тоже начинает передачу, то есть сознательно идет на конфликт.
Это позволяет начать частичную обработку данных прямо на шине. Если я спрашиваю у «всех» какой-то параметр, напряжение например, то в ответ придет куча ответов в порядке возрастания этого параметра, это сортировка коллизиями. Я этим пользуюсь, чтобы узнать у кого минимальный параметр (его пакет будет первым) и максимальный (последний пакет). Суммируя полученные пакеты и разделив на их количество, можно получить среднее значение… это здорово экономит память и время.
Если некоторые пакеты побитно одинаковы, то они объединяются в один, так как «все» ответили хором, без коллизий. Вместо сотни пакетов «Ок» придет только один (или два: «Ок» и «Караул», если кто-то (из сотни) нехорошо себя чувствует).
0
Покопавшись в описаниях модулей TWI разных чипов, пришел к выводу, что сделать на них мультимастер разумеется можно, это штатно предусмотрено… но только в этом «предусмотренном» виде, то есть без проверки CRC и без автоповтора в случае сбоя, без сортировки пакетов коллизиями и «хорового пения» чипов.
«Виной» тому встроенный аппаратный конечный автомат, который упрощает программу реализации протокола I2C, но мешает реализации чего-либо иного.
0
потеря арбитража разве не поможет? А вообще CRC нигде не возбраняется
0
Поможет понять, что неудачно влез в чужую передачу. Это может произойти если стартовать очень близко друг к другу по времени, но если я стартовал на 1 тик быстрее, остальные даже не будут пытаться мне помешать, таким образом побеждает всегда более шустрый. Это относится к I2C, у них коллизия — это исключительная ситуация, а у USI-мультимастер — это рабочая процедура… хотя и не всем нужная.

CRC может не сойтись у «одного из», может он просто «зевнул» один импульс или помеха ему досталась «жирнее», вот он и отбросит пакет, а остальные его «скушают», а это нарушает принцип:«пакет примут все или никто» (подсмотрел у CAN).
0
А как у вас складываются отношения с другими архитектурами контроллеров и (шепотом) ПЛИС?

Кстати, (опять шепотом) что за язык такой непонятный?
+1
Мои личные отношения? Нормально.
Начинал еще с «Радио-86РК» на 580 серии, проходил через МК48 и МК51 потом пересел на AVR (минуя PIC). Юзал и плиски (шепотом, сваял конвертор потока E1 с меди на оптику и обратно, и соединял комп AT с АТС «Квант», там использовал Altera EPM7064 (симпатичная штучка, но дорогая)). STM, MSP и прочие, пока еще не требовались.
Язык обычный ассемблер, только с добавлениями конструкций циклов, условий, макросов и прочего для удобства написания и наглядности листинга. Для компилятора использовался форт, который не признает спецсимволы и разрешает использовать слова типа _/ вместо SBI и \_ вместо CBI… мне так понятнее.
0
У других контроллеров периферия может оказаться лучше приспособленной под задачу.
0
Даже и не сомневаюсь.
Просто я не слишком приспособлен для примерки всех семейств микроконтроллеров. Для перехода на что-то новое, нужна более веская причина, чем удобство реализации того или иного. Мне пока хватает AVR.
0
Ты бы хоть ссылку дал, где с такими средствами разработки ознакомиться можно.
0
Так нету их.
Был парень Brad Eckert, сделал на Win32Forth среду разработки для AVR (и для других), которой я пользовался. Но проект загнулся, осталось только это: web.archive.org/web/20060822200449/http://www.tinyboot.com/index.html
Когда я слез с винды, то не смог перетащить и TOF, поэтому написал свое произведение, с оглядкой на те «вкусняшки», что мне там понравились.
Написал, пользуюсь, но нигде не выкладывал (ибо нафик никому не нужно).
0
В инете еще можно найти мою старую стстью по удобной работе с битами в Firmware Studio Брэда Экерта.
Вот одна из них: «Дополнительные команды ассемблера для микроконтроллеров семейства AVR»
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.