포스트

STM32 GPIO Register 제어

STM32 GPIO Register 제어

STM32L073RZ pinmap

STM32L073RZ pinmap


📟 STM32 GPIO Register 제어

RM0367 Reference manual을 확인하며 작성
RM0367 Reference manual


GPIO 제어를 위해 다음 단계가 필요

  1. RCC GPIO clock enable
  2. GPIO Input/Ouput Mode 설정
  3. GPIO Pin 설정


📌 1. RCC_IOPENR

RCC 위치

0X4002 1000 - 0X4002 13FF 에 RCC 위치

RCC_IOPENR offset 확인

RCC_IOPENR의 offset : 0x2C
즉 0x4002 1000 + 0x2C 에 RCC_IOPENR 위치

RCC_IOPENR RCC_IOPENR_PORTA

RCC_IOPENR의 Bit[0:0]에 1로 설정하여 port A clock enable

1
2
3
//RCC_IOPENR 활성화
//RCC->IOPENR |= 0x1;
*((volatile unsigned int*)0X40021000 + 0x2C) |= 0x1;


📌 2. GPIO Input/Ouput Mode 설정

GPIOA GPIOx_MODER

GPIOA의 시작 주소 : 0X5000 0000
GPIOx_MODER의 offset : 0x00
즉, GPIOA_MODER의 위치 : 0X5000 0000 + 0x00

PA5(port A, pin 5)를 GPIO 제어할 것이므로 MODE5[1:0]01 : General purpose output mode 로 설정

1
2
3
4
5
//GPIO : PA5
//GPIOA->MODER &=~(0x3 << (5 * 2));
//GPIOA->MODER |= (val << (5 * 2));
*((volatile unsigned int*)0X50000000 + 0x00) &= ~(0x3 << (5 * 2));   //나머지 비트는 살리기
*((volatile unsigned int*)0X50000000 + 0x00) |=  (0x01 << (5 * 2));  //MODE5[1:0]를 0x01으로 설정


📌 3. GPIO pin 설정

ODR 레지스터로 제어 (GPIO port output data register)

GPIO_ODR

GPIOA의 시작 주소 : 0X5000 0000
GPIOx_ODR의 offset : 0x14

1
2
3
4
5
//GPIOA->ODR &= 0 << 5;
//GPIOA->ODR |= val << 5;

*((volatile unsigned int*)0X50000000 + 0x14) &= (0x0 << 5); //초기화
*((volatile unsigned int*)0X50000000 + 0x14) |= (0x1 << 5); //HIGH 설정


Interrupt

NVIC Vector Table

NVIC Vector Table

startup_stm32l073xx.s에 interrupt vector table 정의

PC13을 통해 Interrupt 할 경우, EXTI4_15_IRQHandler를 정의해야함
(Pin 4 ~ 15의 Interrupt를 담당)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*-----   stm32l0xx_it.c   -----*/
void EXTI4_15_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);  // 인터럽트 핸들러 호출
}

/*-----   stm32l0xx_hal_gpio.c   -----*/
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{	
  UNUSED(GPIO_Pin); //사용하지 않는 변수 컴파일러 경고 방지용
}

/*-----   main.c   -----*/
/*
* 위의 __weak로 정의된 HAL_GPIO_EXTI_Callback를 오버라이드
* 인터럽트 서비스 루틴(ISR)에 시간이 오래걸리는 작업이 있는 것은 
* 시스템 리소스를 잠금 -> flag를 통해 메인 루프나 다른 태스크에서 처리하는 것이 좋음
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{	
  //올바른 예
  if(GPIO_Pin == GPIO_PIN_13) {
        flag = 1;  // 플래그를 설정
    }

  //올바르지 않은 예 : ISR에서 GPIO 제어
	if(GPIO_Pin == GPIO_PIN_13) {
		HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); 
	}
}



PB5 -> SW420 진동감지센서 연결

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*-----   stm32l0xx_it.c   -----*/
void EXTI4_15_IRQHandler(void)
{
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
}


/*-----   main.c   -----*/
//PB5 
initval.Pin = GPIO_PIN_5;
initval.Mode = GPIO_MODE_IT_RISING; // Interrupt, Rising Edge 감지
initval.Pull = GPIO_NOPULL;
initval.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_GPIO_Init(GPIOB, &initval);

//EXTI4_15_IRQn 인터럽트 설정
HAL_NVIC_SetPriority((IRQn_Type)EXTI4_15_IRQn, 0x0D, 0);		//EXTI4_15_IRQn
HAL_NVIC_EnableIRQ((IRQn_Type)EXTI4_15_IRQn);


while (1)
{
  static int cnt = 0;
  static int detect = 1;
  
  if(g_flag){
    if(flag13){
      HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
      
      detect = !detect;
      
      flag13 = 0;
    }
    if(flag5 && detect){
      if(cnt++ > 10){
        cnt = 0;
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_6);
      }
      flag5 = 0;
    }
    g_flag = 0;
  }
  HAL_Delay(100);
}

// Interrupt Callback
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
	g_flag = 1;
	
	switch(GPIO_Pin){
		case GPIO_PIN_13:
			flag13 = 1;
			break;
		
		case GPIO_PIN_5:
			flag5 = 1;
			break;
		
		default:
			break;
	}
}