Не секрет, что реализация ведомого I2C "на борту" МК - занятие платное, а халявы хочется... ;)
Штука временами полезная при совместной работе двух и более МК.
Есть, разумеется, UART, SPI и пр. интерфейсы и протоколы, но сейчас не об них.
Признаюсь, уже не первый год пытаюсь "разгрызть" этот вопрос, этак года с 2020-го... ;)
Не на постоянной основе, с перерывами, один раз больше двух лет...
Маленький отчет о проделанном... ;)

Использую и сторонние решения...
Вот это BASCOM hardware slave TWI (i2c) чуть помогло...
Приведенный пример работает в Proteus, но только в оригинальном виде...
Использование "нестандартной" тактовой частоты не позволяет опробовать "в железе".
Изменение даже на 100 Гц приводит к сбою или к "каше" в данных...
Не взлетело... Но пошел по этому же пути... ;)

В оригинальных подходах авторов Bascom используются прерывания и таймер.
Я пошел по пути использования только прерывания.
Да, это ограничивает круг используемых МК только имеющими аппаратный модуль TWI, но это несущественно...

Схема в Proteus
Код Master
Код:
$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

' ==============================================================
Код Slave
Код:
$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... ;)
Что-то подталкивает на использование таймера, но упорно отказываюсь от этой идеи.
Продолжаю борьбу... ;)