Операции над портами ввода-вывода или GPIO (general-purpose input/output) делятся на чтение и запись. К GPIO относят порты (выводы МК) не относящиеся к специализированным интерфейсам, таким как например UART, CAN, USB, JTAG и т.д., то есть портами GPIO мы управляем полностью сами, без какого либо встроенного в МК контроллера.
Порт GPIO может находиться в трех состояниях:
Ниже приведен пример программы для записи в GPIO. По умолчанию, после старта программы значения в регистрах GPIO нулевые и строчки инициализации содержащие нулевые значения могут быть опущены. В скобках указаны пункты спецификации на МК описывающие данные регистры.
// Пример записи в GPIO. Мигаем светодиодом VD5 (PORTB 0)
#include "MDR32F9Q2I.h" // Заголовок с наименованиями CMSIS
#define PIN_LED 0 // Номер вывода светодиода
#define PORT_LED MDR_PORTB // Порт светодиода
int main (void)
{
MDR_RST_CLK->PER_CLOCK |= (1 << 22); // Разрешение тактирования порта B (п.14.7.8)
PORT_LED->OE |= (1 << PIN_LED); // Вход или выход. 0 - вход, 1 - выход (п.16.1.2)
PORT_LED->ANALOG |= (1 << PIN_LED); // Аналог или цифра. 0 - аналоговый, 1 - цифровой (п.16.1.4) (п.4 т.2)
PORT_LED->PWR |= (1 << PIN_LED * 2); // Передатчик. 0 - отключен, 1 - медленный, 2 - средний, 3 - быстрый (п.16.1.7)
PORT_LED->FUNC &= ~(1U << PIN_LED); // Функция. 0 - порт (GPIO), 1 - основная, 2 - альтернативная, 3 - переопределенная (п.16.1.2) (п.4 т.2)
PORT_LED->PULL &= ~(1U << PIN_LED); // Подтяжка к нулю. 0 - выключена, 1 - включена (п.16.1.5)
PORT_LED->PULL &= ~(1U << (PIN_LED + 16)); // Подтяжка к питанию. 0 - выключена, 1 - включена (п.16.1.5)
PORT_LED->PD &= ~(1U << PIN_LED); // Тип выхода. 0 - управляемый драйвер, 1 - открытый сток (п.16.1.6)
PORT_LED->PD &= ~(1U << (PIN_LED + 16)); // Триггер Шмидта. 0 - выключен, 1 - включен (п.16.1.6)
PORT_LED->GFEN &= ~(1U << PIN_LED); // Фильтр. 0 - выключен, 1 - включен (п.16.1.8)
while(1){ // Основной бесконечный цикл
for(int i = 0; i < 100000; i++){} // Задержка
PORT_LED->RXTX ^= (1 << PIN_LED); // Инвертировать значение вывода (п.16.1.1)
}
}
Для чтения из порта, он должен быть настроен как вход. Ниже пример программы где мы используем чтение и запись из GPIO используя порт кнопки как вход (чтение из GPIO) и порт светодиода как выход (запись в GPIO). Используется минимальный код, все значения регистров, которые по умолчанию имеют значение ноль, мы не приводим, если нужен полный код инициализации GPIO - берем его из предыдущего примера с записью в GPIO.
// Пример записи в GPIO. При нажатии кнопки SEL (по центру) тушится светодиод VD5 (PORTB 0)
#include "MDR32F9Q2I.h" // Заголовок с наименованиями CMSIS
#define PORT_LED MDR_PORTB // Порт светодиода
#define PIN_LED 0 // Номер вывода светодиода
#define PORT_BTN MDR_PORTC // Порт кнопки
#define PIN_BTN 2 // Номер вывода кнопки
int main (void)
{
MDR_RST_CLK->PER_CLOCK |= (1 << 22); // Разрешение тактирования порта B (п.14.7.8)
MDR_RST_CLK->PER_CLOCK |= (1 << 23); // Разрешение тактирования порта C (п.14.7.8)
PORT_LED->OE |= (1 << PIN_LED); // Вход или выход. 0 - вход, 1 - выход (п.16.1.2)
PORT_LED->ANALOG |= (1 << PIN_LED); // Аналог или цифра. 0 - аналоговый, 1 - цифровой (п.16.1.4) (п.4 т.2)
PORT_LED->PWR |= (1 << PIN_LED * 2); // Передатчик. 0 - отключен, 1 - медленный, 2 - средний, 3 - быстрый (п.16.1.7)
PORT_BTN->ANALOG |= (1 << PIN_BTN); // Аналог или цифра. 0 - аналоговый, 1 - цифровой (п.16.1.4) (п.4 т.2)
while(1){ // Основной бесконечный цикл
for(int i = 0; i < 100000; i++){} // Задержка
if(PORT_BTN->RXTX & (1 << PIN_BTN)){ // Если сигнал высокий
PORT_LED->RXTX |= (1 << PIN_LED); // Зажигаем светодиод
}else{
PORT_LED->RXTX &= ~((1U << PIN_LED)); // Тушим светодиод
}
}
}