Передмова
Для розширення I/O портів існують різні мікросхеми для різних шин/протоколів. Наприклад мікросхема SN74HC595N з керуванням по SPI, про яку вже була в мене стаття. Або мікросхема PCF8574 з керуваннням по I2C, про яку і буде ця стаття. Існують і інші I/O Expander як 8 бітні, так і 16 бітні. Заглиблюватись в протоколи не будемо, в мережі достатньо якісної літератури по цій темі. Зосередимось на самому розширювачі портів вводу виводу. Писати и читати стан ніжок у PCF8574 будемо по шині I2C за допомоги HAL Driver. І щоб зручно було керувати розширювачем портів PCF8574 створив бібліотеку з необхідним набором функцій. За основу бібліотеки взяв готову бібліотеку PCF8574_ESP для ESP8266 та переніс її на нашу платформу STM32, а також переробив з мови C++ на Ci. В цій статті ознайомимось з роботою I/O Expander PCF8574 в режимі: 4 біти читаємо - 4 біти пишемо. Тобто, під'єднаємо до PCF8574 чотири кнопки і чотири світлодіоди. За допомоги кнопок будемо керувати станом світлодіодів.Що потрібно
- Плата розробника на STM32. Я буду використовувати таку плату з таким програматором
- Модуль I/O Expander PCF8574
- Чотирі світлодіоди, чотирі резистори на 100 Ом, чотирі кнопки і шість резисторів на 10 КОм
- Жмут дротів
- та макетні плати breadboard
Схема
Зберіть макет за такою схемою та під'єднайте до шини I2C мікроконтролера. Резистори R7 - R10 можна не встановлювати.
Схема підключення PCF8574 4-входи 4-виходи |
Макет Expander I/O PCF857 |
CubeMX for STM32
Запускаємо CubeMX, створюємо новий проект, обираємо свій чип STM32. У вкладці "Pinout" робимо такі налаштування шпильок:- RCC - Crystal/Ceramic resonator
- SYS - Serial Wire
- обираємо порт I2C до якого буде підключено дисплей. Я обрав I2C1
- обираємо якийсь pin де будемо "ловити" подію по перериваннях від сигналу INT мікросхеми PCF8574. Я обрав PB5, як GPIO_EXTI5 і дав йому мітку PCF8574_INT
Налаштування периферії |
Наалаштування тактування |
- GPIO mode - external interrput mode with falling edge trigger detection (реагуємо на фронт що спадає)
- GPIO Pull-Up/Pull-Down - Pull-up (з підтяжкою до живлення, логічної 1)
- USER Label - PCF8574_INT
Налаштування переривань на PB5 |
Та в розділі NVIC увімкнемо переривання від PB5:
Увімкнемо переривання |
Можна генерувати код програмою CubeMXforSTM32 для совго засобу розробки. В мене це Atolic TrueStudio, професійний засіб розробки абсолютно безкоштовний.
Підключення бібліотеки та демокод
Бібліотека складається з таких файлів:
Ці файли потрібно вкласти до вашого проекту. Файли з розширенням ".h" до теки "inc", а файли з розшиернням ".c" до теки "src". Або всю теку з бібліотекою, але на деяких засобах розробки потрібно в налаштуваннях проекту прописати шлях до теки з бібліотекою.
Відкриємо в зазобі розробки файл pcf857x.h і налаштуємо бібліотеку для правильної роботи з розширювачем портів:
- #define STM32F1XX - вказати серію свого чипу
- #define STM32_I2C_PORT hi2c1 - вказати порт I2C до якого під'єднано PCF8574
- #define PCF857x_ADDRESS 0x38 - вказати адресу PCF8574 на шині I2C (відповідно до схеми це буде адреса 0x38
- Вкладемо заголовний файл з бібліотекою до нашого коду
/* USER CODE BEGIN Includes */ #include "pcf857x.h" /* USER CODE END Includes */
- Напишемо код для демонстрації. Запалимо всі вогники, через пару секунд загасимо всі вогники, зсунемо вогники ліворуч і праворуч та покрутимо вогник праворуч і ліворуч
/* USER CODE BEGIN 2 */ if(pcf857x_Init(0xFF, false) != PCF857x_OK) { while(1); } pcf857x_ResetInterruptPin(); pcf857x_ToggleAll(); HAL_Delay(2000); pcf857x_ToggleAll(); HAL_Delay(2000); for (int i = 0; i < 4; ++i) { pcf857x_ShiftLeft(1); HAL_Delay(200); } for (int i = 0; i < 4; ++i) { pcf857x_ShiftRight(1); HAL_Delay(200); } pcf857x_Write(0, 0); HAL_Delay(200); for (int i = 0; i < 3; ++i) { pcf857x_RotateLeft(1); HAL_Delay(200); } for (int i = 0; i < 3; ++i) { pcf857x_RotateRight(1); HAL_Delay(200); } HAL_Delay(200); pcf857x_Write8(0xFF); /* USER CODE END 2 */
Але це не все. В нашій схемі є ще кнопки. І можна відслідковувати стан натискання кожної кнопки і якимсь чином реагувати на це. Можна просто в лоб, в безкінечному циклі читати стан ніжок на expander PCF8574. Але це не розсудливо. Ми постійно читаємо шину I2C, а потім визначаємо було натискання кнопки чи ні, незалежно від того, чи дійсно кнопку натискали, чи нічого взагалі не відбувається. По суті даремно витрачаємо ресурси мікроконтролера і без будь якої користі ганяємо дані по шині I2C.
То що ж робити? На самому початку в програмі CubeMX ми задіяли ще одину шпильку від розширювача з назвою "INT". Шпилька "INT" виставляє логічний нуль, до наступного читання, коли відбулись якісь зміни на шпильках P0 - P7 розширювача портів PCF8574. А "ловити" цей "0" будемо на PB5 мікроконтролеру, який ми налаштували в режим переривань, підтягнули до "1", та "ловитимемо" саме фронт що спадає.
Весь потрібний початковий код з налаштувань периферії зробив за нас CubeMX, то нам залишається тільки написати обробник переривань. Для зручності в драйверах HAL є функція зворотнього виклику "HAL_GPIO_EXTI_Callback". Ця функція викликається кожного переривання і на вхід цій функції дається на якій шпильці відбулась подія.
- Додамо до коду обробник переривань
/* USER CODE BEGIN PFP */ /* Private function prototypes -----------------------------------------------*/ void HAL_GPIO_EXTI_Callback (uint16_t GPIO_Pin) { if(GPIO_Pin == PCF8574_INT_Pin) { for (int i = 0; i < 4; ++i) { if(!pcf857x_Read(i + 4)) { pcf857x_Toggle(i); } } } } /* USER CODE END PFP */
- Безкінечний цикл залишається "пустим", а вся "магія" по перемиканню стану вогників працює за допомоги апаратного переривання, та невеличкого коду в обробнику переривань.
/* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */
- Тепер можна компілювати проект, заливати до мікроконтролера та милуватись роботою вогників і реакцією на натискання кнопок
Список функцій бібліотеки
PCF857x_TypeDef pcf857x_Init(uint16_t value_init, bool is8575); // Початкова ініціалізація pcf857x. На вхід число, початковий стан P0 - P7 та false якщо pcf8574 або true якщо pcf8575 uint8_t pcf857x_Read8(void); // Читання 8 бітів з P0 - P7 uint16_t pcf857x_Read16(void); // Читання 16 бітів з P0 - P15 bool pcf857x_Read(uint8_t pin); // Читання шпильки pin на pcf857x PCF857x_TypeDef pcf857x_Write8(uint8_t value); // Запис 8 бітів до P0 - P7. Повертає код помилки. PCF857x_TypeDef pcf857x_Write16(uint16_t value); // Запис 16 бітів до P0 - P15. Повертає код помилки. PCF857x_TypeDef pcf857x_Write(uint8_t pin, bool value); // Запис значення до шпильки pin. Повертає код помилки. PCF857x_TypeDef pcf857x_Toggle(uint8_t pin); // Поміняти стан шпильки pin. Повертає код помилки. PCF857x_TypeDef pcf857x_ToggleAll(void); // Поміняти стан всіх шпильок. Повертає код помилки. PCF857x_TypeDef pcf857x_ShiftRight(uint8_t n); // Зсув праворуч на n бітів. Повертає код помилки. PCF857x_TypeDef pcf857x_ShiftLeft(uint8_t n); // Зсув ліворуч на n бітів. Повертає код помилки. PCF857x_TypeDef pcf857x_RotateRight(uint8_t n); // Обертання праворуч на n бітів. Повертає код помилки. PCF857x_TypeDef pcf857x_RotateLeft(uint8_t n); // Обертання ліворуч на n бітів. Повертає код помилки. PCF857x_TypeDef pcf857x_ResetInterruptPin(void); // Скидання шпильки INT в початковий стан. Повертає код помилки. PCF857x_TypeDef pcf857x_GetLastError(void); // Повертає останній код помилки.
Архів з бібліотекою
Бібліотека для зручної роботи з розширювачем портів PCF857X
Немає коментарів:
Дописати коментар