STM32 GPIO Register 제어
STM32 GPIO Register 제어
STM32L073RZ pinmap
📟 STM32 GPIO Register 제어
RM0367 Reference manual을 확인하며 작성
RM0367 Reference manual
GPIO 제어를 위해 다음 단계가 필요
- RCC GPIO clock enable
- GPIO Input/Ouput Mode 설정
- GPIO Pin 설정
📌 1. RCC_IOPENR
0X4002 1000 - 0X4002 13FF 에 RCC 위치
RCC_IOPENR의 offset : 0x2C
즉 0x4002 1000 + 0x2C 에 RCC_IOPENR 위치
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의 시작 주소 : 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)
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
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;
}
}