STM32开发笔记49:STM32F4+DP83848以太网通信指南系列(三)

2019-07-15来源: eefocus关键字:STM32  STM32F4  DP83848  以太网通信  中断向量

本章为系列指南的第三章,这一章将会在正式进入以太网的配置和使用之前,复习一下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)循环快进快出,每次只处理一件任务;中断快进快出,每次只做必要的运算和消息通知,这样的架构最稳定。

中断向量

在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的响应优先级等级判断需要首先执行谁的中断函数。

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

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

#define NVIC_PriorityGroup_0  ((uint32_t)0x700) /*!< 0 bits for pre-emption priority

                                                   4 bits for subpriority */

#define NVIC_PriorityGroup_1  ((uint32_t)0x600) /*!< 1 bits for pre-emption priority

                                                   3 bits for subpriority */

#define NVIC_PriorityGroup_2  ((uint32_t)0x500) /*!< 2 bits for pre-emption priority

                                                   2 bits for subpriority */

#define NVIC_PriorityGroup_3  ((uint32_t)0x400) /*!< 3 bits for pre-emption priority

                                                   1 bits for subpriority */

#define NVIC_PriorityGroup_4  ((uint32_t)0x300) /*!< 4 bits for pre-emption priority

                                                   0 bits for subpriority */

注释中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是一个合理的配置。

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个中断类型。

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

typedef enum IRQn

{

/******  Cortex-M4 Processor Exceptions Numbers ****************************************************************/

  NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable Interrupt                                          */

  MemoryManagement_IRQn       = -12,    /*!< 4 Cortex-M4 Memory Management Interrupt                           */

BusFault_IRQn = -11, /*!< 5 Cortex-M4 Bus Fault Interrupt

[1] [2] [3] [4]

关键字:STM32  STM32F4  DP83848  以太网通信  中断向量

编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic467853.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:STM32开发笔记50:STM32F4+DP83848以太网通信指南系列(四)
下一篇:STM32开发笔记48:STM32F4+DP83848以太网通信指南系列(二):系

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

stm32CAN波特率计算小程序(QT源码)

软件:Qt Creator开发环境:Window7用qt做得一个计算波特率的小程序,在实际的应用中我们设置波特率的时候是通过以下参数来定的:CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;CAN_InitStructure.CAN_BS1=CAN_BS1_9tq;CAN_InitStructure.CAN_BS2=CAN_BS2_6tq;CAN_InitStructure.CAN_Prescaler=5;波特率计算小程序的截图:   根据所需的的采样点、波特率以及错误率得到以上外设初始化所需的参数。据网上资料,采样点的设置有以下规律:75%   
发表于 2019-08-20
stm32CAN波特率计算小程序(QT源码)

stm32之ADC应用实例(单通道、多通道、基于DMA)

硬件:STM32F103VCT6开发工具:Keil uVision4下载调试工具:ARM仿真器网上资料很多,这里做一个详细的整合。(也不是很详细,但很通俗)。所用的芯片内嵌3个12位的模拟/数字转换器(ADC),每个ADC共用多达16个外部通道,2个内部通道。3个:代表ADC1、ADC2、ADC3(下图是芯片固件库的截图)12位:也叫ADC分辨率、采样精度。先来看看二进制的12位可表示0-4095个数,也就是说转换器通过采集转换所得到的最大值是4095,如:“111111111111”=4095,那么我们怎么通过转换器转换出来的值得到实际的电压值呢?如果我们要转换的电压范围是0v-3.3v的话,转换器就会把0v-3.3v平均
发表于 2019-08-20
stm32之ADC应用实例(单通道、多通道、基于DMA)

stm32之TIM-基本定时器应用实例(详细)

开发环境:Window 7开发工具:Keil uVision4硬件:STM32F103VCT6定时器最基本的功能就是定时处理事情。比如定时发送USART数据、定时采集AD数据、定时检测IO口电位、还可以通过IO口输出波形等。可以实现非常丰富的功能。STM32系列的定时器分为基本定时器、通用定时器、高级控制定时器。后者包括前者的全部功能。所以先掌握基本定时器可以更好理解后面功能繁多的定时器。通常地,STM32高级定时器TIM1、TIM8,通用定时器TIM2、TIM3、TIM4、TIM5,基本定时器TIM6、TIM7。有用过STM32的话都知道,STM32所有的外设初始化都是使用标准库里的初始化结构体和初始化函数,下面先说一下
发表于 2019-08-20
stm32之TIM-基本定时器应用实例(详细)

stm32之TIM-高级定时器应用实例一(详细)

硬件:stm32f103c8t6开发工具:Keil uVision4下载调试工具:ARM仿真器        如果第一次接触定时器,可以先看基本定时器。本篇内容较多,如果想直接动手操作,可以跳到后面的实验代码。        stm32标准库对定时器外设建立了4个初始化结构体,定时器分为基本定时器、通用定时器、高级定时器,针对不用的定时器要使用不同初始化结构体。下面是4个初始化结构体的适用分类:TIM_TimeBaseInitTypeDef  (基本定时器、通用定时器、高级定时器)TIM_OCInitTypeDef   
发表于 2019-08-20
stm32之TIM-高级定时器应用实例一(详细)

stm32之iap实现应用(基于串口,上位机,详细源码)

除了用烧录器读写外,还可以在芯片运行时,对自身的内部flash进行读写。如果flash储存了程序后还有剩余的空间,那么可以把它用来保存程序运行时产生需要掉电保存的数据;也可以在芯片运行时将另一个编译后的二进制程序文件写到剩余的flash,然后进行跳转到新的程序上面运行。这也是iap的实现原理。1.先介绍怎么利用stm库对flash进行操作所有flash操作相关的函数接口在stm32f10x_flash.h里面。读flash里面的数据直接根据地址读出来就行了。往写flash里面写数据,需要解锁,擦除,写入数据,上锁;擦除后存储单元都变成1,因为储存单元不能由0变1,所以在写入之前一定要先擦除,不然会写入失败。操作代码如下:#define
发表于 2019-08-20
stm32之iap实现应用(基于串口,上位机,详细源码)

stm32之USB应用实例(官方例程资料下载使用)

开发环境:Window 7开发工具:Keil uVision5硬件:stc32f103c8t6stm32系列芯片很多都拥有一个USB2.0全速的通讯接口,下面介绍怎么使用st官方的usb应用例程。首先从官网下载源码:https://my.st.com/content/my_st_com/en/products/embedded-software/mcu-mpu-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32121.html需要登录才能下载,如果没有账号,注册一个也很快的。下载解压,打开
发表于 2019-08-20
stm32之USB应用实例(官方例程资料下载使用)

小广播

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved