
Проектируя электронное устройство, часто возникает необходимость в большем количестве ножек, чем имеет выбранный контроллер. В такой ситуации на помощь приходит расширитель портов. Различные микросхемы расширителей управляются по I2C или по SPI интерфейсу. В данной статье мы поговорим про расширитель портов I2C PCA9555.
Расширитель портов I2C: общие сведения
Если рассматривать в общем, то расширитель портов увеличивает общее количество доступный портов ввода-вывода. Управляется такая микросхема через интерфейсы I2C, SPI.
ВАЖНО! Такие микросхемы увеличивают цифровые порты ввода-вывода.
Расширитель портов PCA9555
Данная микросхема управляется через интерфейс I2C. Доступно 16 бит или 16 дополнительных выводов. На рисунке ниже представлена подробная блок схема микросхемы:

Контакты А0, А1, А2 задают адрес устройства, а это значит, что в одном устройстве можно использовать до 8 расширителей портов PCA9555. Согласитесь, внушительная получается цифра.
Контакт INT — прерывание по изменению значения на контактах. Прерывание срабатывает если контакты настроены как вход.
ВАЖНО! Прерывание INT срабатывает по любому изменению на контактах. Например: если у вас подключена через расширитель кнопка, то Прерывание будет срабатывать как при нажатии так и при отпускании кнопки.
Регистры PCA9555
Микросхема имеет 8 регистров для настройки и управления портами. они представлены в таблице ниже:
КОД | Имя | Описание |
0 | Input port 0 | Содержит значения порта 0, настроенного на вход |
1 | Input port 1 | Содержит значения порта 1, настроенного на вход |
2 | Output port 0 | Содержит значения порта 0, настроенного на выход. Можно читать и записывать. |
3 | Output port 1 | Содержит значения порта 1, настроенного на выход. Можно читать и записывать. |
4 | Polarity Inversion port 0 | Меняет полярность сигнала на контактах порта 0 |
5 | Polarity Inversion port 1 | Меняет полярность сигнала на контактах порта 1 |
6 | Configuration port 0 | Настраивает контакты порта 0 на вход или выход. 0-вход, 1- выход. |
7 | Configuration port 1 | Настраивает контакты порта 1 на вход или выход. 0-вход, 1- выход. |
Библиотека PCA9555 для STM32
Библиотека написана без использования регистра смены полярности сигнала. Так же читается состояние портов настроенных как вход. Состояние портов настроенных как выход, храниться в переменной без проверки чтением.
ВАЖНО! Для конфигурации, или чтения можно указать адрес первого или второго порта, а данные отправить на 2 порта сразу. Они идут парами, по этому Записав данные в нулевой порт, следующие будут записаны в первый порт одного регистра.
#include "stm32f4xx_hal.h" // Адреса наших устройств A0-A2 #define PCA9555_ADR20 0x20 #define PCA9555_ADR21 0x21 #define PCA9555_ADR22 0x22 #define PCA9555_ADR23 0x23 #define PCA9555_ADR24 0x24 #define PCA9555_ADR25 0x25 #define PCA9555_ADR26 0x26 #define PCA9555_ADR27 0x27 //Команды конфигурации #define PCA9555_INPUT_PORT_0 0 #define PCA9555_INPUT_PORT_1 1 #define PCA9555_OUTPUT_PORT_0 2 #define PCA9555_OUTPUT_PORT_1 3 #define PCA9555_POLARITY_INVERSION_PORT_0 4 #define PCA9555_POLARITY_INVERSION_PORT_1 5 #define PCA9555_CONFIG_PORT_0 6 #define PCA9555_CONFIG_PORT_1 7 HAL_StatusTypeDef Transmit2PCA9555(I2C_HandleTypeDef hi2c, uint8_t addr_dev, uint8_t registr_addr, uint16_t data_write2registr); HAL_StatusTypeDef Transmit2PCA9555_R1(I2C_HandleTypeDef hic, uint8_t addr_dev, uint8_t registr_addr, uint8_t data_write2registr); HAL_StatusTypeDef Transmit2PCA9555_R2(I2C_HandleTypeDef hic, uint8_t addr_dev, uint8_t registr_addr, uint8_t data_write2registr); uint16_t Recieve_fromPCA9555(I2C_HandleTypeDef hi2c, uint8_t addr_dev, uint8_t registr_addr); uint16_t GetPinout(I2C_HandleTypeDef hi2c, uint8_t addr_dev); uint8_t PCA9555_InitPort(I2C_HandleTypeDef hic, uint16_t portDir, uint16_t portInit, uint8_t adr);
#include "PCA9555.h" HAL_StatusTypeDef Transmit2PCA9555(I2C_HandleTypeDef hic, uint8_t addr_dev, uint8_t registr_addr, uint16_t data_write2registr) { HAL_StatusTypeDef stat; uint8_t mas_data[3]; mas_data[0] = registr_addr; mas_data[1] = data_write2registr; mas_data[2] = (data_write2registr>>8)&0xFF; addr_dev = addr_dev << 1 ; stat = HAL_I2C_Master_Transmit(&hic, addr_dev, mas_data,3,50); return stat; } HAL_StatusTypeDef Transmit2PCA9555_R1(I2C_HandleTypeDef hic, uint8_t addr_dev, uint8_t registr_addr, uint8_t data_write2registr) { HAL_StatusTypeDef stat; uint8_t mas_data[3]; mas_data[0] = registr_addr; mas_data[1] = data_write2registr; addr_dev = addr_dev << 1 ; stat = HAL_I2C_Master_Transmit(&hic, addr_dev, mas_data,2,50); return stat; } HAL_StatusTypeDef Transmit2PCA9555_R2(I2C_HandleTypeDef hic, uint8_t addr_dev, uint8_t registr_addr, uint8_t data_write2registr) { HAL_StatusTypeDef stat; uint8_t mas_data[3]; mas_data[0] = registr_addr; mas_data[1] = data_write2registr; addr_dev = addr_dev << 1 ; stat = HAL_I2C_Master_Transmit(&hic, addr_dev, mas_data[0],1,50); stat = HAL_I2C_Master_Transmit(&hic, addr_dev, mas_data[1],1,50); return stat; } uint16_t Recieve_fromPCA9555(I2C_HandleTypeDef hic, uint8_t addr_dev, uint8_t registr_addr) { uint8_t incoming[2]; uint16_t port = 0; HAL_StatusTypeDef stat; stat = HAL_I2C_Master_Transmit(&hic,addr_dev<<1, ®istr_addr,1,50); stat = HAL_I2C_Master_Receive(&hic,(addr_dev<<1) | 0x01,(uint8_t*)&incoming,2,50); port = (incoming[1] <<8) | incoming[0]; if (stat == HAL_ERROR) return 0; return port; } uint16_t GetPinout(I2C_HandleTypeDef hic, uint8_t addr_dev) { uint16_t pinout = 0; pinout = Recieve_fromPCA9555(hic, addr_dev, PCA9555_INPUT_PORT_0); return pinout; } uint8_t PCA9555_InitPort(I2C_HandleTypeDef hic, uint16_t portDir, uint16_t portInit, uint8_t adr) { // 1 - Вход // 0 - Выход HAL_StatusTypeDef stat; uint8_t mas_data[2]; mas_data[0] = portDir; mas_data[1] = (portDir<<8)&0xFF; stat = Transmit2PCA9555_R1(hic, adr, PCA9555_CONFIG_PORT_0,mas_data[0]); stat = Transmit2PCA9555_R1(hic, adr, PCA9555_CONFIG_PORT_1,mas_data[1]); mas_data[0] = portInit; mas_data[1] = (portInit>>8)&0xff; stat = Transmit2PCA9555(hic, adr, PCA9555_OUTPUT_PORT_0,mas_data[0]); stat = Transmit2PCA9555(hic, adr, PCA9555_OUTPUT_PORT_1,mas_data[1]); return stat; }