Порты ввода-вывода

Порты ввода-вывода

На этом уроке мы поговорим о портах ввода-вывода микроконтроллера и наконец-то включим светодиоды на плате.
 

 
Порт ввода-вывода – это логическое объединение сигнальных линий, через которые принимаются и передаются данные.
Каждый порт микроконтроллера SMT32F303VCT6, установленного на отладочной плате, имеет до 16 линий ввода-вывода. Например, для порта A, обозначенного как GPIOA от английского General Purpose Input Output, это линии от PA0 до PA15.
Порт GPIOB соответственно включает линии, обозначенные PB. Кроме того, имеются порты
GPIOC, GPIOD, GPIOE и GPIOF.

Каждая линия порта ввода-вывода может работать в одном из 4 режимов:

  • цифровой или дискретный выход
  • альтернативная функция
  • цифровой или дискретный вход
  • аналоговый вход

Каждый дискретный выход может работать в режимах

  • Push-pull
  • Open-drain.

Режим Push-pull - это стандартный двухтактный выход, и в этом режиме логический сигнал, выставленный в соответствующем бите выходного регистра порта, соответствует логическому уровню на линии ввода-вывода.
Режим Open-drain, или открытый сток, может использоваться, например, для управления нагрузкой с большим напряжением, чем напряжение питания микроконтроллера.

Для каждой линии ввода-вывода можно также доопределить уровень неопределенного вывода до логической единицы (pull-up) или до логического нуля (pull-down) с использованием подтягивающих элементов.

Для каждого вывода, сконфигурированного как выход, задается также скорость переключения.

При сбросе все выходы имеют низкую скорость переключения – до 2 МГц. Но ее также можно изменить на среднюю - от 4 до 10 МГц или высокую - от 10 до 50МГц.
Высокую скорость переключения имеет смысл устанавливать в случае если по соответствующей линии идет обмен данными на высокой скорости. В остальных случаях лучше ограничиться низкой или средней.

 
Теперь давайте перейдем к практической части. На отладочной плате установлены 8 светодиодов и кнопка пользователя User. В соответствии со схемой, все светодиоды разведены на линиях PE8…PE15 порта GPIOE. Пользовательская кнопка занимает линию PA0 порта GPIOA.

Давайте откроем STMCube и создадим новый проект непосредственно для микроконтроллера, установленного на плате. В появившемся окне выбираем STM32F3series, lines – STM32F303 и выбираем тип микроконтроллера STM32F303VCTx.
Создание проекта для STM32F303VCTx
Теперь сконфигурируем линии портов. Для линий PE8…PE15 выбираем режим дискретного выхода. Для этого кликаем левой кнопкой мыши на линию порта и выбираем GPIO_Output. Таким же образом можно будет выбрать альтернативную функцию для работы линии, причем список альтернативных функций включает только те функции, которые может реализовать выбранная линия.

Линию PA0 конфигурируем как вход – выбираем для нее режим GPIO_Input.
Еще разрешим режим отладки. Зайдем в настройки периферийного модуля SYS и установим Debug -> Serial Wire. Мы видим, что для отладки будут использоваться линии PA13 и PA14.

Конфигурация линий портов
Переходим к конфигурации таймера. Заходим во вкладку Clock Configuration и выбираем тактирование от внутреннего высокочастотного генератора HSI.
Тактирование от внешнего генератора HSI

Переходим на вкладку Configuration и открываем для настройки модуль портов ввода-вывода - GPIO.
Конфигурирование модуля GPIO
В появившемся окне мы видим список линий, которые сконфигурировали, и для каждой линии можно настроить параметры, о которых говорилось выше. Оставим все по умолчанию.

Теперь сгенерируем код проекта. Переходим в меню Project-> Generate Code. Выбираем папку проекта, задаем его имя и проверяем, что среда разработки, для которой будет сгенерирован код, установлена в значении EWARM. Нажимаем OK.

Ждем, пока сгенерируется код и открываем проект в IARe. Переходим в папку Application->User и открываем файл main.c. Сейчас в функции main() вызывается 3 функции инициализации:

  • HAL_Init()
  • SystemClock_Config()
  • MX_GPIO_Init().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int main(void)
{
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */
  /* MCU Configuration----------------------------------------------------------*/
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

Если посмотрим на тело функции инициализации портов, то увидим, что STM32CubeMX уже проинициализировал линию PA0 как вход, а PE8-PE15 - как выходы.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  /* GPIO Ports Clock Enable */
  __GPIOA_CLK_ENABLE();
  __GPIOE_CLK_ENABLE();
  /*Configure GPIO pin : PA0 */
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  /*Configure GPIO pins : PE8 PE9 PE10 PE11
  PE12 PE13 PE14 PE15 */

  GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11
    | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11
    | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15, GPIO_PIN_RESET);
}

Последний вызов функции HAL_GPIO_WritePin() сбрасывает логические значения выбранных линий портов в 0 – присваивает им значение GPIO_PIN_RESET.
Скопируем эту функцию и вставим ее в main() перед бесконечным циклом. Напомню, что пользовательский код можно размещать только в пределах комментариев USER BEGIN CODE и USER END CODE.
Теперь заменим в этой функции GPIO_PIN_RESET на GPIO_PIN_SET.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main(void)
{
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */
  /* MCU Configuration----------------------------------------------------------*/
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
  HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11
    | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15, GPIO_PIN_SET);
  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */
  /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

Добавились строки 13, 14

И откомпилируем проект. Выбираем Project->Rebuild All.

А теперь подключим отладочную плату и загрузим в нее наш проект. Выбираем Project ->Download and Debug. Мы видим, что выполнение программы не производится, и на строке int main() появилась зеленая стрелочка.
Запуск программы на выполнение
Запустим программу на выполнения – выбираем меню Debug->Go или нажимаем клавишу F5.
Мы видим, что на отладочной плате засветились все 8 светодиодов.

Чтобы остановить отладку нужно выбрать Debug->Stop Debugging.
После остановки отладки программа возобновляет свое выполнение – светодиоды светятся.
Дальше мы попробуем управлять светодиодами по отдельности.

Прокрутить вверх