4. 中断的处理与解析
本文就STM32中断的以下特性进行解析与归纳
启动代码中的中断映射
系统中断,内部中断,外部中断
中断的优先级设置
DMA中断的设置
在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