中断系统
中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
作用:在没有中断的时候,系统要不断检测是否有中断产生,防止外部中断被忽略还是串口数据被覆盖,(串口通信接收到数据,定时器来说是定时时间到了,外部中断来说就是高低电平变化),有了中断可以执行其他事情,带到满族中断条件才进行。
中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源 ,0,1,2,3,4…..依次降低。
中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回
stm32的中断非常多,若果全部连接到cpu上的话会比较困难,中断很多容易产生拥堵,所以设置NVIC(CPU小助手),/n的意思是有n条线,经过NVIC的优先级筛选,一个又一个的输送到CPU中进行处理。比如医院的叫号系统。
优先级解析:
抢占优先级:病人在看病,来人之后直接打断,让医生帮他看病(中断嵌套)
响应优先级:病人在看病,来人之后还需要等待,但是却可以越过本来在排队的人,上一个看完马上到他
EXTI
中断有很多种,外部中断只是其中一种。
EXTI(Extern Interrupt)外部中断 EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
支持的触发方式:上升沿/下降沿/双边沿/软件触发
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断(比如PA0与PB0)
通道数:16个GPIO_Pin,外加PVD输出(PVD电源电压检测恢复时,电源电压从电压过低恢复时,需要PVD借助一下外部中断进入停止模式)、RTC闹钟(定好闹钟进入休眠,需要用就使用外部中断)、USB唤醒、以太网唤醒 因为外部中断有个功能,从低功耗模式的停止模式下唤醒STM32.
触发响应方式:中断响应/事件响应 (触发中断之后,可以不选择进入cpu处理,而是把中断信号传入外设,触发别的外设进行响应,不走正常的中断流程,ADC,DMA等)
EXTI基本结构
AFIO选择一组连接EXTI, EXTI上只有20个接收引脚,但是每一个GPIO外设就已经是16个引脚了,所以同号引脚只会有一个接在后续的通道上,所以前面会说所有GPIO口口能触发中断,但相同的Pin不能同时触发中断(比如PA0与PB0) 。
其中9-5,15-10是一起的,效果是一样的,进一步区分还需要根据标志位来分别。
底下的20根连接外设的线是事件响应。
从输入线输入,通过出发选择寄存器选择触发方式,下方的出口是触发事件,上面的是正常的中断响应。中断挂起寄存器置1,则信号通过,0则相反,这个需要请求然后给设置的。中断屏蔽寄存器跟信号相与,控制信号的输出是什么,比如中断寄存器置1,则出去的是0或者1由过来的信号自身决定.
支持的触发方式是上升沿下降沿以及软件触发,这个软件中断事件寄存器跟硬件的任意一条路只要存在都行,都能触发中断。
上面表示可以通过总线访问这些寄存器。
或门与门数据选择器,可以多输入,但是只能一个输出,非门是一个输入一个输出。
什么时候用外部中断:对于STM32来说,想要获取的信号是外部驱动的很快的突发信号,比如拧动旋转编码器(不知道什么时候来,辨别很快)。
对于按键的读取,一般采用主函数循环读取的方法,因为中断的话不好处理抖动问题。
图一,对射式红外传感器测速,光栅编码盘不断转动,遮挡透过不断切换,电平不断高低变换的方波,方波的个数代表角度,频率代表转速,只有一路输出,不能测量旋转方向。
图二,内侧两根跟中间c相连,外边分别跟两边的相连,,上面两个跟跟中间额按键相连,按键按下相连,松开断开。编码盘类似光栅一样的东西,可以接通触电的通断,输出两路波形,相对的光栅经过设计,分别相差90度的波形。转动时,分别以相差90度的导通。
正向旋转,B比A提前,反向旋转,B比A滞后90度。
上拉为高电平,导通则下拉为低电平。
中间那个东西旋转时,两边以相差90度相继导通。
第三个图,霍尔传感器型。
是重要开启,外设才能工作,比如NVIC,AFIO,以及I/O口,但是NVIC(内核外设都不需要开启时钟,住在皇宫,跟CPU一起的)跟EXTI两个外设的时钟是一直都开的,就不用手动开启了,只需要开启AFIO以及I/O口就行了。
配置外部中断输入引脚的时候,要看是怎么样进行配置的,可以查阅数据手册,看上面所给的要求以及建议。
AFIO没有单独的函数,都在GPIO文件夹里面,.h文件中寻找。
红外传感器计次
Keil5右键main.c,Close all …关闭除了main其他所有的窗口
第一步,配置GPIO口以及AFIO的开启时钟函数,EXTI唤醒不用开启时钟,另外NVIC也不用。因为他是内核的外设
第二步,结构体进行初始配置,配置GPIO,在输入模式中如果不确定,可以参考手册8.1最后那里。
第三步,AFIO函数在GPIO.h文件中,GPIO_EXTILineConfig(GPIO_PortSourceGPIOX,GPIO_PinSourceX);
这个函数可以配置AFIO的数据选择器,来选择我们想要的中断引脚。如上代表的是号数据选择器上的PB14号引脚导通。
void CountSensor_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line = EXTI_Line14; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_Init(&EXTI_InitStructure); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStructure); }
void EXTI_DeInit(void); //电泳它可以把EXTI所有的配置清除,恢复到上电默认状态。
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); //初始配置,但是因为EXTI只有一个,不用想GPIO 一样,直接用取地址的符号就好了。
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct); //
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);//软件触发外部中断
/*这两个用在主函数中*/
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);//获取指定的标志位是否被置1
void EXTI_ClearFlag(uint32_t EXTI_Line);//对置1标志位清除
/*这两个用在中断函数中*/
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);//获取中断标志位是否被置1
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);//清除中断挂起标志位
最后两个只适用于获取中断标志位的,而倒数第三第四个则是可以获取任意位置的,所以一般中断程序中使用后面两个,主函数中使用倒数第三第四个。
NVIC的库函数就是在misc.h文件之中。
中断函数的函数声明在startup_stm32f10x_md.s;
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); //指定优先级分组,中断不多问题都不大,优先级设置必须都一致,设置一遍就够了,但是如果在模块中,需要每个模块都声明。
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct); //初始化NVIC
对于NVIC的初始化,他的中断通道一些参数不能直接查询到,需要在Ctrl+f之后勾选查找范围才能找到。
在文件夹中,减号收起,加号点开,选择自己的类型。
中断函数的函数名字一般都是固定的,我们可以在startup文件夹中进行参考,找到相应的中断函数名。
void ….(void)
{
}
由于前面说过有几个端口是一起的,所以要用EXTI_GetStatus();进行获取某位信息,具体用法在exti中跳转查看。if它……,之后记得清楚中断标志位,不然会一直进行中断申请,卡死在中断函数中。
中断函数不用声明,应为不用调用,自动执行。
旗帜本质就是把上面那些图的路给打通。
模块函数中的数返回主函数:
void EXTI15_10_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line14) == SET)//判断中断标志位,进入中断之后会置1 { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)//读取GPIO的电平 { LED_TURN(); } EXTI_ClearITPendingBit(EXTI_Line14); } }
如果要共用一个函数,必须放进.文件中声明,主函数也可以用
注意显示屏直接调用up主给的东西作为.c.h文件就行,直接在主函数引用OLED_Init();
NVIC的配置,有不同的端口要分批次配置,重新复制配置。
中断中最好不要设置什么高延时的函数代码,应为它是执行突发事件的,导致中断阻塞,最好不要在中断以及主程序中显示同一个硬件。
————————————————
版权声明:本文为CSDN博主「笔下觅封侯」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_63148816/article/details/125981764