승코딩당당당

[STM32] HAL 기반 GPIO 제어 – LED Blinking 본문

개발/임베디드

[STM32] HAL 기반 GPIO 제어 – LED Blinking

승코딩당당당 2026. 2. 5. 17:37

 

기존에는 GPIO 레지스터를 직접 제어하는 방식(Bare-Metal)으로 LED Blinking을 구현해보았다.

https://xeungcoding.tistory.com/64

 

[STM32] GPIO 레지스터 직접 제어 – LED Blinking

이번 실습에서는 STM32 MCU 기반 개발 보드인 Nucleo-L073RZ를 사용하여 GPIO 레지스터를 직접 제어하는 방식(Bare-Metal)으로 LED Blinking을 구현하였다.HAL 라이브러리나 CubeMX와 같은 고수준 추상화 도구를

xeungcoding.tistory.com

 

이번 글에서는 해당 방식을 확장하여,

HAL(Hardware Abstraction Layer) 기반의 GPIO 제어 방식으로 LED Blinking을 구현해보고자 한다.

STM32에서 GPIO를 제어하는 방법은 여러 가지가 있으며, 대표적으로 다음과 같은 방식들이 있다.

  1. GPIO 레지스터 직접 제어 방식
  2. HAL 라이브러리를 이용한 방식
  3. CubeMX를 활용한 설정 기반 방식

이번 글에서는 HAL 라이브러리를 이용한 방식CubeMX를 함께 사용하는 방식을 적용하여 LED Blinking을 구현한 코드를 소개한다.

 


 

HAL (Hardware Abstraction Layer)

  • 하드웨어를 직접 건드리지 않고도 MCU를 쉽게 제어하게 해주는 라이브러리 계층
    • 주소/비트 하나라도 틀리면 바로 오작동
    • MCU가 바뀌면 코드 대부분을 다시 써야 함
  • 라이브러리: 미리 만들어 둔 코드 묶음
[ Application (main.c, 내가 짠 코드) ]
-------------------------------
[ HAL 라이브러리 ]
-------------------------------
[ MCU 레지스터 (하드웨어) ]

 


 

HAL을 이용해서 PA5, PA8, PB5 LED 동작시키기

#include "stm32l0xx_hal.h"

void SystemClock_Config(void);

int main(void)
{
  HAL_Init();
  // SystemClock_Config();

  // GPIO clock enable
  __HAL_RCC_GPIOA_CLK_ENABLE();
	__HAL_RCC_GPIOB_CLK_ENABLE();

  // pa5 output set (HAL_GPIO_Init)
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin   = GPIO_PIN_5 | GPIO_PIN_8;
  GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull  = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	// pb5
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  // LED init -> OFF (HAL_GPIO_WritePin)
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET);

  // toggle (HAL_GPIO_TogglePin)
  while (1)
  {
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
		HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);
		HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
    HAL_Delay(1000);
  }
}

 


 

CubeMX 를 이용하여 LED 제어

  • 여기서 SPEED만 Very High로 설정해주고, User Label만 임의로 지정해준 후 Gernerate Code 클릭
    • 이미 Keil 프로젝트 진행 중이었다면, reload
  • 기존 main.c 파일에서 while문 안에만 코드 대입해주면 동작함!
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2026 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
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();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* 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 */
		HAL_GPIO_TogglePin(GPIOA, USERS_LED_Pin);              // PA5 (USER_LED)
    HAL_GPIO_TogglePin(USER_LED2_GPIO_Port, USER_LED2_Pin);// PB5 (USER_LED2)
    HAL_GPIO_TogglePin(GPIOA, USER_LED3_Pin);              // PA8 (USER_LED3)

    HAL_Delay(1000); // 1000ms = 1?
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.MSIState = RCC_MSI_ON;
  RCC_OscInitStruct.MSICalibrationValue = 0;
  RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_5;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  /* USER CODE BEGIN MX_GPIO_Init_1 */

  /* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, USERS_LED_Pin|USER_LED3_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(USER_LED2_GPIO_Port, USER_LED2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pins : USERS_LED_Pin USER_LED3_Pin */
  GPIO_InitStruct.Pin = USERS_LED_Pin|USER_LED3_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : USER_LED2_Pin */
  GPIO_InitStruct.Pin = USER_LED2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(USER_LED2_GPIO_Port, &GPIO_InitStruct);

  /* USER CODE BEGIN MX_GPIO_Init_2 */

  /* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

 

  • generate code 후 생성 된 코드에서 다음 부분만 수정하면 된다
while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		HAL_GPIO_TogglePin(GPIOA, USERS_LED_Pin);              // PA5 (USER_LED)
    HAL_GPIO_TogglePin(USER_LED2_GPIO_Port, USER_LED2_Pin);// PB5 (USER_LED2)
    HAL_GPIO_TogglePin(GPIOA, USER_LED3_Pin);              // PA8 (USER_LED3)

    HAL_Delay(1000); // 1000ms = 1?
  }

 


 

결과