GPIO: различия между версиями

Материал из MIK32 микроконтроллер
Нет описания правки
Нет описания правки
Строка 1: Строка 1:
Пример работы с GPIO:<syntaxhighlight lang="c">
'''GPIO''' - General-Purpose Input/Output (интерфейс ввода/вывода общего назначения).


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


#include <pad_config.h>
'''GPIO в MIK32 Амур'''
#include <gpio.h>
#include <power_manager.h>


// Установите в соответствии с подключением светодиода
Контроллер MIK32 предоставляет для работы GPIO три порта, который состоят из:
#define LED_PORT GPIO_1 // Порт
#define LED_PIN 3 // Вывод порта
#define CLOCK_GPIO_PORT PM_CLOCK_GPIO_1_M // Тактирование порта
#define PIN_MODE_GPIO 1 // Режим вывода - GPIO
#define PIN_MODE_PORT PORT_1_CFG // Регистр управления режимом порта


void initClock() {
* Порт 0 - выводы с 0 по 15
PM->CLK_APB_P_SET = CLOCK_GPIO_PORT;
* Порт 1 - выводы с 0 по 15
PM->CLK_APB_M_SET = PM_CLOCK_PAD_CONFIG_M | PM_CLOCK_WU_M | PM_CLOCK_PM_M;
 
for (volatile int i = 0; i < 10; i++) ;
* Порт 2 - выводы с 0 по 7
 
[[Файл:Распиновка MIK32-DIP.png|мини|Распиновка MIK32_DIP]]
При конфигурации порта в режим GPIO и последующей установки на нужный вывод логической единицы, на нём появится напряжение 3,3 В.
 
Выводы, помимо функции программного управления, могут управляться периферийными блоками, а так же иметь аналоговый функционал (ЦАП/АЦП). Каждый вывод снабжен так же встроенными отключаемыми подтяжками к VDD, VSS.
[[Файл:...png|мини|Схема порта]]
Каждый модуль ввода/вывода (GPIO) подключен к шине APB и управляет до 16 внешними выводами. Каждый из внешних выводов может управляться индивидуально.
 
Рассмотрим работу с GPIO на примере проекта Blink из [[/github.com/MikronMIK32/mik32-examples/tree/main/Blink|репозитория примеров]].
 
Определим некоторые константы (Будем рассматривать на примере BOARD_DIP):<syntaxhighlight lang="c">
/* Тип платы */
//#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
 
</syntaxhighlight>Создадим функцию для инициализации часов (это нужно для тактирования порта GPIO):<syntaxhighlight lang="c">
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;      
// включение тактирования блока для смены режима выводов
}
}


void ledBlink() {
</syntaxhighlight>Рассмотрим код функции мигания встроенного светодиода. При каждом вызове функции происходит изменение логического значения на противоположное (с 0 на 1 и наоборот), управляющего наличием напряжения на выводе определённого порта. После чего создаётся задержка в 100000 тактов для того, чтобы можно было визуально отследить изменения. В данном случае на плате DIP будет мигать два светодиода, а на плате Lite один.<syntaxhighlight lang="c">
LED_PORT->OUTPUT = 1 << LED_PIN;   // Установка значения вывода порта 1 в высокий уровень
void ledBlink()
for (volatile int i = 0; i < 1000000; i++);
{
LED_PORT->OUTPUT = 0;        
#ifdef BOARD_LITE
for (volatile int i = 0; i < 1000000; i++);
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
}
}


int main() {
</syntaxhighlight>При использовании отладочных плат DIP и Lite и их встроенных светодиодов стоит проверить перемычки в соответствии с руководством по эксплуатации плат. В ином случае встроенные светодиоды могут быть просто не подключены к выводам платы, следовательно видимого результата не будет.
initClock();
 
Рассмотрим функцию ''ledButton'', которая реализована для платы Lite:<syntaxhighlight lang="c">
#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
 
</syntaxhighlight>Эта функция проверяет наличие напряжения логической единицы на кнопке, используя<syntaxhighlight lang="c">
GPIO_2->STATE & (1 << PIN_BUTTON)
 
</syntaxhighlight>На основе этого выбирается, должно ли присутствовать напряжение логической единицы на выводе светодиода.
 
Теперь рассмотрим функцию ''main.''
 
Инициализируем часы для тактирования GPIO:<syntaxhighlight lang="c">
InitClock(); // Включение тактирования GPIO
 
</syntaxhighlight>Затем конфигурируем выводы портов, устанавливая их в режим GPIO, после чего указываем, в каком направлении будет работать вывод порта (ввод или вывод), устанавливая значение логической единицы определённого для выбранного вывода.<syntaxhighlight lang="c">
#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
 
 
</syntaxhighlight>Чтобы установить вывод порта в режим GPIO, нужно записать значение 0 в битовое поле, соответствующее этому выводу. (таблица 97)
 
Для этого сбрасываем биты, соответствующие данному битовому полю
 
Умножаем наш номер вывода на 2, так как для каждого вывода используются два бита, поэтому сдвиг нам нужен двойной, чтобы записать функцию.
 
Конечно, можно использовать только команду DIRECTION_OUT/DIRECTION_IN для установки порта в качестве выхода/входа, однако лучше сбрасывать биты перед инициализацией, так как если инициализация порта повторная после сброса, может оказаться так, что, к примеру, предыдущая инициализация переключила порт в альтернативный режим (подключен к периферии), а попытка перевести порт в режим выхода переключит его в аналоговый режим, т.к. старший бит окажется 1.
{| class="wikitable"
|+Таблица 97 – Поля регистров PADх_CFG управления функцией выводов
|Номер вывода
|Биты регистра PADх_CFG
|Назначение
|-
|Port_x_0
|[1:0]
| rowspan="16" |Значения двух битов кодируют выбранный функционал для вывода


PAD_CONFIG->PIN_MODE_PORT = PIN_MODE_GPIO << (LED_PIN << 1) ; // Установка порта 1 в режим GPIO
LED_PORT->DIRECTION_OUT = 1 << LED_PIN; // Установка направления порта 1 в выход


while (1) {
 
ledBlink();
 
}
 
}
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, можем задать главный цикл исполнения программы:<syntaxhighlight lang="c">
while (1)
{
  ledBlink(); /* Светодиод мигает */
 
#ifdef BOARD_LITE
  // ledButton(); /* Светодиод зажигается при нажатой кнопке */
#endif
}


</syntaxhighlight>
</syntaxhighlight>

Версия от 10:00, 15 июля 2024

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
 }