3. STM32. Програмування STM32F103. GPIO


11.08.2016

У попередній статті ми використовували простеньку програму, яка блимає світлодіодом. Трохи модифікуємо її і спробуємо розібратися, як налаштувати виводи мікроконтролера для роботи на вхід і вихід. C13 налаштуємо як вихід. До нього підключений світлодіод на тестовій платі. B0 налаштуємо на вхід і підключимо до нього кнопку. У натиснутому положенні кнопка має замикати ногу B0 на землю.

STM32F103_GPIO

Код програми


#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"

int main(void)
{
  int i;
  GPIO_InitTypeDef  GPIO_InitStructure;

  /* Initialize LED which connected to PC13 */
  // Enable PORTC Clock
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
  /* Configure the GPIO_LED pin */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  //GPIO_SetBits(GPIOC, GPIO_Pin_13); // Set C13 to High level ("1")
  GPIO_ResetBits(GPIOC, GPIO_Pin_13); // Set C13 to Low level ("0")

  /* Initialize Button input PB0 */
  // Enable PORTB Clock
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  /* Configure the GPIO_BUTTON pin */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);

  while (1) {

	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) != 0) {
    	/* Toggle LED which connected to PC13*/
    	GPIOC->ODR ^= GPIO_Pin_13; // Invert C13

    	/* delay */
    	for(i=0;i<0x100000;i++);

    	/* Toggle LED which connected to PC13*/
    	GPIOC->ODR ^= GPIO_Pin_13;

    	/* delay */
    	for(i=0;i<0x100000;i++);
    }
    else {
    	GPIO_SetBits(GPIOC, GPIO_Pin_13);
    }

  }
}

Перш за все роздивимось, що ми інклудимо:


#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"

stm32f10x.h - стосується конкретної серії мікроконтролера; stm32f10x_gpio.h - стосується GPIO виводів портів, з чим ми і будемо працювати; stm32f10x_rcc.h - А це що таке? Справа у тому, що у STM32 усі модулі (GPIO, ADC, EXTI, USART, I2C, SPI, та інші) після включення мікроконтролера відключені від тактування. І ми маємо програмно вмикати тактування тій периферії, яку плануємо використовувати. Крім того, ми можемо налаштувати частоту тактування деяких модулів окремо. Докладно про тактування буде у наступній статті.

Зараз нам треба засвоїти, що перед тим, як використовувати чи то порт, чи то інтерфейс, чи то який-небудь модуль, треба подати на нього тактування. У stm32f10x_rcc.h описані функції, які керують тактуванням.

Дивимося у програмі рядок, який вмикає тактування порту GPIOC:


/* Initialize LED which connected to PC13 */
// Enable PORTC Clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

Далі виконуємо налаштування вивода порта. В нашому випадку PC13 (на тестовій платі до нього підключений світлодіод) налаштовуємо як вихід. Налаштування виконуються через структуру GPIO_InitTypeDef.


/* Configure the GPIO_LED pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);

Нарешті можемо задати високий чи низький рівень на виході PC13:


//GPIO_SetBits(GPIOC, GPIO_Pin_13); // Set C13 to High level ("1")
GPIO_ResetBits(GPIOC, GPIO_Pin_13); // Set C13 to Low level ("0")

Коли задаємо високий рівень, світлодіод не горить, низький - горить.

Далі виконуємо конфігурацію PB0. Він буде працювати як вхід з підтяжкою до "1" (GPIO_Mode_IPU). Тобто, коли кнопка не натиснута (нога PB0 висить у повітрі), на вході PB0 буде "1".


/* Initialize Button input PB0 */
// Enable PORTB Clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/* Configure the GPIO_BUTTON pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);

Стан кнопки опитується функцією:


GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0)

Програма опитує стан кнопки і блимає світлодіодом тільки коли кнопка не натиснута, тобто коли на вході B0 високий рівень.

Підключимо замість кнопки датчик руху HC-SR501. Зверніть увагу, живлення датчика 5В. Тепер світлодіод буде блимати, коли сенсор руху зафіксує рух. Вітаю! Тепер можна трохи погратися: помахати перед сенсором руху руками, поганяти перед ним кота, собаку, інших домашніх тварин.

DSCN6944 DSCN6932

Пам`ятка

Структура GPIO_InitTypeDef для налаштування виводів порта має наступні параметри:
  • GPIO_Pin - номера пінів, які конфігуруються. Приклад для декількох пінів: GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
  • GPIO_Speed - задає швидкість для обраних пінів. Може мати наступні значення: GPIO_Speed_10MHz, GPIO_Speed_2MHz, GPIO_Speed_50MHz
  • GPIO_Mode - задає режим роботи пінів. Може мати наступні значення:
    • GPIO_Mode_AIN — аналоговий вхід;
    • GPIO_Mode_IN_FLOATING — вхід без підтяжки (Float)
    • GPIO_Mode_IPD — вхід з підтяжкою до землі (Pull-down)
    • GPIO_Mode_IPU — вхід з підтяжкою до живлення (Pull-up)
    • GPIO_Mode_Out_OD — вихід з відкритим стоком (Open Drain)
    • GPIO_Mode_Out_PP — вихід з двома станами (Push-Pull)
    • GPIO_Mode_AF_OD — вихід з відкритим стоком для альтернативних функцій (Alternate Function). Використовується коли виводи керуються периферією, яка може бути задіяна на цьому виводі. Наприклад USART, I2C тощо.
    • GPIO_Mode_AF_PP — те саме що і перед цим, але з двома станами.

Зауваження

Ви могли звернути увагу, що у програмі ми двічі викликаємо функцію RCC_APB2PeriphClockCmd для різних портів:


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

Це можна зробити і один раз таким чином:


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOB, ENABLE);

Програма буде менша за розміром. Так зазвичай і роблять. Вмикають тактування усім модулям, налаштовують усі переривання і так далі. Це правильний шлях до оптимізації програми. Але на етапі вивчення мікроконтролера, тільки задля наочності, я буду писати саме так, як у цьому прикладі. Щоб було ясно: що і за чим треба увімкнути і як налаштувати, щоб налаштувати ту чи іншу периферію. Це буде не завжди раціонально, але завжди зрозуміло.

Бажаю успіхів!

Дивись також:

STM32
Коментарі:
Сергій говорить:
08.11.2016 18:58
Всередині нескінченного циклу код поплив, ну і зайве треба видалити. А можливо краще в цьому прикладі блимати світлодіодом не toggle, а  саме "підняти, почекати, опустити, почекати". Так наглядніше буде і завжди під рукою команди для "підняти" та "опустити".

Сергій говорить:
12.11.2016 20:41
"stm32f10x.h – стосується GPIO виводів портів, з чим ми і будемо працювати;" - потрібно виправити помилку.

andre говорить:
14.11.2016 09:27
Дякую. Виправив.

Сергій говорить:
14.11.2016 12:37
В циклі красоту наведіть будь ласка. Там зайвий код, про який я вде писав і в попередній статті вже пофіксили. І окріми того сам код поплив (пару переносів строки не вистачає)

Микола говорить:
26.06.2020 10:07
Коли вмикати переферію не застосовуючи функцію RCC_APB2PeriphClockCmd. Треба робити затримку після вмикання переферії, чи відразу можна перейти до інших маніпуляцій з портом?

Андрій говорить:
29.05.2023 19:21
GPIO_InitTypeDef  GPIO_InitStructure; Я так розумію що це "GPIO_InitStructure" створення указника на структуру "GPIO_InitTypeDef". У даному разі нема значення як налаштовані всі інші пині порта. Якщо потрібно налаштувати один пін з порта, то указник треба стровати якось так: "GPIOC_InitPinLed"?

Андрій говорить:
29.05.2023 19:51
#define GPIO_Pin_13                ((uint16_t)0x2000)  /*!< Pin 13 selected */
Для зручності написання коду можливо міняти ці дефайни в проєкті? Або створювати свої з тими значеннями зсунення біт?

andre говорить:
30.05.2023 06:32
Можете назвати  і GPIOC_InitPinLed - як завгодно.
Та коли на цьому ж порту доведеться додати, скажімо, кнопки, тоді доведеться знов якось перейменувати.

Для зручності можна робити так:

#define LED_PIN GPIO_Pin_13

І далі у коді використовувати LED_PIN, а не GPIO_Pin_13.

GPIO_Pin_13 чіпати не треба. Його зроблено, щоб полегшити життя і не морочити собі голову з бітами.

Дмитро говорить:
10.02.2025 16:27
Вчора почав займатися стм, вирішив глянути ці статті, бо по двигунам все було дуже добре розказано. Але взагалі не можу зрозуміти на якій бібліотеці пише тут автор? Типу hal, cmsis, LL, spl?

andre говорить:
11.02.2025 06:41
Шановний Дмитро, код написаний на SPL. Хочу зауважити, що цим статтям більше ніж 8 років, тому рекомендую розглядати ці статті як загальний опис по роботі з периферією мікроконтролера. Сам код на оновлених бібліотеках може працювати не так, я то було 8 років тому. На жаль у світі програмування все занадто швидко змінюється. У сучасних реаліях я б рекомендував писати на HAL та LL.

Додати коментар
Code
* - обов'язкові поля

Архіви