꾸준히
인터럽트 본문
인터럽트 처리 과정
- 인터럽트 컨트롤러를 초기화하고 사용하는 코드를 작성
- 실제 인터럽트를 발생시키는 하드웨어와 인터럽트 컨트롤러를 연결
- 하드웨어가 인터럽트를 발생시키면 인터럽트컨트롤러로 인터럽트 신호를 보냄
- 인터럽트 컨트롤러는 ARM 코어로 인터럽트를 보냄
- 펌웨어에서 cpsr의 IRQ 혹은 FIQ 마스크를 끄면 IRQ나 FIQ가 발생했을 때 코어가 자동으로 익셉션 핸들러를 호출
- 익셉션 핸들러에서 적절한 인터럽트 핸들러를 호출
- 인터럽트 핸들러에서 인터럽트를 처리하면 완료
Block Diagram
Code
1. 인터럽트 컨트롤러를 초기화하고 사용하는 코드를 작성
static InterHdlr_fptr sHandlers[INTERRUPT_HANDLER_NUM];
// initialize interrupt controller
void Hal_interrupt_init(void)
{
GicCpu->cpucontrol.bits.Enable = 1;
GicCpu->prioritymask.bits.Prioritymask = GIC_PRIORITY_MASK_NONE;
GicDist->distributorctrl.bits.Enable = 1;
for (uint32_t i = 0 ; i < INTERRUPT_HANDLER_NUM ; i++)
{
sHandlers[i] = NULL;
}
enable_irq();
}
// interrupt enable
void Hal_interrupt_enable(uint32_t interrupt_num)
{
if ((interrupt_num < GIC_IRQ_START) || (GIC_IRQ_END < interrupt_num))
{
return;
}
uint32_t bit_num = interrupt_num - GIC_IRQ_START;
if (bit_num < GIC_IRQ_START)
{
SET_BIT(GicDist->setenable1, bit_num);
}
else
{
bit_num -= GIC_IRQ_START;
SET_BIT(GicDist->setenable2, bit_num);
}
}
// interrupt disable
void Hal_interrupt_disable(uint32_t interrupt_num)
{
if ((interrupt_num < GIC_IRQ_START) || (GIC_IRQ_END < interrupt_num))
{
return;
}
uint32_t bit_num = interrupt_num - GIC_IRQ_START;
if (bit_num < GIC_IRQ_START)
{
CLR_BIT(GicDist->setenable1, bit_num);
}
else
{
bit_num -= GIC_IRQ_START;
CLR_BIT(GicDist->setenable2, bit_num);
}
}
// register interrupt handler
void Hal_interrupt_register_handler(InterHdlr_fptr handler, uint32_t interrupt_num)
{
sHandlers[interrupt_num] = handler;
}
// run interrupt handler
void Hal_interrupt_run_handler(void)
{
uint32_t interrupt_num = GicCpu->interruptack.bits.InterruptID;
if (sHandlers[interrupt_num] != NULL)
{
sHandlers[interrupt_num]();
}
GicCpu->endofinterrupt.bits.InterruptID = interrupt_num;
}
* cpsr에 irq, fiq 모드로 적용하기 위한 코드 (위 코드에서 enable_irq())
void enable_irq(void)
{
__asm__ ("PUSH {r0, r1}");
__asm__ ("MRS r0, cpsr");
__asm__ ("BIC r1, r0, #0x80");
__asm__ ("MSR cpsr, r1");
__asm__ ("POP {r0, r1}");
}
void enable_fiq(void)
{
__asm__ ("PUSH {r0, r1}");
__asm__ ("MRS r0, cpsr");
__asm__ ("BIC r1, r0, #0x40");
__asm__ ("MSR cpsr, r1");
__asm__ ("POP {r0, r1}");
}
void disable_irq(void)
{
__asm__ ("PUSH {r0, r1}");
__asm__ ("MRS r0, cpsr");
__asm__ ("ORR r1, r0, #0x80");
__asm__ ("MSR cpsr, r1");
__asm__ ("POP {r0, r1}");
}
void disable_fiq(void)
{
__asm__ ("PUSH {r0, r1}");
__asm__ ("MRS r0, cpsr");
__asm__ ("ORR r1, r0, #0x40");
__asm__ ("MSR cpsr, r1");
__asm__ ("POP {r0, r1}");
}
2. 실제 인터럽트를 발생시키는 하드웨어와 인터럽트 컨트롤러를 연결
2-1) 익셉션 벡터와 익셉션 핸들러 연결
vector_start:
LDR PC, reset_handler_addr
LDR PC, undef_handler_addr
LDR PC, svc_handler_addr
LDR PC, pftch_abt_handler_addr
LDR PC, data_abt_handler_addr
B .
LDR PC, irq_handler_addr
LDR PC, fiq_handler_addr
reset_handler_addr: .word reset_handler
undef_handler_addr: .word dummy_handler
svc_handler_addr: .word dummy_handler
pftch_abt_handler_addr: .word dummy_handler
data_abt_handler_addr: .word dummy_handler
irq_handler_addr: .word Irq_Handler // irq exception handler
fiq_handler_addr: .word Fiq_Handler // fiq exception handler
vector_end:
2-2) uart 인터럽트와 핸들러 연결
// uart interrupt handler
static void interrupt_handler(void)
{
uint8_t ch = Hal_uart_get_char();
Hal_uart_put_char(ch);
}
void Hal_uart_init(void)
{
// Enable UART
Uart->uartcr.bits.UARTEN = 0;
Uart->uartcr.bits.TXE = 1;
Uart->uartcr.bits.RXE = 1;
Uart->uartcr.bits.UARTEN = 1;
// Enable input interrupt
Uart->uartimsc.bits.RXIM = 1;
// Register UART interrupt handler (interrupt controller)
Hal_interrupt_enable(UART_INTERRUPT0);
Hal_interrupt_register_handler(interrupt_handler, UART_INTERRUPT0);
}
3. 익셉션 핸들러에서 적절한 인터럽트 핸들러를 호출
// irq exception handler
__attribute__ ((interrupt ("IRQ"))) void Irq_Handler(void)
{
Hal_interrupt_run_handler();
}
// fiq exception handler
__attribute__ ((interrupt ("FIQ"))) void Fiq_Handler(void)
{
while(true);
}
* arm용 gcc의 전용 확장 기능으로써 irq, fiq의 핸들러에 진입하는 코드와 나가는 코드를 자동으로 만들어 줌
__attribute__ ((interrupt ("IRQ"))) void Irq_Handler(void)
__attribute__ ((interrupt ("FIQ"))) void Fiq_Handler(void)
4. 인터럽트 처리
static void interrupt_handler(void)
{
uint8_t ch = Hal_uart_get_char();
Hal_uart_put_char(ch);
}