GPIO_LED_수동제어1
STM32/STM32F103C8T6

GPIO_LED_수동제어1

STM32CubeIDE에서는 HAL 드라이버를 통해 편하게 GPIO를 간편한 코드로 제어할 수 있다.

이번에는 그 원리를 알아내어 GPIO를 수동으로 제어하는 방법에 대해 알아보고자 한다.

LED 핀은 PC13에 GPIO_OUPUT 연결되어 있는 상태로 진행한다.

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

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

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIO_LED_GPIO_Port, GPIO_LED_Pin, GPIO_PIN_SET);

  /*Configure GPIO pin : GPIO_LED_Pin */
  GPIO_InitStruct.Pin = GPIO_LED_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIO_LED_GPIO_Port, &GPIO_InitStruct);
}

위 코드는 MX_GPIO_Init 함수인데 이 함수를 해석하여 코드를 작성해야한다.  코드를 보게 되면 _HAL_RCC_GPIOC_CLK_ENABLE();가 있는데 이 함수를 Ctrl + 마우스 클릭으로 들어가게 되면 다음과 같은 코드가 나오게 된다.

 

#define __HAL_RCC_GPIOC_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg; \
                                        SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPCEN);\
                                        /* Delay after an RCC peripheral clock enabling */\
                                        tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPCEN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)

다른 코드보다도 SET_BIT 코드가 중요하기 때문에 SET_BIT 코드를 또 Ctrl + 마우스 클릭으로 들어가게 되면 다음과 같이 나오게 된다.

 

#define SET_BIT(REG, BIT)     ((REG) |= (BIT))

 SET_BIT 매개변수인 RCC->APB2ENR 먼저 해석해 보자면 우선 RCC는 주소번지이다. CubeIDE에서 디버깅 하여 breakpoint를 통해 그 값을 확인해보면 0x40021000이 나온다.

이어서 RCC->APB2ENR 값을 확인해보면 다음과 같이 나온다.

다음으로 2번째 매개변수인 RCC_APB2ENR_IOPCEN 값을 확인해보면 다음과 같이 나온다.

값이 나오는 과정은 비트연산을 통해 나오게 되는데 자세히 확인해보고 싶으면 똑같이 변수 위에다 Ctrl + 마우스 클릭을 하면 해당 변수 위치로 이동하게 된다. 이상 확인하고 SET_BIT에 대입해보자면

 

*(0x40021018) |= (16)가 나오게 된다. 고로 __HAL_RCC_GPIOC_CLK_ENABLE()는 *(0x40021018) |= (16)와 같다.

 

이어서 HAL_GPIO_WritePin(GPIO_LED_GPIO_Port, GPIO_LED_Pin, GPIO_PIN_SET);에서 대해서 해석해보자면 먼저 코드 안으로 들어가면 다음과 같이 나온다.

void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  assert_param(IS_GPIO_PIN_ACTION(PinState));

  if (PinState != GPIO_PIN_RESET)
  {
    GPIOx->BSRR = GPIO_Pin;
  }
  else
  {
    GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
  }
}

asser_param은 gpio 핀이 유효한지 안 유효한지를 검사하는 함수이다. 사실상 별로 중요한 함수는 아니다.

중요한것은 if문이다. 코드를 보게되면 매개변수 3개가 넘어오게 되는데 먼저 GPIO_LED_GPIO_Port 값을 확인해보면 다음과 같이 나온다.

그 다음 GPIO_LED_Pin의 값을 확인해보면 다음과 같이 나온다.

8192라는 값은 2진수로 1<<13 한것으로 10000000000000가 나오는데 이는 PC13 포트를 제어한다.

마지막으로 GPIO_PIN_SET 값을 확인해보면 다음과 같이 나온다.

사진상으로는 안 나와있지만 IDE 밑 세부사항에보면 Decimal : 1 값으로 설정되어있다.

 

그 다음 if문 안으로 들어가 GPIOx->BSRR 값을 확인해보면 다음과 같이 나온다.

 

if문에 대입해서 확인해보면 PinState는 GPIO_PIN_SET 값에 의해 1이 된다. GPIO_PIN_RESET은 SET 과 반대로 0의 값을 가지고 있다. 그로 인해 if문 안에 들어가게 되고 *(0x40011000) = 8192 가 실행 된다. 즉 PC13을 할당한다는 의미이다. 만약 if문에 들어가지 않고 else문에 들어가면 *(0x40011000) = (8192<<16) 가 실행된다.

 

위에서 구한 값을 통해 메인함수 코드를 작성해보면

  /* Initialize all configured peripherals */
//  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
  volatile unsigned int * reg = 0x40021018;
  *reg |= 16;
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = GPIO_LED_Pin; //PC13 1 << 13
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIO_LED_GPIO_Port, &GPIO_InitStruct);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  volatile unsigned int * reg2 = 0x40011010;
  while (1)
  {
       *reg2 = 0x2000;
       HAL_Delay(100);
       *reg2 = (0x2000 << 16);
       HAL_Delay(100);
    /* USER CODE END WHILE */
  
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

위에 *reg는 __HAL_RCC_GPIOC_CLK_ENABLE()의 값이고 그 밑에는 설정 코드 그 다음 *reg2는 GPIOx->BSRR의 주소번지값이다. while문 안으로 들어가면 *reg2 값을 0x2000로 바꿔 HIGH 값을 주어 LED를 키고 HAL_Delay(100)을 통해 딜레이를 주고 다음 (0x2000 << 16)로 바꿔 LOW 값을 주어 LED를 끈다.

 

이는 데이터시트를 확인해야 알 수 있는데 간단히 설명하자면 LED를 키고 싶다(HIGH) *(0x40011000) = 8192 LED를 끄고 싶다(LOW) *(0x40011000) = (8192<<16)로 보면된다.

 

위의 코드를 실행시켜보면 pc13과 연결된 내장된 LED가 깜빡이는 것을 확인할 수 있다.

 

전체코드

https://github.com/rorosi/STM32F103C8T6/blob/main/LED/GPIO_%EC%88%98%EB%8F%99%EC%A0%9C%EC%96%B41.c

 

GitHub - rorosi/STM32F103C8T6

Contribute to rorosi/STM32F103C8T6 development by creating an account on GitHub.

github.com

 

'STM32 > STM32F103C8T6' 카테고리의 다른 글

GPIO_LED_수동제어2  (0) 2021.12.31