软件中断请求在逻辑上比较简单,但相关程序的解释比较少,本文将在第一部分进行讲解。硬件中断逻辑比较复杂,但网上的例程比较多。虽然我们知道怎么配置单片机的中断,但估计很多人对它的细节还不是很理解,我将在后半部分讲解,但不会涉及寄存器的操作,有关中断时的寄存器操作机理请见文献[2],我也将在后续博客中进行实例分析。
文中的例子是在STM32H743单片机下进行的。
目录
1 软件中断
1.1 目标中断能挂起并激活的条件
1.2 注意事项
2.2 硬件中断请求
在中断的软件触发上,Cortex-M7并没有发现和Cortex-M4或Cortex-M3有不同。
简单的说,就是使用下面三个函数,
NVIC_SetPendingIRQ(TIM2_IRQn);
__DSB();
__ISB();
但我在HAL_TIM_PeriodElapsedCallback()中的函数并没有因为我的软件触发中断而运行定时器2。但在TIM2_IRQHandler()中就能实现我需要的软件触发并打印"hello1"。这是因为HAL_TIM_IRQHandler()中去读了TIM2的中断状态寄存器,并以此来判断这次中断是有那个事件引起的,并执行相应的事件回调函数。因此想要用软件触发中断,那这个中断线上不要使能太多事件而让自己不知道该如何处理。
/**
* @brief Sets Pending bit of an external interrupt.
* @param IRQn External interrupt number
* This parameter can be an enumerator of IRQn_Type enumeration
* (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32h7xxxx.h))
* @retval None
*/
void HAL_NVIC_SetPendingIRQ(IRQn_Type IRQn)
{
/* Check the parameters */
assert_param(IS_NVIC_DEVICE_IRQ(IRQn));
/* Set interrupt pending */
NVIC_SetPendingIRQ(IRQn);
}
void TIM2_IRQHandler(void)
{
Debug_IT_Printf("hello1\r\n");
HAL_TIM_IRQHandler(&TIM2_Handler);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==(&TIM2_Handler))
{
Debug_IT_Printf("hello2\r\n");
}//end of if(htim==(&TIM2_Handler))
}
void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
{
...//省略部分
/* TIM Update event */
if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) !=RESET)
{
__HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
HAL_TIM_PeriodElapsedCallback(htim);
}
}
...//省略部分
}
建议在看下面内容前读写参考文件的相关内容,尤其是书中的图片。
每个中断都有多个属性:
每个中断都可被禁止(默认)或使能
每个中断都可以被挂起(等待服务的请求)或解除挂起。
每个中断都可处于活跃(正在处理)或非活跃状态。
a、目标中断应使能
b、异常的 Exception Number 应该的16或16异常,即IRQ number 应大于等于0。
c、NVIC_SetPendingIRQ(TIM2_IRQn)中设计对软件触发中断寄存器ISPR的操作,因此需要在特权模式下才能配置成功。
在运行NVIC_SetPendingIRQ(TIM2_IRQn)后,还有运行"__DSB();__ISB();"。
要入存储器屏蔽指令的原因: 即便优先级高于当前等级,中断处理也会延迟几个时钟周期。
1、使能
以定时器为例,作为片内外设,想要使能中断,则需要使能外设的中断请求线:HAL_TIM_Base_Start_IT(&TIM2_Handler); ;另外,还要使能CPU的中断信号接收:HAL_NVIC_EnableIRQ(TIM2_IRQn);
2、两次中断(分相同中断源和不同中断源)下:挂起属性和激活属性的关系
当中断请求处于活跃状态时,处理器无法在中断完成和异常返回(有时也称作异常退出)前再次接受同一个中断请求。
若在得到处理后,中断源仍在继续保持中断请求,那么这个中断就会再次进入挂起状态且再次得到处理器的服务。
中断的挂起状态可以在其正被处理时再次置位。
在中断服务程序(ISR)执行期间,更高优先级的中断可能会产生抢占。在此期间,尽管处理器在执行另一个中断处理,之前的中断仍会被定义为活跃状态。[7.8.4]
就是说,如果定时器2中断正处于激活(ative)状态,那么它不会出现”定时器2中断“中再嵌套”定时器2中断“的情况。但可以有激活状态和挂起状态(hanging)共存的情况。在这种情况下,前一个中断服务函数结束后,仅接着再次进入这个中断服务函数。
3、中断清除
当处理器开始出处理中断请求时,中断的请求信号会被自动清除。
若外设持续保持某个中断请求,那么即使软件尝试着清除该中断挂起状态,挂起状态还是会再次置位。
前一句话的意思是,硬件会主动在该中断处于激活状态时清除Interrupt Active Bit Registers寄存器中对应的中断状态位。这并不影响在有更高优先级的中断中,用软件的方式清除这个中断挂起(能否成功还要看后面)。但如果不通过软件代码访问片内外设TIM2的中断状态寄存器中的具体事件(event),如更新事件,那么TIM2通往CPU的中断请求依然是有效的,CPU将会错误地以为又收到了新的中断请求,而进入激活并挂起的状态。
可见,在TIM2合理使能后,并使用了更新事件时,在定时器2的中断服务函数中,需要用__HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE)让外设停止中断请求;另一方面有硬件清除了CPU上的中断激活标志。
4、中断使能/禁止属性与挂起属性的关系
即使中断被禁止了,它的挂起状态仍可置位。在这种情况下,若中断稍后使能了,它可以被触发并得到服务。
这句话告诉我们,在变量处于不可共享的时候——程序将关闭所有中断或部分较低优先级的中断,并让处于临界区的变量退出后才开启这些中断——并不会漏掉某次中断(除非其实时性要求极高,这种情况请使用BASEPRI)。
5、EXTI
前面具的例子是定时器中断,它属于不清除寄存器不终止中断请求的中断方式。还有一种方式是脉冲中断请求。
对于脉冲中断请求,若在处理器开始处理前,中断请求信号产生了多次,它们会被当做一次中断请求。
我们知道有很多实时性要求高且数据频率高的片外传感器有DRDY中断脚,这个脚在每次数据采集完成后,用来通知处理器来读数据。而这些中断脉冲动则保持几毫秒,于是在CPU和外设中断间有了一个叫“外部中断和事件控制器-EXTI”的片内外设,我们可以把MCU对应的中断脚设置为上升沿触发或下降沿触发,这相当于做了一个中断信号转换器,将持续保持的中断请求信号变为了可软件控制寄存器清除的中断请求信号。
参考文献
[1] ARM Cortex-M3 与Cortex-M4 权威指南 ,具体见P7.6节 中断输入和挂起行为
[2] https://blog.csdn.net/layverns/article/details/50935467
[3] ARM® Cortex®-M7 Devices Generic User Guide , 2.3节和4.2节