관련글
잡동사니 세상 :: stm32 레지스터 직접 접근 (5. Exception / Interrupt [설명 & 코드 Part 01]) (tistory.com)
잡동사니 세상 :: stm32 레지스터 직접 접근 (5. Exception / Interrupt [중요 레지스터들 Part 03]) (tistory.com)
잡동사니 세상 :: stm32 레지스터 직접 접근 (소개) (tistory.com)
잡동사니 세상 :: stm32 레지스터 직접 접근 (0. Microcontroller와 ARM ) (tistory.com)
잡동사니 세상 :: stm32 레지스터 직접 접근 (1. STM32F10x란) (tistory.com)
잡동사니 세상 :: stm32 레지스터 직접 접근 (2. 프로그램 설치 및 설정) (tistory.com)
잡동사니 세상 :: stm32 레지스터 직접 접근 (3. RCC) (tistory.com)
잡동사니 세상 :: stm32 레지스터 직접 접근 (4. GPIO [Output]) (tistory.com)
잡동사니 세상 :: stm32 레지스터 직접 접근 (4. GPIO [Input]) (tistory.com)
잡동사니 세상 :: stm32 레지스터 직접 접근 (5. Exception / Interrupt [설명 & 코드 Part 01]) (tistory.com)
stm32 레지스터 직접 접근 (5. Exception / Interrupt [중요 레지스터들 Part 03]) (tistory.com)
서론
예제 파일 주소: STM32_Examples/Ex_06_Exception_easy at master · KwangryeolPark/STM32_Examples (github.com)
(헤더 파일 설정들은 자신의 컴퓨터 환경에 맞춰 아래 내용을 따라 수정하셔야합니다.)
앞서 Interrupt가 발생하면 Handler 실행은 하드웨어가 해준다고 했습니다. 여기서 Handler는 일종의 Interrupt가 발생했을 때 호출되는 함수입니다. Interrupt 전용 함수라고 할 수 있죠. Handler는 함수를 만드는 것처럼 프로그래머가 만들어야합니다.
예를 들어 Reset이라는 Interrupt가 발생했을 때 '소리를 울리고 꺼지는 프로그램을 만들고 싶다!' 라고 한다면, Reset handler에 소리를 울리는 코드를 넣으면 되는 거죠. 다음 그림을 보면 조금 이해가 될 수 있을 것 같습니다.
위의 그림에서 main에 있는 whlie문은 프로그램이 끝나지 않도록 하는 장치입니다.
MCU는 main안에 있는 while문을 돌고 있다 Reset interrupt가 발생하면 자동으로 위에 선언된 Reset_Handler라는 함수를 실행시킵니다. 신기하죠?
Reset_Handler 내부는 프로그래머인 제가 짠 순서에 따라 1. 소리를 우리고, 2. 프로그램을 완전히 종료시킨다.로 이뤄져 있으며, 이들을 수행하고 Handler는 끝납니다.
여기서 중요한 점은 Handler의 이름입니다. 하드웨어가 저게 Reset interrupt가 발생 되었을 때 수행되어야 하는 Handler라는 걸 어떻게 알까요? 단순히 함수 이름에 Handler가 있어서? 절대 아닙니다. 해당 이름들은 제가 밑에 올린 URL로 따라가면 있는 startup_stm32f10x_hd_vl.s라는 파일에 기술되어 있으며, 이 함수 이름은 절대로 변하면 안 되는 표준 이름입니다.
startup_stm32f10x_hd_vl.s
프로젝트 설정 (중요)
startup_stm32f10x_hd_vl.s
stm32 헤더 파일
nvic 헤더 파일
코딩
참고 자료
stm32 레지스터 직접 접근 (Interrupt 및 Register 관련 설정) (tistory.com)
CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/iar at master · tuanpmt/CMSIS · GitHub
startup_stm32f10x_hd_vl.s
startup_stm32f10x_hd_vl.s는 아래 사이트에서 다운 받을 수 있습니다.
CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/iar at master · tuanpmt/CMSIS · GitHub
startup_stm32f10x_hd_vl.s는 수정할 일이 극히 드물어서 어떤 파일인지 그리고 어떤 부분을 수정해야 하는지만 알 필요가 있습니다.
startup_stm32f10x_hd_vl.s는 Assembly 언어로 구성된 파일입니다. (.s라는 확장명이 이를 증명하고 있죠.) 55번째 중에 있는 __vector_table이 interrupt vector table입니다. 그리고 57줄부터 134줄까지 DCD 오른쪽에 있는 이름들이 Handlers의 이름입니다. 보시면 위의 코드에 있는 Reset_Handler라는 이름도 보이네요. Reset_Handler에 대해 조금 더 알고 싶으신 분들은 더보기를 클릭해 주세요.
Reset_Handler는 MCU에 전원을 넣을 때, Reset 버튼을 눌렀을 때 발생되는 Reset interrupt가 호출하는 Handler입니다. 따라서 전원을 넣으면 Reset이 되니 처음 실행되는 함수라고 표현할 수도 있겠네요.
여러분이 작성한 main이라는 함수도 Reset_Handler가 호출하는 것이랍니다. (Reset_Handler는 미리 작성되어 있어 프로그래머가 따로 작성할 필요는 없습니다.)
모든 인터럽트를 다룰 것은 아니지만 다음 인터럽트들은 자주 사용될 수도 있는 것들입니다.
Handler | 설명 |
Reset_Handler | Reset이 발생되었을 때 호출됩니다. |
HardFault_Handler | 치명적인 오류가 발생했을 때 호출됩니다. MCU의 마지막 발악이며, 일종의 블루스크린이라고 생각하면 됩니다. |
PendSV_Handler | Kernel에서 멀티테스킹을 위해 사용됩니다. |
SysTick_Handler | 프로그래머가 설정한 시간마다 호출됩니다. |
EXTIn_IRQHandler | 외부 인터럽트에 반응하여 호출됩니다. 이번 시리즈에서 사용할 Handler입니다. |
TIMn_IRQHandler | SysTick_Handler와 유사하게 설정한 시간 또는 모드에 따라 호출됩니다. |
startup_stm32f10x_hd_vl.s에 대한 건 이정도만 아시면 되고, 수정은 다음 줄만 삭제하시면 됩니다.
51번째 줄( EXTERN SystemInit )
145번째 줄(LDR R0, =SystemInit)
146번째 줄(BLX R0)
프로젝트 설정 (중요)
startup_stm32f10x_hd_vl.s를 다운하고 위에 나온데로 수정을 끝냈다면, 해당 파일을 프로젝트에 연결해야합니다. Preprocessing이라고 표현하며, 말 그대로 선행 처리 / 전처리라고 부릅니다.
어려울 수 있는 작업이니 문제가 있으시면 댓글로 남겨주세요.
startup_stm32f10x_hd_vl.s
다음과 같은 과정을 거친 후 프로젝트에 연결됩니다.
프로젝트를 하나 만드신 후 (혹은 전 포스트에서 사용했던 프로젝트)
CMSIS Group을 만듭니다.
CMSIS에 다운 받고 수정한 startup_stm32f10x_hd_vl.s 파일을 추가합니다.
C/C++ Compiler -> Preprocessor를 클릭합니다.
...으로 된 첫 번째 박스를 클릭합니다. (그림 05에서 붉은색으로 된 부분)
<Click to add> 부분을 클릭한 후, startup_stm32f10x_hd_vl.s 파일이 위치한 폴더로 이동합니다.
(파일 자체가 아닌, 파일이 위치한 폴더 입니다!)
그런 다음 폴더 선택 버튼을 누릅니다. (초록색 부분은 사람마다 다릅니다.)
폴더 위치가 맞게 뜨는지 확인한 후, OK버튼을 누릅니다.
처음에 설명한 2. 프로그램 설치 및 설정 내용을 따라 나머지 설정을 완료합니다.
stm32 헤더 파일
stm32 헤더 파일은 gpio, exti, rcc 등의 레지스터 주소를 지정한 파일들입니다. 반드시 필요한 파일들이며, startup 파일을 등록한 것처럼 동일하게 등록하시면 됩니다.
nvic 헤더 파일
nvic 헤더 파일은 cortex_m3 파일 내에 있으며, nvic의 레지스터 주소를 지정한 파일들입니다. 반드시 필요한 파일이며, startup 파일을 등록한 것처럼 동일하게 등록하시면 됩니다. (위의 사진과 약간의 모순이 있는 점 죄송합니다.)
코딩
위의 모든 설정이 끝났습니다. (이상한 부분은 댓글로 남겨주세요.)
위의 설정들은 인터럽트를 사용하기 위한 작업과 레지스터를 쉽게 다루기 위한 작업 2가지이며, 거의 대부분 위의 과정을 필수로 거쳐야한다고 생각하시면 됩니다.
코드는 (5. Exception / Interrupt [설명 & 코드 Part 01]) 끝부분에 있는 main함수와 추가할 Interrupt handler 코드만 추가하면 됩니다.
아래의 코드는 EXTI6 interrupt가 발생하면 PF6에 연결된 LED가 ON되는 코드입니다.
#include "rcc.h"
#include "gpio.h"
#include "afio.h"
#include "exti.h"
#include "nvic.h"
void EXTI9_5_IRQHandler(void) { // EXTI6이 발생되면 호출되는 Handler입니다. (정확히는 EXTI9 ~ 5 중 하나라도 발생한다면)
//Handler이름은 startup_stm32f10x_hd_vl.s에 정의되어 있습니다.
if(EXTI -> PR & (1 << 6)) { // EXTI9 ~ 5중에 EXTI6가 발생된 것인지 확인합니다.
EXTI -> PR |= (1 << 6); // EXTI6가 한 번 발생되었기 때문에 다시 0으로 만들어 줍니다. (1을 write하면 하드웨어적으로 0으로 초기화 됩니다.)
GPIOF -> ODR = (1 << 6); // PF6 LED를 ON합니다.
}
}
int main(void) {
RCC -> APB2ENR |= (1 << IOPGEN) | (1 << IOPFEN) | (1 << AFIOEN); // GPIOG, F에 클럭을 인가합니다. AFIO에 클럭을 인가합니다.
GPIOG -> CRL |= (8 << MODE6); // PG6를 pull-up/pull-down 모드로 설정합니다.
GPIOG -> ODR |= (1 << 6); // PG6을 pull-up 모드로 설정합니다. (Open103Z 보드에 Pull-up 모드를 사용하도록 설계 되어 있습니다.)
GPIOF -> CRL |= (1 << MODE6); //PF6을 Push-pull 모드로 설정합니다.
AFIO -> EXTICR2 |= (6 << 8); // EXTI6에 PG6를 연결합니다.
EXTI -> FTSR |= (1 << 6); // Falling edge에서 EXTI6을 발생시킵니다.
EXTI -> RTSR &= ~(1 << 6); // 그 어떤 것도 Raising edge에서 인터럽트를 발생시키지 않습니다.
EXTI -> IMR |= (1 << 6); // EXTI6을 활성화 시킵니다.
NVIC_ISER0 |= (1 << 23); //NVIC에 EXTI6을 연결합니다.
while(true); //인터럽트는 동작해야하기 때문에 프로그램을 종료하지 않습니다.
return 0;
}
Continue
'stm32 > 강좌' 카테고리의 다른 글
stm32 레지스터 직접 접근 (6. Timer/Counter [코드 Part 01]) (0) | 2021.03.06 |
---|---|
stm32 레지스터 직접 접근 (5. Exception / Interrupt [중요 레지스터들 Part 03]) (1) | 2021.03.04 |
stm32 레지스터 직접 접근 (5. Exception / Interrupt [설명 & 코드 Part 01]) (0) | 2021.02.27 |
stm32 레지스터 직접 접근 (4. GPIO [Input Part 02]) (0) | 2021.02.19 |
stm32 레지스터 직접 접근 (4. GPIO [Output Part 01]) (7) | 2021.02.13 |