STM32 제품군별 벡터 테이블 차이
벡터 테이블은 인터럽트와 예외 핸들러의 주소를 저장하는 메모리 구조로, ARM Cortex-M 코어의 NVIC(Nested Vectored Interrupt Controller)에 의해 사용됩니다. STM32 제품군별 차이는 코어 아키텍처와 메모리 구성에 따라 발생합니다.
VTOR(Vector Table Offset Register) 지원 여부
- STM32F0 (Cortex-M0):
- 특징: Cortex-M0 코어는 VTOR 레지스터를 지원하지 않음. 벡터 테이블은 기본적으로 플래시 메모리(0x00000000)에 고정되거나, RAM으로 복사하여 사용해야 함
- 제약: 동적 재배치가 불가능하며, 링커 스크립트와 메모리 매핑으로 관리
- STM32F1, F4, H7 (Cortex-M3, M4, M7):
- 특징: VTOR 레지스터를 지원하여 벡터 테이블을 플래시, RAM, 또는 기타 메모리 영역으로 재배치 가능
- 장점: 부트로더와 사용자 애플리케이션 간 전환 시 유연한 설정 가능
벡터 테이블 크기 및 인터럽트 수
- STM32F0:
- Cortex-M0는 기능이 줄어들며 벡터 테이블 엔트리 수도 감소
- 최대 32개의 인터럽트를 지원하며, 벡터 테이블은 초기 스택 포인터(MSP)와 최대 32개의 IRQ 핸들러로 구성
- 벡터 테이블 크기: 약 136바이트 (4바이트 * (1 + 32 + 예외))
- STM32G0 / C0 (Cortex‑M0+ 계열)
- Cortex-M3 기반, 최대 60개의 인터럽트를 지원
- STM32F1:
- Cortex-M3 기반으로 기본 16 exception + 최대 60~80 IRQ 엔트리를 지원
- STM32F4:
- Cortex-M4 기반, 최대 82개의 인터럽트를 지원
- STM32H7:
- Cortex-M7 기반, 최대 150개의 인터럽트를 지원하며, 더 복잡한 인터럽트 우선순위 관리 가능
메모리 정렬 요구사항
- Cortex-M0 (STM32F0):
- 벡터 테이블은 4바이트(32비트) 정렬이 필수. 정렬되지 않은 주소 접근은 HardFault 발생
- 플래시 메모리(0x00000000) 또는 RAM의 정렬된 주소에 배치
- Cortex-M3/M4/M7 (STM32F1, F4, H7):
- VTOR은 7비트 정렬(128바이트)을 요구하며, 상위 비트는 무시됨
- 정렬되지 않은 접근은 일부 허용되지만, 성능을 위해 정렬 권장
부트로더와 벡터 테이블 재배치
- STM32F0:
- VTOR 미지원으로, 부트로더에서 사용자 코드로 전환 시 RAM에 벡터 테이블을 복사하거나 플래시 메모리 매핑 사용
- STM32F1, F4, H7:
- VTOR을 통해 부트로더와 사용자 애플리케이션의 벡터 테이블을 동적으로 변경 가능
- 예: 플래시 메모리의 사용자 코드 시작 주소(예: 0x08010000)로 VTOR 설정
차이의 이유
- 코어 아키텍처:
- Cortex-M0/M0+는 저비용, 저전력 설계를 목표로 VTOR과 같은 고급 기능을 생략
- Cortex-M3/M4/M7은 고성능 응용을 위해 더 많은 인터럽트와 유연한 메모리 관리를 지원
- STM32 설계 목표:
- STM32F0는 소형, 저전력 응용(예: 센서, 간단한 제어)에 최적화되어 간소화된 인터럽트 구조를 가짐
- STM32F4/H7은 고성능 응용(예: GUI, 모터 제어, 네트워킹)에 적합하며, 더 많은 인터럽트와 복잡한 NVIC 설정 지원
- 메모리 제약:
- STM32F0는 플래시(8~256KB)와 RAM(4~32KB)이 제한적이므로, 벡터 테이블 재배치가 제한됨
- STM32F4/H7은 큰 플래시(최대 2MB)와 RAM(최대 1MB)을 제공하여 유연한 설정 가능
/******************************************************************************
*
* The minimal vector table for a Cortex M3. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
*******************************************************************************/
.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
/* External Interrupts */
.word WWDG_IRQHandler /* Window WatchDog */
.word PVD_IRQHandler /* PVD through EXTI Line detection */
.word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */
.word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */
.word FLASH_IRQHandler /* FLASH */
.word RCC_IRQHandler /* RCC */
.word EXTI0_IRQHandler /* EXTI Line0 */
.word EXTI1_IRQHandler /* EXTI Line1 */
.word EXTI2_IRQHandler /* EXTI Line2 */
.word EXTI3_IRQHandler /* EXTI Line3 */
.word EXTI4_IRQHandler /* EXTI Line4 */
.word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */
.word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */
.word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */
.word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */
.word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */
.word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */
.word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */
.word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */
.word CAN1_TX_IRQHandler /* CAN1 TX */
.word CAN1_RX0_IRQHandler /* CAN1 RX0 */
.word CAN1_RX1_IRQHandler /* CAN1 RX1 */
.word CAN1_SCE_IRQHandler /* CAN1 SCE */
.word EXTI9_5_IRQHandler /* External Line[9:5]s */
.word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */
.word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */
.word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
.word TIM2_IRQHandler /* TIM2 */
.word TIM3_IRQHandler /* TIM3 */
.word TIM4_IRQHandler /* TIM4 */
.word I2C1_EV_IRQHandler /* I2C1 Event */
.word I2C1_ER_IRQHandler /* I2C1 Error */
.word I2C2_EV_IRQHandler /* I2C2 Event */
.word I2C2_ER_IRQHandler /* I2C2 Error */
.word SPI1_IRQHandler /* SPI1 */
.word SPI2_IRQHandler /* SPI2 */
.word USART1_IRQHandler /* USART1 */
.word USART2_IRQHandler /* USART2 */
.word USART3_IRQHandler /* USART3 */
.word EXTI15_10_IRQHandler /* External Line[15:10]s */
.word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */
.word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */
.word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */
.word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */
.word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */
.word TIM8_CC_IRQHandler /* TIM8 Capture Compare */
.word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */
.word FMC_IRQHandler /* FMC */
.word SDIO_IRQHandler /* SDIO */
.word TIM5_IRQHandler /* TIM5 */
.word SPI3_IRQHandler /* SPI3 */
.word UART4_IRQHandler /* UART4 */
.word UART5_IRQHandler /* UART5 */
.word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */
.word TIM7_IRQHandler /* TIM7 */
.word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */
.word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */
.word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */
.word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */
.word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word CAN2_TX_IRQHandler /* CAN2 TX */
.word CAN2_RX0_IRQHandler /* CAN2 RX0 */
.word CAN2_RX1_IRQHandler /* CAN2 RX1 */
.word CAN2_SCE_IRQHandler /* CAN2 SCE */
.word OTG_FS_IRQHandler /* USB OTG FS */
.word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */
.word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */
.word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */
.word USART6_IRQHandler /* USART6 */
.word I2C3_EV_IRQHandler /* I2C3 event */
.word I2C3_ER_IRQHandler /* I2C3 error */
.word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */
.word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */
.word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */
.word OTG_HS_IRQHandler /* USB OTG HS */
.word DCMI_IRQHandler /* DCMI */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word FPU_IRQHandler /* FPU */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word SPI4_IRQHandler /* SPI4 */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word SAI1_IRQHandler /* SAI1 */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word SAI2_IRQHandler /* SAI2 */
.word QUADSPI_IRQHandler /* QuadSPI */
.word CEC_IRQHandler /* CEC */
.word SPDIF_RX_IRQHandler /* SPDIF RX */
.word FMPI2C1_EV_IRQHandler /* FMPI2C 1 Event */
.word FMPI2C1_ER_IRQHandler /* FMPI2C 1 Error */
.size g_pfnVectors, .-g_pfnVectors
우선순위 설정
// 우선순위 설정 예시
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); // 최고 우선순위 (응급 버튼)
HAL_NVIC_SetPriority(TIM2_IRQn, 1, 0); // 중간 우선순위 (타이머)
HAL_NVIC_SetPriority(USART2_IRQn, 2, 0); // 낮은 우선순위 (통신)
Handler and Callback
핸들러 함수 (Interrupt Service Routine, ISR)
인터럽트 발생 시 CPU가 즉시 진입하는 함수로, 벡터 테이블에 등록되어 있으며 NVIC에 의해 직접 호출
- 하드웨어 인터럽트와 직접 연결됨
- 함수 이름은 고정 (XX_IRQHandler)
- 매우 빠르게 응답해야 하며, 가볍고 빠르게 실행되어야 함
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); // HAL 라이브러리의 공통 핸들러 호출
}
콜백 함수 (Callback Function)
핸들러 내부에서 라이브러리(HAL 등)에 의해 호출되는 사용자 정의 함수로, 실제 동작(비즈니스 로직)은 여기서 처리
- 사용자가 정의함 (예: 버튼을 눌렀을 때의 동작)
- HAL 내부에서 ISR 처리 후 자동으로 호출됨
- HAL이 제공하는 확장 가능한 후처리 구조
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_0)
{
// 버튼 눌림 처리: LED 토글, 변수 변경 등
}
}
처리 흐름
1. 인터럽트 발생
2. NVIC가 EXTI0_IRQHandler() 실행
3. HAL_GPIO_EXTI_IRQHandler() 호출 → 인터럽트 플래그 클리어
4. HAL 내부에서 HAL_GPIO_EXTI_Callback() 호출
5. 사용자 정의 동작 실행
예제
STM32F0: RAM 기반 벡터 테이블 (VTOR 미지원)
STM32F0는 VTOR이 없으므로, 벡터 테이블을 RAM으로 복사하여 사용.
#include "stm32f0xx.h"
#define VECTOR_TABLE_SIZE 48
uint32_t vectorTable_RAM[VECTOR_TABLE_SIZE] __attribute__((section(".RAMVectorTable")));
void RelocateVectorTable(void) {
for (uint32_t i = 0; i < VECTOR_TABLE_SIZE; i++) {
vectorTable_RAM[i] = *(__IO uint32_t*)(0x00000000 + (i * 4));
}
__disable_irq();
__enable_irq();
}
int main(void) {
HAL_Init();
RelocateVectorTable();
SystemInit();
while (1) {
}
}
- 벡터 테이블은 플래시(0x00000000)에서 RAM으로 복사됨
- 링커 스크립트에서 .RAMVectorTable 섹션을 RAM 영역에 정의해야 함
- NVIC는 RAM 주소를 직접 참조하도록 설정되며, VTOR이 없으므로 하드웨어 매핑에 의존
STM32F4: VTOR을 사용한 벡터 테이블 재배치
STM32F4는 VTOR을 지원하므로, 플래시 메모리 내 다른 주소로 벡터 테이블을 재배치 가능
#include "stm32f4xx.h"
#define NEW_VECTOR_TABLE_ADDRESS 0x08004000
void RelocateVectorTable(void) {
__disable_irq();
SCB->VTOR = NEW_VECTOR_TABLE_ADDRESS;
__enable_irq();
}
int main(void) {
HAL_Init();
SystemInit();
RelocateVectorTable();
while (1) {
}
}
- SCB->VTOR에 새 벡터 테이블 주소를 설정
- 링커 스크립트에서 벡터 테이블을 NEW_VECTOR_TABLE_ADDRESS에 배치해야 함
- 부트로더와 사용자 애플리케이션 간 전환 시 유용
// CMSIS/레지스터 직접 설정
extern uint32_t __vector_table[]; // 벡터 테이블 배열
void VectorTable_Relocate(void) {
SCB->VTOR = (uint32_t)__vector_table;
}
// HAL 기반 설정
// main.c 또는 system_*.c 파일에
#define VECT_TAB_OFFSET 0x2000 // 원하는 플래시 오프셋
void SystemInit(void) {
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
}
STM32F4: 부트로더에서 사용자 코드로 점프
부트로더에서 사용자 코드로 전환 시 VTOR을 재설정하는 예제
#include "stm32f4xx.h"
#define USER_CODE_ADDRESS 0x08010000
void JumpToUserCode(void) {
__disable_irq();
SCB->VTOR = USER_CODE_ADDRESS;
uint32_t stack_pointer = *(__IO uint32_t*)USER_CODE_ADDRESS;
uint32_t reset_handler = *(__IO uint32_t*)(USER_CODE_ADDRESS + 4);
__set_MSP(stack_pointer);
void (*user_reset_handler)(void) = (void (*)(void))reset_handler;
user_reset_handler();
}
int main(void) {
HAL_Init();
JumpToUserCode();
while (1) {
}
}
- VTOR을 사용자 코드 주소로 설정하여 인터럽트 벡터를 재배치
- 스택 포인터(MSP)와 리셋 핸들러를 읽어 사용자 코드로 점프
'MCU' 카테고리의 다른 글
ESP32-BLE-Raspberry pi (0) | 2025.06.09 |
---|---|
STM32-CAN-Raspberry Pi (0) | 2025.04.07 |
DHT22 온습도 센서 (0) | 2025.03.23 |
OLED display 128x32 (0) | 2025.02.28 |
STM32 clock (0) | 2025.01.23 |