1. MX_GPIO_Init();
=> 7강 글에 나와있듯이 MX_GPIO_Init();에서 핵심 함수는 _HAL_RCC_GPIOC_CLK_ENABLE();이었고
=> 또 이 함수에서의 핵심 함수는 SET_BIT(RCC->APB2EENR, RCC_APB2ENR_IOPCEN);이었고
=> 또 이 함수의 실제 연산은 SET_BIT(REG,BIT) ((REG) |= (BIT))였기에
=> 결론적으로 *(0x40021018) |= 16과 같았다.
=> 따라서 아래 오른쪽 사진과 같이 volatile unsigned int * reg = 0x40021018; *reg |= 16;과 작성했다.
2. MX_GPIO_Init(); 내부에 있는 다른 코드를 분석해보자
: 현재 __HAL_RCC_GPIOC_CLK_ENABLE();은 위에서와 같이 분석한 상태 (PC13 제어하기에 GPIOA는 구현 X)
* HAL_GPIO_WritePin();은 이후에 나오는 코드와 동일하기에 생략
2-1. GPIO_InitTypeDef GPIO_InitStruct = {0};
- uint32_t 이므로 unsigned int 32bit 이기에 4byte씩 공간 존재
2-2. 2번째 빨간 네모박스 부분
- GPIO_LED_Pin(우리는 GPIO PC13을 제어하기에 PC13 핀을 의미)은 GPIO_PIN_13으로 정의되어있고, GPIO_PIN_13은 ((uint16_t)0x2000)로 정의되어있음 (이때, 0x2000 = 8192 = 1 << 13)
- GPIO_MODE_OUTPUT_PP은 0x00000001u로 정의
- GPIO_PULLDOWN은 0x00000002u로 정의
- GPIO_SPEED_FREQ_HIGH은 (GPIO_CRL_MODE0)로 정의되어있고, GPIO_CRL_MODE0 은 GPIO_CRL_MODE0_Msk로, GPIO_CRL_MODE0_Msk는 (0x3UL << GPIO_CRL_MODE0_Pos)로, 이때 GPIO_CRL_MODE0_Pos는 (0U)로 정의되어있기에결론적으로 3과 같다
결론.
GPIO_InitStruct.Pin = 1 << 13;
GPIO_InitStruct.Mode = 1;
GPIO_InitStruct.Pull = 2;
GPIO_InitStruct.Speed = 3;
+ HAL_GPIO_Init(GPIO_LED_GPIO_Port, &GPIO_InitStruct);
- GPIO_LED_GPIO_Port는 0x40011000
- &GPIO_InitStruct는 위에 있는 구조체의 주소값 전달
2-3. HAL_GPIO_Init()함수에 들어가보자
- GPIO 모임 중에 우리는 C를 볼 것
- 이때, C모임 중 GPIO 개수는 16개 존재
- 우리는 13이라는 번호 가진 핀 제어하고 싶음
(1) assert 부분
: 정작 하는 일은 없지만 인자값이 0/1이면 어떤 msg를 보내는 함수
: GPIO pin이 제대로 맞는지 chk 함수 (이때, GPIO_Init->Pin)은 1을 옆으로 13번 민 LED PC13
: GPIO mode에 아까 GPIO_MODE_OUTPUT_PP을 대입했었는데 이 값이 유효한지 체크하는 함수
(2) while문 ( 사진에서 긴 부분이 다 while문에 속해있음)
=> 이때 큰 부분만 봐보자
=> GPIO_Init으로 GPIO_InitTypeDef 구조체 주소 넘겼었음
=> 이때 이 구조체의 Pin은 GPIO_LED_Pin으로 초기화했었고 이때 GPIO_LED_Pin은 PC13을 나타내는 (1<<13)이었음
=> 이때 position은 HAL_GPIO_Init 함수 초기 부분에서 position = 0x00u로 초기화했었음
* position은 while문 돌때마다 마지막에 position++; 통해 1씩 증가
=> while문의 전체적인 동작 방식은
while ((1 << 13) >> position) != 0x00u){
...
position++;
}
이므로
1000000000000
0100000000000
0010000000000
0001000000000
...
0000000000001
0000000000000 <- 이때 멈춤
1) while문 내부의 if문
- ioposition = 1 << 0(현재 position : 0)
- iocurrent = GPIO_Init->Pin(1 << 13) & 1;
- if (iocurrent == ioposition)이 참이되려면 결국 iocurrent와 ioposition 모두 1 << 13이 되는 방법밖에 없다.
<=> 즉, Pin13번인지 알고 싶어서 chk하는 코드 - ioposition은,
1
10
100
... - iocurrent는
10 0000 0000 0000 & 1(00 0000 0000 0001) = 0
10 0000 0000 0000 & 10(00 0000 0000 0010) = 0
10 0000 0000 0000 & 100(00 0000 0000 0100) = 0
... - 결국, ioposition과 iocurrent 모두 10 0000 0000 0000 (1 << 13)일 때 if문 만족
- 즉, position이 13이 될 때 ioposition이 10 0000 0000 0000이 되고 결국 iocurrent도 10 0000 0000 0000 & 10 0000 00000 0000이 되어 둘 다 10 0000 0000 0000이 된다.
- if문 만족하면 assert_param(IS_GPIO_AF_INSTANCE(GPIOx));가 실행되는데 이건 아무 동작도 x
2) if문 내부 switch문
=> switch문의 목적 : config 값 설정
- switch문의 GPIO_Init->Mode= GPIO_MODE_OUTPUT_PP(1);로 설정했었음
- 따라서 첫번째 case문에 바로 걸림
- config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
⇔ config = 3 + 0 (speed는 우리가 정의했었고, GPIO_CR_CNF_GP_OUTPUT_PP는 0으로 정의되어있음) - break; 실행되어 switch문 나가짐
3) switch문 탈출 후 3문장(if문 내부이기에 현재 ioposition, iocurrent 모두 1 << 13인 상태
- configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL : &GPIOx->CRH;
=> GPIO에서 8핀 이상인지 아닌지 구분하기 위한 코드
=> 0~7핀이면 configregister에 CRL부분을, 8핀 이상이면 CRH 부분을 조작하게 하는 코드 (CRL, CRH : Control register low/high)
=> (1 << 13 < 0x0100) 이므로 configregister = &GPIOx->CRH ⇔ configregister = (0x40011004);
*GPIOC = 0x40011000 - registeroffset = (iocurrent < GPIO_PIN_8) ? & (position << 2u) : ((position - 8u) << 2u);
=> ( 1 << 13 < 0x0100) 이므로 registeroffset = (13 - 8) << 2; ⇔ registeroffset = 20; - MODIFY_REG((*configregister), ((GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset), (config << registeroffset));
* GPIO_CRL_MODE0, GPIO_CRL_CNF0는 ctrl + 왼쪽 마우스로 다 찾을 수 있음
⇔ MODIFY_REG(*(0x40011004) , (3| 3 << 2) << 20 , (3 << 20));
⇔ MODIFY_REG(*(0x40011004), (3 | 12) << 20, (3 << 20));
⇔ *(0x40011004)에 값이 있는데 변화시키고 싶은 bit가 있어서 바꾸고자하는 코드
=> #define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
=> #define READ_REG(REG) ((REG))
=> #define WRITE_REG(REG, VAL) ((REG) = (VAL))
⇔ *(0x40011004) = VAL
ex) *(0x40011004)에 111100001이 저장되어있을 때 두 bit를 바꾸기 위해 ~001100000 = 110011111로 만들고
111100001 & 110011111 = 110000001이 된다.
=> 이때 SETMASK = 000100000라면, 최종적으로 110000001 | 000100000 = 110100001이 만들어짐 - WRITE_REG의 매개변수 더 자세히 분석해보자
- READ_REG(REG) = READ_REG(*configregister) = 1145324612 = 1000100010001000100010001000100
- CLEARMASK = (3 | 12) << 20 = ((0011) | (1100)) << 20 = 15 << 20 = 1111 0000 0000 0000 0000 0000
- SETMASK = 3 << 20 = 0011 0000 0000 0000 0000 0000
=> READ_REG(REG) & 1111 1111 0000 1111 1111 1111 1111 1111(unsigned int라서 뒤집으면 앞 8bit 1이됨)
= 0100 0100 0100 0100 0100 0100 0100 0100 & 1111 1111 0000 1111 1111 1111 1111 1111
= 0100 0100 0000 0100 0100 0100 0100 0100
=> 0100 0100 0000 0100 0100 0100 0100 0100 | SETMASK
= 0100 0100 0000 0100 0100 0100 0100 0100 | 0000 0000 0011 0000 0000 0000 0000 0000
= 0100 0100 0011 0100 0100 0100 0100 0100
결론.
MODIFY_REG((*configregister), ((GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset), (config << registeroffset));은
*(0x40011004) = (*(0x40011004) & ~(15UL << 20)) | (3 << 20);과 같다
3. 최종코드
4. 주소
- 우리가 주소 접근한 곳은 GPIO Port C이고 이때 메뉴얼을 보면,
0x40011000 ~ 0x400113ff라고 되어있음 - GPIO를 제어하고 싶다면 A~G 주소번지를 접근해서 조작해야함
- 이러한 설계는 ARM코어 설계자가 정한것
- struct GPIO_TypeDef에서 CRL, CRH,...들이 __IO uint32_t로 정의된 이유를 설명해줌
- 이때 우리는 GPIOC_CRH를 설정하였음
- GPIO 0부터 7까지는 CRL(Low), 8 ~ 15는 CRH(High)
- 4비트(MODE 2bit + CNFO 2 bit)마다 GPIOx번을 설정함
- 우리는 CNF는 output mode였기에 00(general purpose output push-pull)로 설정
- MODE는 output mode였기에 01/10/11 중 11로 설정
5. 궁극적인 목적
- GPIO가 뭐지 ?
: 결국 내가 프로그래밍한 소스로 인해 신호를 주면 output일 경우엔 High 전압을 내보내고, 밑으로 내리면 Low 전압을 보내게 하는 것 / input일 경우엔 high 전원이 들어오면 프로그램상에서 1인지 0인지 구분하는 게 GPIO 하는것
- 이 칩에서는 어떻게 GPIO 제어하는 거지?
: 데이터시트를 보면서 GPIO제어하고 싶으면 어떤 방법으로 제어하는지 파악
- 결국 어떻게 소스로 작성하지?
: 학습된 정보를 통해 소스코드를 만들어냄
'임베디드' 카테고리의 다른 글
[오제이 튜브의 임베디드 강의] 10강. 회로도 딱 필요한 만큼만 배우자 (2) | 2025.01.09 |
---|---|
[오제이 튜브의 임베디드 강의] 9강. 지금까지 배운 것을 큰 그림에 저장하기 (0) | 2025.01.09 |
[오제이 튜브의 임베디드 강의] 7강. GPIO제어 부셔먹기 (0) | 2025.01.06 |
[오제이 튜브의 임베디드 강의] 6강. 혼자서 임베디드 고수 되는 방법 (1) | 2025.01.06 |
[오제이 튜브의 임베디드 강의] 5강. 환경 구축해보기 (1) | 2025.01.02 |