GPIO

Материал из MIK32 микроконтроллер

GPIO - General-Purpose Input/Output (интерфейс ввода/вывода общего назначения).

Интерфейс ввода/вывода общего назначения для связи между компонентами микроконтроллера, различными периферийными устройствами и целыми системами.

GPIO в MIK32 Амур

Контроллер MIK32 предоставляет для работы GPIO три порта, который состоят из:

  • Порт 0 - выводы с 0 по 15
  • Порт 1 - выводы с 0 по 15
  • Порт 2 - выводы с 0 по 7
Распиновка MIK32_DIP

При конфигурации порта в режим 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.

Таблица 97 – Поля регистров PADх_CFG управления функцией выводов
Номер вывода Биты регистра 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
 }