Как из камня сделать пар, знает доктор наш Гаспар
Очень часто при выборе МК приходится обращать внимание на наличие в его составе конкретного набора периферийных устройств под конкретную задачу. Между тем ряд стандартных шин может быть реслизован на основе других интерфейсов, имеющихся в уже используемом МК. В даном посте я покажу, как на МК 1986ВЕ1Т можно реализовать отсутствующий в его составе аппаратный интерфейс 1-Wire на основе имеющегося интерфейса SSP, причем данный опыт может быть перенесен на другие МК других производителей аналогичной архитектуры.
Для начала краткое описание используемого микроконтроллера. МК 1986ВЕ1Т производства компании «Миландр» представляет собой весьма неплохое устройство, достоинства которого вовсе на исчерпываются тем, что это отечественная продукция (хотя для автора именно этот параметр и являлся определяющим). Действительно, МК с ядром Cortex-M1 с тактовой частотой до 140 Мгц представляет собой нечастого участника рынка, а если учесть наличие большого числа разнообразных интерфейсов, расширенный температурный диапазон, и наличие версии с ВП, то данный МК становится практически уникальным предложением. Вместе с тем необходимо отметить и ряд свойств, снижающих привлекательность данного МК для широкого применения, а именно: высокая цена, определяемая в первую очередь металлокерамическим корпусом (хотя разработчик анонсировал относительно дешевую версию в пластмассовом корпусе), весьма неудобный в использовании металлокерамический корпус (зато именно такой тип корпуса позволяет добиться «честного» военного температурного диапазона), и (на взгляд автора) недостаточный уровень проработки документации (проблема всех отечественных разработок, что тем не менее не оправдывает).
Приведу основные характеристики данного МК:
ядро CORTEX-M1 (с небольшими ограничениями);
тактовая частота – до 144 Мгц (для частот выше 25 Мгц вводятся задержки при чтении памяти программ, что снижает реальное быстродействие до эквивалентных 100 Мгц);
память программ – 128 Кб, память данных – 48 Кб;
оба вида памяти легко расширяются через встроенный 32-разрядный контроллер внешней системной шины (что требует значительного числа внешних выводов);
развитая система тактирования с двумя внутренними генераторами (не слишком точными), возможностью подключения внешних кварцевых резонаторов и встроенными PLL;
энергонезависимая память (12*4 байт) и часы реального времени (при наличии внешнего батарейного питания);
8*12разрядов АЦП, 2*12разрядов ЦАП, 8 таймеров с режимом ШИМ;
32х канальный ПДП;
и богатый набор собственно периферии – 2*CAN, ГОСТ 18977, 2*ГОСТ Р-52070, Ethernet 10/100 ( со встроенным PHY !), USB Host/Device, 2*UART (со встроенным IRDA), 3*SSP;
до 96 конфигурируемых ножек пользователя;
возможность программирования и отладки из под распространенных сред программирования (автор предпочитает IAR) через SWD и JTAG.
В общем и целом кристалл вполне достойный, дополнительную информацию смотрите на сайте производителя. Из недостатков аппаратуры автор отметил бы неразвитый аппарат прерываний по внешним событиям (внешние прерывания могут поступать только на небольшое количество предопределенных выводов), что ограничивает возможности разработчика.
Другой «герой» данной статьи – интерфейс 1-Wire. Интерфейс не стандартизирован, является собственностью компании Dallas Semiconductor, известен довольно широко, применяется для взаимодействия с низкоскоростными устройствами, среди которых особо известны ключи идентификации i-Button и термодатчики разного вида, топология шины — звезда с индивидуальной адресацией, скорость обмена – до 15 Кбод, количество проводов – 2, более подробная спецификация доступна на сайте фирмы. Многие микроконтроллеры имеют аппаратную реализацию данного интерфейса, однако рассматриваемый МК к таковым не относится. Поэтому рассмотрим возможности реализации интерфейса с помощью стандартных периферийных устройств МК 986ВЕ1Т.
Прежде всего нам необходима реализация слоя 1 модели OSI – физический интерфейс. С этой точки зрения интерфейс совсем несложен – требуется земляной провод и 1 провод, по которому осуществляется двухстороннее взаимодействие устройств и опционально передается питание к оконечному устройству. Для реализации информационного провода необходим выход МК с открытым коллектором и подтягивающий резистор номиналом 5 Ком. Все выводы используемого МК конфигурируемые и могут быть снабжены встроенными подтягивающими вверх и вниз резисторами (за дополнительной информацией обращайтесь к описанию МК). Номиналы встроенных резисторов отличаются от требуемых (по слухам, составляют около 10 Ком — по слухам, поскольку в докуметации НИКАКИХ данных не приведено), тем не менее для случая с одним источником информации возможно использование встроенных резисторов, при необходимости легко добавить внешний резистор, подключенный к соответствующему питанию. Среди допустимых конфигураций выводов МК есть и режим вывода с открытым стоком, что удовлетворяет требованиям интерфейса. Таким образом, принципиальной невозможности физической реализации интерфейса не обнаружено.
При взаимодействии ведущего устройства (а мы реализуем именно его) с ведомым используются активный нулевой уровень, подаваемый в виде импульсов различной длительности. Используются 3 основных длительности импульсов, а именно:
импульс сброса длительностью не менее 480 мксек, импульс передачи единицы длительностью от 1 до 15мксек (этот же импульс используется для синхронизации чтения), импульс передачи ноля длительностью от 15 до 60 мксек. Защитный интервал между импульсами должен составлять не менее 1 мксек, длительность импульса с защитным интервалом должна составлять от 60 до 120 мксек. Здесь следует заметить, что если требования минимальной длительности понятны сточки зрения игнорирования помех, то ограничения битовых интервалов сверху не столь очевидно и не продиктовано логикой работы устройств. Тем не менее ограничения явно прописаны и соответственно должны соблюдаться. При взаимодействии ведомого устройства с ведущим используются 2 вида импульсов, а именно: импульс присутствия в виде нуля в ответ на импульс сброса с задержкой относительно прекращения последнего от 15 до 60 мксек и длительностью от 60 до 240 мксек, и импульс передачи ноля, выдаваемый в ответ на импульс чтения с задержкой относительно начала последнего не более 1 мксек и длительностью от 15 до 60 мксек (в документации на интерфейс установлено требование длительности РОВНО 15 мксек с последующей задержкой на снятие импульса от 0 до 45 мксек – странновато как-то написано). Общий вывод из вышеперечисленного – для реализации интерфейса 1-Wire требуется выдерживание временных интервалов с точностью порядка 10 мксек с ограничениями сверху и снизу.
Очевидная реализация данного протокола может быть основана на прямом управлении выходным портом, сконфигурированном в режиме с открытым истоком и подключенном подтягивающем резистором. Например, реализация импульса сброса с контролем наличия импульса присутствия может быть представлена следующим псевдокодом:
выдать на выход активный уровень (установить 0),
выполнить задержку 500 мксек,
снять активный уровень (установить 1),
на протяжении 15 мксек :
если есть 0 – завершение - ошибка протокола (неисправность),
на протяжении 45 мксек:
если есть 0 – перейти к ИМП::
завершение - ошибка протокола (отсутствие активных устройств),
ИМП:: на протяжении 60 мксек:
если есть 1- завершение - ошибка протокола (неисправность устройства),
на протяжении 180 мксек:
если есть 1- перейти к НОРМ::
завершение - ошибка протокола (неисправность устройства),
НОРМ:: завершение - устройство обнаружено.
На самом деле даже эта процедура должна быть сложнее для исключения влияния импульсных помех, но для целей данной статьи допустимы некоторые упрощения.
Аналогичные процедуры можно реализовать и для остальных импульсов. Теперь рассмотрим недостатки данного варианта:
недостаток программной реализации задержек – зависимость получаемого временного интервала от тактовой частоты МК и времени исполнения отдельных команд а в условиях введения задержек чтения памяти программ последние становятся практически неопределенными),
и даже если мы тонкими настройками преодолели первый недостаток, то в условиях функционирования совместно с другими модулями в рамках RTOS либо при наличии прерываний (а они почти всегда будут, мы ведь пишем не сферическую программу в вакууме) получаемые временные интервалы могут быть намного длиннее (но не короче), чем ожидаемые.
Поскольку интерфейс весьма время-зависим и требуемые точности значительны – 10 мксек это не так много даже на быстрых МК, с высокой вероятностью мы будем получать хаотические сбои при попытке общаться с устройствами по шине 1-Wire. Хотя протоколы более высокого уровня и позволяют обнаружить подобные сбои за счет избыточности кодирования, рекомендовать подобный метод реализации не представляется возможным. Для борьбы с подобными ошибками необходимо в пределах критических секций (а в вышеприведенном алгоритме это все операции) запрещать прерывания и блокировать возможность переключения задач на достаточно длительное время, что не является хорошим стилем программирования.
Вариантом данной реализации может быть использование одного из таймеров общего назначения либо системного таймера для формирования необходимых временных интервалов. Если учесть задержки на накладные расходы, то можно довольно точно выдерживать временную диаграмму и устранить первый недостаток вышеприведенной реализации. Тем не менее второй недостаток является принципиально неустранимым в рамках принятого решения, поскольку во всех RTOS (или при наличии прерываний) можно гарантировать только то, что заказанное событие не произойдет раньше указанного времени, но ограничения сверху невозможны.
Итак, для реализации интерфейса 1-Wire нам необходима возможность выдачи жестко ограниченных сверху и снизу временных интервалов и возможность контролировать состояние выводов МК в точно заданные интервалы времени. Такая возможность есть у МК с развитыми таймерами (включающими аппаратные автоматы состояний), но рассматриваемый МК к их числу не относится. Также возможно использование аппаратно-реализованных последовательных интерфейсов, чем мы и займемся. В МК реализовано много последовательных интерфейсов, тем не менее, не все из них могут быть использованы в наших целях. Например, интерфейс USB функционирует на фиксированной частоте и его аппаратная часть жестко привязана к протоколу, так что едва ли возможно столь существенно его видоизменить для наших целей, что касается также и CAN и ГОСТ. C UART ситуация намного лучше, его скорость можно менять в широких пределах, а наличие старт-бита не является препятствием, поскольку любой импульс начинается с активного уровня, а наличие стоп-бита может быть учтено при вычислении временных параметров. Единственным существенным ограничением является несовпадение активных уровней, что может быть устранено введением согласующего элемента. Но, поскольку все 2 UARTа были заняты под другие задачи, такое решение не прорабатывалось.
В результате было принято решение о реализации 1-Wire интерфейса на основе одного из трех универсальных модулей синхронной последовательной связи SSP. Подробное описание возможностей этих модулей можно найти в документации производителя МК. Если сказать коротко, то данные модули позволяют синхронно с тактовой частотой модуля, формируемой внутри МК, выдавать информацию на выходной порт последовательным кодом и одновременно принимать информацию на входном порте МК с привязкой к вышеуказанной частоте. При этом для организации двухстороннего обмена будем использовать выход и вход данных модуля, соединенные вместе, а сигналы на линии тактирования и синхронизации не используем и их можно использовать как линии общего назначения, что допускается гибкой системой конфигурирования выводов МК. Предполагается использовать передаваемый байт для кодирования интервалов времени, связанных с передачей/приемом одного бита, при этом значения битов байта определят длительности активного уровня (бит 0) и его отсутствия (бит1). При определении параметров настройки SSP следует учитывать, что минимальный передаваемый интервал должен составлять от 1 до 15 мксек, а максимальный отсчитываемый интервал составляет от 60 до 120 мксек для битов информации и не менее 480 для сигнала сброса. Если выбрать в качестве минимального интервала 10 мксек, то первое условие выполняется, а длительность битового интервала составит 6-12 минимальных интервалов, и цифра 8 хорошо укладывается в этот диапазон. Тогда длительность сигнала сброса составит минимум 48 тактов, что превосходит количество битов в байте. Для решения последнего вопроса следует либо изменять длительность минимального интервала, либо использовать для передачи импульса более чем 1 байт информации. Последний метод мог бы создать проблему с неопределенностью интервалов между передачами отдельных байтов посылки и, соответственно, длительность результирующего импульса могла непредсказуемо увеличиваться. К счастью, модуль SSP снабжен буфером FIFO на 8 16-разрядных слов, так что итоговая длина буфера 16*8=128 бит перекрывает требуемую минимально возможную длину последовательности 480+60+240=780/10=78 бит.
Таким образом, выстраивается следующий алгоритм работы с SSP: настраиваем необходимый режим и требуемую частоту передачи, запрещаем работу передатчика, заполняем буфер передатчика информацией для выдачи требуемых временных интервалов, разрешаем работу передатчика. Модуль начинает передачу информации последовательным кодом по линии выходных данных, по окончанию передачи взводится флаг готовности, который служит МК сигналом о том, что можно формировать очередную порцию данных. При этом временная диаграмма передаваемого сигнала жестко привязана к тактовой частоте модуля, которая зависит только от тактовой частоты МК и не может быть никоим образом изменена, какие бы программы в этот момент не исполнялись. При этом между передачей отдельных байтов последовательности могут быть только определяемые аппаратурой интервалы, которые можно учесть при формировании последовательности (исследования показали, что такие интервалы можно устранить). Рассмотрим процесс настройки модуля SSP.
Модуль SSP может работать в трех режимах —
интерфейс SPI фирмы Motorola;
интерфейс SSI фирмы Texas Instruments;
интерфейс Microwire фирмы National Semiconductor.
Из этих трех режимов последний не подходит, поскольку налагает дополнительные требования на формат сигнала, первые два выглядят эквивалентными, до того как мы начнем исследовать их на осциллографе. Тут то и выясняется что режим SPI характеризуется задержками в 1 бит между передачей соседних слов, который служит для передачи сигнала синхронизации, который мы не используем. Это было бы не так важно, поскольку увеличивало бы незначительно меж-битовые интервалы, если бы в эти моменты на линии данных не появлялся 0, что совершенно недопустимо по требованиям 1-Wire.
Рисунок 1. Ложные импульсы в режиме SPI
Очень удачно, что режим SSI лишен такого недостатка, иначе пришлось бы существенно усложнять программу. Исследования показали, что в режиме SSI меж-словные интервалы попросту отсутствуют (импульс синхронизации передается во время последнего бита) и, соответственно, никаких «просечек» на линии передачи нет. Следует отметить, что в момент конфигурирования на линиях передачи и синхронизации наблюдаются недокументированное изменение уровней сигналов, но на данную реализацию они не оказывают влияния, поскольку работа с устройством после настройки модуля SSP все равно начинается с импульса сброса, который подавит любые последствия этих выбросов.
Рисунок 2. Ложные импульсы в момент включения SSI
Поскольку проблему решили легким путем, искать причину выбросов не стали, хотя для приложений, в которых используется настоящий режим SSI, пришлось бы разбираться. В вышеприведенных осциллограммах подавался следующий эталонный сигнал, соответствующий паттерну 0xBF81.
Рисунок 3. Сигнал без ложных импульсов.
На всех рисунках верхний сигнал (желтый) – линия данных, нижний сигнал (синий) на рисунке 1 и 3 – линия тактовой частоты, на рисунке 2 – линия синхронизации. Нижний сигнал в реализации интерфейса не используется и дан в справочном порядке для определения временных интервалов.
Таким образом, передача импульса сброса и контроля наличия импульса присутствия может быть представлена следующим псевдокодом:
запретить работу передатчика;
загрузить буфер последовательностью 0хF800 0х0000 0х0000 0х01FF 0xFFFF 0xFFFF
// (50 мксек пауза, 500 мксек импульс, 410 мксек пауза для приема ответа);
разрешить работу передатчика;
ждать флага "устройство свободно";
прочитать содержимое буфера приема;
если прочитано не 6 слов – завершение – ошибка SSP;
сравнить принятую информацию с паттерном 0xF800 0x0000 0x0000 0b000X1XXXXX0XXXXX 0xXXXX 0xFFFF
если не совпало – завершение – ошибка устройства
завершение - устройство обнаружено.
Поясню некоторые моменты – передача в SSP идет одновременно с приемом, причем момент стробирования входных данных сдвинут на половину такта (при нашей тактовой частоте на 5 мксек), поэтому при чтении следует получить ровно столько же слов, сколько передавали, причем при передаче нулей должен быть принят ноль, за исключением последнего (здесь должна быть 1, но конкретное значение зависит от емкости линии и сопротивления подтягивающего резистора). Следующая особенность – флаг «буфер пуст» выставляется сразу после передачи последнего слова информации в сдвиговый регистр, но до завершения передачи битов, поэтому следует использовать именно флаг «устройство свободно» (смотри техническую документацию), иначе мы не увидим последнее принятое слово. Далее можно использовать различные паттерны для получения более подробной информации о неисправностях устройства, но это уже тонкости технической реализации.
Для передачи импульсов ноля и единицы легко создаются аналогичные процедуры, причем логично передавать по 2 бита 1-Wire подряд в одном 16-битовом слове SSP. Рекомендуется процедура, передающая сразу байт 1-Wire в 4х словах SSP. По окончанию передачи следует не забыть прочитать приемный буфер, чтобы удалить оттуда копию передаваемых данных. Для чтения следует сформировать последовательность, соответствующую записи 1, и проанализировать принятую информация для извлечения значения принятого бита (передаем паттерн 0b01111111, принимаем 0b01111111 в случае 1 либо 0b00XXXX11 в случае 0). Для передачи длинных сообщений можно (и это было реализовано) рассмотреть вариант организации программного обеспечения с использованием развитого аппарата прямого доступа в память (опять таки недостаточно документированного), один из каналов которого отвечает за взаимодействие с модулем SSP.
Следует отметить, что предложенная организация интерфейса имеет недостаток – принимаемые значения фиксируются по фронту синхросигнала, то есть мы можем «поймать» короткие импульсы на линии, которые исказят принятую информацию. Данная проблема может быть решена более подробным анализом принятой информации, например, в принятом паттерне 0b00100111 третий слева единичный бит следует считать помехой. Конечно, такой метод нельзя считать оптимальным, хотя нужно понимать, что вероятность подобного события несоизмеримо меньше, чем вероятность сбоя при варианте чисто программной реализации. Тем не менее в используемом МК 1986ВЕ1Т есть возможность бороться с помехами на аппаратном уровне, а именно каждый входной порт снабжен подключаемым триггером Шмидта для повышения помехозащищенности и более того, снабжен подключаемым фильтром, который подавляет короткие импульсы на входах. К сожалению, как и многие другие интересные решения разработчиков микроконтроллера, эта часть весьма скудно задокументирована.
На основе вышеописанных методик реализована библиотека подпрограмм на языке C для работы с интерфейсом 1-Wire через SSP модуль микроконтроллера 1986ВЕ1Т следующего состава:
1. Нижний слой работы с аппаратным обеспечением составляют подпрограммы управления регистрами МК, входящие в состав BSP MDR1986VE1T, доступного на сайте производителя.
2. Следующий слой включает в себя низкоуровневые функции записи и чтения, которые не являются публичными.
3. Слой реализации интерфейса предоставляет разработчику полный доступ к интерфейсу 1-Wire и включает в свой состав следующие модули:
Init1Wire – инициализирует аппаратную часть запрашиваемого модуля SSP и возвращает указатель на управляющую структуру;
Reset1Wire – производит начальную установку подключенных устройств и сообщает об их наличии;
Search1Wire – проводит поиск подключенных к интерфейсу устройств и возвращает их адреса;
Adres1Wire – адресует конкретное устройство на интерфейсе;
WriteByte1Wire – записывает произвольный байт в выбранное устройство;
ReadByte1wire – считывает 1 байт из выбранного устройства.
4. В слое реализации интерфейса дополнительно созданы функции для упрощения взаимодействия с устройством:
WriteData1Wire – записывает набор данных в выбранное устройство;
ReadData1Wire – считывает набор данных с выбранного устройства.
5. Слой реализации устройства построен на основе слоя 3 и представлен примером взаимодействия с электронным ключом типа DS1990.
Общий вывод – МК 1986ВЕ1Т представляет собой интересный продукт отечественного производителя с возможностью создания на его основе различных цифровых устройств и представляет подготовленному специалисту богатые возможности по реализации с минимальными программными затратами различных интерфейсов, в том числе и нестандартных для данного микроконтроллера.
P.S. Если пост интересен публике, то в следующем я мог бы описать реализацию все на том же SSP интерфейса I2C.
Автор: GarryC