С номиналами от 10 Ом до 1 МОм);

  • 2 резистора по 4,7 кОм (из того же набора);
  • соединительные провода (например, вот хороший набор);
  • компьютер с Arduino IDE.
  • 1 Описание интерфейса I2C

    Последовательный протокол обмена данными IIC (также называемый I2C - Inter-Integrated Circuits, межмикросхемное соединение) использует для передачи данных две двунаправленные линии связи, которые называются шина последовательных данных SDA (Serial Data) и шина тактирования SCL (Serial Clock) . Также имеются две линии для питания. Шины SDA и SCL подтягиваются к шине питания через резисторы.

    В сети есть хотя бы одно ведущее устройство (Master) , которое инициализирует передачу данных и генерирует сигналы синхронизации. В сети также есть ведомые устройства (Slave) , которые передают данные по запросу ведущего. У каждого ведомого устройства есть уникальный адрес, по которому ведущий и обращается к нему. Адрес устройства указывается в паспорте (datasheet). К одной шине I2C может быть подключено до 127 устройств, в том числе несколько ведущих. К шине можно подключать устройства в процессе работы, т.е. она поддерживает «горячее подключение».

    Давайте рассмотрим временную диаграмму обмена по протоколу I2C. Есть несколько различающихся вариантов, рассмотрим один из распространённых. Воспользуемся логическим анализатором, подключённым к шинам SCL и SDA.

    Мастер инициирует обмен. Для этого он начинает генерировать тактовые импульсы и посылает их по линии SCL пачкой из 9-ти штук. Одновременно на линии данных SDA он выставляет адрес устройства , с которым необходимо установить связь, которые тактируются первыми 7-ми тактовыми импульсами (отсюда ограничение на диапазон адресов: 2 7 = 128 минус нулевой адрес). Следующий бит посылки - это код операции (чтение или запись) и ещё один бит - бит подтверждения (ACK), что ведомое устройство приняло запрос. Если бит подтверждения не пришёл, на этом обмен заканчивается. Или мастер продолжает посылать повторные запросы.

    Это проиллюстрировано на рисунке ниже.. В первом случае, для примера, отключим ведомое устройство от шины. Видно, что мастер пытается установить связь с устройством с адресом 0x27, но не получает подтверждения (NAK). Обмен заканчивается.


    Теперь подключим к шине I2C ведомое устройство и повторим операцию. Ситуация изменилась. На первый пакет с адресом пришло подтверждение (ACK) от ведомого. Обмен продолжился. Информация передаётся также 9-битовыми посылками, но теперь 8 битов занимают данные и 1 бит - бит подтверждения получения ведомым каждого байта данных. Если в какой-то момент связь оборвётся и бит подтверждения не придёт, мастер прекратит передачу.

    2 Реализация I2C в Arduino

    Arduino использует для работы по интерфейсу I2C два порта. Например, в Arduino UNO и Arduino Nano аналоговый порт A4 соответствует SDA, аналоговый порт A5 соответствует SCL.


    Для других моделей плат соответствие выводов такое:

    3 Библиотека "Wire" для работы с IIC

    Для облегчения обмена данными с устройствами по шине I2C для Arduino написана стандартная библиотека Wire . Она имеет следующие функции:

    Функция Назначение
    begin(address) инициализация библиотеки и подключение к шине I2C; если не указан адрес, то присоединённое устройство считается ведущим; используется 7-битная адресация;
    requestFrom() используется ведущим устройством для запроса определённого количества байтов от ведомого;
    beginTransmission(address) начало передачи данных к ведомому устройству по определённому адресу;
    endTransmission() прекращение передачи данных ведомому;
    write() запись данных от ведомого в ответ на запрос;
    available() возвращает количество байт информации, доступных для приёма от ведомого;
    read() чтение байта, переданного от ведомого ведущему или от ведущего ведомому;
    onReceive() указывает на функцию, которая должна быть вызвана, когда ведомое устройство получит передачу от ведущего;
    onRequest() указывает на функцию, которая должна быть вызвана, когда ведущее устройство получит передачу от ведомого.

    4 Подключение I2C устройства к Arduino

    Давайте посмотрим, как работать с шиной I2C с помощью Arduino.

    Сначала соберём схему, как на рисунке. Будем управлять яркостью светодиода, используя цифровой 64-позиционный потенциометр AD5171 (см. техническое описание), который подключается к шине I2C. Адрес, по которому мы будем обращаться к потенциометру - 0x2c (44 в десятичной системе).


    5 Управление устройством по шине IIC

    Рассмотрим диаграммы информационного обмена с цифровым потенциометром AD5171, представленные в техническом описании:


    Нас тут интересует диаграмма записи данных в регистр RDAC . Этот регистр используется для управления сопротивлением потенциометра.

    Откроем из примеров библиотеки "Wire" скетч: Файл Образцы Wire digital_potentiometer . Загрузим его в память Arduino.

    #include // подключаем библиотеку "Wire" byte val = 0; // значение для передачи потенциометру void setup() { Wire.begin(); // подключаемся к шине I2C как мастер } void loop() { Wire.beginTransmission(44); // начинаем обмен с устройством с I2C адресом "44" (0x2C) Wire.write(byte(0x00)); // посылаем инструкцию записи в регистр RDAC Wire.write(val); // задаём положение 64-позиционного потенциометра Wire.endTransmission(); // завершаем I2C передачу val++; // инкрементируем val на 1 if (val == 63) { // по достижении максимума потенциометра val = 0; // сбрасываем val } delay(500); }

    После включения вы видите, как яркость светодиода циклически нарастает, а потом гаснет. При этом мы управляем потенциометром с помощью Arduino по шине I2C.

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

    SDA - линия данных
    SCL - линия сихронизации
    Обратите внимание, что к линиям SDA и SCL можно подключить несколько ведомых устройств. SDA и SCL являются двунаправленными линиями и подключаются к источнику питания через оттягивающий резистор. В большинстве случаев ведущим устройством выступает микроконтроллер, а ведомыми могут быть часы реального времени и датчики температуры, например, DS1307 и DS1624 от www.maxim-ic.com.

    Сигналы START и STOP

    Данные могут передаваться только после создания стартого условия. Переход линии SDA из ВЫСОКОГО состояния в НИЗКОЕ, в то время как SCL находится в ВЫСОКОМ состоянии, означает START. После каждой передачи данных выполняется условие остановки. Переход линии SDA из НИЗКОГО состояния в ВЫСОКОЕ при SCL в ВЫСОКОМ состоянии означает STOP.


    Длина данных, передаваемых по линии SDA, всегда равна 8 битам (включая команды чтения и записи). Каждый байт должен оканчиваться битом подтверждения. Старший бит MSB передается первым. Если происходит команда записи, то R/W бит = 0, и если чтения - R/W бит = 1. Подтверждение при передаче данных обязательно. Приемник должен удерживать линию данных в течение подверждения в стабильно Низком состоянии. Приёмник, которому были адресованы данные, обязан генерировать подтверждение после каждого принятого байта, исключая те случаи, когда посылка начинается с адреса CBUS. В том случае, когда ведомый-приёмник не может подтвердить свой адрес, линия данных должна быть оставлена в ВЫСОКОМ состоянии. После этого ведущий может выдать сигнал СТОП для прерывания пересылки данных. Ведущий может в любое время остановить передачу данных.
    Допустим, у нас есть ведомое устройство с адресом 1101000, и ведущий хочет, чтобы он выступал в роле приемника.


    На рисунке можно увидеть, что ведомый всегда порождает условия старта и посылает ведущему R/W бит, равный 0. После этого ведущий может начать передачу данных или остановить ее условием СТОП.


    На рисунке показано все то же самое, только R/W бит равен 1. Ведомый может начать передачу данных после потверждения от ведущего, он также может прекратить прием данных, создав условие СТОП.
    Пример.
    Этот пример показывает, как настроить чтение с датчика температуры DS1624.
    Схема подключения DS1624.


    Код программы представлен ниже.

    $regfile = "m88def. dat" ;Define the chip you use
    $crystal = 8000000 ;Define speed
    $baud = 19200 ;Define UART BAUD rate
    ;Declare RAM for temperature storage
    Dim I2ctemp As Byte ;Storage for the temperature
    ;Configure pins we want to use for the I²C bus
    Config Scl = Portd. 1 ;Is serial clock SCL
    Config Sda = Portd. 3 ;Is serial data SDA
    ;Declare constants – I2C chip addresses
    Const Ds1624wr = &B10010000 ;DS1624 Sensor write
    Const Ds1624rd = &B10010001 ;DS1624 Sensor read
    ;This section initializes the DS1624
    I2cstart ;Sends start condition
    I2cwbyte Ds1624wr ;Sends the address
    ;byte with r/w 0
    ;Access the CONFIG register (&HAC address byte)
    I2cwbyte &HAC
    ;Set continuous conversion (&H00 command byte)
    I2cwbyte &H00
    I2cstop ;Sends stop condition
    Waitms 25 ;We have to wait some time after a stop
    I2cstart
    I2cwbyte Ds1624wr
    ;Start conversion (&HEE command byte)
    I2cwbyte &HEE
    I2cstop
    Waitms 25
    ;End of initialization
    Print ;Print empty line
    Do
    ;Get the current temperature
    I2cstart
    I2cwbyte Ds1624wr
    I2cwbyte &HAA ;Read temperature (&HAA command byte)
    I2cstart
    I2cwbyte Ds1624rd ;The chip will give register contents
    ;Temperature is stored as 12, 5 but the , 5 first
    I2crbyte I2ctemp
    ;So you"ll have to read twice… first the , 5
    I2crbyte I2ctemp , Nack
    ;And then the 12… we don"t store the , 5
    I2cstop
    ;That"s why we read twice.
    ;We give NACK if the last byte is read
    ;Finally we print
    Print "Temperature: " ; Str(i2ctemp) ; " degrees" ; Chr(13);
    Waitms 25
    Loop
    End

    Отметим, что используемые команды в этом примере можно найти в документации к DS1624.
    Программа написана в среде Bascom AVR IDE, в которой можно разрабатывать программы для микроконтроллеров на языке высокого уровня Бейсик (Basic). Bascom AVR имеет встроенный компилятор, который преобразует программы, написанные на языке Basic, в шестнадцатеричный код, который, в свою очередь, можно запрограммировать (прошить) в микроконтроллер.
    Краткий обзор команд.
    Config Sda = Portx. x
    Настраивает контактный порт для использования в качестве последовательной передачи данных SDA.
    Config Scl = Portx. x
    Настраивает контактный порт для использования в качестве линии SCL.
    I2cstart
    Отправляет условие СТАРТ.
    I2cstop
    Отправляет условие СТОП.
    I2cwbyte
    Записывает один байт в I2C.
    I2crbyte
    Считывает один байт из I2C.
    I2csend
    Записывает количество байт, I2C.
    I2creceive
    Читает количество байтов от I2C.

    Практика
    Ниже показано, как реализовать I2C. В схеме использeтся контроллер Mega88 как мастер.

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

    • определенный «умный» узел управления, который в преимущественном большинстве случаев представляет собой однокристалльную микроЭВМ;
    • узлы общего назначения наподобие буферов ЖК, ОЗУ, портов ввода/вывода, ЭСПЗУ или же специализированные преобразователи данных;
    • специфические узлы, включающие в себя схемы цифровой настройки и обрабатывания сигналов для видео- и радиосистем.

    Как оптимизировать их применение?

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

    На сегодняшний день ассортимент указанного производителя включает в себя более 150 КМОП, а также биполярных устройств, совместимых с I2C и предназначенных для ведения работы в любых перечисленных категориях. При этом стоит отметить, что интерфейс I2C является изначально встроенным во все совместимые устройства, за счет чего они и могут безо всяких сложностей поддерживать связь между собой при использовании специальной шины. За счет применения такого конструкторского решения получилось решить достаточно большое количество проблем сопряжения различного оборудования, что является довольно характерным для сферы разработки цифровых систем.

    Основные преимущества

    Даже если посмотреть кратко SPI, I2C, можно выделить следующие преимущества последнего:

    • Для работы нужно всего две линии - синхронизации и данных. Любое устройство, которое подключается к такой шине, в дальнейшем может программно адресоваться по абсолютно уникальному адресу. В любой момент существует простое отношение, позволяющее ведущим работать в качестве ведущего-передатчика или ведущего-приемника.
    • Данная шина предусматривает возможность иметь сразу несколько ведущих, предоставляя все необходимые средства для определения коллизий, а также арбитраж, позволяющий предотвратить повреждение данных в том случае, если два или большее количество ведущих начинает одновременно передавать информацию. В стандартном режиме предусматривается только передача последовательных восьмибитных данных при скорости не более 100 кбит/с, а в быстром режиме этот порог может быть увеличен в четыре раза.
    • В микросхемах используется специальный встроенный фильтр, который достаточно эффективно подавляет всплески и обеспечивает максимальную целостность данных.
    • Предельно возможное количество микросхем, которые могут быть подсоединены к одной шине, ограничивается только ее предельно возможной емкостью, составляющей 400 пФ.

    Преимущества для конструкторов

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

    Есть масса преимуществ, которые выделяют интерфейс I2C. Описание, в частности, позволяет увидеть следующие достоинства для конструкторов:

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

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

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

    Преимущества для технологов

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

    • Стандартная двухпроводная последовательная шина с таким интерфейсом позволяет минимизировать соединения между микросхемами, то есть в них присутствует меньше контактов и требуется меньшее количество дорожек, благодаря чему печатные платы становятся не такими дорогими и имеют гораздо меньшие габариты.
    • Полностью интегрированный I2C интерфейс LCD1602 или какой-то другой вариант полностью устраняет необходимость в использовании дешифраторов адреса, а также другой внешней мелкой логике.
    • Предусматривается возможность использования одновременно нескольких ведущих на такой шине, благодаря чему существенно ускоряется тестирование и последующая настройка оборудования, так как шина может быть подключена к компьютеру сборочной линии.
    • Доступность совместимых с этим интерфейсом микросхем в VSO, SO и специализированном DIL-корпусе позволяет существенно снизить требования к размеру устройства.

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

    Дальнейшая модернизация оборудования и расширение его функций могут осуществляться посредством стандартного подключения к шине соответствующей микросхемы, использующей 2C интерфейс Arduino или какой-нибудь другой из доступного перечня. Если требуется обеспечение большей ПЗУ, то в таком случае достаточно будет только выбрать другой микроконтроллер, имеющий увеличенный объем ПЗУ. Так как обновленные микросхемы при необходимости способны полностью заместить старые, можно запросто добавлять новые свойства в оборудование или повышать его общую производительность посредством обычного отсоединения уже устаревших микросхем и дальнейшей замены их на более новое оборудование.

    ACCESS.bus

    За счет того, что шина имеет двухпроводную природу, а также возможность программной адресации, для ACCESS.bus одной из наиболее идеальных платформ является именно I2C интерфейс. Спецификация (описание на русском представлено в статье) данного устройства делает его гораздо более дешевой альтернативой активно использующемуся ранее интерфейсу RS-232C для подсоединения различной периферии к компьютерам, используя стандартный четырехконтактный коннектор.

    Введение в спецификацию

    Для современных приложений 8-битного управления, в которых используются микроконтроллеры, предусматривается возможность установки некоторых конструкторских критериев:

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

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

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

    Основная концепция

    Шина I2C может поддерживать любые использующиеся технологии изготовления микросхем. Интерфейс I2C LabVIEW и другие аналогичные ему предусматривают использования двух линий для переноса информации - данных и синхронизации. Любое устройство, подключенное таким образом, распознается за счет уникального адреса вне зависимости от того, идет ли речь о ЖКИ-буфере, микроконтроллере, памяти или интерфейсе клавиатуры, и при этом может работать в качестве приемника или передатчика в зависимости от того, для чего конкретно предназначается данное оборудование.

    В преимущественном большинстве случаев ЖКИ-буфер представляет собой стандартный приемник, а память может не только принимать, но и передавать различные данные. Помимо всего прочего, по процессу перемещения информации приборы можно классифицировать как ведомые и ведущие.

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

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

    Генерация синхросигнала представляет собой обязанность ведущего, и каждый из них вырабатывает собственный сигнал в процессе пересылки данных, и в дальнейшем он может изменяться только в том случае, если его «вытягивает» медленное ведомое устройство или другое ведущее при возникновении столкновения.

    Общие параметры

    Как SCL, так и SDA представляют собой двунаправленные линии, которые подключаются к положительному источнику питания при помощи подтягивающего резистора. Когда шина оказывается абсолютно свободной, каждая линия пребывает в высоком положении. Выходные каскады устройств, которые подключены к шине, должны быть с открытым стоком или открытым коллектором, чтобы могла обеспечиваться функция монтажного И. Информация через I2C интерфейс может передаваться при скорости не более 400 кбит/с в быстром режиме, в то время как в стандартном скорость не превышает 100 кбит/с. Общее же количество устройств, которые могут быть одновременно подключены к шине, зависит только от одного параметра. Это емкость линии, составляющая не более 400 пф.

    Подтверждение

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

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

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

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

    Чтобы проверить работоспособность оборудования, можно попробовать ввести стандартные примеры скетчей для интерфейса I2C в Arduino, как на фото выше.

    Арбитраж

    Ведущим может начинаться пересылка информации только после полного освобождения шины, но при этом два и более ведущих могут провести генерирование сигнала о старте при времени минимального удерживания. Это в конечном итоге приводит к определенному сигналу "Старт" на шине.

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

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

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

    Интерфейс 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 на вход