一、提高程序实时性的架构方案
- 轮询式
指的是在程序运行时,首先对所有的硬件进行初始化,然后在主程序中写一个死循环,需要运行的功能按照顺序进行执行,轮询系统是一种简单可靠的方式,一般适用于在只需要按照顺序执行的并且没有外部事件的影响的情况下。
//自定义延时函数
void Delay();
int main() {
//1.硬件初始化
LED_Init();
BEEP_Init();
KEY_Init();
//2.进入死循环 for(;;)
{
//判断按键状态,导致程序的实时性较差
if(KEY0 == 0 ) {
//控制BEEP发声 GPIO_SetBits();
Delay(50); //50ms
//控制LED点亮GPIO_ResetBits();
Delay(100); //100ms
} } }
- 前后台
相比于轮询系统,前后台系统增加中断的概念,如果外部事件发生,则在中断中进行处理,主程序在轮询系统中运行,中断被称为前台,主程序中的while(1)就称为后台。中断会终止后台程序的运行,然后跳转到对应的中断服务函数中去处理,处理完成后,在继续执行后台的程序。使用前后台系统可以极大程度的提高程序的实时响应能力,避免造成外部事件的缺失。
//定义信号的回调函数,该函数不需要调用
void signal_handler(int signum)
{
//定义状态机
switch(signum){
case SIGUSR1: xxxx;break;case SIGUSR2: xxxx;
break; .....
} }
int main(){
//1.硬件初始化
LED_Init();
BEEP_Init();
KEY_Init();
//2.注册信号,进程收到该信号,会触发信号的回调函数,此时会中断当前程序
signal(SIGUSR1, signal_handler);
//3.进入死循环
for(;;)
{
//控制BEEP发声GPIO_SetBits();
Delay(50); //50ms
//控制LED点亮
GPIO_ResetBits();
Delay(100); //100ms } }
- 多任务
相比于前后台系统,多任务系统的外部事件也是在中断中进行响应,但是外部事件的处理是任务中进行处理。任务具有优先级,优先级高的任务先处理,所以程序就会被分割为一个个的任务,任务是一个独立的死循环,并且不能返回,可以由操作系统进行任务的调度,程序段的实时响应能力又得到提升。
|
二、MCU中断的原理与应用
- 中断的概念
中断指的是CPU来处理和响应外部发生的异常,中断也就意味着打断,比如打断正在做的事,然后去处理一个紧急的事,处理完成后在继续做刚才没做完的事。
- 中断源分析
中断源指的是异常发生的源头,中断源在内核中已经定义好了,中断源表也称为中断向量表,向量表在STM32F4中文参考手册参考。
上图中的表格就是STM32F4系列的异常向量表,对于STM32F407xx有82个可屏蔽异常!!!!
- 中断的管理
NVIC指的是嵌套向量中断控制器,属于内核中的外设,作用是管理所有的中断,比如中断的使能或失能、中断的优先级.....。
不管是Cortex A系列还是Cortex M系列的内核内部均有NVIC,通过NVIC来管理内核异常和外部异常。
NVIC管理中断通道的打开与关闭,可以把NVIC理解为负责管理所有中断的开关,想要使用中断发送中断请求,就必须提前打开中断的通道。关于NVIC的使用都存储在一个结构体中,这个结构体和NVIC的函数接口都定义在misc.c和misc.h中。
- 中断的顺序
NVIC利用4bit的优先级来管理所有的中断通道,STM32中断的优先级分为两种:抢占式优先级(主优先级) + 响应式优先级(子优先级),有16个优先级(0~15),数字越小,优先级越高。
意义:如果同时发生多个中断请求,但是又不能同时处理,就根据中断请求的优先级来处理和响应中断。
抢占优先级(主优先级):抢占优先级高的中断可以打断正在执行的抢占优先级低的中断!!!
响应优先级(子优先级):在同时发生多个中断的情况下,响应优先级高的中断可先执行!!!
- 抢占优先级高的中断可以打断正在执行的抢占优先级低的中断
- 抢占优先级相同的中断同时发生,响应优先级高的中断先执行
- 抢占优先级相同的多个中断发生,响应优先级高的中断不能打算响应优先级低的中断
- 抢占优先级和响应优先级相同的多个中断同时发生,则按照向量表的中断编号来执行
为了方便用户管理和响应中断,NVIC需要对中断优先级进行分组,这样用户可以方便配置
注意:该函数必须在主程序的入口进行调用,并且整个项目应该只调用一次,因为设置好优先级分组之后就不应该随意更改分组,否则中断管理比较混乱,另外用户在设置中断优先级的时候应该遵守分组对应的优先级范围。
三、EXTI外设的基本原理与应用
- 基本概念
EXTI指的是外部中断/事件控制器,一共有23个,每个都有一个内部的边沿检测器,可以检测上升沿或者下降沿,每根线都可以产生事件或者中断。
注意:每个GPIO引脚都可以配置为外部中断,但是和GPIO相关的外部中断线一共有16根,分别为EXTI0~EXTI15。
- 基本原理
思考:STM32F407系列有114个GPIO口,那如何和外部中断线进行关联?通过映射的方式
注意:STM32F407所有IO口都可以设置为外部中断,但是必须把GPIO引脚配置为输入模式。
- 程序设计
- 把EXTI外设的源文件以及SYSCFG外设的源文件添加到项目工程中,具体操作如下所示:
- 参考ST公司提供的外设的源文件的开头的注释以及参数ST公司提供的关于外设的例程
- 打开GPIOA端口时钟(因为KEY0按键对应的引脚PA0)以及SYSCFG外设时钟(映射)!
- 配置GPIOA端口PA0引脚的模式为输入模式,因为需要利用该引脚检测外部事件,如下
- 利用SYSCFG外设的寄存器对GPIOA端口的引脚PA0以及EXTI0建立映射关系(自动建立)
- 定义EXTI外设的结构体变量,对结构体成员初始化(编号+模式+边沿+状态),如下图
- 配置EXTI外设的中断,需要使用NVIC外设管理中断的通道以及中断的优先级,如下图
- 对EXTI中断通道配置完成后,需要编写对应的中断服务函数,中断服务函数的格式固定
注意:ISR中断服务函数的名字是已经提前定义在启动文件中向量表内,启动文件是在程序运行之前先运行的一个汇编文件(xxx.s结尾的),用户在选择使用某个外设的中断的时候,必须从启动文件中复制中断服务函数的名称,比如EXTI0外部中断线的中断服务函数名字是EXTI0_IRQHandler
启动文件的意思是在程序运行之后第一个被执行的文件,启动文件是使用汇编语言实现的,启动文件会对系统的堆栈空间进行初始化、以及会对系统的时钟进行初始化、以及会设置向量表的ISR地址,最后会跳转到标准C库的main函数中。
启动流程:堆栈空间初始化 ---> 设置中断向量表 ---> 系统时钟初始化 ---> 跳转到主函数
- 堆栈空间的初始化
- 设置中断向量表
中断向量表其实是用于记录异常的ISR中断服务程序的地址,向量表的本质就是一个指针数组,数组中的每个成员都是一个地址(ISR的地址),但是数组中每个元素本身也有地址,不要把地址搞混。
STM32F4的中文参考手册中记录了向量表中每个元素的地址,然后每个元素的地址下都会存储对应的异常的ISR的地址。
- 系统时钟的初始化
复位也属于异常,所以在MCU复位之后,CPU会自动跳转到复位的ISR函数执行,复位异常的ISR函数已经定义在启动文件中,ISR中会对系统的时钟进行初始化,并且会跳转到main函数。
提示:ISR中断服务程序的结构是固定,是不允许有返回值,以及不允许有参数,结构如下:
void PPP_IRQHandler(void) {
}
注意:如果需要使用异常,就一定要在汇编文件外部定义异常的ISR程序,并且ISR函数名称必须和启动文件的向量表中的异常名称一致,如果不一致,则会导致程序死循环!!!!!
注意:当异常发生后,CPU会跳转到异常的ISR程序进行执行,ISR程序应该精简,执行的越快越好。如果异常确实要使用较多代码解除,则可以选择回到后台对异常进行解决,可以选择采用状态机思想实现。
|
作业:利用中断机制实现对开发板的4个按键的检测,另外要求了解4*4矩阵键盘的原理,并提供状态机方式实现16个按键的检测。