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 чіпати не треба. Його зроблено, щоб полегшити життя і не морочити собі голову з бітами.

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

Архіви