Не секрет, что реализация ведомого I2C "на борту" МК - занятие платное, а халявы хочется... 
Штука временами полезная при совместной работе двух и более МК.
Есть, разумеется, UART, SPI и пр. интерфейсы и протоколы, но сейчас не об них.
Признаюсь, уже не первый год пытаюсь "разгрызть" этот вопрос, этак года с 2020-го... 
Не на постоянной основе, с перерывами, один раз больше двух лет...
Маленький отчет о проделанном... 
Использую и сторонние решения...
Вот это BASCOM hardware slave TWI (i2c) чуть помогло...
Приведенный пример работает в Proteus, но только в оригинальном виде...
Использование "нестандартной" тактовой частоты не позволяет опробовать "в железе".
Изменение даже на 100 Гц приводит к сбою или к "каше" в данных...
Не взлетело... Но пошел по этому же пути... 
В оригинальных подходах авторов Bascom используются прерывания и таймер.
Я пошел по пути использования только прерывания.
Да, это ограничивает круг используемых МК только имеющими аппаратный модуль TWI, но это несущественно...
Код:$Regfile="m48pdef.dat" $Crystal=8000000 '$crystal = 7372800 $hwstack=8 $swstack=8 $framesize=8 ' ============================================================== $lib "i2c_twi.lbx" 'Config I2cdelay = 1 ' Задержки i2c "10 - 100кГц, 5 - 200кГц, 2 - 500кГц, 1 - 1Мгц" 'TWBR = &B11110000 ' Устанавливаем скорость I2C Config SCL = Portc.5 ' Конфигурируем I2c SCL Config SDA = Portc.4 ' Конфигурируем I2c SDA I2cinit Config PortB.0 = Input Config PortB.1 = Input Config PortB.6 = Output Config PortB.7 = Output PORTB.6 = 1 PORTB.7 = 1 Config PortD = Output PORTD = 0 ' ============================================================== 'Const pcf1 = &H40 ' Адрес PCF8574 U2 (запись) 'Const pcf2 = &H4F ' Адрес PCF8574 U3 (чтение) 'Const probe = &H70 ' Адрес I2C-Slave Mega48 (запись) 'Const probe1 = &H71 ' Адрес I2C-Slave Mega48 (чтение) 'Dim a As Byte Dim b As Byte 'Waitms 10 ' ============================================================== I2cstart I2cwbyte &H40 ' Обращаемся к PCF8574 U2 I2cwbyte &H00 ' Записываем стартовое значение I2cstop ' ============================================================== Do PORTB.6 = 1 PORTB.7 = 1 'b = 0 If PINB.0 = 0 Then While PINB.0 = 0 : Wend Gosub запись ' Записываем в PORTD I2C-Slave Mega48 End If If PINB.1 = 0 Then While PINB.1 = 0 : Wend Gosub чтение ' Читаем PORTB I2C-Slave Mega48 End If Waitms 10 Loop End ' ============================================================== чтение: ' Передача из PCF в эмулятор PORTB.6 = 0 I2cstart 'I2cwbyte &H1D ' Обращаемся к I2C-Slave Mega48 I2cwbyte &H71 ' Обращаемся к I2C-Slave Mega48 I2crbyte b, Ack ' Считываем значение PORTD в Мастер I2cstop PORTD = b 'Waitus 10 I2cstart I2cwbyte &H40 ' Обращаемся к PCF8574 U2 I2cwbyte b ' Записываем значение порта I2cstop Return ' ============================================================== запись: ' Передача из эмулятора в PCF PORTB.7 = 0 I2cstart I2cwbyte &H4F ' Обращаемся к PCF8574 U3 I2crbyte b, NAck ' Считываем значение порта PCF I2cstop PORTD = b 'Waitus 10 I2cstart 'I2cwbyte &H40 ' Обращаемся к PCF8574 U2 'I2cwbyte &H1C ' Обращаемся к I2C-Slave Mega48 I2cwbyte &H70 ' Обращаемся к I2C-Slave Mega48 I2cwbyte b ' Записываем значение в PORTB I2cstop Incr b Return ' ==============================================================
Код:$Regfile="m48pdef.dat" $Crystal=8000000 '$crystal = 7372800 $hwstack=40 $swstack=16 $framesize=32 $baud = 9600 ' ============================================================== '$lib "i2c_twi.lbx" '$lib "i2c.lbx" ' ============================================================== 'Config PortD = Input Config PortB = Output PORTB = 0 'Config SCL = Portc.5 ' Конфигурируем I2c SCL 'Config SDA = Portc.4 ' Конфигурируем I2c SDA Const Twi_slave_adr = &H70 ' адрес слейва (&B01110000), ' TWGCE (нулевой бит) сброшен, не распознаем общий вызов ' ============================================================== Dim Twi_status As Byte 'Dim TWI_buf(4) As Byte Dim msg As Byte Dim TWI_msgSize As Byte : TWI_msgSize = 0 Dim TWI_state As Byte Dim TWI_statusReg As Byte 'Dim lastTransOK As Bit 'Dim RxDataInBuf As Bit 'Dim genAddressCall As Bit 'lastTransOK Alias TWI_statusReg.0 'RxDataInBuf Alias TWI_statusReg.1 'genAddressCall Alias TWI_statusReg.2 TWI_statusReg = 0 Dim TWI_bufPtr As Byte : TWI_bufPtr = 0 ' ============================================================== Enable TWI On TWI Twi_int_slave Gosub Twi_init_slave Enable Interrupts ' ============================================================== ' ============================================================== Do While TWCR.TWIE = 1 : Wend 'If lastTransOK = 1 And genAddressCall = 0 Then ' Disable Interrupts ' I2CRECEIVE Twi_slave_adr, TWI_bufPtr ' PORTD = TWI_bufPtr ' Enable Interrupts 'End If 'If TWI_statusReg = 1 Then PORTB = msg If TWI_statusReg = 1 Then PORTB = TWDR msg = 0 TWI_statusReg = 0 'Waitms 10 Loop End ' ============================================================== ' ============================================================== ' Обработка прерывания TWI-модуля Twi_int_slave: 'While TWCR.TWIE = 1 : Wend Twi_state = TWSR Twi_status = Twi_state And &HF8 ' отбрасываем 3 лишних бита (-,TWSP1,TWSP2) 'PORTB = Twi_status Print "TWSR - &H" ; HEX (Twi_status) 'Print "TWDR - &H" ; HEX (TWDR) ' --------------------------------------------------------------------------------------------------------------- Select Case Twi_status ' Режим Slave Receiver (кнопка "Запись") Case &H60: ' &B0110_0000 ' Адрес совпадает ' Получен собственный SLA+W; ACK возвращен 'While TWCR.TWIE = 1 : Wend 'Print "TWSR - &H" ; HEX (Twi_status) 'Print "TWDR - &H" ; HEX (TWDR) TWCR.TWSTO = 0 TWCR.TWEA = 1 TWCR.TWEN = 1 ' TWCR.TWIE = 1 'Print "TWSR - &H" ; HEX (Twi_status) 'Print "TWDR - &H" ; HEX (TWDR) ' Twcr = &B11000100 ' TWCR.TWINT = 1 'Case &H68 ' &B0110_1000 'Print "TWDR - &H" ; HEX (TWDR) ' RxDataInBuf = 1 ' TWI_bufPtr = 0 ' TWCR.TWEN = 1 ' TWCR.TWIE = 1 ' TWCR.TWEA = 1 ' TWCR.TWSTA = 0 ' TWCR.TWSTO = 0 ' TWCR.TWWC = 0 ' TWCR.TWINT = 1 Case &H70 : ' &B0111_0000 'Print "TWSR - &H" ; HEX (Twi_status) ' TWI_statusReg = 1 ' lastTransOK = 1 ' genAddressCall = TWAR.0 ' TWCR.TWSTO = 0 ' TWCR.TWINT = 1 ' TWCR.TWINT = 0 ' TWCR.TWEA = 1 ' TWCR.TWINT = 1 ' Twcr = &B11000100 Case &H80: ' &B1000_0000 приняли байт 'Print "TWDR - &H" ; HEX (TWDR) ' TWI_statusReg = 1 ' TWCR.TWSTA = 0 ' TWCR.TWSTO = 0 ' TWCR.TWEA = 1 ' TWCR.TWEA = 0 ' TWCR.TWINT = 1 ' Twcr = &B11000100 Case &H88: ' &B1000_1000 устройство уже адресовано, был неверно принят байт данных и послан NACK 'Print "TWDR - &H" ; HEX (TWDR) ' TWI_statusReg = 1 ' TWCR.TWSTA = 0 ' TWCR.TWSTO = 0 ' TWCR.TWEA = 1 ' TWCR.TWINT = 1 Case &H90 ' &B1001_0000 'Print "TWDR - &H" ; HEX (TWDR) ' TWI_statusReg = 1 ' PortB = TWDR ' lastTransOK = 1 ' TWCR.TWEN = 1 ' TWCR.TWIE = 1 ' TWCR.TWEA = 1 ' TWCR.TWSTA = 0 ' TWCR.TWSTO = 0 ' TWCR.TWWC = 0 ' TWCR.TWINT = 1 ' Twcr = &B11000100 Case &H98 ' &B1001_1000 'Print "TWSR - &H" ; HEX (Twi_status) ' TWI_statusReg = 1 ' TWCR.TWEN = 1 ' TWCR.TWIE = 1 ' TWCR.TWEA = 1 ' TWCR.TWSTA = 1 ' TWCR.TWSTO = 0 ' TWCR.TWINT = 1 ' TWCR.TWWC = 0 Case &HA0 : ' &B1010_0000 ' Условие СТОП или повторное условие ПУСК было получено, ' хотя адрес все еще был ведомым. 'While TWCR.TWIE = 1 : Wend 'Print "TWDR - &H" ; HEX (TWDR) ' TWI_statusReg = 1 ' PortB = TWDR ' msg = TWDR TWCR.TWSTA = 0 TWCR.TWSTO = 0 TWCR.TWEA = 1 TWCR.TWIE = 1 'Print "TWDR - &H" ; HEX (TWDR) ' TWCR.TWEN = 1 ' TWCR,TWWC = 0 ' TWCR.TWINT = 1 ' Twcr = &B11000100 ' --------------------------------------------------------------------------------------------------------------- ' Режим Slave Transmitter (кнопка "Чтение") Case &HA8 : ' &B1010_1000 ' TWI_statusReg = 2 'Print "TWDR - &H" ; HEX (TWDR) ' TWDR = PIND TWDR = &HAA TWCR.TWSTO = 0 TWCR.TWEA = 1 ' Goto state_b8 Print "TWDR - &H" ; HEX (TWDR) ' TWCR.TWINT = 1 'Case &HB0 : ' &B1011_0000 'Print "TWDR - &H" ; HEX (TWDR) ' TWCR.TWSTO = 0 ' TWCR.TWEA = 1 ' TWCR.TWINT = 1 Case &HB8 : ' &B1011_1000 байт передан, получен ACK state_b8: 'Print "TWDR - &H" ; HEX (TWDR) ' TWI_statusReg = 2 TWCR.TWEN = 1 TWCR.TWIE = 1 TWCR.TWEA = 0 TWCR.TWSTA = 0 TWCR.TWSTO = 0 ' TWCR.TWINT = 1 ' TWCR.TWWC = 0 Case &HC0 : ' &B1100_0000 байт передан, получен NACK, ' переключение в режим неадресованного ведомого, ' разрешено распознавание вызовов ' If TWI_bufPtr = TWI_msgSize Then ' lastTransOK = 1 ' Else ' TWI_state = TWSR ' End If TWCR.TWEN = 1 TWCR.TWIE = 0 TWCR.TWEA = 1 TWCR.TWSTA = 0 TWCR.TWSTO = 0 ' TWCR.TWINT = 1 ' TWCR.TWWC = 0 Case &HC8 : ' &B1100_1000 ' TWCR.TWSTA = 0 ' освобождаем шину ' TWCR.TWSTO = 0 TWCR.TWEA = 0 ' --------------------------------------------------------------------------------------------------------------- ' Необрабатываемые режимы 'Case &HF8 : ' неопределенность состояния, в прерывании никогда не произойдет Case &H00 : ' ошибка на шине TWCR.TWEN = 1 TWCR.TWIE = 0 TWCR.TWEA = 1 TWCR.TWSTA = 0 TWCR.TWSTO = 0 ' TWCR.TWINT = 1 ' TWCR.TWWC = 0 Case Else: TWCR = &B01000100 ' перегрузили шину End Select TWCR.TWINT = 1 Return ' ============================================================== ' ============================================================== ' TWI инициализация SR или ST (зависит от старт-пакета) Twi_init_slave: 'Twi_data = 0 ' очищаем переменную передачи 'Buf_len = 0 ' обнуляем счетчик буфера TWSR = 0 ' для ведомого состояние регистра безразлично TWDR = &HFF ' обнуляем данные 'TWBR = &B11110000 ' Устанавливаем скорость I2C TWAR = Twi_slave_adr ' устанавливаем адрес слейва 'TWAMR = 0 ' устанавливаем маску адреса слейва TWAMR = Twi_slave_adr ' устанавливаем адрес слейва TWAR.TWGCE = 0 ' запрет распознавания общего адреса TWCR.TWSTA = 0 ' устанавливаем МК как слейв TWCR.TWSTO = 0 ' переводим модуль TWI в режим "Стоп" ' В режиме ведомого установка этого бита может ' использоваться для восстановления после ошибки. ' При этом состояние СТОП не формируется, но TWI модуль ' возвращается к начальному неадресованному состоянию. TWCR.TWEA = 1 ' разрешение автоматической генерации подтверждения TWCR.TWEN = 1 ' разрешение работы модуля TWI ' Когда бит TWEN устанавливается в 1, TWI модуль включается ' и берет на себя управление выводами SCL и SDA. ' Когда бит TWEN сбрасывается, TWI модуль выключается. TWCR.TWIE = 1 ' разрешаем прерывание TWI TWCR.TWINT = 1 ' !!! ВНИМАНИЕ !!! сбрасываем флаг прерывания Return ' ==============================================================
Многое закомментировано, но это - результат поиска решения.
При нажатии кнопки "Запись" Мастер считывает значение с U3 и передает в эмулятор (ведомому).
При нажатии "Чтение" считывается значение порта ведомого и передается на U2.
На каком-то этапе от чтения порта отказался и ведомый всегда отправляет &HAA.
В обоих случаях передаваемое значение Мастер отображает на своем порту.
Что имеем в результате (пока)...
Эмулятор признает свой адрес, но байт не принимает, в регистре TWDR - его адрес (как и должно быть)...
Разумеется, что на этом этапе TWDR не должен читаться, но это сделано только для демонстрации.
Судя по логу I2C, Мастер байт таки посылает и что-то даже получает в ответ...
При чтении из эмулятора всегда читается &HFF и добросовестно передается Мастером на U2...
А в логе UART'а - адрес принимающей PCF (U2)!
Изменение скорости (регистр TWBR) на качество приема/передачи не влияет.
Изменение тактовой частоты от 1 МГц до 16 МГц (пробовал даже с "кварцами") тоже не меняет картину.
С одной стороны даже хорошо, но - "включаешь - не работает" (с) М. Жванецкий
В процессе использовал (не полностью, правда) примеры из AN 311.
Перевёл полностью, даже примеры из Си в Bascom... 
Что-то подталкивает на использование таймера, но упорно отказываюсь от этой идеи.
Продолжаю борьбу... 