4. 中断的处理与解析

本文就STM32中断的以下特性进行解析与归纳

在STM32标配的启动代码中,定义了所有的中断入口,并且集中为向量列表在所有代码的初始位置

RESET section

AREA    RESET, DATA, READONLY

__Vectors       DCD     __initial_sp               ; Top of Stack

                    DCD     Reset_Handler              ; Reset Handler

向量的初始位置是以512字节任何地址,其初始地址可以设置SCB的VTOR寄存器

以下代码是对标准中断向量表地址偏移512字节进行的测试

Vectors Address

Handler_Shift_Size EQU 0x00000080

__Vectors_Shift SPACE Handler_Shift_Size

__Vectors       DCD     __initial_sp               ; Top of Stack

*scb_vtor = addr & 0x3FFFFF80;

所有中断函数共享相同的最简内容,无限循环

Event Handler

WWDG_IRQHandler

:

:

OTG_FS_IRQHandler

                B       .

                ENDP

所有的中断函数以“脆弱”方式对外部公开,如果有其他同名的函数实现,连接时就会覆盖原有函数

Weak Handler

EXPORT  RCC_IRQHandler             [WEAK]

EXPORT  EXTI0_IRQHandler           [WEAK]

为了精确的实现中断连接,可以将原始的函数实现删除,强制使用外部函数

以下代码是强制使用C语言函数响应中断

Import Handler

IMPORT SysTick_Handler

IMPORT EXTI2_IRQHandler

;SysTick_Handler PROC

;EXPORT  SysTick_Handler            [WEAK]

;EXPORT  EXTI2_IRQHandler           [WEAK]

;EXTI2_IRQHandler

STM32的中断分为系统中断,内部中断和外部中断

系统中断和内部中断只需设置相关寄存器即可触发中断

系统中断如SysTick是较常用的中断,设置非常简单,且中断响应也只需单纯的用户逻辑

以下代码仅对用户变量加一操作,主程序中读取数据

SysTick Intterupt

*systick_ctrl |= 0x1; //systicker enable

void SysTick_Handler(void) {

Pub_Tick_Value++;

}

内部中断设置也相对简单,而中断处理程序需要对停止标志进行设置,并且需要将相应的中断源作处理

这里以DMA的内存到内存(MEM2MEM)为例,中断响应中需要将DMA停止,并且将中断事件位清除,否则会被持续调用

DMA Handler

*dma1_ccr1 = 0x40C2; // PL:low M/PSIZE:8bit M/PINC:yes Circ:no Dir:P->M TCIE:yes

*nvic_iscr0 = 0x800; //enable IRQ11

*nvic_icpr0 = 0x800;

*nvic_iser0 = 0x800;

*dma1_cndtr1 = DMA_Buffer_Size;

*dma1_ccr1 |= 0x1;

if ((*dma1_isr&0x01)==0x01) {

*dma1_ccr1 &= ~0x1;

DMA_Int_Flag = *dma1_isr;

*dma1_ifcr = DMA_Int_Flag;

}

外部中断不但需要对中断控制寄存器进行设置,还需要对外部中断进行映射,即EXTI0~EXTI15响应GPIO A~F的映射,然后设置外部中断控制器

响应程序也需要将中断准备控制器清除,否则会被持续调用

代码使用LED1、2线为外部中断响应,由于电路设计GPIO的低电平点亮LED,设置EXTI的上升沿即是LED由亮到灭响应中断

EXTI Process

*afio_exticr1 &= ~0xFF00;

*afio_exticr1 |= 0x3300; //EXT2/3 map PD

*afio_exticr2 &= ~0xF;

*afio_exticr2 |= 0x2; //EXT4 map PC

*exti_imr &= ~0x1C;

*exti_emr &= ~0x1C;

*exti_pr = 0x1C;

*exti_rtsr &= ~0x1C;

*exti_rtsr |= 0x1C;

*nvic_iscr0 = 0x700; //enable IRQ8 IRQ9 IRQ10

*nvic_icpr0 = 0x700;

*nvic_iser0 = 0x700;

*exti_imr |= 0x1C; //not mask exti2/3/4

if ((*exti_pr&0x8)==0x8) {

*exti_pr = 0x8;

优先级的设置需要两个步骤

优先级分组控制器和优先级控制器

这里使用最细化分组0x7,优先级控制器虽然是以字节为单位,但只有高4位被使用[7:4],数字越低,优先级越高

Interrupt Priority

*scb_aircr = 0x05FA0700; //group use 4bit detail

nvic_ipr[8] = 0x20; //set IRQ8=2

nvic_ipr[9] = 0x10; //set IRQ9=1

light_led(0, 0x3); //raise EXTI2/3 same time