Урок 8. Управление портами ввода-вывода через функции библиотеки HAL.

Уроки STM32

Научимся работать с портами с помощью библиотеки HAL.

Предыдущий урок     Список уроков     Следующий урок

Кто дочитал предыдущий урок до конца и не свихнулся, наверняка оценит, как просто работать с портами через функции библиотеки HAL.

 

В прошлом уроке мы обращались к регистрам непосредственно через имена библиотеки CMSIS. Приходилось разбираться с форматами регистров конфигурации, вычислять данные для загрузки в них, искать в заголовочном файле имена регистров и переопределений.

А представьте, что нашу последнюю программу необходимо переписать на другой микроконтроллер семейства STM32. Придется все делать заново. Опять открывать техническую документацию, проверять имена и форматы, корректировать программу, исправлять ошибки.

Все эти проблемы решает библиотека HAL. В ней мы работаем не со сложными регистрами конфигурации, а с теми объектами, которые нам необходимы и, главное, понятны. В данном уроке - с портами GPIO и выводами портов. Управлять портами становится не сложнее, чем в системе Ардуино. Только режимов больше.

Небольшое отступление.

Параллельно урокам STM32 я буду выкладывать на сайт строгую справочную информацию на русском языке. Прежде всего, это описание функций библиотеки HAL и библиотеки  LL (о ней в следующем уроке).

Я буду все время ссылаться на эти документы. Описание любой функции, которую мы изучаем в уроке, лучше дополнительно посмотреть в справочнике, в формализованном виде. Тем более, что я обязательно сопровождаю строгую информацию примерами.

Кроме ссылок в уроках, справочники по функциям HAL и LL можно открыть в верхнем меню сайта в выпадающем списке STM32.

Справочник в меню

Информации на русском языке по этим библиотекам очень мало.

 

Постановка задачи.

Вернемся к задаче из предыдущего урока. Надо:

  • Установить конфигурацию выводов:
    • PB12 – Вход с подтягивающим на шину питания резистором. К нему у нас подключена кнопка.
    • PB13 – Активный выход. Мы подключили к нему светодиод.
    • PC13 – Активный выход. К нему подключен светодиод платы.
  • Реализовать алгоритм управления светодиодом от кнопки:
    • кнопка нажата – светодиод светится;
    • кнопка отжата – светодиод погашен.

Схема подключения светодиода и кнопки

Давайте с помощью STM32CubeMX  создадим проект Lesson8_1, в котором настроим только систему тактирования. Конфигурацию портов создавать нее будем. Сделаем это с помощью функций библиотеки HAL. Будем заполнять файл main.c постепенно.

 

Конфигурация портов с помощью библиотеки HAL.

Команды инициализации будем вставлять в блок

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

файла main.c.

Если мы не разрешили тактирование портов в STM32CubeMX, то надо это сделать функцией  __HAL_RCC_GPIOx_CLK_ENABLE().

Не будем вдаваться в подробности, просто выполним команды разрешения портов B и C.

__HAL_RCC_GPIOB_CLK_ENABLE(); // разрешение порта B
__HAL_RCC_GPIOC_CLK_ENABLE(); // разрешение порта C

Заходим в справочник по функциям HAL для управления портами и видим на первом месте функцию инициализации HAL_GPIO_Init.

void HAL_GPIO_Init (GPIO_TypeDef * GPIOx, GPIO_InitTypeDef * GPIO_Init)

Функция имеет 2 аргумента:

  • GPIOx – это имя порта, с которым мы работаем. Указывается в общепринятом виде:  GPIOA, GPIOB, GPIOC и т.д.
  • GPIO_Init – указатель на структуру параметров инициализации.

Структура описывается так:

typedef struct
{
  uint32_t Pin;       // номер вывода
  uint32_t Mode;      // режим
  uint32_t Pull;      // режим подтягивающего резистора
  uint32_t Speed;     // скорость переключение выходного сигнала
} GPIO_InitTypeDef;

Подробно назначение элементов структуры и варианты значений для них можно посмотреть в справочнике.

Для инициализации порта нам необходимо объявить структуру типа GPIO_InitTypeDef.

GPIO_InitTypeDef GPIO_InitStruct = {0};

Задаем нужные элементы структуры для вывода PB12.

/* конфигурация вывода PB12 на вход с подтягивающим резистором */
GPIO_InitStruct.Pin = GPIO_PIN_12; // номер вывода
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // режим вход
GPIO_InitStruct.Pull = GPIO_PULLUP; // резистор к шине питания

Вызываем функцию HAL_GPIO_Init для порта B.

HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

Делаем тоже самое для выводов PB13 и PC13.

/* конфигурация вывода PB13 на активный выход */
GPIO_InitStruct.Pin = GPIO_PIN_13; // номер вывода
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // режим выход
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; // средняя скорость выхода
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

/* конфигурация вывода PC13 на активный выход */
GPIO_InitStruct.Pin = GPIO_PIN_13; // номер вывода
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // режим выход
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; // средняя скорость выхода
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

Установка конфигурации наших трех выходов закончена.

Дальше нам необходимо считать состояние вывода PB12. К нему мы подключили кнопку.

Сделать это можно функцией HAL_GPIO_ReadPin.

Полный формат: GPIO_PinState HAL_GPIO_ReadPin (GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin).

У функции 2 аргумента:

  • GPIOx – выбор порта (GPIOA, GPIOB, GPIOC … ).
  • Pin – номер вывода (GPIO_PIN_0 … GPIO_PIN_15).

Функция возвращает состояние вывода, которое может принимать 2 значения:

GPIO_PIN_SET – высокий уровень;

GPIO_PIN_RESET – низкий уровень.

Блок проверки состояния кнопки будет выглядеть так.

if( HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == GPIO_PIN_SET ) {
    // на выводе PB12 высокий уровень, кнопка отжата
}
else {
    // на выводе PB12 низкий уровень, кнопка нажата
}

Вставляем его в программу, но уже в цикл while(1) перед завершением блока

/* USER CODE END WHILE */

Теперь надо в зависимости от положения кнопки установить вывод PB13 (светодиод) в нужное состояние.

Для установки и сброса выводов портов есть функция HAL_GPIO_WritePin.

Формат: void HAL_GPIO_WritePin (GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState).

Аргументы:

  • GPIOx – выбор порта (GPIOA, GPIOB, GPIOC … ).
  • Pin – номер вывода (GPIO_PIN_0 … GPIO_PIN_15).
  • PinState – состояние  вывода:
    • GPIO_PIN_SET – высокий уровень;
    • GPIO_PIN_RESET - низкий уровень.

Чтобы зажечь светодиод надо вызвать функцию:

HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET); // сброс вывода PB13 

Погасить светодиод можно функцией:

HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET); // установка вывода PB13 

Вставляем эти команды в ветки оператора условного перехода.

if( HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == GPIO_PIN_SET ) {
    // на выводе PB12 высокий уровень, кнопка отжата
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET); // установка вывода PB13                         
}
else {
    // на выводе PB12 низкий уровень, кнопка нажата
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET); // сброс вывода PB13                             
}

Можно компилировать, загрузить программу в микроконтроллер и проверить.

Вот ссылка на мой проект полностью:

  Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!  

Для обращения к кнопке, светодиоду мы использовали абсолютные имена – номера выводов, название портов. Это не совсем удобно.

 

Давайте создадим новый проект Lesson8_2. Настроим конфигурацию системы тактирования, назначим вывод PB12 на режим вход с подтягивающим к питанию резистором, определим вывод PB13, как активный вывод. И самое главное – присвоим им имена (User label) Button и Led.

Откроем новый проект. Заглянем в файл main.h.

Файл main.h в списке

Там вот такой блок.

/* Private defines -----------------------------------------------------------*/
#define Button_Pin GPIO_PIN_12
#define Button_GPIO_Port GPIOB
#define Led_Pin GPIO_PIN_13
#define Led_GPIO_Port GPIOB

STM32CubeMX сделал для кнопки 2 переназначения основных аргументов HAL-функций управления портами :

  • К имени вывода (Button) он добавил _Pin и присвоил стандартное название номера вывода GPIO_PIN_12.
  • К имени вывода (Button) он добавил _GPIO_Port и присвоил стандартное название порта GPIOB.

И такую же операцию конфигуратор совершил над именем светодиода (Led).

Теперь в функциях в качестве параметров мы можем использовать символьные имена, часть которых мы задали на этапе конфигурации с помощью STM32CubeMX.

С учетом этого наша конструкция в цикле while() будет выглядеть так:

if( HAL_GPIO_ReadPin(Button_GPIO_Port, Button_Pin) == GPIO_PIN_SET ) {
    // на выводе PB12 высокий уровень, кнопка отжата
    HAL_GPIO_WritePin(Led_GPIO_Port, Led_Pin, GPIO_PIN_SET); // установка вывода PB13
}
else {
    // на выводе PB12 низкий уровень, кнопка отжата
    HAL_GPIO_WritePin(Led_GPIO_Port, Led_Pin, GPIO_PIN_RESET); // сброс вывода PB13
}

Читаемость программы значительно улучшилась.

Вот такой вариант проекта.

  Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!  

Думаю, вы вспомнили Ардуино и с облегчением вздохнули. Все намного проще и понятнее, чем в предыдущем уроке.

 

В следующем уроке займемся работой с портами через LL-функции. Что-то среднее. Сложнее, чем в этом уроке, но проще, чем в предыдущем.

Предыдущий урок     Список уроков     Следующий урок

0

Автор публикации

не в сети 7 дней

Эдуард

56
Комментарии: 1319Публикации: 148Регистрация: 13-12-2015

2 комментария на «Урок 8. Управление портами ввода-вывода через функции библиотеки HAL.»

  1. Отличные уроки, а главное все понятно разжевано с примерами и картинками, спасибо за ваши труды.

    0

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *