NVIC:管理中断、分配优先级(内核 外设,是CPU的小组手)
中断地址:程序中中断函数的地址由编译器分配,是不固定的;中断跳转由于硬件限制,只能跳到固定地址执行程序,所以为了让硬件跳转到一个不固定的中断函数里就需要在内存中定义一个地址列表,列表地址固定,中断发送后,就跳到固定位置,再由编译器加上一条跳转到中断函数的代码,这样中断跳转就可以跳到任意位置了
NVIC只有一个输出口,根据每个中断优先级分配中断的先后顺序,通过一个输出口发送给CPU执行哪个中断
NVIC中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为搞N位抢占优先级和低4-N位响应优先级;抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队(值越小优先级越高)
EXTI外部中断:上升沿/下降沿/双边沿/软件触发(中断响应/事件响应:触发事件响应,外部中断信号不会通向CPU,不触发中断,而是通向其他外设,用来触发其他外设的操作,ADC,DMA等,属于外设之间的联合工作)
GPIO_Pin一样的只能选一个作为中断引脚(中间有AFIO中断引脚选择电路)
通道数:16个GPIO_Pin,外加PVD输出,RTC闹钟,USB唤醒,以太网唤醒
AFIO主要用于引脚复用功能的选择和重定义:复用功能引脚重映射、外部中断引脚的选择
EXTI外部中断控制实例(检测旋转编码器正反转)
int16_t Counter;
void Exti_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB总线时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO时钟,EXTI和NVIC不用单独开启,这两个外设时钟是一直打开的
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);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0); //配置AFIO外部中断引脚设置的数据选择器,选择想要中断的引脚
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); //分组要放在初始化前面,只能有一种分组,可以直接放在main函数里面
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);
}
/**
*@brief 返回每一次变化的值,外部函数要接受到本函数模块的变量,需要传参返回
*@param 无
*@retval Temp,上一次获取到下一次获取中间变化值
*/
int16_t Get_Counter(void)
{
int16_t Temp;
Temp = Counter;
Counter = 0;
return Temp;
}
void EXTI0_IRQHandler(void)
{
/* 在主函数中查看和清除标志位
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
void EXTI_ClearFlag(uint32_t EXTI_Line);
*/
if(EXTI_GetITStatus(EXTI_Line0) == SET) //在中断中查看标志位和清除标志位
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
Counter--; //反转
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1) == SET) //在中断中查看标志位和清除标志位
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
Counter++; //正转
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}