Вот и наступил прекрасный момент, когда все доступные порты заняты, а битбэнгать как-то не сподручно. На сцену выходит микросхема-преобразователь UART в-из I2C/SPI

Есть аналоги подобных микросхем-преобразователей (MAX3107, MAX14830), которые скорей всего будут лучшим выбором, чем представленная выше. У них нет “магических” регистров доступа и FIFO длиннее. Так вышло, что под рукой была именно эта микросхема. Работа с доступом к определенным регистрам у нее очень специфичная.
Вот, например, сноски под описанием регистров из даташита:

Обратите внимание на п.п. 2, 7, 10 и 11. Такой выверт легко объяснить – под адресацию регистров микрухи разработчики заложили только 1 байт (16 адресов), а их понадобилось чуть больше.
Интересные дизайн-решения для этой микрухи были отмечены и разработчиками драйвера под Линукс:

Один неприятный момент заключается в том, что у микросхемы нет регистра с уникальным ID. Но зато у нее есть два регистра доступные для записи кастомных значений (Scratchpad Register (SPR)). Этот момент позволит нам определить – жива ли вообще микросхема на линии (если не доверять ASK по адресу)
А возможных адресов микросхемы целая коробка:

То есть, на один интерфейс I2C можно посадить до 16 UART’ов (если не брать в расчет расширители I2C). Да, достаточно низкоскоростных, но, вполне функциональных. При подключении по SPI, мы вообще ограничены только количеством ножек под CS (если не брать в расчет расширители портов)
Для работы с этой микрухой была написана примитивная, портируемая библиотека на голом Си.
Если FIFO не используется надо закомментить это в коллбэк-функции:
//fifo_len = SC16_read_register(SC16IS7XX_RXLVL); //while (fifo_len--){ data = SC16_RX(); get_data(data); //}
По-умолчанию, включен FIFO, прерывания на RX линии и классические 8 бит, без четности и 1 стоп-бит. Эти настройки можно изменить тут (или дописать свои функции):
SC16_write_register(SC16IS7XX_FCR, 0x01); //enable fifo SC16_write_register(SC16IS7XX_IER, 0x05); //enable Rx data ready, lsi interrupt SC16_write_register(SC16IS7XX_LCR, 0x03); // 8 data bit, no parity (add "| SC16_2STOPBITS" to 2-stopbits support)
Если не использовать прерывания, то можно бесконечно опрашивать этой функцией
uint8_t SC16_is_data_aval(void)
Если нужно опрашивать/писать_в много таких микрух (действительно нужна сотня) на одной линии с разными адресами, то стоит подумать над созданием структуры объекта микросхемы расширителя, указатель на который можно будет передавать как параметр каждой функции.
Использование драйвера: Функция SC16_interrupt_callback(void) должна вызываться в цикле или при внешнем прерывании от микросхемы SC16IS7XX. Функции i2c_write(uint8_t addr, const uint8_t *data, int len), i2c_read(uint8_t addr, uint8_t *data, int len) и get_data(uint8_t data) должны быть определены для вашей целевой платформы.
Пример для STM32 и HAL:
#define ARR_SIZE 8 static uint8_t my_best_array[ARR_SIZE] = {0}; static uint8_t array_pointer = 0; HAL_StatusTypeDef i2c_write_ext(uint8_t addr, const uint8_t *data, int len) { HAL_StatusTypeDef status; status = HAL_I2C_Master_Transmit(I2C_EXT_ADDR, addr, (uint8_t*)data, len, 0x05); return status; } HAL_StatusTypeDef i2c_read_ext(uint8_t addr, const uint8_t *data, int len) { HAL_StatusTypeDef status; status = HAL_I2C_Master_Recieve(I2C_EXT_ADDR, addr, (uint8_t*)data, len, 0x05); return status; } void get_data(uint8_t data){ my_best_array[array_pointer] = data; array_pointer++; }
Для вашего устройства необходимо изменить частоту генератора микросхемы SC16IS7XX X_TALL_FREQ и адрес микросхемы I2C_EXT_ADDR.
При использовании скорости I2C в 100кГц и длинных пачек данных через UART на 115200 бод, внутренний FIFO микросхемы может быстро переполняться. Короткие посылки передаются вполне хорошо даже на такой скорости.
Ссылка на GitHub:
Comment