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
'STM32 > STM32F103C8T6' 카테고리의 다른 글
GPIO_LED_수동제어2 (0) | 2021.12.31 |
---|