
- 개발보드 회로도


개발보드에 있는 SW를 PA0에, LED를 PC13에 설정해주었다.
SW: GPIO INPUT MODE
LED: GPIO OUTPUT MODE
설정을 해주면 코드가 알아서 생성된다.
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_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_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIO_LED_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : GPIO_SW_Pin */
GPIO_InitStruct.Pin = GPIO_SW_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIO_SW_GPIO_Port, &GPIO_InitStruct);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();


/*GPOPC*/
SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPCEN);
/*GPIOA*/
SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);
#define SET_BIT(REG, BIT) ((REG) |= (BIT))
or 연산을 하는 코드이다.


REG = RCC->APB2ENR = 0x40021018
BIT = RCC_APB2ENR_IOPCEN = 16
*(0x40021018) |= (1<<4) : BIT 4 활성화
REG = RCC->APB2ENR = 0x40021018
BIT = RCC_APB2ENR_IOPAEN = 4
*(0x40021018) |= (1<<2) : BIT 2 활성화


RCC_APB2ENR (APB2 Peripheral Clock Enable Register)는 STM32 마이크로컨트롤러에서 APB2(Advanced Peripheral Bus 2)에 연결된 다양한 주변 장치의 클럭을 활성화하거나 비활성화하는 역할을 하는 레지스터이다.
이 레지스터는 RCC(Reset and Clock Control) 모듈의 일부로, 시스템의 주변 장치 클럭을 관리한다.
따라서 PORT A의 클럭을 활성화 시키기 위해서는 BIT 2를 활성화해야 하고,
PORT C의 클럭을 활성화 시키기 위해서는 BIT 4를 활성화해야 한다.
그래서
*(0x40021018) |= (1<<2) : BIT 2 활성화
*(0x40021018) |= (1<<4) : BIT 4 활성화
이러한 코드가 나온 것이다.

장치마다 클럭이 다르기 때문에 클럭 속도마다 연결되는 버스가 다르다.
* APB 란?
APB는 CPU와 주변 장치 간의 통신을 효율적으로 처리하는 역할을 한다.
APB1 : 상대적으로 속도가 낮은 장치들과 연결된다.
APB2 : 고속장치들이 연결된다.
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIO_LED_GPIO_Port, GPIO_LED_Pin, GPIO_PIN_SET);


GPIO_LED_GPIO_Port : 0x40011000
GPIO_LED_GPIO_Port ->BSRR : 0x40011010
GPIO_LED_Pin : 8192 = binary 1000000000000 : BIT 13
GPIO_PIN_SET : 1
PC13 포트를 제어한다.
if (PinState != GPIO_PIN_RESET)
{
GPIOx->BSRR = GPIO_Pin;
}
else
{
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
}
PinState 가 1이면 GPIOx_BSRR에서 BIT 13(BS13)에 1을 넣어라 = set
PinState 가 0이면 GPIOx_BSRR에서 BIT 29(BR13)에 1을 넣어라 = reset

/* Initialize all configured peripherals */
//MX_GPIO_Init(); //주석처리
/* USER CODE BEGIN 2 */
// __HAL_RCC_GPIOC_CLK_ENABLE(); 클럭 활성화
volatile unsigned int * reg = 0x40021018;
*reg |= 16;
// 다음시간에 자세히 다룸
GPIO_InitTypeDef GPIO_InitStruct = {0};
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);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
volatile unsigned int *reg2 = 0x40011010;
while (1)
{
//HAL_GPIO_WritePin();
*reg2 = 0x2000; //high - led off
HAL_Delay(1000);
*reg2 = (0x2000 << 16); // low - led on
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
이렇게하면 LED가 깜빡거리는 코드를 실행할 수 있다.
/*Configure GPIO pin : GPIO_LED_Pin */
GPIO_InitStruct.Pin = GPIO_LED_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIO_LED_GPIO_Port, &GPIO_InitStruct);

처음에 first.ioc 에서 LED를 GPIO OUTPUT 으로 설정할 때 옵션을 설정한 것을 자동으로 코드로 바꿔준 부분이다.

PIN = 8192 (= 1<<13) = 13번 핀을 설정 (PC13)
MODE = 1 : OUTPUT 옵션
PULL = 2 : PULLDOWN 옵션
SPEED = 3 : HIGH 옵션
HAL_GPIO_Init(GPIO_LED_GPIO_Port, &GPIO_InitStruct);

위에서 설정한 옵션을 GPIO_LED_GPIO_Port로 넣어준다
이때 GPIO_LED_GPIO_Port는 GPIOC이다.

void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
{
uint32_t position = 0x00u;
uint32_t ioposition;
uint32_t iocurrent;
uint32_t temp;
uint32_t config = 0x00u;
__IO uint32_t *configregister; /* Store the address of CRL or CRH register based on pin number */
uint32_t registeroffset; /* offset used during computation of CNF and MODE bits placement inside CRL or CRH register */
/* Check the parameters */
assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
assert_param(IS_GPIO_MODE(GPIO_Init->Mode));
/* Configure the port pins */
while (((GPIO_Init->Pin) >> position) != 0x00u)
{
/* Get the IO position */
ioposition = (0x01uL << position);
/* Get the current IO position */
iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;
if (iocurrent == ioposition)
{
/* Check the Alternate function parameters */
assert_param(IS_GPIO_AF_INSTANCE(GPIOx));
/* Based on the required mode, filling config variable with MODEy[1:0] and CNFy[3:2] corresponding bits */
switch (GPIO_Init->Mode)
{
/* If we are configuring the pin in OUTPUT push-pull mode */
case GPIO_MODE_OUTPUT_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
break;
/* If we are configuring the pin in OUTPUT open-drain mode */
case GPIO_MODE_OUTPUT_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_OD;
break;
/* If we are configuring the pin in ALTERNATE FUNCTION push-pull mode */
case GPIO_MODE_AF_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_PP;
break;
/* If we are configuring the pin in ALTERNATE FUNCTION open-drain mode */
case GPIO_MODE_AF_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_OD;
break;
/* If we are configuring the pin in INPUT (also applicable to EVENT and IT mode) */
case GPIO_MODE_INPUT:
case GPIO_MODE_IT_RISING:
case GPIO_MODE_IT_FALLING:
case GPIO_MODE_IT_RISING_FALLING:
case GPIO_MODE_EVT_RISING:
case GPIO_MODE_EVT_FALLING:
case GPIO_MODE_EVT_RISING_FALLING:
/* Check the GPIO pull parameter */
assert_param(IS_GPIO_PULL(GPIO_Init->Pull));
if (GPIO_Init->Pull == GPIO_NOPULL)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_FLOATING;
}
else if (GPIO_Init->Pull == GPIO_PULLUP)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;
/* Set the corresponding ODR bit */
GPIOx->BSRR = ioposition;
}
else /* GPIO_PULLDOWN */
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;
/* Reset the corresponding ODR bit */
GPIOx->BRR = ioposition;
}
break;
/* If we are configuring the pin in INPUT analog mode */
case GPIO_MODE_ANALOG:
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_ANALOG;
break;
/* Parameters are checked with assert_param */
default:
break;
}
/* Check if the current bit belongs to first half or last half of the pin count number
in order to address CRH or CRL register*/
configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL : &GPIOx->CRH;
registeroffset = (iocurrent < GPIO_PIN_8) ? (position << 2u) : ((position - 8u) << 2u);
/* Apply the new configuration of the pin to the register */
MODIFY_REG((*configregister), ((GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset), (config << registeroffset));
/*--------------------- EXTI Mode Configuration ------------------------*/
/* Configure the External Interrupt or event for the current IO */
if ((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
{
/* Enable AFIO Clock */
__HAL_RCC_AFIO_CLK_ENABLE();
temp = AFIO->EXTICR[position >> 2u];
CLEAR_BIT(temp, (0x0Fu) << (4u * (position & 0x03u)));
SET_BIT(temp, (GPIO_GET_INDEX(GPIOx)) << (4u * (position & 0x03u)));
AFIO->EXTICR[position >> 2u] = temp;
/* Enable or disable the rising trigger */
if ((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
{
SET_BIT(EXTI->RTSR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->RTSR, iocurrent);
}
/* Enable or disable the falling trigger */
if ((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
{
SET_BIT(EXTI->FTSR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->FTSR, iocurrent);
}
/* Configure the event mask */
if ((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
{
SET_BIT(EXTI->EMR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->EMR, iocurrent);
}
/* Configure the interrupt mask */
if ((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
{
SET_BIT(EXTI->IMR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->IMR, iocurrent);
}
}
}
position++;
}
}
코드를 확인해보면 이렇게 긴데 while문부터 해석해보면,
while (((GPIO_Init->Pin) >> position) != 0x00u){
.
.
position++;
}
binary
10000000000000 : GPIO_Init -> Pin , position == 0
01000000000000 position == 1
00100000000000 position == 2
00000000000001 position == 13
00000000000000 <- 이 때 while문 멈춤
ioposition = (0x01uL << position);
binary
1 position == 0
10
100
1000
10000
10000000000000 position == 13
iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;
GPIO_Init->Pin : 10 0000 0000 0000
ioposition은 1부터 ~ 10 0000 0000 0000까지
ioposition이 10 0000 0000 0000 이 아니라면 iocurrent == 0이다.
if (iocurrent == ioposition)
{
}
-> iocurrent == ioposition 라는 것은 PIN 13번일 때를 찾는 코드였던것이다.
position == 13 일 때 if문에 들어갈 수 있다.
/* Based on the required mode, filling config variable with MODEy[1:0] and CNFy[3:2] corresponding bits */
switch (GPIO_Init->Mode)
{
/* If we are configuring the pin in OUTPUT push-pull mode */
case GPIO_MODE_OUTPUT_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
break;
/* If we are configuring the pin in OUTPUT open-drain mode */
case GPIO_MODE_OUTPUT_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_OD;
break;
/* If we are configuring the pin in ALTERNATE FUNCTION push-pull mode */
case GPIO_MODE_AF_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_PP;
break;
/* If we are configuring the pin in ALTERNATE FUNCTION open-drain mode */
case GPIO_MODE_AF_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_OD;
break;
/* If we are configuring the pin in INPUT (also applicable to EVENT and IT mode) */
case GPIO_MODE_INPUT:
case GPIO_MODE_IT_RISING:
case GPIO_MODE_IT_FALLING:
case GPIO_MODE_IT_RISING_FALLING:
case GPIO_MODE_EVT_RISING:
case GPIO_MODE_EVT_FALLING:
case GPIO_MODE_EVT_RISING_FALLING:
/* Check the GPIO pull parameter */
assert_param(IS_GPIO_PULL(GPIO_Init->Pull));
if (GPIO_Init->Pull == GPIO_NOPULL)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_FLOATING;
}
else if (GPIO_Init->Pull == GPIO_PULLUP)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;
/* Set the corresponding ODR bit */
GPIOx->BSRR = ioposition;
}
else /* GPIO_PULLDOWN */
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;
/* Reset the corresponding ODR bit */
GPIOx->BRR = ioposition;
}
break;
/* If we are configuring the pin in INPUT analog mode */
case GPIO_MODE_ANALOG:
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_ANALOG;
break;
/* Parameters are checked with assert_param */
default:
break;
}
case GPIO_MODE_OUTPUT_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
break;
스위치 문의 목적은 config 값을 설정하기 위함이였다.
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
/* GPIO_Init-> Speed == 3*/
#define GPIO_SPEED_FREQ_HIGH (GPIO_CRL_MODE0) /*!< High speed */
#define GPIO_CRL_MODE0 GPIO_CRL_MODE0_Msk
#define GPIO_CRL_MODE0_Msk (0x3UL << GPIO_CRL_MODE0_Pos)
#define GPIO_CRL_MODE0_Pos (0U)
/* GPIO_CR_CNF_GP_OUTPUT_PP == 0*/
#define GPIO_CR_CNF_GP_OUTPUT_PP 0x00000000u /*!< 00: General purpose output push-pull */
config = 3 + 0 = 3
/* Check if the current bit belongs to first half or last half of the pin count number
in order to address CRH or CRL register*/
configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL : &GPIOx->CRH;
registeroffset = (iocurrent < GPIO_PIN_8) ? (position << 2u) : ((position - 8u) << 2u);
configregister, registeroffset:
13 pin이므로,
configregister = 0x40011004
registeroffset = 20

/* Apply the new configuration of the pin to the register */
MODIFY_REG((*configregister), ((GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset), (config << registeroffset));
#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
#define WRITE_REG(REG, VAL) ((REG) = (VAL))
#define READ_REG(REG) ((REG))
/* GPIO_CRL_MODE0 = (3<<0) = 3 */
#define GPIO_CRL_MODE0_Pos (0U)
#define GPIO_CRL_MODE0_Msk (0x3UL << GPIO_CRL_MODE0_Pos) /*!< 0x00000003 */
#define GPIO_CRL_MODE0 GPIO_CRL_MODE0_Msk
/* GPIO_CRL_CNF0 (3<<2) = 12 */
#define GPIO_CRL_CNF0_Pos (2U)
#define GPIO_CRL_CNF0_Msk (0x3UL << GPIO_CRL_CNF0_Pos) /*!< 0x0000000C */
#define GPIO_CRL_CNF0 GPIO_CRL_CNF0_Msk
정리하면,
REG = *configregister = *0x40011004
CLEARMASK = (GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset = (3 | 12) << 20 = 15 << 20 = 15728640
SETMASK = config << registeroffset = 3 << 20 = 3145728


WRITE_REG(REG, (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))
: REG에 이미 설정된 값에 내가 설정하고싶은 값을 비트마스크로 CLEAR, SET을 하는 것이다.
A & (~(B)) : CLEAR, RESET
만약 A : 11001110, B: 01100000이면
~(B) = 10011111
A &(~(B)) 10001110
A | B : SET
만약 A : 11001110, B: 01100000이면
A | B = 11101110
HAL_GPIO_Init()은 핀 번호를 찾고,
config 값을 구한 후,
configregister의 값을 원하는 옵션으로 clear, set을 해주는 것이다.
*(configregister) = (*(configregister) & ~(CLEARMASK)) | (SETMASK);
*(0x40011004) = (*(0x40011004) & ~(15 << 20)) | (3 << 20);

GPIO Port C는 0x40011000 - 0x400113FF 주소에서 다룬다.

/* Initialize all configured peripherals */
//MX_GPIO_Init(); 주석처리
/* USER CODE BEGIN 2 */
// __HAL_RCC_GPIOC_CLK_ENABLE(); 클럭활성화
volatile unsigned int * reg = 0x40021018;
*reg |= 16;
// Configure GPIO pin 핀 옵션
volatile unsigned int * configreg = 0x40011004;
*configreg = (*configreg & ~(15 << 20)) | (3 << 20);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
volatile unsigned int *reg2 = 0x40011010;
while (1)
{
//HAL_GPIO_WritePin(GPIO_LED_GPIO_Port, GPIO_LED_Pin, GPIO_PIN_SET);
*reg2 = 0x2000; //high - led off
HAL_Delay(1000);
*reg2 = (0x2000 << 16); // low - led on
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}