GPIO
GPIO - General-Purpose Input/Output (интерфейс ввода/вывода общего назначения).
Интерфейс ввода/вывода общего назначения для связи между компонентами микроконтроллера, различными периферийными устройствами и целыми системами.
GPIO в MIK32 Амур
Контроллер MIK32 предоставляет для работы GPIO три порта, который состоят из:
- Порт 0 - выводы с 0 по 15
- Порт 1 - выводы с 0 по 15
- Порт 2 - выводы с 0 по 7
При конфигурации порта в режим GPIO и последующей установки на нужный вывод логической единицы, на нём появится напряжение 3,3 В.
Выводы, помимо функции программного управления, могут управляться периферийными блоками, а так же иметь аналоговый функционал (ЦАП/АЦП). Каждый вывод снабжен так же встроенными отключаемыми подтяжками к VDD, VSS.
Каждый модуль ввода/вывода (GPIO) подключен к шине APB и управляет до 16 внешними выводами. Каждый из внешних выводов может управляться индивидуально.
Рассмотрим работу с GPIO на примере проекта Blink из репозитория примеров.
Определим некоторые константы (Будем рассматривать на примере BOARD_DIP):
/* Тип платы */ //#define BOARD_LITE #define BOARD_DIP #ifdef BOARD_LITE #define PIN_LED 7 // Светодиод управляется выводом PORT_2_7 #define PIN_BUTTON 6 // Кнопка управляет сигналом на выводе PORT_2_6 #endif #ifdef BOARD_DIP #define PIN_LED1 3 // Светодиод управляется выводом PORT_0_3 #define PIN_LED2 3 // Светодиод управляется выводом PORT_1_3 #endif
Создадим функцию для инициализации часов (это нужно для тактирования порта GPIO):
void InitClock() { PM->CLK_APB_P_SET |= PM_CLOCK_APB_P_UART_0_M | PM_CLOCK_APB_P_GPIO_0_M | PM_CLOCK_APB_P_GPIO_1_M | PM_CLOCK_APB_P_GPIO_2_M; // включение тактирования GPIO PM->CLK_APB_M_SET |= PM_CLOCK_APB_M_PAD_CONFIG_M | PM_CLOCK_APB_M_WU_M | PM_CLOCK_APB_M_PM_M; // включение тактирования блока для смены режима выводов }
Рассмотрим код функции мигания встроенного светодиода. При каждом вызове функции происходит изменение логического значения на противоположное (с 0 на 1 и наоборот), управляющего наличием напряжения на выводе определённого порта. После чего создаётся задержка в 100000 тактов для того, чтобы можно было визуально отследить изменения. В данном случае на плате DIP будет мигать два светодиода, а на плате Lite один.
void ledBlink() { #ifdef BOARD_LITE GPIO_2->OUTPUT ^= 1 << PIN_LED; // Установка сигнала вывода 7 порта 2 в противоположный уровень (^= Исключающее ИЛИ (XOR)) for (volatile int i = 0; i < 100000; i++) ; #endif #ifdef BOARD_DIP GPIO_0->OUTPUT ^= 1 << PIN_LED1; // Установка сигнала вывода 3 порта 0 в противоположный уровень GPIO_1->OUTPUT ^= 1 << PIN_LED2; // Установка сигнала вывода 3 порта 1 в противоположный уровень for (volatile int i = 0; i < 100000; i++); #endif }
При использовании отладочных плат DIP и Lite и их встроенных светодиодов стоит проверить перемычки в соответствии с руководством по эксплуатации плат. В ином случае встроенные светодиоды могут быть просто не подключены к выводам платы, следовательно видимого результата не будет. Рассмотрим функцию ledButton, которая реализована для платы Lite:
#ifdef BOARD_LITE void ledButton() { if (GPIO_2->STATE & (1 << PIN_BUTTON)) { GPIO_2->OUTPUT |= 1 << PIN_LED; // Установка сигнала вывода 7 порта 2 в высокий уровень (|= «Или» ) } else { GPIO_2->OUTPUT &= ~(1 << PIN_LED); // Установка сигнала вывода 7 порта в низкий уровень } } #endif
Эта функция проверяет наличие напряжения логической единицы на кнопке, используя
GPIO_2->STATE & (1 << PIN_BUTTON)
На основе этого выбирается, должно ли присутствовать напряжение логической единицы на выводе светодиода.
Теперь рассмотрим функцию main.
Инициализируем часы для тактирования GPIO:
InitClock(); // Включение тактирования GPIO
Затем конфигурируем выводы портов, устанавливая их в режим GPIO, после чего указываем, в каком направлении будет работать вывод порта (ввод или вывод), устанавливая значение логической единицы определённого для выбранного вывода.
#ifdef BOARD_LITE #ifdef MIK32V0 PAD_CONFIG->PORT_2_CFG |= (1 << (2 * PIN_LED)); // Установка вывода 7 порта 2 в режим GPIO PAD_CONFIG->PORT_2_CFG |= (1 << (2 * PIN_BUTTON)); // Установка вывода 6 порта 2 в режим GPIO GPIO_2->DIRECTION_OUT = 1 << PIN_LED; // Установка направления вывода 7 порта 2 на выход GPIO_2->DIRECTION_IN = 1 << PIN_BUTTON; // Установка направления вывода 6 порта 2 на вход #endif #ifdef MIK32V2 PAD_CONFIG->PORT_2_CFG &= ~(0b11 << (2 * PIN_LED)); Установка вывода 7 порта 2 в режим GPIO PAD_CONFIG->PORT_2_CFG &= ~(0b11 << (2 * PIN_BUTTON)); // Установка вывода 6 порта 2 в режим GPIO GPIO_2->DIRECTION_OUT = 1 << PIN_LED; // Установка направления вывода 7 порта 2 на выход GPIO_2->DIRECTION_IN = 1 << PIN_BUTTON; // Установка направления вывода 6 порта 2 на вход #endif #endif #ifdef BOARD_DIP #ifdef MIK32V0 PAD_CONFIG->PORT_0_CFG |= (1 << (2 * PIN_LED1)); // Установка вывода 3 порта 0 в режим GPIO PAD_CONFIG->PORT_1_CFG |= (1 << (2 * PIN_LED2)); // Установка вывода 3 порта 1 в режим GPIO GPIO_0->DIRECTION_OUT = 1 << PIN_LED1; // Установка направления вывода 3 порта 0 на выход GPIO_1->DIRECTION_OUT = 1 << PIN_LED2; // Установка направления вывода 3 порта 1 на выход #endif #ifdef MIK32V2 PAD_CONFIG->PORT_0_CFG &= ~(0b11 << (2 * PIN_LED1)); // Установка вывода 3 порта 0 в режим GPIO PAD_CONFIG->PORT_1_CFG &= ~(0b11 << (2 * PIN_LED2)); // Установка вывода 3 порта 1 в режим GPIO GPIO_0->DIRECTION_OUT = 1 << PIN_LED1; // Установка направления вывода 3 порта 0 на выход GPIO_1->DIRECTION_OUT = 1 << PIN_LED2; // Установка направления вывода 3 порта 1 на выход #endif #endif
Чтобы установить вывод порта в режим GPIO, нужно записать значение 0 в битовое поле, соответствующее этому выводу. (таблица 97)
Для этого сбрасываем биты, соответствующие данному битовому полю
Умножаем наш номер вывода на 2, так как для каждого вывода используются два бита, поэтому сдвиг нам нужен двойной, чтобы записать функцию.
Конечно, можно использовать только команду DIRECTION_OUT/DIRECTION_IN для установки порта в качестве выхода/входа, однако лучше сбрасывать биты перед инициализацией, так как если инициализация порта повторная после сброса, может оказаться так, что, к примеру, предыдущая инициализация переключила порт в альтернативный режим (подключен к периферии), а попытка перевести порт в режим выхода переключит его в аналоговый режим, т.к. старший бит окажется 1.
Номер вывода | Биты регистра PADх_CFG | Назначение |
Port_x_0 | [1:0] | Значения двух битов кодируют выбранный функционал для вывода
00 – первая функция (порт общего назначения) 01 – вторая функция (последовательный интерфейс) 10 – третья функция (последовательный интерфейс или таймер) 11 – четвертая функция (аналоговый сигнал) |
Port_x_1 | [3:2] | |
Port_x_2 | [5:4] | |
Port_x_3 | [7:4] | |
Port_x_4 | [9:8] | |
Port_x_5 | [11:10] | |
Port_x_6 | [13:12] | |
Port_x_7 | [15:14] | |
Port_x_8 | [17:16] | |
Port_x_9 | [19:18] | |
Port_x_10 | [21:20] | |
Port_x_11 | [23:22] | |
Port_x_12 | [25:24] | |
Port_x_13 | [27:26] | |
Port_x_14 | [29:28] | |
Port_x_15 | [31:30] |
После того, как мы настроили выводы в режим GPIO, можем задать главный цикл исполнения программы:
while (1) { ledBlink(); /* Светодиод мигает */ #ifdef BOARD_LITE // ledButton(); /* Светодиод зажигается при нажатой кнопке */ #endif }