Notice: Memcache::get(): Server localhost (tcp 11211) failed with: Connection refused (111) in /home/a146/www/we.easyelectronics.ru/engine/lib/external/DklabCache/Zend/Cache/Backend/Memcached.php on line 134
USI в двухпроводном режиме / AVR / Сообщество EasyElectronics.ru

USI в двухпроводном режиме

AVR
Потребовалось мне сделать связь между микроконтроллерам, причем такую, что бы каждый мог начинать передачу по своему желанию и не мешать при этом высказываться другим, иначе это называется «режим мультимастер». Очень нужно НЕ использовать прерывания (они нужны для других функций).

Присмотревшись к коммуникационным возможностям, аппаратно реализованных в AVR, перепробовав их на зуб, остановился на USI. Вроде бы странный выбор, повсеместно его стараются обойти, ссылаясь на странности поведения, на сложности использования и прочие страшилки…
Согласен, есть такое дело, сам «нашишковал» достаточно, пока догадался разобраться со странностями непосредственно «в железе», благо цифровой осциллограф есть в наличии.

Как выяснилось, причиной «чуднОго поведения USI» является недостаточно полное описание его в документации. Нет, явных ошибок я там не нашел, просто читаю одно, понимаю другое, пишу код… и получаю: «ничего не работает» и «фигня полная».


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

Уверен, что реальная схема там совсем другая, гораздо более сложная, но эта хотя бы описывает поведение аппаратной части и по ней можно спрогнозировать поведение «железяки» в ответ на те или иные программные действия. Что бы облегчить восприятие, некоторый линии я сделал серыми, показав тем самым, что они не используются в двухпроводном режиме. Уже этого достаточно, что бы понять невозможность использования «TIMER0_COMPARE» для выбранного режима.
Далее я постараюсь рассказать своими словами, что нужно делать и что из этого получается. Хотя работа над кодом еще не завершена, я решил опубликовать наработанную информация, как говориться «по горячим следам», пока свежо в памяти и не лень.

Итак, работа с USI в двухпроводном режиме сводится к управлению всего двумя линиями SDA (данные) и SCL (тактирование), причем обе они организованы по схеме «открытый сток» ( или по ихнему «open drain»). Если все ключевые транзисторы закрыты у всех подключенных чипов, то линии подтянуты через внешние резисторы к шине питания и на линиях будет «1». Если обратится к терминологии CAN, то это можно назвать «рецессивное состояние». Если же, хоть у одного чипа, какая-либо линия прижата к земле открытым ключом, то мы получим «0» на линии или «доминантное состояние», оно потому и доминантное, что никто не сможет его изменить, кроме того кто это сделал.
Фишка USI (именно в двухпроводном режиме) в том, что любой участник разговора, может остановить поток передачи и заставить остальных дожидаться его соизволения продолжить разговор («М-м-минуточку… я зап-п-писываю», как сказал Шурик). Это позволяет не заморачиваться с таймингами и не суетится с приемом-передачей, что бы «Успеть Ухватить» (У2).

Начальная инициализация USI производится через регистр USICR. Для перевода USI в двухпроводной режим необходимо установить его биты:
  • USIWM1 = 1 он что-то делает с выводами, готовя их к двухпроводному режиму;
  • USIWM0 = по вкусу, но если 1, тогда USI остановит трансферт (приём-передачу) по приему последнего бита байта. Но можно и «0», если есть уверенность, что успеете сохранить данные, до прихода следующего бита… не рекомендую без прерываний, хотя и с ними могут возникнуть проблемы;
  • USICS1 = 1; USICS0 = 0 без вариантов;
  • USICLK = 0, если 1, то счетчик USISR будет считать собственные тики, и ему будет глубоко безразлично состояние линии SCL;
  • USISIE, USIOIE = 0 без прерываний или 1 с прерываниями по событиям;

    \ двухпроводный режим с захватом SCL по старту и по переполнению
    \ без прерываний 
    ldi A,{b USIWM1 USIWM0 USICS1 }   out USICR,A  \ перечисленные биты в 1, остальные в 0



Теперь нужно подготовить выходы на линии, но сначала перевести соответствующие биты в PortA в 1 и сбросить флаги в USISR. На картинке вверху можно увидеть, что флаги USISIF и USIOIF напрямую могут воздействует на линию SCL, как и соответствующий бит в PortA, причем на равных условиях.


    ldi A,{b USISIF USIOIF USIPF USIDC } out USISR,A \ гасим флаги и сбрасываем счетчик
    _/ SDA  _/ SCL \ освободить линии
    \_ dSDA \ запретить выход SDA, режим слейва 
    _/ dSCL \ разрешить выход SCL


После подготовки разрешаем выходы (через DDRA) устанавливая:
  • dSCL=1, так как и мастер и слейв должны влиять на эту линию, мастер через нее тактирует передачу, а слейв останавливает ее в меру своих возможностей по приему;
  • dSDA=0.

Бит dSDA до начала трансферта, должен быть сброшен у всех, так как в противном случае мы можем сразу-же получить «start condition». Вот когда мы объявляем себя мастером и начинаем передачу, самое время установить в 1 бит dSDA.

Инициализация аппаратной части на этом закончена — на линиях SDA и SCL рецессивное состояние (1), и USI готов к приему стартового состояния (но не байта).

Сейчас можно несколько упростить картинку структуры USI в двухпроводном режиме, убрав «шумовые» линии.

Регистр USIBR убран из-за того, что по моему мнению он имеет смысл только в трехпроводном режиме (SPI).
Дело в том, что в режиме SPI нельзя тормозить передачу никому кроме мастера и слейву необходимо быстро обработать принятый байт, прежде чем его испортит следующий бит, вот тут USIBR и позволяет снизить напряг, давая больше времени на реакцию, так как хранит принятый байт, пока идет прием следующего в USIDR.
И напротив, он добавляет квест в двухпроводном режиме. Во-первых, чтение из USIBR автоматически гасит флаг USIOIF, а во-вторых, в некоторых случаях можно прочитать из него «кривой» байт (сдвинут влево на 1 разряд).


Теперь остается только контролировать поднятие флага USISIF.

Делать это можно, либо периодически проверяя его, либо через прерывание. В USI (что важно) оба метода функционально равноправны, так как этот флаг, поднявшись, будет удерживать линию SCL у земли, как только она туда опустится, тем самым останавливая дальнейшую передачу. USISIF будет держать линию пока мы программно его не погасим. Таким образом у нас есть куча времени, что бы неспеша разобраться что к чему.

Допустим, что некто решил передать байт (с этим бы справиться, с пакетами позже разберемся) и сформировал старт, то есть перевел в 0 линию SDA при высокой линии SCL. Все участники детектируют его, выставляют свои флаги USISIF и как только мастер прижмет SCL — станут удерживать ее, не давая подняться. Все ждут пока самый тормознутый разберется в ситуации и сбросит свой флаг USISIF, освободив тем самым линию. Теперь мастер, увидев что линия SCL поднялась, то есть все готовы принимать, может начать опускать и поднимать ее, формируя тактовые импульсы…

Но тут есть два возможных сценария.
Поскольку счетчик в USISR считает не сами тактовые импульсы, а их фронты, как положительные (0 _/ 1), так и отрицательные (1 \_ 0), от того как обработать старт зависит на каком фронте сработает флаг переполнения USIOIF, на положительном или отрицательном.

Пусть во время старта счетчик USICNT (часть USISR) был очищен и первым станет положительный фронт SCL:


Тогда переполнение счетчика (15->0) произойдет на отрицательном фронте SCL. Встанет флаг USIOIF и будет удерживать линию SCL внизу. Все хорошо, диаграмма красивая (вот только USIBR будет содержать «кривой байт»).

Пойдем другим путем, не будем чистить USICNT во время старта, погасим только флаг USISIF:


Теперь переполнение счетчика произойдет на положительном фронте, вскочит флаг USIOIF и самостоятельно, аппаратно уложит SCL в 0, сделав еще один фронт. Вроде ничего страшного, просто последний импульс короче остальных (тем более, что USIBR будет копией USIDR), но…

Приведенные выше осциллограммы (трансферт байта 0x01) снимались при тактовой частоте микроконтроллера 1МГц, подтягивающие резисторы 4k7. Голубая линия это SDA, желтая — SCL.

А вот как «Вариант В» выглядит при частоте 4МГц:


И при 8МГц:


Видно, что амплитуда последнего импульса зависит от скорости микроконтроллера и скорости заряда паразитной емкости линии. В этом случае нет никакой гарантии, что последний импульс заметят ВСЕ участники разговора.
Если кто-то пропустит хоть один импульс, то он выйдет из синхронизации, запорет всю передачу и будет «Караул!».

Лично мне такой сценарий не нравиться и я буду использовать «Вариант А», т.е. чистить USICNT после приема стартовой позиции.

Теперь пару слов о портах и линиях.
На первый взгляд они организованы одинаково, но линия SDA управляется параллельно и портом и выходной защелкой регистра USIDR, а значит если в порту будет сидеть 0, то потуги USIDR изменить состояние линии будут бесполезны.
Линия SCL управляется напрямую портом (ну не считая флагов из USISR), мастер может самостоятельно поднимать или опускать линию записывая в порт 1 и 0 по очереди, но проще каждый раз записывать 1 в бит USITC (тем более, что этот бит может устанавливаться отдельно через команду sbi), он-то и будет постоянно опрокидывать соответствующий бит непосредственно в порту и менять состояние SCL.

Из этого следует, что для формирования стартового состояния, мастеру необходимо и достаточно записать 0 в SDA (PortA) (ведь нам неизвестно заранее, что сидит в защелке 0 или 1) и 1 в dSDA (DDRA), кроме того нужно (чуть погодя) записать 0 в порт SCL, чтобы он стартовал с правильной позиции и первый тик USITC не пропал даром.
После того как все отдетектируют старт, нужно записать 1 в SDA, передав тем самым бразды правления регистру USIDR.
Слейв на старте может ничего не делать, его порт определенно в 1 (ведь иначе никакого старта не будет, получится банальный «завис»).

Далее следуют «дела протокольные»: как подтверждать, что контролировать и прочее, непосредственно к самому USI отношения не имеющее. Можно использовать USI для связи с каким либо датчиком по протоколу I2C (ознакомившись с его требованиями и ограничениями), а можно изобрести свой «велосипед»… чем я теперь и займусь.

P.S. Связь USIDR с линией SDA несколько сложнее.
Пока линия SCL находится в 0, все что пишется в USIDR7 сразу же появляется на линии SDA, но как только SCL поднимется, халява кончится и SDA застывает в последнем состоянии. Таким образом ни СТАРТ ни СТОП состояния (SCL=1) через USIDR сформировать нельзя (исключением является первый СТАРТ после RESET, там достаточно только разрешить выход SDA), нужно оперировать портом и его выходом.

P.S. Связь флага USISIF с линией SCL не прямая.
Пытаясь оптимизировать код и убрать «лишние» команды, наткнулся на еще одну зависимость: пока линия SCL=1 флаг USISIF сам по себе не может ее положить в 0, он ее захватывает только после того как кто-то другой ее «положит».
Как оказалось, в доке об этом прямо написано "..the start detector will hold the SCL line low after the master has forced a negative edge on this line."

Чувствую, что еще не все я расковырял, не все понял, не на все «грабли» наступил… так что квест продолжается.

Продолжение см. Мультимастер на USI
  • +5
  • 18 января 2016, 20:09
  • iva

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

RSS свернуть / развернуть
в тему easyelectronics.ru/ispolzovanie-interfejsa-usi-v-rezhime-mastera-twi.html
а вообще «двухпроводной» это TWI/I2C о чем наверно стоит в заголовке и тексте упомянуть
0
  • avatar
  • xar
  • 19 января 2016, 09:23
Я специально отгородился от TWI/I2C, так как они регламентируют еще и протокол (метод адресации,R/W, ASC, NASK и прочее). USI к ним не привязан, а может использоваться для их реализации. Сам я собираюсь делать иной протокол, так как мне нужно быть уверенным в том, что пакет гарантированно передан и гарантированно верно принят. У протоколов TWI/I2C контроля нет вообще, максимум мастер может узнать (по ASC), что его кто-то слушает.
Именно поэтому я и выбрал USI — он свободен от протокольных КА.
0
А слабо еще питание пустить по той же паре? ;)
0
Смотря скока тока надо. Пару микроампер для часов запросто.
0
Например 20мА
0
Проще к трем проводам (GND, SDA,SCL) добавить четвертый +5В.
С паразитным питанием чаще используют 1-wire, там реально два проводника, но токи все равно микроамперные (из-за ограничений подтягивающего резистора).
0
Может тему здесь открыть перечислить все варианты стандартных и нестандартных интерфейсов для передачи данных по питанию?
0
Тогда уж лучше предложенную Вами тему раскрыть в отдельной статье здесь в Сообществе!
0
а зачем?
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.