打印
[应用相关]

STM32F4+DP83848以太网通信指南系列(三):中断向量

[复制链接]
653|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

本章为系列指南的第三章,这一章将会在正式进入以太网的配置和使用之前,复习一下STM32的中断以及中断向量,因为我们以后要在中断中响应以太网收包。

中断—嵌入式中的多线程

从51单片机到ARM架构的32位微芯片,到树莓派、Ardunio等单板机,中断的概念对于这些芯片都非常重要。本人是纯软件工程师出身,科班学习时根本没有接触过嵌入式开发,学的都是C++,C#,JAVA,Go这些语言。在我看来嵌入式中的中断就相当于这些高级语言中的多线程,main()函数定义了一条主线程,然后各种配置出来的中断Handle就是游离在主线程之外的各种事件的回调函数,他们会在不同的事件下响应并触发,一旦触发中断,CPU的运算逻辑将会在主线程中打个断点,并立即离开主线程,进入中断函数中去以支线程的方式处理逻辑,分支线程逻辑执行完毕后再回到主线程继续执行逻辑,这时候有一些全局变量可能已经被中断中的逻辑更新了,因此也会出现高级语言多线程编程中常出现的并发冲突问题,因此,对于中断,我总结了以下注意点:

一定要让中断函数能顺利return,而且,尽量迅速地return。

为了能尽快让中断return,我们一般在全局做消息通知,让主线程判断消息,主线程中根据消息状态处理所有业务逻辑,中断只负责发出通知,更新通知。

中断可以嵌套发生,比如A中断执行一半,B中断来了,此时CPU有两个选择:1.立刻离开A,进入B,B执行完了再回到A;2.将A执行完成后,再进入B。

上述两种选择可通过配置中断优先级来确定,如果B配置的优先级比A低,则选择前者,如果B配置的优先级比A高,则选择后者
STM32的中断优先级通过中断向量表来配置,相比51单片机上的线性优先级结构,向量表显得更为灵活,呃。。。复杂。

如果考虑到中断嵌套的情况发生,我们不能将消息通知覆盖,比如回到中断A后将B的消息覆盖掉,并且主线程中的消息通知逻辑也应该有优先响应的逻辑。这一点是我个人的编程经验,以后本系列实际开发时可以观察到。

要想保证嵌入式项目能稳定、流畅地运行,必须尽可能保证逻辑的清晰,主函数的while(1)循环快进快出,每次只处理一件任务;中断快进快出,每次只做必要的运算和消息通知,这样的架构最稳定。


使用特权

评论回复
沙发
keaibukelian|  楼主 | 2019-6-18 14:15 | 只看该作者
中断向量

在51单片机中,响应优先级一般固定了:外部中断0 > 定时器中断0 > 外部中断1 > 定时器中断1 > 串口中断;优先级如果需要修改是通过IP寄存器来设置的,这里就不展开讲了。

对于STM32来说,配置中断的响应优先级和抢占优先级更加灵活也更加复杂。下面是关于中断向量的知识点:

任何一个需要使用中断的STM32工程,我们都需要一个全局的,配置且仅配置一次的中断分组,函数为:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_X);,X范围从0-4。我看到有很多工程项目中,这个函数调用了一次以上,几乎是每次配置一个小的中断类型时都会调用一次这个函数,每次设置的分组还不一样,可能作者是从各式各样的代码片段中强行拷贝的,这是一个很严重的误区。再次重申,这个分组函数,只需要在初始化阶段调用一次!

在设计整个嵌入式工程之前,我们需要大致地梳理一下项目中需要用到的中断类型,以及它们的优先级关系,以此来确定X的数值。

中断优先级关系有两种,一种是抢占优先级,另一种是响应优先级,前者的优先级强度要高于后者。

抢占优先级:主线程运行时,A中断产生了,CPU离开主线程,进入A的中断函数,A中断函数执行了一半,B中断产生了,如果B的抢占优先级高于A,则CPU会立刻离开A,进入B的中断函数,执行完B再回到A,执行完A再回到主线程;如果B的抢占优先级等于或者低于A,此时CPU并不会离开A,而是将A全部执行结束,再进入B,执行完B后,再回到主线程。

响应优先级:主线程运行时,A中断,B中断同时产生,并且它们的抢占优先级相同,此时CPU根据AB的响应优先级等级判断需要首先执行谁的中断函数。

从上述的两种场景可以看出,抢占优先级的强度要明显高于响应优先级,只有在很特殊的场景下,两个不同类型的中断才会在同一时间点发生,因此响应优先级的力度相对较弱,而且抢占优先级关系到中断能否嵌套执行,这关系到整个系统架构的流程走向,比较重要。


使用特权

评论回复
板凳
keaibukelian|  楼主 | 2019-6-18 14:16 | 只看该作者

STM32分配了4个bit让开发者对每一类型的中断进行优先级的配置,包括抢占优先级和响应优先级。为了充分利用这4个bit,并且能够灵活配置让其满足不同开发者的需求,STM32做了一个分组配置,在不同的分组情况下,这4个bit分给嵌套和响应优先级的位数不同,在Keil中查看NVIC_PriorityGroup_0的宏定义,在misc.h文件中有以下代码:

注释中pre-emption priority就是我上文所说的抢占优先级,subpriority则是响应优先级,可以看到,针对不同的分组,STM32允许使用4个bit中不同的位数来分别表示其抢占优先级和响应优先级。关于响应优先级的英文subpriority,应该是子优先级的意思,而pre-emption priority英文我查了一下,确实没找到很贴切的翻译。

无论是抢占优先级还是响应优先级,都是数值小的优先级高。

根据以上知识点,假设我们规划的项目需要六个中断,分别是UART中断,SPI中断,以太网中断,SysTick中断,外部中断1和外部中断2。SysTick中断优先级最高,并且能够嵌入任何其他的中断,以太网中断次高,下面是UART中断,SPI中断,最低的是外部中断1+外部中断2,但当外部中断1和外部中断2同时发生时,我们希望2优先响应。根据以上的需求,我们先规划一下抢占中断的层次,依次是:

SysTick中断 > 以太网中断 > UART中断 > SPI中断 > 外部中断1和外部中断2

然后规划一下外部中断1和外部中断2的响应优先级:外部中断2 > 外部中断1

OK,根据以上分析,我们需要1个bit来代表响应中断优先级,另外的3个bit可以用来代表抢占优先级,因此,配置中断向量的分组为NVIC_PriorityGroup_3是一个合理的配置。


使用特权

评论回复
地板
keaibukelian|  楼主 | 2019-6-18 14:16 | 只看该作者
Ethernet中断

搞懂中断向量分组的概念后,我们需要对每一个不同的中断配置其抢占优先级和响应优先级,以Ethernet中断为例,我们使用以下代码配置:



  • void ETH_NVIC_Config(void)



  • {



  •   NVIC_InitTypeDef   NVIC_InitStructure;







  •   /* Enable the Ethernet global Interrupt */



  •   NVIC_InitStructure.NVIC_IRQChannel = ETH_IRQn;



  •   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;



  •   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;



  •   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;



  •   NVIC_Init(&NVIC_InitStructure);   



  • }


以上代码比较容易理解,先定义了一个结构体对象,然后配置好要设置的中断类型,抢占优先级,响应优先级,最后传递给NVIC_Init()函数,使用中断。

对于任意中断类型,都可以使用上述代码配置,不同的中断类型宏定义可以到stm32f4xx.h文件中去查看,在该文件的172行-268行为STM32F407共定义了约81个中断类型。


使用特权

评论回复
5
keaibukelian|  楼主 | 2019-6-18 14:17 | 只看该作者

附:节选中断类型片段代码如下:

使用特权

评论回复
6
keaibukelian|  楼主 | 2019-6-18 14:18 | 只看该作者

使用特权

评论回复
7
keaibukelian|  楼主 | 2019-6-18 14:18 | 只看该作者

使用特权

评论回复
8
643757107| | 2019-6-18 22:51 | 只看该作者
STM32的中断优先级通过中断向量表来配置

使用特权

评论回复
9
wakayi| | 2019-7-9 11:06 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

77

主题

4146

帖子

5

粉丝