基于STM32F407系列外部中断学习

STM32外部中断学习

1)外部中断概述

1.外部中断描述
在日常生活中,例如早上在睡觉被闹钟吵醒,你去关闭闹钟就属于中断事件。
在主函数里的代码是由CPU运行的,CPU在执行过程中突然发生了异常事件(中断),CPU必须暂停当前的工作(设下断点),然后跑去处理这个异常事件的函数(中断服务函数),处理完这个异常事件后(执行完中断服务函数),CPU就会回到刚才的断点,继续执行其它代码。所以主函数和中断服务函数属于同级,互相不能被调用。
中断发生的时间不可知、一旦发生就需要立马去执行
发生的条件:由外部输入数据决定,例如按键输入0/1执行中断服务函数
中断分类:外部中断 软件中断 系统中断

中断示意图

在这里插入图片描述

2)设置中断好处

1.提高硬件的利用率;2.可完善代码的逻辑

举例说明

如寄存器的查询机制

void Usart1_Echo(void)
{
	u8 data;
	while(!(USART1->SR & (1<<5)));
	data=USART1->DR;
	while(!(USART1->SR & (1<<7)));
	USART1->DR=data;
}
	主函数调用了这个函数一直在循环判断标志位,若DR里没有数据到来,一直卡在输入输出的状态位上,不会执行main函数内的剩下的代码。
	如果加入中断服务函数,主函数可一直执行其它代码,把串口数据接收配置为中断,
串口有数据过来 触发断点执行对应的中断服务函数
	中断方式就会大大提升硬件的利用率和完善代码逻辑

3)中断入口及与主函数关系

子函数与主函数

1.主函数可以调用子函数;
2.子函数可以调用子函数 (递归函数)
3.子函数不能调用主函数

中断服务函数与主函数:
中断服务函数与主函数之间不存在调用关系(平级),发生了断点中断服务函数抢占CPU执行中断服务函数、执行完后把CPU还给主函数
执行中断服务函数过程中也有可能触发新的断点

中断嵌套关系图

在这里插入图片描述
如果抢占优先级比当前执行的中断服务函数的优先级更高,可以进行多层嵌套。如果抢占优先级同级,不能进行嵌套。

4)中断优先级说明

中断体系中: 数字越小优先级越高   
优先级别分为:抢占优先级  响应优先级  自然优先级
先比较抢占优先级 抢占一样时才比较响应优先级

在ARM结构下的优先级

ARM体系下的优先级:技术手册第五章(5.3)

在这里插入图片描述

ST公司的ARM体系下进一步封装

ST在ARM体系下中断优先级分组(内核编程手册213页)
在这里插入图片描述
在这里插入图片描述
一定要注意:选择优先级时一定要先选分组号 再写抢占优先级和响应优先级。
一个工程内只能有一个优先级分组!!!否则后续的再次分组会替换掉前面的优先级分组。

5)设置优先级分组函数

函数原型: void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
函数功能:设置优先级分组
形参: PriorityGroup 这个函数的组号按照下图的组号分配

在这里插入图片描述

NVIC_SetPriorityGrouping(1);//1位抢占 3位响应,可以理解为传入的形参就是多少位抢占优先级

6)设置中断优先级函数

函数原型:

void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)

函数功能:设置优先级
形参:
IRQn----中断号
Priority----优先级
例如:

NVIC_SetPriorityGrouping(2;//2位抢占 2位响应
NVIC_SetPriority(USART1_IRQn,6);//串口1的中断设置成抢占优先级为1,响应优先级为2(0110)

NVIC_SetPriorityGrouping(1;//1位抢占 3位响应
NVIC_SetPriority(USART2_IRQn,7);//串口2的中断设置成抢占优先级为0,响应优先级为7(0111)

8)中断使能函数

void __NVIC_EnableIRQ(IRQn_Type IRQn)

传参是中断号

9)3Stm32F4XX中断向量表(参考中文手册或者启动文件stm32f40_41.s)

STM32F4系列有82个中断向量都封装在库函数内,具体可以按CTRL+F搜索IRQn查询需要用到的中断号

10)外部中断使用

GPIO发生电平变化产生的中断(外部中断)
23个中断源(16个EXTI线+7根另外的中断线)—其中16根是IO口触发的
82个IO口 —不可能每个IO口配置中断
相同引脚的IO口公用一个中断—EXTI线
这种公共入口的中断 进入中断服务函数内还需要判断具体哪个中断触发的
在这里插入图片描述
IO口的中断都是由引脚n映射到EXTIn(外部中断线n)上,5-9位共用一个映射,10-15共用一个映射,公用的映射都共用同一个中断服务函数
在这里插入图片描述
所以一个程序内最多只能容纳7+7=14个外部中断服务函数

例如配置PF4引脚的外部中断

首先打开PF时钟和EXTI时钟(SYSCFG时钟)

在数据手册中找到EXTI时钟线对应的SYSCFG时钟

在这里插入图片描述
GPIOF对应的时钟线是AHB1,EXIT(SYSCFG)对应的时钟线是APB2。这里图中标注的EXI时钟线,在参考手册对应是SYSCFG时钟。
在这里插入图片描述
所以我们应该置1该寄存器的第14位
需要配置SYSCFG的寄存器如下
在这里插入图片描述

SYSCFG->EXTICR[1] |=(5<<0);//PF是0101

在这里插入图片描述
这样可以将PF4映射到EXTI4线上

外部中断框图

在这里插入图片描述
从外部中断框图可知,数据通过IO口进来,先经过边沿检测电路(上升沿或下降沿触发),再和中断屏蔽寄存器(置1,开放第EXTIn中断线)进行逻辑与门后,通过挂起寄存器判断第n位数据到来,就可以执行中断服务函数的代码。
在这里插入图片描述
在这里插入图片描述
值得注意是:当发生中断请求,例如PF4中断请求时,
EXTI->PR的第四位会置1,当进入中断服务函数时,对第4位写1可以将标志位清除。
在这里插入图片描述

void system_Config()
{
	RCC->APB2ENR |= (1<<14);//打开外部中断时钟
	RCC->AHB1ENR |= (1<<6);//打开F端口时钟源
	
	SYSCFG->EXTICR[1] |=  (4<<0);   //将PE4引脚映射到EXTI4上
	
	EXTI->RTSR |= (1<<4);//设置为上升沿触发
	EXTI->IMR |= (1<<4);//中断屏蔽寄存器设置
	NVIC_SetPriorityGrouping(2);//设置优先级分组号,2位抢占,2位响应
	NVIC_SetPriority(EXTI4_IRQn, 9);//EXTI0设置抢占2,响应1(1001)
	NVIC_EnableIRQ(EXTI4_IRQn);//使能EXTI4
}

11)同理编写按键中断思路

1.代码中按键能正常工作
2.打开时钟(SYSCFG)
3.配置SYSCFG_EXTICR
4.配置边沿选择寄存器(上升沿 下降沿)
5.使能中断屏蔽寄存器
6.优先级(根据优先级分组,设置n位抢占,4-n位响应,使能中断号)
7.中断服务函数
进服务函数先将标志位写1清零!!

12)中断服务函数的注意事项(重点!)

1.中断服务函数名尽量用复制,不要自己写,因为只要你写错一个字母,这个函数就变成普通函数了。
2.(如果中断服务函数是公共入口)进入到中断服务函数后先要查询是哪种中断
3.先清中断标志,然后再做中断处理,不要把清中断标志放在函数的最后。(如果把清除中断标志放在中断服务函数的最后,会出现当发出清中断标志指令后,硬件还没有把相关标志清除掉,程序就已经跳出了中断服务函数,这个时候NVIC又会识别到标志是1,出现重复中断)。—可以清除中断标志命令发出后,等待清除成功再往下执行。
4.中断服务函数应该尽量简短,一般是做一些标识,不要在中断中做延时之类的占用CPU很长时间的工作。要做到快进快出
5.中断服务函数不会被任何一个函数调用,当中断条件满足后,NVIC控制把CPU拉到中断服务函数中执行。

STM32F407系列微控制器的标准库中并没有直接提供ADC定时中断和GPIO操作的个人库函数。然而,你可以通过标准库函数如`HAL_ADC_Start`, `HAL_ADC_IRQHandler`, 和`HAL_GPIO_Init`等来进行这些功能的自定义整合。 首先,你需要初始化ADC模块,例如: ```c void init_adc(void) { ADC_HandleTypeDef hadc1; // 创建ADC handle hadc1.Instance = ADC1; // 设置ADC实例 HAL_ADC_Init(&hadc1); // 初始化ADC配置 // 配置定时器用于触发ADC转换 TIM_HandleTypeDef htim1; ... (配置TIM1为ADC外部触发) HAL_TIM_Base_Start(&htim1); } ``` 然后,设置ADC的中断处理函数,当转换完成时会被调用: ```c void MX_ADC1_IRQHandler(void) { HAL_ADC_IRQHandler(&hadc1); // 处理ADC中断 if (hadc1.State == HAL_ADC_STATE_COMPLETED) { process_adc_data(); // 处理获取到的数据 } } ``` 对于GPIO的操作,比如配置输入或输出,可以这样做: ```c void setup_gpio(uint8_t gpio_pin, PinState state) { GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_x; // replace x with your GPIO pin number GPIO_InitStruct.Mode = GPIO_MODE_INPUT_OUTPUT; // 输入输出模式 GPIO_InitStruct.Pull = state ? GPIO_NOPULL : GPIO_PULLDOWN; // 根据需要设置拉高或拉低 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOx, &GPIO_InitStruct); // GPIOx is the GPIO port register, like GPIOA for example } ``` 记得在适当的时候注册ADC的中断,并开启它: ```c void enable_adc_timer_and_int(void) { // Enable ADC interrupt and set callback function __HAL_ADC_ENABLE_IT(&hadc1, ADC_IT_EOC); // Register ADC1 interruption handler HAL_NVIC_SetPriority(ADC1_IRQn, YOUR_INTERRUPT_PRIORITY, 0); HAL_NVIC_EnableIRQ(ADC1_IRQn); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值