这节来学习中断,中断系统是管理和执行中断的逻辑结构,外部中断是众多能产生中断的外设之一
1.中断的基本知识与介绍
中断:在主程序运行的过程中,出现了特定的中断触发的条件(中断源),使CPU暂时停止了正在运行的程序,转而去处理中断程序,处理完之后又返回原来被暂停的位置继续运行。
中断触发的条件例如:对于外部中断,引脚发生电平跳变,对于定时器来说,定时时间到了,对于串口通信来说,接收到了数据。
中断的优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先相应更加紧急的中断源。
中断嵌套:当一个中断程序正在执行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前的中断程序,转而去处理新的中断程序,处理完成过后一次返回
stm32中断:包含EXTI,TIM,ADC,USART,SPI,I2CRTC等中断源
2.中断的执行流程
程序由硬件电路自动跳转到中断程序中
程序分析(中断满足时,程序暂停)
3.NVIC优先级分组
NVIC的中断优先级由优先级寄存器的4位(0-15)决定,这4位可以进行切分,分为高唯位的抢占优先级和低件(4-n)位的响应优先级
抢占优先级高的可以中断嵌套响优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队(任何时候都是优先级高的先响应)
4.EXT1外部中断(ExtemZnterupt)
EXT1可以监测指定GPIO口的电平信号,当其定的GP1O产生电平变化时,EXT1将立即向NVIC发出中断申请,经过NVIC裁决后部可中断CPU主程亮、使CP执行田对应的中断程序。简单来说:引脚电平变化申请中断
交持触发方式:上升治,下降治,双边识,软件触发
支持所有GPIO口,但相同的Pin不能同时触发中断(PA1和PB1不能同时用)
通道数:16个GPIO_Pin,PVD输出,RTC闹钟,USB唤醒,以太网唤醒
触发方式:中断响应,事件响应
中断响应是让CPU执行中断函数,事件响应是当外部中断检测到引脚变化时,中断信号就不会通向CPU了,而是通到其他外设,用来触发其他外设的操作(ADC,DMA),属于外设之间联合工作。
5.AFIO复用IO口介绍
AFIO主要用于引脚复用功能的选择和重定义
作用:复用引脚的重映射和中断引脚的选择
6.对射红外传感器计次
配置过程(这里以配置countSensor.c为例):
1.配置RCC,把涉及的外设时钟都打开GPIOB,AFIO,(NVIC,EXIT)<非必需>
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
2.配置GPIO,选择端口为输出模式
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);
3.配置AFIO,选择我们用的GPIO连接EXIT
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
4.配置EXTI,选择边沿触发方式,及其响应方式(中断响应)
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);
5.配置NVIC,选择合适的优先级
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);
接线如下图:
代码参考:
main.c
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"
int main(void)
{
OLED_Init();
CountSensor_Init();
OLED_ShowString(1, 1, "Count:");
while (1)
{
OLED_ShowNum(1, 7, CountSensor_Get(), 5);
}
}
计数传感器驱动程序
CountSensor.c
#include "stm32f10x.h"
uint16_t CountSensor_Count;
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);
}
//返回计数值
uint16_t CountSensor_Get(void)
{
return CountSensor_Count;
}
//中断函数
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line14) == SET)
{
/如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
{
CountSensor_Count ++;
}
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
CountSensor.h
#ifndef __COUNT_SENSOR_H
#define __COUNT_SENSOR_H
void CountSensor_Init(void);
uint16_t CountSensor_Get(void);
#endif
OLED.c和.h可从第五节参考,Delay.c和.h可在第一节参考
7.旋转编码器介绍及其应用
用于测量位置,速度或旋转方向的的装置,当其旋转轴旋转时,其输出端可以输出旋转方向和方向对应的方波信号读取方波信号的频率和相位信息即可得知旋转轴的速度与方向
外部中断的使用好处:对于stm32,想要获取的信号是外部驱动,很快的突发信号,比如旋转编码器的输出信号,它产生的信号是突发的,32只能被动读取,如果不及时就要错过许多信号,所以要要有脉冲过来,32立即进入中断函数处理。
9.旋转编码器计次
接线图如下:
程序代码
main.c
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"
int16_t Num;
int main(void)
{
OLED_Init();
Encoder_Init();
OLED_ShowString(1, 1, "Num:");
while (1)
{
Num += Encoder_Get();
OLED_ShowSignedNum(1, 5, Num, 5);
}
}
Encoder.c
#include "stm32f10x.h"
int16_t Encoder_Count;
void Encoder_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_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//AFIO
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
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 = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
}
//返回Count的变化值
int16_t Encoder_Get(void)
{
int16_t Temp;
Temp = Encoder_Count;
Encoder_Count = 0;
return Temp;
}
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) == SET)
{
/如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
Encoder_Count --;
}
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) == SET)
{
/如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
Encoder_Count ++;
}
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
Encoder.h
#ifndef __ENCODER_H
#define __ENCODER_H
void Encoder_Init(void);
int16_t Encoder_Get(void);
#endif