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

Программирование STM32 / Порты ввода-вывода

 

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

 
Порт ввода-вывода – это логическое объединение сигнальных линий, через которые принимаются и передаются данные.
Каждый порт микроконтроллера 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.
После остановки отладки программа возобновляет свое выполнение – светодиоды светятся.
Дальше мы попробуем управлять светодиодами по отдельности.


Назад: Программирование STM32

Комментариев к записи: 6

  • Очень жаль что вы не коснулись темы регистров, и ручной конфигурации выводов. Cube делает из STM - arduino с его setup() и loop() Куча всего, сделали бы уж действительно как в ардуино. Main() имеет 2 функции остальной ужас скрыт от пользователя. Может, вы сможите расширить уроки? Углублясь в суть темы. По ручному кофигурированию портов, таймера, написание передачи данных аля SPI, подключение дисплея. Как написать самому библиотеку для дисплея или для передачи данных. Буду очень рад, приобрести дополнительный расширенный курс по STM. Одна тема - 1000. Как вам такое предложение?

    • И очень бы хотелось подробный разбор файла stm32xxxxxx.h А так же разбор даташита.


      • Елена Вставская
        Хорошее пожелание. Но Data Sheet занимает более 1000 страниц, а уроки более 15 минут у меня делать не получается из-за сложности монтажа :( Поэтому это тоже как вариант курса по углубленному изучению

        • Так не обязательно весь даташит разбирать. Хотя бы основные главы. Например: регистры питания, регистры портов, управление тактированием. А когда я сравниваю с ардуино, то что я имею ввиду. Как происходит программирование на ардуино? Надо подключить датчик, идешь и скачиваешь готовую библиотеку к этому датчику. А когда у тебя на руках оказывается датчик к которому нет библиотеки, то встаешь в ступор так как кроме как подключать библиотеки тебя другому не учили. Вот для этого и хочется разбираться в даташите, к периферии, и писать самому драйвера для устройств. Если вы создадите урок по написанию библиотеки по интерфейсу связи например SPI или какой либо другой. Буду очень благодарен. А если опишите как написать драйвера для дисплея (из старого смартфона) буду премного благодарен.

    • Елена Вставская
      Когда этот курс только планировался, я сама использовала для программирования периферийные библиотеки. При этом на на создание проекта в IAR вручную приходилось потратить некоторое время. Когда же мне попался Cube - стало намного проще реализовывать проекты. Согласна, что до некоторых тонких настроек Cube со своими библиотеками, к сожалению, не даёт добраться. И некоторые такие моменты я показываю по ходу уроков. Но сравнивать такой способ создания проекта с Arduino я бы всё-таки не стала (по причине того, что здесь мы получаем проект для микроконтроллера (который можно использовать в разработках и легко переносить на другие микроконтроллеры из линейки), а не для конкретной платы). Насчет углубления в настройки - момент интересный. Действительно, было бы неплохо влезть вглубь функций, объяснить, как там всё устроено и как произвести тонкую настройку. Но хотелось дать быстрый старт. Возможно, в будущем я решусь на расширенную версию курса. Но пока еще много вопросов, которые вообще никак не затронуты. В частности, на следующей неделе я планирую опубликовать урок по ЦАП (а их будет 2 подряд). Еще хочется в ближайшее время осветить темы интерфейсов связи (хотя бы одного), возможности DSP-ядра, а также часы реального времени. P.S. К сожалению, запись уроков идет по остаточному принципу (когда есть свободное время), да и сам монтаж занимает много времени. Поэтому пополнение курса новыми уроками производится не так быстро, как хотелось бы.

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

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