Интерфейс 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.
Скорость передачи данных
По спецификации интерфейс поддерживает три скоростных режима передачи:
- Самый распространенный до 100 Кбит/сек, частота тактирования линии SCL до 100 кГц, длительность высокого и низкого уровней не менее 5 мкс.
- Скоростной режим до 400 Кбит/сек, частота тактирования до 400 кГц.
- Высокоскоростной режим до 3,4 Мбит/сек, частота тактирования до 3,4 МГц.
Стандартный режим до 100 Кбит/сек поддерживают все устройства, возможность функционирования на больших скоростях необходимо уточнять в технической документации на устройство.
Программная реализация интерфейса I2C
Не у всех микроконтроллеров серии PIC16 имеется встроенный аппаратный модуль I2C, но интерфейс можно реализовать программно и использовать на любом микроконтроллере. Ниже представлен код реализующий функции ведущего применительно к микроконтроллеру PIC16F628A:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #include <P16F628A.INC> 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 ;установка начального регистра хранения данных для передачи movwf FSR ;с помощью косвенной аддресации movlw .10 ;запись числа 10 в первый регистр хранения movwf INDF ; incf FSR,F ;инкремент регистра FSR, подготовка следующего регистра хранения movlw .20 ;запись числа 20 во второй регистр хранения movwf INDF ; 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 ;с помощью косвенной аддресации 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 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 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 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 на вход 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 ;возврат из подпрограммы паузы ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
Для примера в коде под линии sda и scl выбраны линии микроконтроллера RB0 и RB1, также битам направления линий присваиваются названия sda_io и scl_io. В начале основной программы линии настраиваются на вход. Для записи данных используется подпрограмма write_i2c, для чтения read_i2c. Оперирование с байтами данных выполняется через косвенную адресацию, data_i2c это начальный регистр хранения данных.
Перед операциями записи/чтения необходимо записать адрес ведомого в регистр slave_adr, в коде для примера приведен 7-битный адрес 0111111, значение 8-го бита может быть любым.
Для операции записи необходимо задать адрес внутреннего регистра ведомого, в который или начиная с которого необходимо записать данные, то же относится и к операции чтения. Адрес внутреннего регистра ведомого записывается в регистр adr_i2c. Далее нужно задать кол-во передаваемых байтов в регистре tmp_i2c. Перед записью с помощью косвенной адресации необходимо записать в регистры хранения байты, которые нужно передать, в коде передаются числа 10 и 20, после чего вызывается подпрограмма записи write_i2c. После возврата нужно проверить флаг ошибки (flag,6), чтобы удостовериться в успешной передаче данных.
Ошибка может возникнуть в случае, если ведомый не выдаст подтверждение в течение 100 мкс. Данное время можно подкорректировать, для этого необходимо записать другое число в регистр scetbit в подпрограмме peredi2c, в коде записывается число 20.
Для чтения, также задается адрес внутреннего регистра ведомого и количество байт, которое нужно прочитать, далее вызывается подпрограмма read_i2c и проверяется флаг ошибки. Полученные данные находятся в регистрах хранения, начиная с адреса data_i2c.
Приведенный код рассчитан на частоту тактового генератора в 4 МГц, для скорости передачи данных до 100 Кбит/сек. При использовании других частот и скоростей, необходимо корректировать подпрограмму паузы pausi2c, в коде пауза составляет 5 мкс.
Используя программный I2C, я подключал датчик давления BMP180, часы реального времени DS3231, модуль АЦП ADS1115, данный интерфейс также поддерживает акселерометр ADXL345.
Для подключения LCD 1602 по I2C, я также применил вышеприведенный код.
Сергей Казанский
21 Май 2018Добрый день!
В подпрограмме передачи байта на шину есть маленькая ошибочка, могущая привести к потере бит на скоростных ядрах. Дело в том, что нет защитной паузы в 1 мкс между отправкой бита и импульсом тактирования. Нужно туда хотя-бы пару nop-ов вставить.
Константин
7 Июл 2020Приветствую.
прошу просто маленький намек. Как подключить к шине I2C электромагнитный датчик толщиномера. Датчик представляет собой трансформаторный преоразователь. (по сути трансформатор, имеющий первичную и вторичную обмотки). На первичную катушку подается сигнал. А со вторичной обмотки трансформатора снимается сигнал .
В схеме задействована микросхемка памяти 24LC32AI