STM32学习笔记之:第四章 中断

        由于现在学习的是以寄存器库为基础来实现功能,所以以后的知识点和代码都是以寄存器库来配置的,还有目前现在用的板子是STM32F407VET6,该笔记只是自己的见解和理解,大佬勿喷。

4.1 中断的概述

4.1.1 什么是中断

        从上往下跑主函数(int main()),但是有一些事件(紧急事件/异常事件)打断主程序,打断之后会跑到这些事件运行

        CPU在执行主程序时,接收到处理紧急事件的请求(标志位变化),CPU中断当前操作(中断执行主程序),去执行紧急事件(运行中断服务函数),紧急事件处理完成后会继续执行主程序

        总结:也就是说中断就是程序在正常运行的过程中发生紧急的事情,必须要暂停一下去处理这个紧急的事情,然后跑回来继续干正常的事情。

        进一步理解中断过程:

场景1:看书时候来电话,去接电话,接完电话继续看书  

        看书:一直在做事情(主程序一直跑的程序)

        接电话:紧急事件

选择: ①跑去接电话的时候,把书直接盖起来  (麻烦)

            ②跑去接电话的时候,贴书签记录书看到位置(入栈和出栈操作)

        入栈:保存现场(代码运行的位置;变量、数组....值)

        出栈:恢复现场(代码运行的位置;变量、数组....值)

        

场景2:看书时候,来电话,并且门铃也响了

        看书:一直在做事情(主程序一直跑的程序)

        紧急事件:接电话、开门

选择:①先跑去接电话,再去开门,重新回去看书

           ②先跑去开门,再去接电话,重新回去看书

总结:紧急事件有紧急的程度,用户可以根据紧急事件的紧急程度,决定紧急事件执行的优先级

场景3:看书时候来电话,去接电话,在接电话的过程中门铃响了

        ①在接电话的过程中,暂停接电话跑去开门,开完门之后继续回去接电话

        ②在接电话的过程中,继续回去看书

总结:中断嵌套;中断优先级高可以抢断优先低,优先低抢断不了优先级高的中断

中断工作原理:

正常运行的程序是主函数,代码是由CPU运行的。CPU在主函数里运行是正常的执行过程,当在这个过程中突然发生了紧急事件,CPU必须暂停当前的工作,然后跑去能处理这个紧急事件的函数中做异常处理,处理完这个异常事件后,CPU就会跑回刚才的断点处,继续正常运行下去。

中断优点:减少CPU占用,防止程序卡死,增加程序实时性

说明:为了不影响主程序运行,所以中断服务函数里面不能有大量的延时和死循环

紧急事件执行顺序,用户来决定(用户根据紧急事件的紧急程度设置优先级)

        ①产生紧急事件(打开中断开关,标志位置一)

        ②写一个中断服务函数

        ③决定中断优先级

4.1.2 中断控制系统

NVIC特性

        嵌套向量中断控制器 NVIC 包含以下特性:

● STM32F405xx/07xx 和 STM32F415xx/17xx 具有 82 个可屏蔽中断通道(82个中断源),STM32F42xxx

        和 STM32F43xxx 具有多达 86 个可屏蔽中断通道(不包括 Cortex™-M4F 的 16 根中

断线)

● 16 个可编程优先级(使用了 4 位中断优先级)

● 低延迟异常和中断处理

●电源管理控制

● 系统控制寄存器的实现

 ARM的体系:

        ARM设计/开发内核,中断体系ARM公司设计和开发,因为STM32F407使用的也是ARM的中断体系,在学习完STM32F407的中断后,中断的内容所有使用ARM内核芯片通用

        当前我们使用的STM32F407VET6,使用的就是ARM的内核,使用的中断体系也是ARM的中断体系(M0、M3、M4、M7都是用这种体系的---M系列通用)。        M4系列里面,中断配置方式一样

中断体系就是管理中断的一套机制,在CM4中集成了一个用来管理中断机制的控制器-----NVIC中断管理控制器(内核里面 --- 与内核相关手册)。    NVIC在内核里面,需要用到M4或者M3内核手册进行配置

NVIC控制器的作用:

        接收中断请求信号①,判断中断事件优先级②,指挥CPU按 先后顺序执行紧急事件③(中断服务函数)

NVIC控制器属于内核级的模块,专门做中断管理,包括中断响应,优先级的设置,接收中断请求...

        手册:技术参考手册、内核手册

        特别注意:中断服务函数中不要有大量延时和死循环

4.1.3中断优先级

        优先级:决定事件执行顺序

C语言运算符:等级范围1 – 15,级别越小优先级越高,级别越大优先级越小

                不用死记硬背,翻表查看        使用括号()

NVIC优先级:从0开始的,级别(优先级标号)越小优先级越高,级别越大优先级越小

中断优先级分类:(设置中断优先级名词)

        ARM中断优先级分类分为3个类型:

        抢占优先级/占先优先级(占先):抢占优先级高的函数可以抢断抢占优先级低的函数

        响应有效级/次级优先级(次级):如抢占优先级相同,则会比较响应优先级(不具有抢占特性)

        自然优先级:厂家固定,如抢占优先级相同和响应优先级相同,则遵循厂家设定的自然优先级

        总结:NVIC优先级比较顺序    抢占优先级    >  响应优先级  > 自然优先级

        自然优先级:去哪里查                              中文参考手册

        NVIC优先级说明(事件说明例子)

事件占先次级
A16
B22
C24

        遵循级别越小优先级越高的特点

        事件A与事件B同时到来:先执行事件A

        事件B与事件C同时到来:先执行事件B

  • 如果事件C正在执行,事件B到来:

                占先:可抢断正在执行的优先级低的任务

                继续执行事件C,当事件C结束后才可以执行事件B

  • 如果事件C正在执行,事件A到来:

                事件A抢断事件C,事件A执行完之后再回到事件C执行

        总结:通过设置抢占优先级和次级优先级决定紧急事件执行顺序,抢占优先级具有抢断低优先级任务特性,而次级优先级没有抢断特性

        注意:在设置NVIC优先级时,抢占优先级和次级优先级都必须设置

4.1.4 NVIC优先级分配方式

ARM中断体系:

NVIC 支持由软件指定的优先级。 通过对中断优先级寄存器的 8 位 PRI_N 区执行写操作,来将中断的优先级指定为 0~255。(设置占先和次级优先级) 设置抢占和次级优先级

占先优先级和次级优先级公用一个PRI_N区域   PRI_N区域8bit    0 - 255

为了对具有大量中断的系统加强优先级控制, NVIC 支持优先级分组机制。

例子:分组设置为3,占先设置为4,次级设置3,求PRI_N写入值为多少?

        PRIGROUP |= 3  << x;//设置优先级分组为3

        占先            |          次级

        0100                       0011              -->     0100 0011   -->  PRI_N填入值:0x43             67

配置方法:

  • 决定分组,将分组写入值PRIGROUP区域里面
  • 决定占先优先级和次级优先级,计算出PRI_N的写入值,将写入值写入到对应PRI_N区域里

思考:同一个程序里面可以设置多个分组吗?

        不可以,一个程序只能有一个分组(主函数硬件初始化地方)

STM32FXX中断体系:(重点)

ST公司精简了ARM的分组机制,占先和次级的分配区域为PRI_N高四位

使用的是ST芯片,所以需要遵循ST公司精简过的分组机制

 

例子:设置分组为5,占先设置为2,次级设置为2,PRI_N写入值?

PRIGROUP |= 5 << x;

占先                 次级

10                     10            --->   PRI_N:10 10      -->    10   -->   0xa

精简ST芯片分组表:

写入值/分组号占先位数次级位数占先取值范围次级取值范围
3400-15None
4310-70-1
5220-30-3
6130-10-7
704None0-15

例子:

占先级别值设置为 1      次级级别值设置为1       //分组如何选择  4、5、6

练习:

占先 级别值3           次级 级别值 2               //分组如何选择          5  

占先 级别值4           次级 级别值1               //分组如何选择           4

占先 级别值1           次级 级别值 1              //分组如何选择            4、5、6

占先 级别值5           次级 级别值 2              //分组如何选择           none 

公式:设置分组写入值 =  7 – 占先所占位数

4.1.5 NVIC相关配置函数

中断概述 ---->  中文参考手册

        由于NVIC属于CM4内核级的外设,所有芯片厂家在寄存器上是完全相同的(配置方式,作用)。

所以ARM公司提供了一份通用的NVIC配置函数。我们只需要学会这些函数的使用方法和作用就可以配置    中断分组和优先级了,中断相关的配置函数在core_cm4.h 头文件中.

如何在工程中找到NVIC配置函数:搜索大法(CTRL + F)

        ①使用CTRL + F 进入搜索窗口

        ②点击在所有文件中查找

        ③在Find what输入NVIC,点击Find All

NVIC设置的步骤和设置函数说明:

1.NVIC里中断源优先级分组设置

        设置分组(决定抢占优先级的位数)

         函数:

                void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)

                函数名:NVIC_SetPriorityGrouping

                函数功能:设置中断的优先级分组, 分配抢占和响应的位数

                函数返回值:无

                函数参数:uint32_t PriorityGroup   //   分组写入值        3     4     5     6    7

                注意:一个工程里只需要一个中断分组,所有中断源共用一个优先级分组

                位置:通常放在主函数中的其他模块初始化函数上面

                公式:优先级分组写入值 = 7 – 占先所占位数

        练习:

                抢占优先级设置为占2位   0 - 3

                利用公式:分组号/写入值 = 7 – 需要设置的占先优先级位数

                NVIC_SetPriorityGrouping(7 - 2);//设置优先级分组为第五组    占先:0-3    次级:0-3

2.计算优先级编码(计算PRI_N的值)

        设置具体的抢占优先级和响应优先级级别值

        函数:

     uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority)------core-cm4 1592

        函数名:NVIC_EncodePriority

        功能:将优先级分组的写入值、抢占优先级值、响应优先级值、计算成一个整型数据返回 //PRI_N编码值     0- 15

        返回值:u32           //PRI_N编码值(PRI_N写入值)     0- 15      

        参  数:优先级分组(寄存器的 8~10 位 的写值)    //写入值

                        抢占优先级值(抢占优先级值)    

                        响应优先级值(响应优先级值)

        注意:注意各优先级的值的范围  

        说明:  将优先级分组值,抢占级别值,响应级别值,计算成一个u32的数据,返回.

        位置:  设置哪个中断源,就把此函数写在哪个中断源初始化函数中

        练习:

                设置抢占优先级为1,响应优先级为2

        NVIC_SetPriorityGrouping(7 - 2);//设置优先级分组为第五组    占先:0-3    次级:0-3

        u32 pri = NVIC_EncodePriority (5, 1, 2);

3.具体某个中断源的优先级设置

        将刚才计算出来的优先级编码值,与具体某个中断源联系起来

        函数:

                void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)-------------core-cm4 1550

                函数名:NVIC_SetPriority

                功能:设置具体某个中断源的优先级(编码)     0 - 15

                返回值:无

                参数:IRQn_Type IRQn  //   中断源名字(中断源编号)

                           uint32_t priority //计算出来的中断优先级编码(PRI_N写入值)

                如何找中断源:

                        每个中断源的结尾:IRQn

                                搜索:ctrl + F        IRQn   

        练习:

                将usart1中断,抢占优先级为1,响应优先级为2的中断

                        NVIC_SetPriorityGrouping(7 - 2);//设置优先级分组为第五组    占先:0-3    次级:0-3

                        u32 pri = NVIC_EncodePriority (5, 1, 2);

                        NVIC_SetPriority(USART1_IRQn, pri);

4.NVIC中断信号响应通道使能

        NVIC模块响应片上外设中断源的开关     //使能NVIC响应通道

        函数:

                void NVIC_EnableIRQ(IRQn_Type IRQn)

                函数名:NVIC_EnableIRQ

                作  用:NVIC模块响应片上外设中断源的信号

                返回值:无

                参  数:IRQn_Type IRQn ----具体中断的编号(可以直接写名字)     

        练习:

                将usart1中断,抢占优先级为1,响应优先级为2的中断

                NVIC_SetPriorityGrouping(7 - 2);//设置优先级分组为第五组    占先:0-3    次级:0-3

                u32 pri = NVIC_EncodePriority (5, 1, 2);

                NVIC_SetPriority(USART1_IRQn, pri);

                NVIC_EnableIRQ(USART1_IRQn);

                将usart2中断,抢占优先级为3,响应优先级为1的中断

                NVIC_SetPriorityGrouping(7 - 2);//设置优先级分组为第五组    占先:0-3    次级:0-3

                u32 pri = NVIC_EncodePriority (5, 3, 1);

                NVIC_SetPriority(USART2_IRQn, pri);

                NVIC_EnableIRQ(USART2_IRQn);

4.4.6 中断服务函数

中断服务函数本质就是一个函数,此函数运行就是紧急事件

             注意:中断服务函数有名字固定、格式固定;中断服务函数不需要调用

例子:USART1中断服务函数

格式:

        void 中断服务函数名字(void)

        {

                //紧急事件

        }

①中断服务函数如何查找     

        搜索大法:CTRL + F        “IRQH”

②鼠标双击进入此函数的定义,复制粘贴此函数名字使用 

USART1_IRQHandler

        void USART1_IRQHandler(void)

        {

                

        }

总结:习惯使用CTRL + F

  • CTRL + F :NVIC    找到配置优先级分组和优先级的函数
  • CTRL + F:IRQn    找到中断源名称
  • CTRL + F:IRQH   找到中断服务函数名称

注意:一定复制粘贴,千万手敲(很容易出错)

4.2 串口中断的使用

4.2.1串口接收和串口空闲中断

配置串口1接收中断和串口空闲中断

接收中断:接收到数据触发

①USART1初始化补充将CR1寄存器中RXNEIE位置1

②当RXNE标志位为1时触发中断,进入中断服务函数  --->   接收数据

空闲中断:记录数据是否接收完成

①在USART1初始化中补充CR1寄存器IDLEIE位置1

代码步骤:

Int main()

{

        //配置优先级分组

        While(1)

        {

        }

}

①USART1初始化

{

        USART1初始化

        补充:CR1  RXNEIE 置一

                   CR1 IDLEIE置一

                   计算优先级编码置

                   配置USART1优先级

                   使能USART1中断源

}

②USART1中断服务函数

        中断服务函数在工程中任意都能写,千万不要写错名字,建议复制粘贴

4.2.2程序设计

usart.c

#include "usart1.h"
 
/*
函数功能:USART1初始化
返回值:void
形参:u32 bps	波特率
作者:jerry
版本:1.0
函数说明:
USART1_TX   ---  PA9  --- 复用模式
USART1_RX   ---  PA10  --- 复用模式
低位寄存器:GPIOA->AFR[0]
高位寄存器:GPIOA->AFR[1]
*/
 
void Usart1_Init(u32 bps)
{
	
	float USARTDIV = 0;
	u32 DIV_M ,DIV_F;
	
	//1、打开GPIOA、USART1时钟
	RCC->AHB1ENR |= (1 << 0);
	RCC->APB2ENR |= (1 << 4);//打开USART1时钟
	//2、PA9、PA10初始化
	GPIOA->MODER &=~(0xf << 18);//清零
	GPIOA->MODER |= (0xa << 18);//将PA9和PA10配置为复用模式
	//3、配置复用关系(IO映射)
	GPIOA->AFR[1] |= 7 << 4;	//将PA9复用到USART1
	GPIOA->AFR[1] |= 7 << 8;	//将PA10复用到USART1
	//4、USART1初始化
	USART1->CR1 &=~ (1 << 15);	//16倍过采样:OVER8 = 0
	USART1->CR1 &=~ (1 << 12);	//数据长度为8bit
	USART1->CR1 |= (1 << 3);	//发送器使能
	USART1->CR1 |= (1 << 2);	//接收器使能
	
	USART1->CR2 &=~(3 << 12);	//停止位为1bit
	//5、计算波特率
	USARTDIV = 84000000 / 16.0 / bps;
	DIV_M = (u32)USARTDIV;			//整数部分
	DIV_F = (USARTDIV-DIV_M) * 16;	//小数部分
	USART1->BRR |= DIV_M << 4 | DIV_F;	//设置BRR寄存器
	
	//6、使能接收中断和空闲中断
	USART1->CR1 |= (1 << 5);	//使能接收中断	RXNE
	USART1->CR1 |= (1 << 4);	//空闲接收中断	IDLE
	
	//7、配置NVIC 
	//计算编码值		占先:1		次级:1
	u32 pri=NVIC_EncodePriority (5, 1, 1);
	//设置优先级
	NVIC_SetPriority(USART1_IRQn, pri);
	//使能中断源
	NVIC_EnableIRQ(USART1_IRQn);
 
	//使能USART1
	USART1->CR1 |= (1 << 13);	//使能USART1
}


/*
函数功能:USART1中断服务函数
注意:中断服务函数没有返回值,也没有形参
作者:jerry
版本:1.0
函数说明:CTRL + F		IRQH
*/

U1_SRTUCT u1 = {0};

void USART1_IRQHandler(void)
{
	u8 data = 0;
	if(USART1->SR & (1 << 5))//判断进入接收中断		0-->1
	{
		//清除接收标志位
		USART1->SR &=~ (1 << 5);
		//执行接收中断的紧急事件
		data = USART1->DR;
		u1.buff[u1.len++] = data;
      
		
		
	}
	if(USART1->SR & (1 << 4))//判断进入空闲中断		0-->1
	{
		//清除空闲标志位
		USART1->SR;
		USART1->DR;		//***调用过程就是读取过程
		//空闲中断紧急事件	表示数据接收完成
		u1.buff[u1.len] = '\0';		//在字符串结尾补'\0'
		u1.len = 0;		//接收下一次的数据
		printf("%s",u1.buff);	//回显接收到的数据
	}
		
}

usart.h

#ifndef _USART1_H
#define _USART1_H



#include "stm32f4xx.h"
#include "stdio.h"
#include "string.h"


void Usart1_Init(u32 bps);




#endif

        以上就是我对中断的一点拙见,由于我现在用的板子是STM32F407VET6,所以后面的代码和图片都是基于这块板子的。后面会继续更新相关我的STM32的学习之路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值