HAL_GPIO_Init

김지성·2022년 7월 7일
1

Embedded

목록 보기
4/19

MX_GPIO_Init()을 따라가보면 아래와 같은 코드들이 나온다.

1.1 MX_GPIO_Init() + GPIO_InitTypeDef

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;

1.2 HAL_GPIO_Init()

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문을 탈출하게 된다.


1.3 ioposition & iocurrent

   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)"가 실행된다.

왜 이런짓을 하는걸까?

  • 그냥 핀이 몇 번째인지 확인하고 싶어서 만든 코드인거 같다.

1.4 switch(GPIO_Init->Mode)

      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


1.5 configregister & registeroffset

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로 들어간다.

  • configregister

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가 나뉘어 움직이는듯 싶다.

  • registeroffset

Q : configregister과 마찬가지로 3항 연산자 중에서도 거짓의 값을 가져올 것이다.
(position - 8u) << 2u)를 풀어써보면 5<<2u이므로 20이 나올것이다.



configregister = 0x40011004
registeroffset = 20


1.6 MODIFY_REG

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)))이런식으로 작동하는걸까?

기존에 설정된 값에서 변화시키고 싶은 비트가 있어서 "~"를 사용한다.


1.7 최종

결과적으로 위와같이 복잡한 코드를
"(0x40011004) = ((0x40011004) & ~(15UL << 20 )) | (3 <<20);"로 정리할 수 있다.

profile
JUST DO IT

0개의 댓글