Шина Двухпроводного Последовательного Интерфейса
Двухпроводной последовательный интерфейс (TWI) идеально подходит для типичных использования в микроконтроллерной технике. Протокол TWI позволяет разработчику систем соединять между собой до 128 различных устройств, используя только две двунаправленные шины, одну для синхронизации (SCL) и одну для передачи данных (SDA). Единственное внешнее оборудование, необходимое для реализации шины, - это подтягивающий резистор для каждой из линий TWI. Все устройства, подключенные к шине, имеют индивидуальные адреса, а механизмы разрешения конфликтов между ними общий в TWI.
Передача по шине TWI осуществляется в полудуплексном режиме, это означает что в один момент времени может осуществляться либо прием, либо передача. В связи с этим, различают главное и ведомое устройства, передатчик и приемник. Совсем не обязательно, чтобы главное устройство было передатчиком.
Вообще, Master-устройство это то, которое инициирует и прекращает передачу, оно также генерирует сигнал синхронизации на линии SCL.
Под Slave-устройством понимается то, к которому обращается Master.
Передатчиком зовется то устройство, которое помещает данные на шину, а приемником - то, что считывает их с шины.
Как показано на рисунке 1, обе шины подключены к положительному напряжению питания через подтягивающие резисторы. Драйверы шин всех устройств, совместимых с TWI, имеют открытый сток или открытый коллектор. Это реализует проводную "И" функцию, которая необходима для работы интерфейса. Низкий уровень на шине TWI генерируется, когда одно или несколько устройств TWI выводят ноль. Высокий уровень выводится, когда все устройства TWI переключают свои выходы, позволяя подтягивающим резисторам вытягивать линию высоко. Обратите внимание, что все устройства AVR, подключенные к шине TWI, должны быть запитаны, чтобы обеспечить работу любой шины.
Количество устройств, которые могут быть подключены к шине, ограничено только пределом емкости шины 400 пФ и 7-разрядным ведомым адресным пространством. Скорость передачи данных по шине не может быть выше 400кГц, но обычно стандартом считается скорость передачи 100 кГц и 400 кГц.
Передача данных по шине
Каждый бит данных, передаваемый по шине TWI, сопровождается импульсом на тактовой линии. Уровень линии передачи данных должен быть стабильным, когда тактовая линия высока. Единственным исключением из этого правила является создание условий запуска и остановки.
Использование аппаратного TWI
AVR TWI ориентирован на байты и основан на прерываниях. Прерывания выдаются после любых событий на шине, таких как прием байта или передача условия START. Поскольку TWI основан на прерываниях, прикладное программное обеспечение может выполнять другие операции во время передачи. Обратите внимание, что бит разрешения прерывания TWI (TWIE) в TWCR вместе с битом глобального разрешения прерывания в SREG позволяют приложению решать, должна ли установка флага TWINT генерировать запрос на прерывание. Если бит TWIE очищен, приложение должно опросить флаг TWINT, чтобы определить действия на шине TWI.
Когда флаг TWINT установлен, то TWI завершил текущую операцию и ожидает ответа приложения. В этом случае регистр состояния TWI (TWSR) содержит значение, указывающее текущее состояние шины TWI. Затем прикладное программное обеспечение может решить, как они должны вести себя в следующем цикле шины TWI, манипулируя регистрами TWCR и TWDR.
На рисунке 3 представлен простой пример того, как приложение может взаимодействовать с оборудованием TWI. В этом примере ведущий хочет передать один байт данных ведомому устройству. Приводимое описание довольно абстрактно, а подробное объяснение следует далее в этом разделе.
Действия по передаче байта по шине TWI
- Первым шагом в передаче по шине TWI является отправка условия START. Это делается путем записи определенного значения в регистр TWCR, инструктируя оборудование TWI передавать условие START. Какое значение следует записать, будет описано ниже. Однако важно, чтобы бит TWINT также был установлен в этом регистре. Установка "1" для TWINT сбрасывает этот флаг. TWI не начнет никакой операции, пока установлен бит TWINT в TWCR. Сразу после того, как приложение сбросит бит TWINT, TWI инициирует передачу условия START.
- Когда условие START было передано, флаг TWINT в TWCR снова устанавливается, а TWSR обновляется кодом состояния, указывающим, что условие START успешно отправлено.
- Теперь прикладное программное обеспечение должно проверить значение TWSR, чтобы убедиться, что условие START было успешно передано. Если TWSR указывает на иное, прикладное программное обеспечение может предпринять некоторые специальные действия, например, вызвать процедуру ошибки. Если предполагается, что код состояния соответствует ожидаемому, приложение должно загрузить SLA+W в TWDR. Помните, что TWDR используется как для адреса, так и для данных. После загрузки TWDR с требуемым SLA+W в TWCR должно быть записано определенное значение, указывающее аппаратному обеспечению TWI передавать SLA+W, присутствующее в TWDR. Какое значение следует записать, будет описано ниже. Однако важно, чтобы бит TWINT был сброшен записью "1" в соответствующую позицию. Сразу после того, как приложение очистит TWINT, TWI инициирует передачу адресного пакета.
- По окончании передачи адресного пакета, флаг TWINT в TWCR слова устанавливается, а TWSR обновляется кодом состояния, указывающим, что адресный пакет успешно отправлен. Код состояния также будет отражать, подтвердил ли ведомый пакет или нет.
- Теперь прикладное программное обеспечение должно проверить значение TWSR, чтобы убедиться, что адресный пакет был успешно передан и что значение бита ACK соответствует ожидаемому. Если TWSR указывает на другое, прикладное программное обеспечение может предпринять некоторые специальные действия, например, вызвать процедуру ошибки. Предполагая, что код состояния соответствует ожидаемому, приложение должно загрузить пакет данных в TWDR. Впоследствии в TWCR должно быть записано специальное значение, указывающее аппаратному обеспечению TWI передавать пакет данных, присутствующий в TWDR. Какое значение следует записать, будет описано ниже. Однако важно, чтобы бит TWINT был сброшен записью "1" в соответствующую позицию. Сразу после того, как приложение очистит TWINT, TWI инициирует передачу пакета с данными.
- После передачи пакта данных флаг TWINT в TWCR устанавливается, а TWSR обновляется кодом состояния, указывающим, что пакет данных успешно отправлен. Код состояния также будет отражать, подтвердил ли ведомый пакет или нет.
- Теперь прикладное программное обеспечение должно проверить значение TWSR, чтобы убедиться, что пакет данных был успешно передан и что значение бита ACK соответствует ожидаемому. Если TWSR указывает на иное, прикладное программное обеспечение может предпринять некоторые специальные действия, например вызвать процедуру ошибки. Предполагая, что код состояния соответствует ожидаемому, приложение должно записать определенное значение в TWCR, проинструктировав аппаратное обеспечение TWI передать условие STOP. Какое значение следует записать, будет описано ниже. Однако важно, чтобы бит TWINT был сброшен записью "1" в соответствующую позицию. Сразу после того, как приложение очистит TWINT, TWI инициирует передачу пакета STOP. Обратите внимание, что TWINT НЕ устанавливается после отправки условия ОСТАНОВКИ.
Несмотря на простоту этого примера, он показывает основные принципы, используемые во всех передачах по TWI. Их можно резюмировать следующим образом:
- Когда TWI завершает операцию и ожидает ответа приложения, устанавливается флаг TWINT. Линия SCL опускается низко, пока TWINT не будет очищен.
- Когда установлен флаг TWINT, пользователь должен обновить все регистры TWI значением, соответствующим следующему циклу шины TWI. Например, TWDR должен быть загружен значением, которое будет передано в следующем цикле шины.
- После завершения всех обновлений реестра TWI и других незавершенных задач прикладного программного обеспечения записывается TWCR. При записи TWCR должен быть установлен бит TWINT. Установка единицы в бите TWINT сбрасывает этот флаг. Затем TWI начнет выполнять любую операцию, указанную в настройках TWCR.
Ок, все понятно, но что конкретно делать, чтоб все заработало?
В даташите на микроконтроллер приводится формула для вычисления параметров TWBR и TWPS:
К примеру, контроллер работает на частоте 16 МГц, мы хотим, чтобы частота шины TWI составляла 100 кГц, тогда используя эту формулу получим:
То есть, если TWPS положить равным 0, TWBR будет равно 72.
Нас это устраивает, поскольку 72 помещается в байт данных
Что касается TWPS, то это на самом деле не регистр, а 0 и 1 биты регистра TWSR
Биты TWPS определяют предделитель шины TWI, однако здесь нужно понимать, что в формулу расчета TWBR в показатель степени 4 нужно подставлять не значение Prescaler Value, а значение, формируемое этими двумя битами. Так например для получения Prescaler Value = 16, нужно подставить 2, а для получения Prescaler Value = 64 соответственно 3.
Итак, для инициализации шины мы должны установить TWBR равным 72, а биты TWPS равные 0.
макрос outi — это умный макрос записи в порт ввода/вывода, дело в том, что команду out мы можем использовать только если адрес порта меньше 0x40, если от больше либо равен 0x40, запись в порт осуществляется командой sts.
После инициализации шины, действуем в соответствии с этим. Ниже я привожу подпрограммы для взаимодействия с TWI на языке ассемблера.
В подпрограммах i2c_start, i2c_stop запихивать в стек регистр r16 вовсе не обязательно, если вы точно знаете, что делаете. Ко всему прочему, это экономит нам 4 байта в ROM (push + pop) и 4 такта процессора.
в подпрограмме i2c_send сохранение регистра r16 тоже не обязательно, что также экономит нам 4 байта в ROM (push+pop) и 4 такта процессора.
Эти подпрограммы осуществляют основную деятельность при взаимодействии с шиной, мы лишь упустили одно — проверку состояния нашей шины при возникновении очередного TWINT. Состояние шины находится в регистре TWSR, а точнее определяется битами 7:3, бит 2 всегда 0, а биты 1:0 используются для задания предделителя.
Эти пять битов (7:3) отражают состояние логики TWI и двухпроводной последовательной шины. Разработчик приложения должен замаскировать биты предделителя нулями при проверке битов состояния (то есть выполнить логическое «и» с 0xF8 в шестнадцатиричной форме или 0b11111000 в бинарной).
При написании надежной системы проверять статус необходимо каждый раз после установки флага TWINT, сверяясь с текущим состоянием шины и ожидаемым статусом.
Ниже привожу таблицы со статусами для двух режимов Master Transmit и Master Receive.
Первая колонка соответствует значению TWSR & 0xF8, вторая колонка соответствует текущему состоянию шины TWI, следующая большая колонка с субколонками описывает будущие действия с регистрами TWDR и TWCR, и последняя колонка показывает следующее возможное состояние шины TWI.
Как это все понимать? Допустим, вы послали на шину сигнал START. Если TWINT выставлен, то значит шина ангажирована и результат будет 0x08. Он предполагает, что дальше будет либо перевод шины в режим Master Transmit /Master Receive. Елси мы хотим в режим mastet transmit, передаем в регистр TWDR SLA+W, а в TWCR сбрасываем флаг TWINT. После этого, наша шина может оказаться в следующих состояниях с кодами 0x18, 0x20, 0x38. Первые два кода соответствуют тому, что SLA+W был передан, а вот последний — говорит о потере арбитража. В первых двух состояниях возможна дальнейшая работа, здесь все зависит от формата возвращаемых значений, а вот в последнем случае шина сможет лишь 1) освободить шину без перехода в режим slave, 2) передать условие START когда шина освободится.
Если опрашиваемое устройство установило состояние TWSR в 0x18 или 0x20 и мы решили передавать данные, мы сможем получить статусы. 0x28, 0x30 которые соответствуют тому, что данные переданы и получен NACK или ACK.
Чтобы продолжить, нам необходимо вернуться к описанию адресуемого устройства и посмотреть что должно возвращать.
Например, взаимодействуя с дисплеем SSD1306 мы должны следовать его маршруту (последовательности) передачи данных.
Заметим, что после передачи в дисплей очередного байта мы должны получать ACK, то есть, после передачи байта по TWI мы должны контролировать установку статусов 0x08, 0x18, 0x28,…,0x28. После передачи START мы должны получить 0x08, после передачи SLA+W — 0x18, и 0x28 после передачи остальных байтов в посылке. Любое отклонение от этой последовательности означает возникновение ошибки передачи.