Подробное описание интерфейса I2C. Шина управления I2C I2c описание на русском

Главная / Разное

Интерфейс I2C (или по другому IIC) — это достаточно широко распространённый сетевой последовательный интерфейс, придуманный фирмой Philips и завоевавший популярность относительно высокой скоростью передачи данных (обычно до 100 кбит/с, в современных микросхемах до 400 кбит/с), дешевизной и простотой реализации.

1) Физика .

Физически сеть представляет собой двухпроводную шину, линии которой называются DATA и CLOCK (необходим ещё и третий провод — земля, но интерфейс принято называть двухпроводным по количеству сигнальных проводов). Соответственно, по линии DATA передаются данные, линия CLOCK служит для тактирования. К шине может быть подключено до 128 абонентов, каждый со своим уникальным номером. В каждый момент времени информация передаётся только одним абонентом и только в одну сторону.

Устройства I2C имеют выход с "открытым коллектором". Когда выходной транзистор закрыт — на соответствующей линии через внешний подтягивающий резистор устанавливается высокий уровень, когда выходной транзистор открыт — он притягивает соответствующую линию к земле и на ней устанавливается низкий уровень (смотрите рисунок). Резисторы имеют номинал от нескольких килоОм до нескольких десятков килоОм (чем выше скорость — тем меньше номинал резисторов, но больше энергопотребление). На рисунке треугольниками на входе показано, что входы высокоомные и, соответственно, влияния на уровни сигналов на линиях они не оказывают, а только "считывают" эти уровни. Обычно используются уровни 5В или 3,3В.

2) Логика .

Любое устройство на шине I2C может быть одного из двух типов: Master (ведущий) или Slave (ведомый). Обмен данными происходит сеансами. "Мастер"-устройство полностью управляет сеансом: инициирует сеанс обмена данными, управляет передачей, подавая тактовые импульсы на линию Clock, и завершает сеанс.

Кроме этого, в зависимости от направления передачи данных и "Мастер" и "Слэйв"-устройства могут быть "Приёмниками" или "Передатчиками". Когда "Мастер" принимает данные от "Слэйва" — он является "Приёмником", а "Слэйв" — "Передатчиком". Когда же "Слэйв" принимает данные от "Мастера", то он уже является "Приёмником", а "Мастер" в этом случае является "Передатчиком".

Не надо путать тип устройства "Мастер" со статусом "Передатчика". Несмотря на то, что при чтении "Мастером" информации из "Слэйва", последний выставляет данные на шину Data, делает он это только тогда, когда "Мастер" ему это разрешит, установкой соответствующего уровня на линии Clock. Так что, хотя "Слэйв" в этом случае и управляет шиной Data, — самим обменом всё равно управляет "Мастер".

В режиме ожидания (когда не идёт сеанс обмена данными) обе сигнальные линии (Data и Clock) находятся в состоянии высокого уровня (притянуты к питанию).

Каждый сеанс обмена начинается с подачи "Мастером" так называемого Start-условия. "Старт-условие" — это изменение уровня на линии Data с высокого на низкий при наличии высокого уровня на линии Clock.

После подачи "Старт-условия" первым делом "Мастер" должен сказать с кем он хочет пообщаться и указать, что именно он хочет — передавать данные в устройство или читать их из него. Для этого он выдаёт на шину 7-ми битный адрес "Слэйв" устройства (по другому говорят: "адресует "Слэйв" устройство"), с которым хочет общаться, и один бит, указывающий направление передачи данных (0 — если от "Мастера" к "Слэйву" и 1 — если от "Слэйва" к "Мастеру"). Первый байт после подачи "Старт"-условия всегда всеми "Слэйвами" воспринимается как адресация.

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

После того, как "Мастер" скажет, к кому именно он обращается и укажет направление передачи данных, — начинается собственно передача: "Мастер" выдаёт на шину данные для "Слэйва" или получает их от него. Эта часть обмена (какие именно данные и в каком порядке "Мастер" должен выдавать на шину, чтобы устройство его поняло и сделало то, что ему нужно) уже определяется каждым конкретным устройством.

Заканчивается каждый сеанс обмена подачей "Мастером" так называемого Stop-условия, которое заключается в изменении уровня на линии Data с низкого на высокий, опять же при наличии высокого уровня на линии Clock. Если на шине сформировано Stop-условие, то закрываются все открытые сеансы обмена .

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

Несколько слов по поводу того, в чём в данном случае разница между "прекращением обмена данными" и "окончанием сеанса обмена". В принципе "Мастеру" разрешается, не закрыв первый сеанс обмена, открыть ещё один или несколько сеансов обмена с этим же (например, как было сказано выше, для изменения направления передачи данных) или даже с другими "Слэйвами", подав новое "Старт"-условие без подачи "Стоп"-условия для закрытия предыдущего сеанса. Управлять линией Data, для того, чтобы отвечать "Мастеру", в этом случае будет разрешено тому устройству, к которому "Мастер" обратился последним, однако старый сеанс при этом нельзя считать законченным. И вот почему. Многие устройства (например те же eeprom-ки 24Схх) для ускорения работы складывают данные, полученные от "Мастера" в буфер, а разбираться с этими полученными данными начинают только после получения сигнала об окончании сеанса обмена (то есть "Стоп-условия").

То есть, например, если на шине висит 2 микросхемы eeprom 24Cxx и вы открыли сеанс записи в одну микросхему и передали ей данные для записи, а потом, не закрывая этот первый сеанс, открыли новый сеанс для записи в другую микросхему, то реальная запись и в первую и во вторую микросхему произойдёт только после формирования на шине "Стоп-условия", которое закроет оба сеанса. После получения данных от "Мастера" eeprom-ка складывает их во внутренний буфер и ждёт окончания сеанса, для того, чтобы начать собственно процесс записи из своего внутреннего буфера непосредственно в eeprom. То есть, если вы после после передачи данных для записи в первую микруху не закрыли этот сеанс, открыли второй сеанс и отправили данные для записи во вторую микруху, а потом, не сформировав "Стоп-условие", выключили питание, то реально данные не запишутся ни в первую микросхему, ни во вторую. Или, например, если вы пишете данные попеременно в две микрухи, то в принципе вы можете открыть один сеанс для записи в первую, потом другой сеанс для записи во вторую, потом третий сеанс для записи опять в первую и т.д., но если вы не будете закрывать эти сеансы, то в конце концов это приведёт к переполнению внутренних буферов и в итоге к потере данных .

Здесь можно привести такую аналогию: ученики в классе ("слэйвы") и учитель ("мастер"). Допустим учитель вызвал какого-то ученика (пусть будет Вася) к доске и попросил его решить какой-то пример. После того как Вася этот пример решил, учитель вызвал к доске Петю и начал спрашивать у него домашнее задание, но Васю на место не отпустил. Вот в этом случае вроде бы разговор с Васей закончен, — учитель разговаривает с Петей, но Вася стоит у доски и не может спокойно заниматься своими делами (сеанс общения с ним не закрыт).

В случае, если "Слэйв" во время сеанса обмена не успевает обрабатывать данные, — он может растягивать процесс обмена, удерживая линию Clock в состоянии низкого уровня, поэтому "Мастер" должен проверять возврат линии Clock к высокому уровню после того, как он её отпустит. Хотелось бы подчеркнуть, что не стоит путать состояние, когда "Слэйв" не успевает принимать или посылать данные, с состоянием, когда он просто занят обработкой данных, полученных в результате сеанса обмена. В первом случае (во время обмена данными) он может растягивать обмен, удерживая линию Clock, а во втором случае (когда сеанс обмена с ним закончен) он никакие линии трогать не имеет права. В последнем случае он просто не будет отвечать на "обращение" к нему от "Мастера".

Внутри сеанса передача состоит из пакетов по девять бит, передаваемых в обычной положительной логике (то есть высокий уровень — это 1, а низкий уровень — это 0). Из них 8 бит передаёт "Передатчик" "Приёмнику", а последний девятый бит передаёт "Приёмник" "Передатчику". Биты в пакете передаются старшим битом вперёд. Последний, девятый бит называется битом подтверждения ACK (от английского слова acknowledge — подтверждение). Он передаётся в инвертированном виде, то есть 0 на линии соответствует наличию бита подтверждения, а 1 — его отсутствию. Бит подтверждения может сигнализировать как об отсутствии или занятости устройства (если он не установился при адресации), так и о том, что "Приёмник" хочет закончить передачу или о том, что команда, посланная "Мастером", не выполнена.

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

Вот собственно и всё. На рисунках ниже всё это описание показано в графической форме.

3) Диаграммы и тайминги.



Параметр Обозн. Мин.знач.

Интерфейс I2C (или по другому IIC) — это достаточно широко распространённый сетевой последовательный интерфейс, придуманный фирмой Philips и завоевавший популярность относительно высокой скоростью передачи данных (обычно до 100 кбит/с, в современных микросхемах до 400 кбит/с), дешевизной и простотой реализации.

1) Физика .

Физически сеть представляет собой двухпроводную шину, линии которой называются DATA и CLOCK (необходим ещё и третий провод — земля, но интерфейс принято называть двухпроводным по количеству сигнальных проводов). Соответственно, по линии DATA передаются данные, линия CLOCK служит для тактирования. К шине может быть подключено до 128 абонентов, каждый со своим уникальным номером. В каждый момент времени информация передаётся только одним абонентом и только в одну сторону.

Устройства I2C имеют выход с "открытым коллектором". Когда выходной транзистор закрыт — на соответствующей линии через внешний подтягивающий резистор устанавливается высокий уровень, когда выходной транзистор открыт — он притягивает соответствующую линию к земле и на ней устанавливается низкий уровень (смотрите рисунок). Резисторы имеют номинал от нескольких килоОм до нескольких десятков килоОм (чем выше скорость — тем меньше номинал резисторов, но больше энергопотребление). На рисунке треугольниками на входе показано, что входы высокоомные и, соответственно, влияния на уровни сигналов на линиях они не оказывают, а только "считывают" эти уровни. Обычно используются уровни 5В или 3,3В.

2) Логика .

Любое устройство на шине I2C может быть одного из двух типов: Master (ведущий) или Slave (ведомый). Обмен данными происходит сеансами. "Мастер"-устройство полностью управляет сеансом: инициирует сеанс обмена данными, управляет передачей, подавая тактовые импульсы на линию Clock, и завершает сеанс.

Кроме этого, в зависимости от направления передачи данных и "Мастер" и "Слэйв"-устройства могут быть "Приёмниками" или "Передатчиками". Когда "Мастер" принимает данные от "Слэйва" — он является "Приёмником", а "Слэйв" — "Передатчиком". Когда же "Слэйв" принимает данные от "Мастера", то он уже является "Приёмником", а "Мастер" в этом случае является "Передатчиком".

Не надо путать тип устройства "Мастер" со статусом "Передатчика". Несмотря на то, что при чтении "Мастером" информации из "Слэйва", последний выставляет данные на шину Data, делает он это только тогда, когда "Мастер" ему это разрешит, установкой соответствующего уровня на линии Clock. Так что, хотя "Слэйв" в этом случае и управляет шиной Data, — самим обменом всё равно управляет "Мастер".

В режиме ожидания (когда не идёт сеанс обмена данными) обе сигнальные линии (Data и Clock) находятся в состоянии высокого уровня (притянуты к питанию).

Каждый сеанс обмена начинается с подачи "Мастером" так называемого Start-условия. "Старт-условие" — это изменение уровня на линии Data с высокого на низкий при наличии высокого уровня на линии Clock.

После подачи "Старт-условия" первым делом "Мастер" должен сказать с кем он хочет пообщаться и указать, что именно он хочет — передавать данные в устройство или читать их из него. Для этого он выдаёт на шину 7-ми битный адрес "Слэйв" устройства (по другому говорят: "адресует "Слэйв" устройство"), с которым хочет общаться, и один бит, указывающий направление передачи данных (0 — если от "Мастера" к "Слэйву" и 1 — если от "Слэйва" к "Мастеру"). Первый байт после подачи "Старт"-условия всегда всеми "Слэйвами" воспринимается как адресация.

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

После того, как "Мастер" скажет, к кому именно он обращается и укажет направление передачи данных, — начинается собственно передача: "Мастер" выдаёт на шину данные для "Слэйва" или получает их от него. Эта часть обмена (какие именно данные и в каком порядке "Мастер" должен выдавать на шину, чтобы устройство его поняло и сделало то, что ему нужно) уже определяется каждым конкретным устройством.

Заканчивается каждый сеанс обмена подачей "Мастером" так называемого Stop-условия, которое заключается в изменении уровня на линии Data с низкого на высокий, опять же при наличии высокого уровня на линии Clock. Если на шине сформировано Stop-условие, то закрываются все открытые сеансы обмена .

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

Несколько слов по поводу того, в чём в данном случае разница между "прекращением обмена данными" и "окончанием сеанса обмена". В принципе "Мастеру" разрешается, не закрыв первый сеанс обмена, открыть ещё один или несколько сеансов обмена с этим же (например, как было сказано выше, для изменения направления передачи данных) или даже с другими "Слэйвами", подав новое "Старт"-условие без подачи "Стоп"-условия для закрытия предыдущего сеанса. Управлять линией Data, для того, чтобы отвечать "Мастеру", в этом случае будет разрешено тому устройству, к которому "Мастер" обратился последним, однако старый сеанс при этом нельзя считать законченным. И вот почему. Многие устройства (например те же eeprom-ки 24Схх) для ускорения работы складывают данные, полученные от "Мастера" в буфер, а разбираться с этими полученными данными начинают только после получения сигнала об окончании сеанса обмена (то есть "Стоп-условия").

То есть, например, если на шине висит 2 микросхемы eeprom 24Cxx и вы открыли сеанс записи в одну микросхему и передали ей данные для записи, а потом, не закрывая этот первый сеанс, открыли новый сеанс для записи в другую микросхему, то реальная запись и в первую и во вторую микросхему произойдёт только после формирования на шине "Стоп-условия", которое закроет оба сеанса. После получения данных от "Мастера" eeprom-ка складывает их во внутренний буфер и ждёт окончания сеанса, для того, чтобы начать собственно процесс записи из своего внутреннего буфера непосредственно в eeprom. То есть, если вы после после передачи данных для записи в первую микруху не закрыли этот сеанс, открыли второй сеанс и отправили данные для записи во вторую микруху, а потом, не сформировав "Стоп-условие", выключили питание, то реально данные не запишутся ни в первую микросхему, ни во вторую. Или, например, если вы пишете данные попеременно в две микрухи, то в принципе вы можете открыть один сеанс для записи в первую, потом другой сеанс для записи во вторую, потом третий сеанс для записи опять в первую и т.д., но если вы не будете закрывать эти сеансы, то в конце концов это приведёт к переполнению внутренних буферов и в итоге к потере данных .

Здесь можно привести такую аналогию: ученики в классе ("слэйвы") и учитель ("мастер"). Допустим учитель вызвал какого-то ученика (пусть будет Вася) к доске и попросил его решить какой-то пример. После того как Вася этот пример решил, учитель вызвал к доске Петю и начал спрашивать у него домашнее задание, но Васю на место не отпустил. Вот в этом случае вроде бы разговор с Васей закончен, — учитель разговаривает с Петей, но Вася стоит у доски и не может спокойно заниматься своими делами (сеанс общения с ним не закрыт).

В случае, если "Слэйв" во время сеанса обмена не успевает обрабатывать данные, — он может растягивать процесс обмена, удерживая линию Clock в состоянии низкого уровня, поэтому "Мастер" должен проверять возврат линии Clock к высокому уровню после того, как он её отпустит. Хотелось бы подчеркнуть, что не стоит путать состояние, когда "Слэйв" не успевает принимать или посылать данные, с состоянием, когда он просто занят обработкой данных, полученных в результате сеанса обмена. В первом случае (во время обмена данными) он может растягивать обмен, удерживая линию Clock, а во втором случае (когда сеанс обмена с ним закончен) он никакие линии трогать не имеет права. В последнем случае он просто не будет отвечать на "обращение" к нему от "Мастера".

Внутри сеанса передача состоит из пакетов по девять бит, передаваемых в обычной положительной логике (то есть высокий уровень — это 1, а низкий уровень — это 0). Из них 8 бит передаёт "Передатчик" "Приёмнику", а последний девятый бит передаёт "Приёмник" "Передатчику". Биты в пакете передаются старшим битом вперёд. Последний, девятый бит называется битом подтверждения ACK (от английского слова acknowledge — подтверждение). Он передаётся в инвертированном виде, то есть 0 на линии соответствует наличию бита подтверждения, а 1 — его отсутствию. Бит подтверждения может сигнализировать как об отсутствии или занятости устройства (если он не установился при адресации), так и о том, что "Приёмник" хочет закончить передачу или о том, что команда, посланная "Мастером", не выполнена.

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

Вот собственно и всё. На рисунках ниже всё это описание показано в графической форме.

3) Диаграммы и тайминги.



Параметр Обозн. Мин.знач.

Урок 16

Часть 1

Интерфейс TWI (I2C)

Опыт нашей предыдущей работы с микроконтроллерами AVR показал то, что мы уже многому научились, в том числе общаться с внешними устройствами типа ПК. Теперь, я думаю, все понимают, что этого недостаточно. Есть масса устройств и датчиков, которые нам хочется подключить к микроконтроллера и, мало того, ещё ими и поуправлять, забрать у них какие-то данные. Только данные девайзы подключаются к МК с помощью проводов и существует несколько видов подключений. Один из таких видов — шина I2C . Данный интерфейс является авторской разработкой компании Phillips и поэтому Atmel, чтобы не нарушать авторские права, немного изменила наименование данной шины и назвала её TWI (Two-wire Serial Interface ).

Вот мы сегодня и заёмёмся изучением данного интерфейса, а также в данном уроке мы попробуем его как-то и использовать, что-то к нему подключить и с этим чем-то по общаться под интерфейсу TWI.

Как мы уже поняли, данный интерфейс предаёт данные по проводам. Причем именно по двум проводам

Как мы видим из данной блок-схемы, у нас есть два провода или контакта — SDA и SCL, подсоединённые к контроллеру к его соответствующим ножкам, а затем к этим проводам мы ещё подключаем какое-нибудь устройство или несколько устройств. Теоретически мы можем подключить до 127 устройств, впоследствии мы поймём, почему именно столько. Ножка SDA отвечает за передачу и приём данных по данной шине, а ножка SCL — это ножка тактирования, таким образом шина I2C является синхронной, что увеличивает стабильность и гарантированность передачи данных по ней. Также в данной блок-схеме мы наблюдаем то, что ещё к данным ножкам необходимо подтянуть резисторы на шину питания, причём как правило в каких-то модулях или устройствах данные резисторы уже подтянуты, и. если мы подключим их несколько, то у нас получится ряд параллельно подключенных подтягивающих резисторов, что будет очень нехорошо и придется оставить их в каком-то одном усторйстве, а в других выпаять. Всё это происходит потому, что есть ограничение на номинал данных резисторов в зависимости от определенных условия

Как показала практика, данные резисторы не должны быть сопротивлением более 10 килоом и менее 4,7 килоом.

Также, само собой, сигналы должны быть толерантными к логическим уровням нашего МК, поэтому подключенные устройства должны быть соответствующим образом запитаны, а также общий провод устройств и контроллера также должен быть объединён.

Хочу подчеркнуть, что данный интерфейс в микроконтроллрах AVR реализован на аппаратном уровне. Можно также организовывать его и программным способом, путём включение в определённое время двух любых ножек портов в определенные логические состояния, но само собой, аппаратная организация всегда лучше по всем показателям. Программная реализация как правило используется, если не хватит одной шины, но как правило хватает, так как мы выше уже видели, что устройств можно к одной шине подключить несколько. Таким образом, мы будем рассматривать именно аппаратную реализацию — на уровне регистров контроллера.

В сдедующей блок-схеме мы видим следующие регистры интерфейса TWI в контроллере Atmega8

Давайте с ними кратко ознакомимся.

Регистр TWBR — это регистр, в котором мы устанавливаем значения для обеспечения определённой скорости шины. Устройства, которые мы подключаем по I2C требуют определённой скорости ну или определённого диапазона скоростей. Расчитывается эта скорость или битрейт по следующей формуле

Как мы видим, в данной формуле для расчета частоты шины участвует частота тактирования контроллера, значение регистра TWBR, а также значение TWPS , судя по формуле представляющий собой предделитель, то есть, чем он больше, тем больше будет коэффициент деления, так как в степерь значения данного регистра мы возводим цифру 4.

Причем TWPS — это не отдельный регистр, а совокупность всего лишь двух битов статусного регистра TWSR , в котором помимо этих битов есть ещё и другие, посредством которых мы будем отслеживать определенные состояния или ошибки обмена данными. Есть перечень кодов статуса, по которым мы и будем узнавать, удачно ли прошла команда или что-то не так.

Также само собой существует ещё и управляющий регистр TWCR , с помощью установки в соответствующее состояния битов которого мы будем определённым образом настраивать нашу шину.

Есть ещё адресный регистр TWAR , с помощью которого, если мы настраиваем контроллер ведущим устройством, мы передаем ведомому устройству 7-битный адрес (отсюда и 127 максимально устройств на шине), по которому-то как раз и будет обращение именно к определённому устройству из всех навешанных на наши два провода. А восьмой бит к адресу будет добавляться для того, чтобы передать, читаем мы данные или пишем. А если у нас контроллер будет настроен как ведомое устройство, то в данном регистре мы будем получать от ведущего адрес и сравнивать его с определённым, чтобы понять, обратился ведущий именно к нам или к кому-то другому.

Интерфейс I2C является широко распространенным и популярным стандартом передачи данных между устройствами. Данный интерфейс поддерживают множество различных датчиков и микросхем, наиболее известные это микросхемы EEPROM памяти серии 24cXX. Для передачи данных используются всего две линии, которые представляют собой шину данных, причем на одну шину можно подключать несколько различных устройств. В этой статье я приведу описание интерфейса и реализацию на программном уровне.

Описание интерфейса I2C

Способ подключения

В интерфейсе используются два провода, это линия тактирования SCL, и линия передачи данных SDA, которые вместе образуют шину данных. Устройства, подключенные к шине подразделяются на ведущего и ведомого. Ведущий инициализирует процесс передачи данных и выдает тактовые импульсы на линию SCL, ведомый принимает команды /данные, а также выдает данные по запросу ведущего. Линии SDA и SCL двунаправленные, устройства подключаемые к шине должны иметь выводы перенастраиваемые на вход и выход. Причем тип выхода должен быть с открытым коллектором или открытым стоком, в связи с чем, обе линии SDA и SCL через резисторы подтягиваются к положительному полюсу источника питания. На следующей картинке приведена схема подключения интерфейса I2C:


В случае использования микроконтроллера, для установки лог. 1 на линии, достаточно перенастроить порт микроконтроллера на вход, при этом резистор “подтянет” линию к высокому логическому уровню, подача высокого логического уровня с порта микроконтроллера на линию не допускается. Для установки лог. 0 на линии, порт перенастраивается на выход, в выходную защелку заранее записывается значение 0, при этом линия “прижимается” к низкому логическому уровню.

Адресация

В интерфейсе предусмотрена программная адресация устройств подключенных к шине, наиболее распространена длина адреса в 7 бит, теоретически это позволяет подключать на шину до 127 устройств, но часть адресов по спецификации зарезервированы и не могут использоваться разработчиками. Каждое устройство имеет свой уникальный адрес, который заложен производителем и указан в технической документации. Адрес устройства может быть фиксированным, или с возможностью аппаратной настройки, в этом случае устройство имеет дополнительные входы, в зависимости от уровня напряжения на входах (высокое или низкое), можно получить различные адреса. Обычно количество входов варьируется от 1-го до 3-х, которые задают значения определенных битов 7-битного адреса. Аппаратная настройка адреса предусмотрена для возможности подключения нескольких однотипных устройств на одну шину.

Также интерфейс предусматривает более редкую 10-битную адресацию, под которую зарезервирован 7-битный адрес 11110XX (XX-зависят от значения адреса), в этом случае сначала предается зарезервированный адрес, в котором два последних бита представляют собой старшие биты 10-битного адреса, затем передаются младшие 8 бит адреса. При использовании данной адресации на шину можно подключать более 1000 устройств.

Условия “Старт” и “Стоп”

Каждый сеанс передачи данных начинается со специального условия, называемого “Старт”. В исходном состоянии, когда шина свободна, обе линии SDA и SCL подтянуты к высокому логическому уровню, условие “Старт” подразумевает переключение линии SDA с высокого логического уровня на низкий, в то время когда на линии SCL установлен высокий уровень.

Аналогично, сеанс передачи данных завершается специальным условием “Стоп”, это переключение линии SDA с низкого логического уровня на высокий, при высоком уровне на линии SCL. Данные условия генерирует ведущий (микроконтроллер).

Исходя из условий “Старт” и “Стоп”, во время передачи данных линия SDA может переключаться только при низком уровне на линии SCL, то есть установка новых данных на линии SDA возможна только после спада уровня на SCL. В течение импульса тактирования (высокий уровень на SCL), состояние линии SDA не должно меняться, в это время выполняется считывание данных на SDA.

Формат передачи данных

Данные по интерфейсу передаются побайтно, старшим битом вперед, за каждым переданным байтом (8 бит) следует бит подтверждения, устройство (ведущий или ведомый) принявшее байт данных, устанавливает низкий уровень на линии SDA на следующем тактовом импульсе SCL, тем самым подтверждая получение байта. В это время передающее устройство должно опрашивать линию SDA, ожидая ответ об успешном получении байта. Ниже на картинке представлена диаграмма передачи данных по шине I2C:


Сначала передается байт с 7-битным адресом ведомого, значение 8-го бита (R/W) определяет направление передачи данных, нулевое значение соответствует записи данных, то есть передача от ведущего к ведомому. Если бит направления равен 1, то выполняется чтение данных из ведомого.

Ведомый сравнивает переданный адрес со своим и при совпадении откликается, устанавливая низкий уровень на линии SDA (бит подтверждения). Ведущий, получив подтверждение, начинает передавать байты с данными, или принимает их, в зависимости от направления передачи. На следующей картинке более подробно представлены различные варианты передачи данных по шине I2C:


После передачи адреса ведомого, передается адрес регистра, над которым будут производиться операции чтения/записи. Каждое устройство обладает своим набором внутренних регистров, назначение которых указано в технической документации.

Запись одного байта состоит из следующей последовательности: условие “Старт” – адрес ведомого (бит R/W сброшен) – адрес внутреннего регистра ведомого – данные (1 байт) – условие “Стоп”. Запись нескольких байтов практически ничем не отличается, после отправки первого байта данных, передаются остальные байты, сеанс заканчивается условием “Стоп”. При этом данные записываются в регистры последовательно, начиная с заданного адреса, обычно ведомый выполняет автоматический инкремент адреса внутренних регистров.

Для чтения одного байта данных, необходимо сначала передать адрес ведомого и адрес требуемого регистра, при этом бит направления должен быть сброшен на запись, после чего повторно передается условие “Старт”, затем снова адрес ведомого, в этот раз с установленным битом направления на чтение. Далее выполняется прием байта данных от ведомого, для окончания сеанса передачи ведущий не выдает подтверждения, то есть на линии SDA остается высокий уровень на время бита подтверждения, далее следует условие “Стоп”. Чтение нескольких байтов выглядит аналогично, ведущий выдает подтверждение после каждого принятого байта, за исключением последнего байта. Как и в случае записи, ведомый выполняет автоматический инкремент адреса, начиная с заданного.

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

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

Скорость передачи данных

По спецификации интерфейс поддерживает три скоростных режима передачи:

  1. Самый распространенный до 100 Кбит/сек, частота тактирования линии SCL до 100 кГц, длительность высокого и низкого уровней не менее 5 мкс.
  2. Скоростной режим до 400 Кбит/сек, частота тактирования до 400 кГц.
  3. Высокоскоростной режим до 3,4 Мбит/сек, частота тактирования до 3,4 МГц.

Стандартный режим до 100 Кбит/сек поддерживают все устройства, возможность функционирования на больших скоростях необходимо уточнять в технической документации на устройство.

Программная реализация интерфейса I2C

Не у всех микроконтроллеров серии PIC16 имеется встроенный аппаратный модуль I2C, но интерфейс можно реализовать программно и использовать на любом микроконтроллере. Ниже представлен код реализующий функции ведущего применительно к микроконтроллеру PIC16F628A:

#include LIST p=16F628A __CONFIG H"3F10" ;Конфигурация микроконтроллера errorlevel -302 ;не выводить сообщения с ошибкой 302 в листинге scetbit equ 0020h ;вспомогательный регистр счета кол-ва бит perem equ 0021h ;вспомогательный регистр приема/передачи байта adr_i2c equ 0022h ;регистр хранения адреса внутреннего регистра ведомого tmp_i2c equ 0023h ;регистр кол-ва передаваемых байт slave_adr equ 0024h ;регистр хранения адреса ведомого data_i2c equ 0025h ;начальный регистр хранения данных для передачи flag equ 007Fh ;регистр флагов #DEFINE sda PORTB,0 ;линия sda #DEFINE scl PORTB,1 ;линия scl #DEFINE sda_io TRISB,0 ;бит управления направлением линии sda #DEFINE scl_io TRISB,1 ;бит управления направлением линии scl ;flag,4 - флаг направления передачи (0 - чтение, 1 - запись) ;flag,5 - флаг окончания приема данных от ведомого;flag,6 - флаг ошибки передачи по интерфейсу I2C (отсутствие подтверждения от ведомого) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 0000h ;начать выполнение программы с адреса 0000h goto Start ;переход на метку Start ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Основная программа Start movlw b"00000000" ;установка значений выходных защелок порта B movwf PORTB ; movlw b"00000111" ;выключение компараторов movwf CMCON ; bsf STATUS,RP0 ;выбрать 1-й банк movlw b"11111111" ;настройка линий ввода\вывода порта B movwf TRISB ;все линии на вход bcf STATUS,RP0 ;выбрать 0-й банк;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; movlw b"01111110" ;запись адреса ведомого (0111111) в регистр slave_adr movwf slave_adr ; ;пример записи 2-х байт данных (числа 10 и 20) во внутренние регистры;устройства (ведомого) начиная с адреса 0x03, адрес ведомого 0111111 movlw 0x03 ;запись адреса внутреннего регистра ведомого в регистр adr_i2c movwf adr_i2c ;(для примера адрес внутреннего регистра ведомого равен 0x03) movlw .2 ;запись кол-ва передаваемых байт в регистр tmp_i2c movwf tmp_i2c ;(для примера передается 2 байта) movlw data_i2c ;установка начального регистра хранения данных для передачи movlw .10 ;запись числа 10 в первый регистр хранения movwf INDF ; , подготовка следующего регистра хранения movlw .20 ;запись числа 20 во второй регистр хранения movwf INDF ; call write_i2c ;вызов подпрограммы записи по интерфейсу I2C goto dalee_1 ;нет ошибки, переход на метку dalee_1 ................... ................... ;пример чтения 3-х байт данных из внутренних регистров устройства (ведомого) ;начиная с адреса 0x05, адрес ведомого был задан ранее 0111111 movlw 0x05 ;запись адреса внутреннего регистра ведомого в регистр adr_i2c movwf adr_i2c ;(для примера адрес внутреннего регистра ведомого равен 0x05) movlw .3 ;запись кол-ва принимаемых байт в регистр tmp_i2c movwf tmp_i2c ;(для примера принимается 3 байта) call read_i2c ;вызов подпрограммы чтения по интерфейсу I2C btfss flag,6 ;проверка ошибки передачи данных goto obrabotka ;нет ошибки, переход на метку obrabotka ................... ;обработка ошибки................... ; ................... ; obrabotka movlw data_i2c ;установка начального регистра хранения принятых данных movwf FSR ;с помощью косвенной аддресации movf INDF,W ;копирование первого принятого байта в аккумулятор................... ;обработка первого байта................... ; incf FSR,F ;инкремент регистра FSR movf INDF,W ;копирование второго принятого байта в аккумулятор................... ;обработка второго байта................... ; incf FSR,F ;инкремент регистра FSR movf INDF,W ;копирование третьего принятого байта в аккумулятор................... ;обработка третьего байта................... ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;подпрограмма чтения/записи по интерфейсу I2C write_i2c bsf flag,4 ;установка флага направления передачи (для записи) goto i2c_1 ;переход на метку i2c_1 read_i2c bcf flag,4 ;сброс флага направления передачи (для чтения) bcf flag,5 ;сброс флага окончания приема данных i2c_1 bcf flag,6 ;сброс флага ошибки I2C call starti2c ;вызов подпрограммы "Старт" bcf slave_adr,0 ;сброс бита R/W для операции записи goto err_i2c ;ошибка:переход на метку err_i2c movf adr_i2c,W ;адреса регистра ведомого I2C call peredi2c ;вызов подпрограммы передача байта btfsc flag,6 ;проверка флага ошибки movlw .0 ;проверка кол-ва передаваемых байт xorwf tmp_i2c,W ; btfss STATUS,Z ; goto i2c_3 ;кол-во передаваемых байт не равно 0: переход на метку i2c_3 call stopi2c ;кол-во передаваемых байт равно 0: вызов подпрограммы "Стоп" i2c_3 movlw data_i2c ;установка начального регистра хранения данных для передачи movwf FSR ;с помощью косвенной аддресации decf FSR,F ; btfss flag,4 ;проверка флага направления передачи goto rd_i2c ;флаг равен 0: переход на метку rd_i2c (чтение) i2c_2 incf FSR,F ;инкремент регистра FSR, подготовка следующего байта для передачи movf INDF,W ;копирование байта данных для передачи call peredi2c ;вызов подпрограммы передача байта btfsc flag,6 ;проверка флага ошибки goto err_i2c ;ошибка: переход на метку err_i2c decfsz tmp_i2c,F ;декремент счетчика кол-ва передаваемых байт goto i2c_2 ;счетчик не равен 0: переход на метку i2c_2 return ;выход из подпрограммы чтения/записи I2C ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; rd_i2c call starti2c ;вызов подпрограммы "Старт" (Повторный Старт) bsf slave_adr,0 ;установка бита R/W для операции чтения movf slave_adr,W ;копирование адреса ведомого call peredi2c ;вызов подпрограммы передача байта btfsc flag,6 ;проверка флага ошибки goto err_i2c ;ошибка: переход на метку err_i2c rd_i2c_2 decfsz tmp_i2c,F ;декремент счетчика кол-ва передаваемых байт goto rd_i2c_1 ;счетчик не равен 0: переход на метку rd_i2c_1 bsf flag,5 ;установка флага окончания приема данных от ведомого rd_i2c_1 incf FSR,F ;инкремент регистра FSR, подготовка следующего регистра для приема данных call priemi2c ;вызов подпрограммы приема байта movwf INDF ;сохранение принятого байта в регистр INDF btfss flag,5 ;проверка флага окончания приема данных от ведомого goto rd_i2c_2 ;флаг не равен 0: переход на метку rd_i2c_2 call stopi2c ;вызов подпрограммы "Стоп" return ;выход из подпрограммы чтения/записи I2C err_i2c call stopi2c ;вызов подпрограммы "Стоп" return ;выход из подпрограммы чтения/записи I2C ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; starti2c call scl_1 ;отпустить линию scl call sda_0 ;притянуть к 0 линию sda call pausi2c ;вызов подпрограммы паузы call scl_0 ;притянуть к 0 линию scl call pausi2c ;вызов подпрограммы паузы stopi2c call sda_0 ;притянуть к 0 линию sda call pausi2c ;вызов подпрограммы паузы call scl_1 ;отпустить линию scl stpi2c_1 call pausi2c ;вызов подпрограммы паузы btfss scl ;проверка линии SCL на занятость ведомого goto stpi2c_1 ;ведомый занят: переход на метку stpi2c_1 call sda_1 ;отпустить линию sda call pausi2c ;вызов подпрограммы паузы return ;возврат из подпрограммы sda_1 bsf STATUS,RP0 ;перенастройка линии sda на вход bsf sda_io ; bcf STATUS,RP0 ; return ;возврат из подпрограммы sda_0 bcf sda ;перенастройка линии sda на выход bsf STATUS,RP0 ; bcf sda_io ; bcf STATUS,RP0 ; return ;возврат из подпрограммы scl_1 bsf STATUS,RP0 ;перенастройка линии scl на вход bsf scl_io ; bcf STATUS,RP0 ; return ;возврат из подпрограммы scl_0 bcf scl ;перенастройка линии scl на выход bsf STATUS,RP0 ; bcf scl_io ; bcf STATUS,RP0 ; return ;возврат из подпрограммы;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Подпрограмма передачи байта по интерфейсу i2c peredi2c movwf perem ;сохранение передаваемых данных в регистр perem movlw .8 ;запись счетчика бит movwf scetbit ; prd_i2c_1 btfsc perem,7 ;опрос старшего бита передаваемого байта call sda_1 ;отпустить линию sda btfss perem,7 ;опрос старшего бита передаваемого байта call sda_0 ;притянуть к 0 линию sda call scl_1 ;отпустить линию scl prd_i2c_2 call pausi2c ;вызов подпрограммы паузы btfss scl ;проверка линии SCL на занятость ведомого goto prd_i2c_2 ;ведомый занят: переход на метку prd_i2c_2 call scl_0 ;притянуть к 0 линию scl call pausi2c ;вызов подпрограммы паузы rlf perem,F ;смещение содержимого регистра perem, подготовка бита для передачи decfsz scetbit,F ;декремент счетчика бит goto prd_i2c_1 ;счетчик не равен 0: переход на метку prd_i2c_1 call sda_1 ;отпустить линию sda call scl_1 ;отпустить линию scl prd_i2c_3 call pausi2c ;вызов подпрограммы паузы btfss scl ;проверка линии SCL на занятость ведомого goto prd_i2c_3 ;ведомый занят: переход на метку prd_i2c_3 movlw .20 ;запись счетчика времени (100 мкс) для приема подтверждения movwf scetbit ; prd_i2c_4 btfss sda ;опрос линии sda goto prd_i2c_5 ;низкий уровень на sda, подтверждение получено: переход на prd_i2c_5 decfsz scetbit,F ;высокий уровень на sda, декремент счетчика времени goto prd_i2c_4 ;счетчик не равен 0: переход на метку prd_i2c_4 bsf flag,6 ;счетчик равен 0, нет потверждения, устанавливаем флаг ошибки prd_i2c_5 call scl_0 ;притянуть к 0 линию scl call pausi2c ;вызов подпрограммы паузы return ;возврат из подпрограммы передачи байта;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;подпрограмма приема байта по интерфейсу i2c priemi2c movlw .8 ;запись счетчика бит movwf scetbit ; call sda_1 ;отпустить линию sda prm_i2c_1 rlf perem,F ;смещение содержимого регистра perem, подготовка для приема бита call scl_1 ;отпустить линию scl prm_i2c_2 call pausi2c ;вызов подпрограммы паузы btfss scl ;проверка линии SCL на занятость ведомого goto prm_i2c_2 ;ведомый занят: переход на метку prm_i2c_2 btfsc sda ;опрос линии sda bsf perem,0 ;установка 0-го бита регистра perem, принят бит=1 btfss sda ;опрос линии sda bcf perem,0 ;сброс 0-го бита регистра perem, принят бит=0 call scl_0 ;притянуть к 0 линию scl call pausi2c ;вызов подпрограммы паузы decfsz scetbit,F ;декремент счетчика бит goto prm_i2c_1 ;счетчик не равен 0: переход на метку prm_i2c_1 btfss flag,5 ;проверка флага окончания приема данных от ведомого call sda_0 ;флаг равен 0, прием не окончен, притянуть к 0 линию sda (подтверждение) call scl_1 ;отпустить линию scl prm_i2c_3 call pausi2c ;вызов подпрограммы паузы btfss scl ;проверка линии SCL на занятость ведомого goto prm_i2c_3 ;ведомый занят: переход на метку prm_i2c_3 call scl_0 ;притянуть к 0 линию scl call pausi2c ;вызов подпрограммы паузы movf perem,W ;копирование принятого байта от ведомого return ;возврат из подпрограммы;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Подпрограмма паузы 5 машинных тактов pausi2c nop ;пустая команда return ;возврат из подпрограммы паузы;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

#include

CONFIG H"3F10" ;Конфигурация микроконтроллера

errorlevel -302 ;не выводить сообщения с ошибкой 302 в листинге

scetbit equ 0020h ;вспомогательный регистр счета кол-ва бит

perem equ 0021h ;вспомогательный регистр приема/передачи байта

adr_i2c equ 0022h ;регистр хранения адреса внутреннего регистра ведомого

tmp_i2c equ 0023h ;регистр кол-ва передаваемых байт

slave_adr equ 0024h ;регистр хранения адреса ведомого

data_i2c equ 0025h ;начальный регистр хранения данных для передачи

flag equ 007Fh ;регистр флагов

#DEFINE sda PORTB,0 ;линия sda

#DEFINE scl PORTB,1 ;линия scl

#DEFINE sda_io TRISB,0 ;бит управления направлением линии sda

#DEFINE scl_io TRISB,1 ;бит управления направлением линии scl

;flag,4 - флаг направления передачи (0 - чтение, 1 - запись)

;flag,5 - флаг окончания приема данных от ведомого

;flag,6 - флаг ошибки передачи по интерфейсу I2C (отсутствие подтверждения от ведомого)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

org 0000h ;начать выполнение программы с адреса 0000h

goto Start ;переход на метку Start

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;Основная программа

Start movlw b"00000000" ;установка значений выходных защелок порта B

movlw b"00000111" ;выключение компараторов

bsf STATUS,RP0 ;выбрать 1-й банк

movlw b"11111111" ;настройка линий ввода\вывода порта B

movwf TRISB ;все линии на вход

bcf STATUS,RP0 ;выбрать 0-й банк

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

movlw b"01111110" ;запись адреса ведомого (0111111) в регистр slave_adr

movwf slave_adr ;

;пример записи 2-х байт данных (числа 10 и 20) во внутренние регистры

;устройства (ведомого) начиная с адреса 0x03, адрес ведомого 0111111

movlw 0x03 ;запись адреса внутреннего регистра ведомого в регистр adr_i2c

movwf adr_i2c ;(для примера адрес внутреннего регистра ведомого равен 0x03)

movlw .2 ;запись кол-ва передаваемых байт в регистр tmp_i2c

movwf tmp_i2c ;(для примера передается 2 байта)

movlw data_i2c ;установка начального регистра хранения данных для передачи

movwf FSR ;с помощью косвенной аддресации

movlw .10 ;запись числа 10 в первый регистр хранения

incf FSR,F ;инкремент регистра FSR, подготовка следующего регистра хранения

movlw .20 ;запись числа 20 во второй регистр хранения

call write_i2c ;вызов подпрограммы записи по интерфейсу I2C

btfss flag,6 ;проверка ошибки передачи данных

goto dalee_1 ;нет ошибки, переход на метку dalee_1

................... ;обработка ошибки

...................

...................

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;пример чтения 3-х байт данных из внутренних регистров устройства (ведомого)

;начиная с адреса 0x05, адрес ведомого был задан ранее 0111111

movlw 0x05 ;запись адреса внутреннего регистра ведомого в регистр adr_i2c

movwf adr_i2c ;(для примера адрес внутреннего регистра ведомого равен 0x05)

movlw .3 ;запись кол-ва принимаемых байт в регистр tmp_i2c

movwf tmp_i2c ;(для примера принимается 3 байта)

call read_i2c ;вызов подпрограммы чтения по интерфейсу I2C

btfss flag,6 ;проверка ошибки передачи данных

goto obrabotka ;нет ошибки, переход на метку obrabotka

................... ;обработка ошибки

................... ;

................... ;

obrabotka movlw data_i2c ;установка начального регистра хранения принятых данных

movwf FSR ;с помощью косвенной аддресации

movf INDF,W ;копирование первого принятого байта в аккумулятор

................... ;обработка первого байта

................... ;

incf FSR,F ;инкремент регистра FSR

movf INDF,W ;копирование второго принятого байта в аккумулятор

................... ;обработка второго байта

................... ;

incf FSR,F ;инкремент регистра FSR

movf INDF,W ;копирование третьего принятого байта в аккумулятор

................... ;обработка третьего байта

................... ;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;подпрограмма чтения/записи по интерфейсу I2C

write_i2c bsf flag,4 ;установка флага направления передачи (для записи)

goto i2c_1 ;переход на метку i2c_1

read_i2c bcf flag,4 ;сброс флага направления передачи (для чтения)

bcf flag,5 ;сброс флага окончания приема данных

i2c_1 bcf flag,6 ;сброс флага ошибки I2C

call starti2c ;вызов подпрограммы "Старт"

bcf slave_adr,0 ;сброс бита R/W для операции записи

movf slave_adr,W ;копирование адреса ведомого

call peredi2c ;вызов подпрограммы передача байта

btfsc flag,6 ;проверка флага ошибки

goto err_i2c ;ошибка:переход на метку err_i2c

movf adr_i2c,W ;адреса регистра ведомого I2C

call peredi2c ;вызов подпрограммы передача байта

btfsc flag,6 ;проверка флага ошибки

goto err_i2c ;ошибка: переход на метку err_i2c

movlw .0 ;проверка кол-ва передаваемых байт

xorwf tmp_i2c,W ;

btfss STATUS,Z ;

goto i2c_3 ;кол-во передаваемых байт не равно 0: переход на метку i2c_3

call stopi2c ;кол-во передаваемых байт равно 0: вызов подпрограммы "Стоп"

return ;выход из подпрограммы чтения/записи I2C

i2c_3 movlw data_i2c ;установка начального регистра хранения данных для передачи

movwf FSR ;с помощью косвенной аддресации

btfss flag,4 ;проверка флага направления передачи

goto rd_i2c ;флаг равен 0: переход на метку rd_i2c (чтение)

i2c_2 incf FSR,F ;инкремент регистра FSR, подготовка следующего байта для передачи

movf INDF,W ;копирование байта данных для передачи

call peredi2c ;вызов подпрограммы передача байта

btfsc flag,6 ;проверка флага ошибки

goto err_i2c ;ошибка: переход на метку err_i2c

goto i2c_2 ;счетчик не равен 0: переход на метку i2c_2

call stopi2c ;вызов подпрограммы "Стоп"

return ;выход из подпрограммы чтения/записи I2C

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

rd_i2c call starti2c ;вызов подпрограммы "Старт" (Повторный Старт)

bsf slave_adr,0 ;установка бита R/W для операции чтения

movf slave_adr,W ;копирование адреса ведомого

call peredi2c ;вызов подпрограммы передача байта

btfsc flag,6 ;проверка флага ошибки

goto err_i2c ;ошибка: переход на метку err_i2c

goto rd_i2c_1 ;счетчик не равен 0: переход на метку rd_i2c_1

bsf flag,5 ;установка флага окончания приема данных от ведомого

rd_i2c_1 incf FSR,F ;инкремент регистра FSR, подготовка следующего регистра для приема данных

call priemi2c ;вызов подпрограммы приема байта

movwf INDF ;сохранение принятого байта в регистр INDF

btfss flag,5 ;проверка флага окончания приема данных от ведомого

goto rd_i2c_2 ;флаг не равен 0: переход на метку rd_i2c_2

call stopi2c ;вызов подпрограммы "Стоп"

return ;выход из подпрограммы чтения/записи I2C

err_i2c call stopi2c ;вызов подпрограммы "Стоп"

return ;выход из подпрограммы чтения/записи I2C

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

starti2c call scl_1 ;отпустить линию scl

call pausi2c ;вызов подпрограммы паузы

call sda_0 ;притянуть к 0 линию sda

call pausi2c ;вызов подпрограммы паузы

call scl_0 ;притянуть к 0 линию scl

call pausi2c ;вызов подпрограммы паузы

return ;возврат из подпрограммы

stopi2c call sda_0 ;притянуть к 0 линию sda

call pausi2c ;вызов подпрограммы паузы

call scl_1 ;отпустить линию scl

stpi2c_1 call pausi2c ;вызов подпрограммы паузы

btfss scl ;проверка линии SCL на занятость ведомого

goto stpi2c_1 ;ведомый занят: переход на метку stpi2c_1

call sda_1 ;отпустить линию sda

call pausi2c ;вызов подпрограммы паузы

return ;возврат из подпрограммы

sda_1 bsf STATUS,RP0 ;перенастройка линии sda на вход

bcf STATUS,RP0 ;

return ;возврат из подпрограммы

sda_0 bcf sda ;перенастройка линии sda на выход

bsf STATUS,RP0 ;

bcf STATUS,RP0 ;

return ;возврат из подпрограммы

scl_1 bsf STATUS,RP0 ;перенастройка линии scl на вход

LCD дисплей – частый гость в проектах ардуино. Но в сложных схемах у нас может возникнуть проблема недостатка портов Arduino из-за необходимости подключить экран, у которого очень очень много контактов. Выходом в этой ситуации может стать I2C /IIC переходник, который подключает практически стандартный для Arduino экран 1602 к платам Uno, Nano или Mega всего лишь при помощи 4 пинов. В этой статье мы посмотрим, как можно подключить LCD экран с интерфейсом I2C, какие можно использовать библиотеки, напишем короткий скетч-пример и разберем типовые ошибки.

Жидкокристаллический дисплей (Liquid Crystal Display) LCD 1602 является хорошим выбором для вывода строк символов в различных проектах. Он стоит недорого, есть различные модификации с разными цветами подсветки, вы можете легко скачать готовые библиотеки для скетчей Ардуино. Но самым главным недостатком этого экрана является тот факт, что дисплей имеет 16 цифровых выводов, из которых обязательными являются минимум 6. Поэтому использование этого LCD экрана без i2c добавляет серьезные ограничения для плат Arduino Uno или Nano. Если контактов не хватает, то вам придется покупать плату Arduino Mega или же сэкономить контакты, в том числе за счет подключения дисплея через i2c.

Краткое описание пинов LCD 1602

Давайте посмотрим на выводы LCD1602 повнимательней:

Каждый из выводов имеет свое назначение:

  1. Земля GND;
  2. Питание 5 В;
  3. Установка контрастности монитора;
  4. Команда, данные;
  5. Записывание и чтение данных;
  6. Enable;

7-14. Линии данных;

  1. Плюс подсветки;
  2. Минус подсветки.

Технические характеристики дисплея:

  • Символьный тип отображения, есть возможность загрузки символов;
  • Светодиодная подсветка;
  • Контроллер HD44780;
  • Напряжение питания 5В;
  • Формат 16х2 символов;
  • Диапазон рабочих температур от -20С до +70С, диапазон температур хранения от -30С до +80 С;
  • Угол обзора 180 градусов.

Схема подключения LCD к плате Ардуино без i2C

Стандартная схема присоединения монитора напрямую к микроконтроллеру Ардуино без I2C выглядит следующим образом.

Из-за большого количества подключаемых контактов может не хватить места для присоединения нужных элементов. Использование I2C уменьшает количество проводов до 4, а занятых пинов до 2.

Где купить LCD экраны и шилды для ардуино

LCD экран 1602 (и вариант 2004) довольно популярен, поэтому вы без проблем сможете найти его как в отечественных интернет-магазинах, так и на зарубежных площадках. Приведем несколько ссылок на наиболее доступные варианты:

Модуль LCD1602+I2C с синим экраном, совместим с Arduino Простой дисплей LCD1602 (зеленая подсветка) дешевле 80 рублей Большой экран LCD2004 с I2C HD44780 для ардуино (синяя и зеленая подсветка)
Дисплей 1602 с IIC адаптером и синей подсветкой Еще один вариант LCD1602 со впаянным I2C модулем Модуль адаптера Port IIC/I2C/TWI/SPI для экрана 1602, совместим с Ардуино
Дисплей с RGB-подсветкой! LCD 16×2 + keypad +Buzzer Shield for Arduino Шилд для Ардуино с кнопками и экраном LCD1602 LCD 1602 LCD дисплей для 3D принтера (Smart Controller for RAMPS 1.4, Text LCD 20×4), модулем кардридера SD и MicroSD-

Описание протокола I2C

Прежде чем обсуждать подключение дисплея к ардуино через i2c-переходник, давайте вкратце поговорим о самом протоколе i2C.

I2C / IIC (Inter-Integrated Circuit) – это протокол, изначально создававшийся для связи интегральных микросхем внутри электронного устройства. Разработка принадлежит фирме Philips. В основе i2c протокола является использование 8-битной шины, которая нужна для связи блоков в управляющей электронике, и системе адресации, благодаря которой можно общаться по одним и тем же проводам с несколькими устройствами. Мы просто передаем данные то одному, то другому устройству, добавляя к пакетам данных идентификатор нужного элемента.

Самая простая схема I2C может содержать одно ведущее устройство (чаще всего это микроконтроллер Ардуино) и несколько ведомых (например, дисплей LCD). Каждое устройство имеет адрес в диапазоне от 7 до 127. Двух устройств с одинаковым адресом в одной схеме быть не должно.

Плата Arduino поддерживает i2c на аппаратном уровне. Вы можете использовать пины A4 и A5 для подключения устройств по данному протоколу.

В работе I2C можно выделить несколько преимуществ:

  • Для работы требуется всего 2 линии – SDA (линия данных) и SCL (линия синхронизации).
  • Подключение большого количества ведущих приборов.
  • Уменьшение времени разработки.
  • Для управления всем набором устройств требуется только один микроконтроллер.
  • Возможное число подключаемых микросхем к одной шине ограничивается только предельной емкостью.
  • Высокая степень сохранности данных из-за специального фильтра подавляющего всплески, встроенного в схемы.
  • Простая процедура диагностики возникающих сбоев, быстрая отладка неисправностей.
  • Шина уже интегрирована в саму Arduino, поэтому не нужно разрабатывать дополнительно шинный интерфейс.

Недостатки:

  • Существует емкостное ограничение на линии – 400 пФ.
  • Трудное программирование контроллера I2C, если на шине имеется несколько различных устройств.
  • При большом количестве устройств возникает трудности локализации сбоя, если одно из них ошибочно устанавливает состояние низкого уровня.

Модуль i2c для LCD 1602 Arduino

Самый быстрый и удобный способ использования i2c дисплея в ардуино – это покупка готового экрана со встроенной поддержкой протокола. Но таких экранов не очень много истоят они не дешево. А вот разнообразных стандартных экранов выпущено уже огромное количество. Поэтому самым доступным и популярным сегодня вариантом является покупка и использование отдельного I2C модуля – переходника, который выглядит вот так:

С одной стороны модуля мы видим выводы i2c – земля, питание и 2 для передачи данных. С другой переходника видим разъемы внешнего питания. И, естественно, на плате есть множество ножек, с помощью которых модуль припаивается к стандартным выводам экрана.


Для подключения к плате ардуино используются i2c выходы. Если нужно, подключаем внешнее питание для подстветки. С помощью встроенного подстроечного резистора мы можем настроить настраиваемые значения контрастности J

На рынке можно встретить LCD 1602 модули с уже припаянными переходниками, их использование максимально упощено. Если вы купили отдельный переходник, нужно будет предварительно припаять его к модулю.

Подключение ЖК экрана к Ардуино по I2C

Для подключения необходимы сама плата Ардуино, дисплей, макетная плата, соединительные провода и потенциометр.

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


Жидкокристаллический монитор с поддержкой i2c подключается к плате при помощи четырех проводов – два провода для данных, два провода для питания.

  • Вывод GND подключается к GND на плате.
  • Вывод VCC – на 5V.
  • SCL подключается к пину A5.
  • SDA подключается к пину A.

И это все! Никаких паутин проводов, в которых очень легко запутаться. При этом всю сложность реализации i2C протокола мы можем просто доверить библиотекам.

Библиотеки для работы с i2c LCD дисплеем

Для взаимодействие Arduino c LCD 1602 по шине I2C вам потребуются как минимум две библиотеки:

  • Библиотека Wire.h для работы с I2C уже имеется в стандартной программе Arduino IDE.
  • Библиотека LiquidCrystal_I2C.h, которая включает в себя большое разнообразие команд для управления монитором по шине I2C и позволяет сделать скетч проще и короче. Нужно дополнительно установить библиотеку После подключения дисплея нужно дополнительно установить библиотеку LiquidCrystal_I2C.h

После подключения к скетчу всех необходимых библиотек мы создаем объект и можем использовать все его функции. Для тестирования давайте загрузим следующий стандартный скетч из примера.

#include #include // Подключение библиотеки //#include // Подключение альтернативной библиотеки LiquidCrystal_I2C lcd(0x27,16,2); // Указываем I2C адрес (наиболее распространенное значение), а также параметры экрана (в случае LCD 1602 - 2 строки по 16 символов в каждой //LiquidCrystal_PCF8574 lcd(0x27); // Вариант для библиотеки PCF8574 void setup() { lcd.init(); // Инициализация дисплея lcd.backlight(); // Подключение подсветки lcd.setCursor(0,0); // Установка курсора в начало первой строки lcd.print("Hello"); // Набор текста на первой строке lcd.setCursor(0,1); // Установка курсора в начало второй строки lcd.print("ArduinoMaster"); // Набор текста на второй строке } void loop() { }

Описание функций и методов библиотеки LiquidCrystal_I2C:

  • home() и clear() – первая функция позволяет вернуть курсор в начало экрана, вторая тоже, но при этом удаляет все, что было на мониторе до этого.
  • write(ch) – позволяет вывести одиночный символ ch на экран.
  • cursor() и noCursor() – показывает/скрывает курсор на экране.
  • blink() и noBlink() – курсор мигает/не мигает (если до этого было включено его отображение).
  • display() и noDisplay() – позволяет подключить/отключить дисплей.
  • scrollDisplayLeft() и scrollDisplayRight() – прокручивает экран на один знак влево/вправо.
  • autoscroll() и noAutoscroll() – позволяет включить/выключить режим автопрокручивания. В этом режиме каждый новый символ записывается в одном и том же месте, вытесняя ранее написанное на экране.
  • leftToRight() и rightToLeft() – Установка направление выводимого текста – слева направо или справа налево.
  • createChar(ch, bitmap) – создает символ с кодом ch (0 – 7), используя массив битовых масок bitmap для создания черных и белых точек.

Альтернативная библиотека для работы с i2c дисплеем

В некоторых случаях при использовании указанной библиотеки с устройствами, оснащенными контроллерами PCF8574 могут возникать ошибки. В этом случае в качестве альтернативы можно предложить библиотеку LiquidCrystal_PCF8574.h. Она расширяет LiquidCrystal_I2C, поэтому проблем с ее использованием быть не должно.

Проблемы подключения i2c lcd дисплея

Если после загрузки скетча у вас не появилось никакой надписи на дисплее, попробуйте выполнить следующие действия.

Во-первых, можно увеличить или уменьшить контрастность монитора. Часто символы просто не видны из-за режима контрастности и подсветки.

Если это не помогло, то проверьте правильность подключения контактов, подключено ли питание подсветки. Если вы использовали отдельный i2c переходник, то проверьте еще раз качество пайки контактов.

Другой часто встречающейся причиной отсутствия текста на экране может стать неправильный i2c адрес. Попробуйте сперва поменять в скетче адрес устройства с 0x27 0x20 или на 0x3F. У разных производителей могут быть зашиты разные адреса по умолчанию. Если и это не помогло, можете запустить скетч i2c сканера, который просматривает все подключенные устройства и определяет их адрес методом перебора. Пример скетча i2c сканера .

Если экран все еще останется нерабочим, попробуйте отпаять переходник и подключить LCD обычным образом.

Заключение

В этой статье мы рассмотрели основные вопросы использования LCD экрана в сложных проектах ардуино, когда нам нужно экономить свободные пины на плате. Простой и недорогой переходник i2c позволит подключить LCD экран 1602, занимая всего 2 аналоговых пина. Во многих ситуациях это может быть очень важным. Плата за удобство – необходимость в использовании дополнительного модуля – конвертера и библиотеки. На наш взгляд, совсем не высокая цена за удобство и мы крайне рекомендуем использовать эту возможность в проектах.

© 2024 baraxlo2020.ru -- Немного о компьютере и современных гаджетах