Подпрограмма I2C_IN, код которой приведен в Программе 12.18, загружает принимаемое значение в регистр DATA_IN посредством восьми операций сдвига через флаг переноса; значение флага соответствует состоянию вывода SDA. Одновременно на линии тактового сигнала SCL формируются импульсы в соответствии со спецификацией шины I2С, как и в подпрограмме I2C_OUT из Программы 12.9. В соответствии с этим протоколом ведущий приказывает ведомому остановить посылку данных путем выдачи на линию SDA ВЫСОКОГО уровня во время 9-го тактового импульса (см. Рис. 12.13). Наличие во время этого временного интервала НИЗКОГО уровня на линии данных называется АСК (подтверждение), а наличие ВЫСОКОГО уровня — NACK (нет подтверждения). Наша подпрограмма может генерировать оба сигнала, в зависимости от значения переменной ACKNO, которое задается вызывающей подпрограммой. Если при вызове подпрограммы содержимое регистра ACKNO равно нулю, то после приема 8-го бита данных отсылается АСК. Соответственно, любое ненулевое значение регистра ACKNO приведет к отсылке ведомому сигнала NACK. После получения этого сигнала ведомый прекратит передачу и начнет отслеживать появление на шине состояний СТАРТ/СТОП.
; **************
; * ФУНКЦИЯ: Принимает байт от ведомого, отсылая в ответ АСК или NACK *
; * ВХОД: ACKNO = 00 для отсылки АСК, ИНАЧЕ NACK *
; * РЕСУРСЫ: п/п START и STOP, макрокоманда Delay_600 *
; * ВЫХОД: Байт данных, посланный ведомым — в DATA_IN, ведомому отослан АСК или NACK, на SCL — НИЗКИЙ уровень *
; **************
I2C_IN bcf INDF,SCL; Гарантируем наличие НИЗКОГО уровня на SCL
movlw 8; Счетчик цикла = 8
movwf COUNT
I2C_IN_LOOP
bcf INDF,SCL; Формируем на тактовой линии
Delay_600; отрицательный импульс
Delay_600
bsf INDF,SCL; минимальной длительности
bcf STATUS,С; Сбросим флаг переноса
btfsc INDF,SDA; Проверяем значение принятого бита
bsf STATUS,С; ЕСЛИ 1, ТО устанавливаем флаг С
rlf DATA_IN,f; и вдвигаем его в регистр
decfsz COUNT,f; Декрементируем счетчик цикла
goto I2C_IN_LOOP; и повторяем восемь раз
; Теперь посмотрим, что надо отослать (АСК или NACK)
bcf INDF,SCL; Выставим на SCL НИЗКИЙ уровень
bsf INDF,SDA; Высвободим линию данных (NACK)
movf ACKNO,f; Проверим регистр
btfsc STATUS,Z; ЕСЛИ не равно 0, ТО ничего не делаем
bcf INDF,SDA; ИНАЧЕ выставляем на линию данных НИЗКИЙ уровень (АСК)
Delay_600; Удерживает на тактовой линии
Delay_600; НИЗКИЙ уровень
bsf INDF,SCL; Теперь выставляем ВЫСОКИЙ уровень
Delay_600
bcf INDF,SCL; На линии SCL оставляем НИЗКИЙ уровень
return
Во многих микроконтроллерных устройствах требуется сохранять данные в энергонезависимой памяти для того, чтобы считывать их после повторного включения. В качестве примера можно указать счетчик суммарного пробега, пройденного автомобилем, значение которого должно сохраняться независимо от состояния аккумулятора. Обычно такого рода данные хранятся в EEPROM-памяти, которая была подробно описана на стр. 43. Хотя во многих микроконтроллерах PIC имеется встроенный модуль EEPROM, о котором мы поговорим в главе 15, его емкость ограничена в лучшем случае 256 байтами[165]. При больших объемах необходимо задействовать внешние микросхемы EEPROM. Большинство таких микросхем используют интерфейс SPI или I2С, как, например, микросхема 24LCXXX, применяющаяся в схеме на Рис. 12.26. Микросхемы EEPROM с последовательным интерфейсом серии 24LCXXX, выпускающиеся в 8-выводных корпусах, имеют емкость от 1 Кбит (24LC01B) до 512 Кбит (24LC512), организованных побайтно; т. е. от 128 байт до 64 Кбайт.
Рис. 12.26.