MX_GPIO_Init()을 따라가보면 아래와 같은 코드들이 나온다.
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_LED_Pin; //8192 = 1 << 13
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 1
GPIO_InitStruct.Pull = GPIO_PULLDOWN; // 2
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 3
HAL_GPIO_Init(GPIO_LED_GPIO_Port, &GPIO_InitStruct);
그리고 GPIO_InitTypeDef를 따라가보면 아래와 같은 코드들이 나온다.
typedef struct
{
uint32_t Pin;
uint32_t Mode;
uint32_t Pull;
uint32_t Speed;
} GPIO_InitTypeDef;
HAL_GPIO_Init()을 따라가다보면 아래와 같은 코드가 나온다.
while (((GPIO_Init->Pin) >> position) != 0x00u)
{
생략
}
position++;
}
}
GPIO_Init->Pin = GPIO_InitStruct.Pin과 같고 GPIO_LED_Pin값이 들어가므로 GPIO_Init->Pin은 "1<<13"과 같다. 그리고 우측으로 position만큼 쉬프트 될 것이다. 이진수로 표현하면 아래와 같다.
*(GPIO_InitStruct.Pin) = 0x20004fe0 //포인터 주소
GPIO_InitStruct.Pin = 134222428 //포인터 주소에 들어있는 값
GPIO_InitStruct.Pin = GPIO_LED_Pin //134222428 대신 8192가 들어감
1000000000000
0100000000000
0010000000000
0001000000000
0000100000000
0000010000000
0000001000000
0000000100000
0000000010000
0000000001000
0000000000100
0000000000010
0000000000001
0000000000000 이때 STOP!
마지막으로 "!=0x00"이므로 0x00이 되는순간 거짓이되서 while문을 탈출하게 된다.
ioposition = (0x01uL << position);
iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;
if (iocurrent == ioposition)
{
생략
}
//ioposition과 관련된 코드(ioposition은 1씩 왼쪽으로 position만큼 쉬프트한다는걸 알 수 있다.)
1 // 0x01uL << 1
10 // 0x01uL << 2
100 // 0x01uL << 3
1000 // 0x01uL << 4
...
// iocurrent과 관련된 코드
10000000000000 & ioposition
결과적으로 ioposition이 왼쪽으로 1씩 쉬프트하고 1<<13에 도달했을때 "if (iocurrent == ioposition)"가 실행된다.
왜 이런짓을 하는걸까?
- 그냥 핀이 몇 번째인지 확인하고 싶어서 만든 코드인거 같다.
switch (GPIO_Init->Mode)
{
// 우리는 여기서 걸린다.
case GPIO_MODE_OUTPUT_PP:
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed)); //작동안함
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
break;
case GPIO_MODE_OUTPUT_OD:
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed)); //작동안함
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_OD; //이게 중요해
break;
...
}
config를 분석해보면 결국은 3이 나온다는걸 알 수 있다.
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
config = 3 + 0 = 3
configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL : &GPIOx->CRH;
registeroffset = (iocurrent < GPIO_PIN_8) ? (position << 2u) : ((position - 8u) << 2u);
TIP : 3항 연산자
X = A ? B : C
--> A의 조건이 참이라면 B가 X로 들어가고 거짓이라면 C가 X로 들어간다.
Q : 그렇다면 (iocurrent < GPIO_PIN_8)가 뜻하는걸 뭘까?
A : iocurrent는 position값이 증가함에 따라 1이 왼쪽으로 쉬프트 된다. GPIO_PIN_8은 1이 왼쪽으로 8번 쉬프트된 값이다. 위 코드를 보면 iocurrent가 GPIO_PIN_8보다 작다면 "참" 크다면 "거짓"이라고 말해준다.
결과가 참이라면 &GPIOx->CRL(0~7)을 넣어줄거고, 거짓이라면 &GPIOx->CRH(8~15);이다.
iocurrent는 1<<13이므로 configregister에는 &GPIOx->CRH가 들어가게 될 것이다.
(주소가 들어간다는점 주의!)
GPIOC는 CRL, CRH가 나뉘어 움직이는듯 싶다.
Q : configregister과 마찬가지로 3항 연산자 중에서도 거짓의 값을 가져올 것이다.
(position - 8u) << 2u)를 풀어써보면 5<<2u이므로 20이 나올것이다.
즉
configregister = 0x40011004
registeroffset = 20
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)))
#WRITE_REG(REG, VAL) ((REG) = (VAL))
// MODIFY_REG(WRITE_REG)
// WRITE_REG는 *(4001004) = (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))이다.
#define MODIFY_REG
1) *(configregister) = REG
2) (GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset = CLEARMASK
3) (config << registeroffset) = SETMASK
#WRITE_REG(REG, VAL) ((REG) = (VAL)) -----------VAL값을 REG에 넣겠다.
1) (REG) = REG
2) ((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)) = VAL
** (pointer)(configregister) = (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))
** (pointer)0x40011004 = (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))
WRITE_REG의 REG와 VAL를 해석해보자.
VAL = (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))
1) SETMASK
--> 3<<20
2) CLEARMASK
--> GPIO_CRL_MODE0 | GPIO_CRL_CNF0 = 3|12 = 15
--> 15 << registeroffset = 15 << 20 = 1111 0000 0000 0000 0000 0000
** 근데 왜 (((READ_REG(REG)) & (~(CLEARMASK)))이런식으로 작동하는걸까?
기존에 설정된 값에서 변화시키고 싶은 비트가 있어서 "~"를 사용한다.
결과적으로 위와같이 복잡한 코드를
"(0x40011004) = ((0x40011004) & ~(15UL << 20 )) | (3 <<20);"로 정리할 수 있다.