iTOP-STM32MP157开发板采用ST推出的双核cortex-A7+单核cortex-M4异构处理器,既可用Linux、又可以用于STM32单片机开发。开发板采用核心板+底板结构,主频650M、1G内存、8G存储,核心板采用工业级板对板连接器,高可靠,牢固耐用,可满足高速信号环境下使用。共240PIN,CPU功能全部引出:底板扩展接口丰富底板板载4G接口(选配)、千兆以太网、WIFI蓝牙模块HDMI、CAN、RS485、LVDS接口、温湿度传感器(选配)光环境传感器、六轴传感器、2路USB OTG、3路串口,CAMERA接口、ADC电位器、SPDIF、SDIO接口等
第二十八章Cortex-M4外部中断实验
本章节最终所完成的实验例程存放路径为“iTOP-STM32MP157开发板网盘资料汇总\06_Cortex-M4实验例程\04_EXTI.zip”。
28.1 认识中断
28.1.1对中断的初步理解
中断其实就是当 CPU 执行程序时,由于发生了某种随机的事件(外部或内部),引起 CPU 暂时中断正在运行的程序,转去执行一段特殊的服务程序(中断服务子程序或中断处理程序),以处理该事件,该事件处理完后又返回被中断的程序继续执行,这一过程就称为中断,引发中断的称为中断源。比如:看电视时突然门铃响,那么门铃响就相当于中断源。有些中断还能够被其他高优先级的中断所中断,那么这种情况又叫做中断的嵌套。中断示意图如下图所示:
CPU与硬件通信的方式分为轮询和中断。什么是轮询呢,轮询的定义如下:
轮询(Polling)I/O方式或程序控制I/O方式,是让CPU以一定的周期按次序查询每一个外设,看它是否有数据输入或输出的要求,若有,则进行相应的输入/输出服务;若无,或I/O处理完毕,CPU就接着查询下一个外设。
我们以上一章节的按键实验为例,实验主函数代码如下图所示:
在while循环之中,不断的检测key的值,根据不同的key值来实现不同的功能,在这里所用到的就是轮询,但轮询的弊端也在此体现了出来,不断的检测会造成资源的浪费,且反应速度慢。而“中断处理方式”(外设请求方式)能够协调CPU与外设间的速度差异,能够协调各种外设间的速度差异,提高系统的工作效率(速度指标)。使应用程序与外设操作基本脱离开来,降低了程序的设备相关性(关联可靠性指标、局部修改指标)。
28.1.2中断的分类
中断包括软件中断(不可屏蔽)和硬件中断。软中断为内核触发机制引起,模拟硬件中断。硬件中断又分为外部中断(可屏蔽)和内部中断(不可屏蔽)。
外部中断为一般外设请求;内部中断包括硬件出错(掉电,校验,传输)和运算出错(非法数据,地址,越界,溢出)
硬件中断是由与系统相连的外设(比如网卡 硬盘 键盘等)自动产生的. 每个设备或设备集都有他自己的IRQ(中断请求), 基于IRQ, CPU可以将相应的请求分发到相应的硬件驱动上(注: 硬件驱动通常是内核中的一个子程序, 而不是一个独立的进程).处理中断的驱动是需要运行在CPU上的, 因此, 当中断产生时, CPU会暂时停止当前程序的程序转而执行中断请求.
软中断不会直接中断CPU, 只有当前正在运行的代码(或进程)才会产生软中断. 软中断是一种需要内核为正在运行的进程去做一些事情(通常为I/O)的请求.
28.1.3 NVIC的初步认识
NVIC 即嵌套向量中断控制器(Nested Vectored Interrupt Controller ),它是 Cortex-M 内核的器件,用于管理内核所有中断和事件,包括中断的使能和除能、中断的优先级等。M3/M4/M7 内核都是支持 256 个中断,其中包含了 16 个系统中断和 240 个外部中断,并且 具有 256 级的可编程中断设置。然而芯片厂商一般不会把内核的这些资源全部用完,如 STM32MP157 的系统中断有 10 个,外部中断有 150 个。
由于在STM32CubeIDE软件之中会自动帮我们设置好相应的配置,所以我们在此只写出之后会用到的一些内容,一些基本配置与说明在这里就不一一列出。
每个中断的优先级都是用寄存器中的8位来设置的。8位的话就可以设置2^8 =256级中断,实际中用不了这么多,所以芯片厂商根据自己生产的芯片做出了调整。 STM32MP157 只使用了这个8位中的高四位[7:4],低四位取零,这样2^4=16,只能表示16级中断嵌套。优先级分组真值表如下图所示:
例如优先级分组设置为 2,每个中断的中断优先寄存器的高四位中的最高2位是抢占优先级,低 2 位是响应优先级,抢占优先级共有 2^2=4 种,子优先级共有 2^2=4种,共有 4*4=16 级嵌套, 每个中断,可以设置抢占优先级为 0~4,响应优先级为0~4。
具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以以打断正在执行的抢占优先级低的中断。
当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。
对于系统中断(比如:PendSV,SVC,SysTick)和外部中断(比如SPI,USART)它们是在同一个NVIC下面设置的,也要根据根据他们的抢占优先级和子优先级来。
28.1.4 EXTI的初步认识
EXTI(External interrupt/event controller)—外部中断/事件控制器管理了控制器的 16 个中断/事件线。EXIT 时钟挂载在 AHB 高级高性能总线上,每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
从上图中断的系统框图可以了解到:
1)中断/事件的输入线可以通过寄存器设置为任意一个GPIO,也可以是一些外设的事件
2)Event Trigger 边沿检测电路以输入线作为信号输入端,它会根据上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发选择寄存器(EXTI_FTSR)对应位的设置来控制信号触发
3)EXTI_PR寄存器内容输出到NVIC内,从而实现系统中断事件控制
4)而CPU(m)是我们的M4内核部分
EXTI 有 16 个中断/事件线,每个 GPIO 都可以被设置为输入线,占用 EXTI0 至 EXTI15。GPIO 的引脚 Px0 ~ Px15(x=A,B,C,D,E,F,G,H,I,J,K,Z)分别映射到了中断线 0~15。这样每个中断线对应了最多 12 个 IO 口。如上图所示。
GPIO 和中断线的映射关系如下表:
中断/事件线 | 输入源 |
EXTI0 | PX0(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI1 | PX1(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI2 | PX2(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI3 | PX3(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI4 | PX4(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI5 | PX5(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI6 | PX6(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI7 | PX7(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI8 | PX8(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI9 | PX9(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI10 | PX10(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI1 | PX11(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI2 | PX12(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI3 | PX13(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI4 | PX14(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI5 | PX15(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
28.2 实验目的
1)IO口作为外部中断的使用
2)学习中断优先级和中断的设置
3)STM32CubeIED的熟练
28.3 按键电路的分析
本章节所要完成的任务和上一章节的任务相同,涉及到的实现方法不同,但硬件的原理相同,所以在这里又重新分析了一遍按键的原理部分。
iTOP-STM32MP157开发板板载了5个输入按键,默认为高电平,按下去的时候为低电平(这里并没有列出复位按键和B1),原理如下图所示:
我们以PWRON3为例,默认状态下按键为弹起状态,IO口和3.3V的电位相同,为高电平,当按键 PWRON3按下时,+3.3V 通过电阻 R22 然后再通过按键PWRON3最终进入 GND 形成一条通路,那么这条线路的全部电压都加到了R22这个电阻上,DCMI_D8这个引脚就是个低电平。当松开按键后,线路断开,就不会有电流通过,那么 PWRON3和+3.3V 又恢复为等电位,是一个高电平。我们就可以通过 DCMI_D8这个 IO 口的按键按下的下降沿来判断按键是否按下。
三个用户按键原理图对应的控制引脚为:
原理图名称 | PWRON2 | PWRON1 | PWRON3 |
按键名称 | BACK | VOL-UP | VOL-DN |
引脚名称 | PI2 | DCMI_D10 | DCMI_D8 |
对应引脚 | PI2 | PI3 | PI1 |
在本小节所要实现功能为,通过三个输入按键来分别控制LED2、LED3和BEEP的状态,当按下VOL-UP按键时会使LED2的状态反转,当按下VOL-DN按键时会使LED3的状态反转,当按下BACK按键时会使蜂鸣器的状态反转,以下为LED和蜂鸣器对应的控制引脚:
BEEP | LED2 | LED3 |
PI11 | PE14 | PE1 |
28.4 实验步骤
28.4.1建立EXTI工程
首先我们打开STM32CubeIDE软件,进入软件界面之后,我们点击File属性,选择NEW下的STM32 Project的选项,如下图所示:
然后我们会进入下图所示界面:在Part Number选择框输入STM32MP157A,然后在右边的选择界面选择STM32MP157AAA,然后点击Next选项
在Project Name框中输入工程名字EXTI,然后点击Finish选项即可,如下图所示:
等待工程创建完毕,会询问我们是否要安装OpenSTLinux ,由于我们是在windows环境下,所以我们不需要安装,点击NO即可
至此我们的工程创建完毕,进入工程界面如下图所示界面:
28.4.2 GPIO功能引脚配置
28.4.2.1输出引脚的配置(LED和蜂鸣器)
首先我们在下面的搜索框之中输入我们要配置的引脚,我们在这里以PE1为例进行搜索,输入名称之后,对应的引脚在工程中会闪烁,如下图所示:
配置完复用功能之后,我们还要配置 Pin Reserved 选项,如果不配置此项,在生成工程代码的时候将不会看到有关这个 Pin 的初始化代码。继续选中 PE1,右键弹出设置项,我们选择Pin ReservedàCortex-M4。如下图所示:
第二个LED的控制管脚PE14按同样的方法进行配置。
配置完成之后打开左侧菜单的 System CoreàGPIO 进入 GPIO 模式配置界面:如下图所示:
点击对应的引脚配置之后会弹出右下方的管脚配置界面,如上图所示:
在下方会列出要配置选项的具体说明和我们要进行的配置。
1)选项 GPIO output level 用来设置IO口的输出电平的高低,这这里我们选择LOW
2)选项 GPIO mode 用来设置 IO 口输出模式为 Output Push Pull(推挽)还是 Output Open Drain(开漏)。本实验我们设置为推挽输出 Output Push Pull。
3)选项 GPIO Pull-up/Pull-down 用来设置 IO 口是上拉/下拉/没有上下拉。本实验我们设置为上拉(Pull-up)。
4)选项 Mzximum ouput speed 用来设置 IO 口输出速度为低速(Low)/中速(Medium)/高速 (Hign)/快速(Very High)。我们设置为高速 High 。
5)选项 User Label 是用来设置初始化的 IO 口 Pin 值为我们自定义的宏,这里我们填写为 LED3。按照如上要求设置后的界面如下(由于PE14的配置相同,只是最后的Label值不同,也在下方列了出来):
然后我们继续在搜索框之中输入PI11进行搜索,输入名称之后,对应的引脚在工程中会闪烁,如下图所示:
然后我们使用鼠标左键点击对应的引脚会弹出PI11的复用功能选择,我们在这里选择复用为GPIO_Output功能,如下图所示:
配置完复用功能之后,我们还要配置 Pin Reserved 选项,如果不配置此项,在生成工程代码的时候将不会看到有关这个 Pin 的初始化代码。继续选中 PI11,右键弹出设置项,我们选择Pin ReservedàCortex-M4。如下图所示:
配置完成之后打开左侧菜单的 System CoreàGPIO 进入 GPIO 模式配置界面,跟配置LED的步骤相同,配置完成如下图所示:
28.4.2.2 中断引脚的配置(按键)
首先我们在下面的搜索框之中输入我们要配置的引脚,我们在这里以PI2为例进行搜索(由于三个按键的配置相同,在这里我们只是列出了BACK按键的配置步骤),输入名称之后,对应的引脚在工程中会闪烁,如下图所示:
然后我们使用鼠标左键点击对应的引脚会弹出PI2的复用功能选择,我们在这里选择复用为GPIO_EXTI2功能,如下图所示:
配置完复用功能之后,我们还要配置 Pin Reserved 选项,如果不配置此项,在生成工程代码的时候将不会看到有关这个 Pin 的初始化代码。继续选中 PI2,右键弹出设置项,我们选择Pin ReservedàCortex-M4。如下图所示:
VOL-UP和VOL-DN对应的PI3和PI1引脚按同样的方法进行配置。在此就不一一展示。
配置完成之后打开左侧菜单的 System CoreàGPIO 进入 GPIO 模式配置界面:如下图所示:
选项 GPIO mode 用来设置 IO 口中断模式,可以设置的中断模式为
GPIO的中断触发模式 |
Extemal Interrupt Mode with Rising edge trigger detection 具有上升沿触发检测功能的外部中断模式 |
External Interrupt Mode with Falling edge trigger detection 具有下降沿触发检测的外部中断模式 |
External Interrupt Mode with Rising/Falling edge trigger detection 具有上升/下降沿触发检测的外部中断模式 |
External Event Mode with Rising edge trigger detection 具有上升沿触发检测的外部事件模式 |
External Event Mode with Falling edge trigger detection 具有下降沿触发检测的外部事件模式 |
External Event Mode with Rising/Falling edge trigger detection 具有上升/下降沿触发检测的外部事件模式 |
当我们按下按键时,对应的引脚由高电平转变为低电平,所以在这里我们选择具有下降沿触发检测的外部中断模式。
选项 GPIO Pull-up/Pull-down 用来设置 IO 口是上拉/下拉/没有上下拉。本实验我们设置为上拉(Pull-up)。
选项 User Label 是用来设置初始化的 IO 口 Pin 值为我们自定义的宏,这里我们填写为VOL-DN。按照如上要求设置后的界面如下(由于PI2和PI3的配置相同,只是最后的Label值不同,也在下方列了出来):
配置完成之后我们进入NVIC配置,对三个中断进行使能,如下图所示:
最后需要在Project Manage下的Code Generator选项下勾选 Generate peripheral initialization as a pair of ".c/.h' files per peripheral 选项,这样可以独立生成对应外设的初始化.h 和.c 文件(方便配置的查看),如下图所示:
28.4.3工程的生成与完善
在上述的步骤完成之后,按下键盘的“Ctrl+S”组合键保存保存 EXTI.ioc 文件,系统开始生成初始化代码,工程生成之后如下图所示:
然后我们进行工程的完善,以及添加对应的逻辑代码。
28.4.3.1 对应文件与文件夹的添加
首先在左侧的工程浏览页之中通过鼠标右键在EXIT_CM4的Core目录下创建名字为BSP的文件,具体步骤如下图所示:
由于我们前两个章节已经完善了对应的LED和BEEP的文件,所以我们将iTOP-STM32MP157开发板网盘资料汇总\06_Cortex-M4实验例程\01_LED\LED\CM4\Core\BSP目录下和iTOP-STM32MP157开发板网盘资料汇总\06_Cortex-M4实验例程\02_BEEP\BEEP\CM4\Core\BSP目录下对应的.c、.h文件拷贝到当前工程的BSP目录下,拷贝完成如下下图所示:
然后在BSP目录下以创建key.c文件,在Include目录下的key.h文件,创建完成如下图所示:
28.4.3.2 key.h文件的完善
我们对key.h文件进行代码的添加,将以下内容复制到key.h文件之中如下图所示:
#ifndef __KEY_H
#define __KEY_H
#include "gpio.h"
#endif
28.4.3.3 key.c文件的完善
在key.c文件下,添加以下内容,以下内容实现的功能为定义key_scan函数,该函数用来实现按键的扫描,每一个按键按下后,会有不同的返回值,以此来确定按下的按键。
#include "./Include/key.h"
#include "./Include/led.h"
#include "./Include/beep.h"
void delay_short(volatile unsigned int n)
{
while(n--){}
}
void delay(volatile unsigned int n)
{
while(n--)
{
delay_short(0x7fff);
}
}
void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
for(long n = 1;n<72000;n++){};
delay(2);
if (VOL_UP == 0)
{
LED2_TOGGLE();
}
else if(VOL_DN == 0)
{
LED3_TOGGLE();
}
else if(BACK == 0)
{
BEEP_TOGGLE();
}
}
在这里也可以使用系统延时的方法来达到和上述代码相同的效果,只需要将
for(long n = 1;n<72000;n++){};
delay(2);
部分代码替换为
HAL_Delay(10);
而在6.1.3关于NVIC章节中讲到对于系统中断(比如:PendSV,SVC,SysTick)和外部中断它们是在同一个NVIC下面设置的,也要根据根据他们的抢占优先级和子优先级来进行判定,而在此我们要使用的系统延时SysTick要设置的中断优先级要比外部中断的优先级高,否则会造成程序无法正常运行,修改中断优先级操作如下:
打开EXTI.ioc之后,在左侧菜单栏中的System Core下选择NVIC进入如下图所示:
可以看到Time base:System tick timer 的优先级默认设置为15,而三个外部中断的优先级默认为1,外部中断的优先级是高于默认优先级的,所以我们要对优先级进行修改
我们将Time base:System tick timer 的优先级设置为1,而三个外部中断的优先级设置为2,修改完成如下图所示:
注意:修改优先级中断部分的代码会重置,需要重新进行代码的完善。
28.4.3.4 main.c文件的完善
我们要修改的main.c文件路径如下图所示:
打开main.c文件,为了规范我们在/* USER CODE BEGIN Includes */和/* USER CODE END Includes */之间添加以下内容
#include "../BSP/Include/led.h"
#include "../BSP/Include/beep.h"
#include "../BSP/Include/key.h"
添加完成如下图所示:
然后在 /* USER CODE BEGIN 2 */和/* USER CODE END 2 */之间添加以下内容:
beep_init();
led_init();
通过调用led_init()函数来使LED2和LED3的初始状态分别为亮和灭。
通过调用beep_init()函数来使BEEP的初始状态分别为不发声。
该逻辑代码所实现的功能为当按下VOL-UP按键时会使LED2的状态反转,当按下VOL-DN按键时会使LED3的状态反转,当按下BACK按键时会使蜂鸣器的状态反转
28.4.4工程的编译
在完成以上步骤之后我们点击工具栏的小锤子进行编译,编译图标如下图所示:
编译完成会在下方的终端中显示打印信息,如下图所示:
如果报错,需要自己根据错误的提示信息来进行问题的寻找和改正。
28.4.5工程的调试
由于STM32MP157的裸机部分和一般的单片机有些区别,他没有内部的存储,所以只能在程序编译成功之后,通过debug的方式来进行调试(将程序放在内存之中),调试过程如下:
首先,点击菜单栏中的小甲虫Debug调试按钮,弹出以下界面,
在弹出来的界面,按步骤,选择响应的属性(该步骤为Jlink的步骤,如果是STLink,调试探头选择对应的即可)。如下图所示:
选择完成之后,点击右下角的Debug按钮,点击之后,会进行再一次的编译,编译完成之后会弹出如下内容(作者用的是J-LinK),这里弹出的是J-link关于设备的选择,不同调试器的弹窗可能会不同
在弹出来的界面中,选择Accept接受,会弹出以下内容,继续点击下方的OK。
之后会来到设备选择界面,我们选择Cortex-M4,如下图所示:
选择Cortex-M4之后,点击右下角的OK,会弹出以下界面,选择右下角Switch.
然后会弹出一个新的页面,选择菜单栏的 resume按钮开始调试。
当按下VOL-UP按键时会使LED2的状态反转,当按下VOL-DN按键时会使LED3的状态反转,当按下BACK按键时会使蜂鸣器的状态反转。
如果想关闭调试,则点击菜单栏的终止按钮即可。