STM32-Day10(中断-红外旋转计次)
5.6 硬件介绍5.6.1 使用外部中断模块的特性对于STM32来说想要获取的信号是外部驱动的很快的突发信号比如旋转编码器的输出信号可能很长时间不会触发这时不需要STM32做任何事情一触发就会有很多脉冲波形需要STM32接收信号是突发的STM32不知道什么时候会来同时是外部驱动只能被动读取并且很快速以上情况就可以使用外部中断去读取。5.6.2旋转编码器旋转编码器用来测量位置、速度或旋转方向的装置当其旋转轴旋转时其输出端可以输出与旋转速度和方向对应的方波信号读取方波信号的频率和相位信息即可得知旋转轴的速度和方向类型机械触点式、霍尔传感器式、光栅式硬件电路左图是模块的电路图中间圆形的是旋转轴旋转轴旋转时下方的两个触点四个小圆点以相位相差90度的方式交替导通接10K的上拉电阻默认没有旋转的时候交点电阻之间红色小圆点被上拉为高电平通过R3输出到A端口的就也是高电平。当旋转时内部触点导通交点被拉低到GND.再通过R3输出A端口就是低电平C1的作用是滤波电容5.7 外部中断实战5.7.1 对射式红外传感器计次接线图效果当挡光片或编码盘在对射式红外传感器中间经过时DO就会触发电平跳变电平跳变的信号触发STM32 PB14号口的中断在中断函数中执行变量的程序然后主循环里调用OLED显示这个变量中断函数的配置流程启动时钟后先配置GPIO输入输出然后配置AFIO进行选择再配置EXIT对触发方式进行设置最后设置NVIC选择响应优先级最后进入CPU时钟只需要打开GPIO和AFIO即可EXIT和NVIC内核的外设不需要开启时钟是一直打开的状态配置AFIO用到的库函数void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);可以配置AFIO的数据选择器来选择我们想要的中断引脚配置EXTI需要用到的库函数void EXTI_DeInit(void); void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct); void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line); FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line); void EXTI_ClearFlag(uint32_t EXTI_Line); ITStatus EXTI_GetITStatus(uint32_t EXTI_Line); void EXTI_ClearITPendingBit(uint32_t EXTI_Line);EXTI_DeInit恢复成默认上电状态EXTI_Init配置EXIT外设初始化用的就是该函数使用方法和GPIO_Init相同EXTI_StructInit把参数传递的结构体变量赋一个默认的值以上三个函数基本上每个外设都有EXTI_GenerateSWInterrupt软件触发外部中断EXTI_GetFlagStatus获取指定的标志位是否被置1EXTI_ClearFlag对置1的标志位进行清除EXTI_GetITStatus获取中断标志位是否被置1EXTI_ClearITPendingBit清除中断挂起标志位以上四个是查看和清除标志位的函数在主程序中进行用前两个中断中用后两个配置NVIC需要用到的库函数void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct); void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset); void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);NVIC_PriorityGroupConfig进行中断分组参数是中断分组的方式NVIC_Init根据结构体里面指定的参数初始化NVICNVIC_SetVectorTable设置中断向量表NVIC_SystemLPConfig系统低功耗配置中断分组函数定义中pre-emption priority抢占优先级subpriority响应优先级中断函数配置完整代码#include stm32f10x.h // Device header 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_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);//配置AFIO EXTI_InitTypeDef EXTI_InitStruct;//配置EXIt EXTI_InitStruct.EXTI_Line EXTI_Line14; EXTI_InitStruct.EXTI_LineCmd ENABLE; EXTI_InitStruct.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger EXTI_Trigger_Falling; EXTI_Init(EXTI_InitStruct); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组的方式整个芯片只能使用一组要保证每个模块的分组都是同一个 NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel EXTI15_10_IRQn; NVIC_InitStruct.NVIC_IRQChannelCmd ENABLE; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStruct.NVIC_IRQChannelSubPriority 1; NVIC_Init(NVIC_InitStruct); }中断程序应该防止在中断函数中在STM32中中断函数名字是固定的每个中断通道都对应一个中断函数。函数名在Start文件夹中的startup文件中以IRQHandler结尾的就是中断函数名中断函数写在CountSensor.c文件夹中在中断函数里一般都是先进行一个中断标志位的判断确保是正确的中断源触发的函数。因为这个函数EXIT10~15都可以进入所以要先判断一下是不是EXIT14进入中断函数一般不需要声明因为其不需要主动调用在中断产生的时候进行调用即可在写完中断函数之后可以使用调试模式进行查看中断是否可以成功跳转使用Debug按钮在中断函数地方打一个断点查看中断之后是否会进入中断函数。这样就是已经进入中断函数然后用一个变量统计中断触发的次数即可本次采用无符号整型变量-unint16_t在CountSensor文件中定义变量然后再在文件中定义一个返回这个变量的函数即可。完整文件代码#include stm32f10x.h // Device header 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);//配置AFIO EXTI_InitTypeDef EXTI_InitStruct;//配置EXIt EXTI_InitStruct.EXTI_Line EXTI_Line14; EXTI_InitStruct.EXTI_LineCmd ENABLE; EXTI_InitStruct.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger EXTI_Trigger_Rising; EXTI_Init(EXTI_InitStruct); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组的方式整个芯片只能使用一组要保证每个模块的分组都是同一个 NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel EXTI15_10_IRQn; NVIC_InitStruct.NVIC_IRQChannelCmd ENABLE; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStruct.NVIC_IRQChannelSubPriority 1; NVIC_Init(NVIC_InitStruct); } uint16_t CountSensor_Get(void) { return CountSensor_Count; } void EXTI15_10_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line14) SET) { CountSensor_Count; EXTI_ClearITPendingBit(EXTI_Line14); } }主函数代码#include stm32f10x.h // Device header #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); } }5.7.2 旋转编码器计次接线图上面是正转下面是反转上下两条线分别是A相和B相正转的时候保证A相在低电平同时B相下降沿的时候计次反转的时候反之这样保证了转到位才计次所以需要用到PB0和PB1两个口。因此此Demo需要用到两个中断的初始化在设计中断函数的具体内容时参考5.6.2中的硬件电路进行设计代码完整模块代码#include stm32f10x.h // Device header 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); GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);//配置AFIO GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1); EXTI_InitTypeDef EXTI_InitStruct;//配置EXIt EXTI_InitStruct.EXTI_Line EXTI_Line0|EXTI_Line1; EXTI_InitStruct.EXTI_LineCmd ENABLE; EXTI_InitStruct.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger EXTI_Trigger_Falling; EXTI_Init(EXTI_InitStruct); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组的方式整个芯片只能使用一组要保证每个模块的分组都是同一个 NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel EXTI0_IRQn; NVIC_InitStruct.NVIC_IRQChannelCmd ENABLE; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStruct.NVIC_IRQChannelSubPriority 1; NVIC_Init(NVIC_InitStruct); NVIC_InitStruct.NVIC_IRQChannel EXTI1_IRQn; NVIC_InitStruct.NVIC_IRQChannelCmd ENABLE; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStruct.NVIC_IRQChannelSubPriority 2; NVIC_Init(NVIC_InitStruct); } int16_t Encoder_Get(void) { int16_t Temp; TempEncoder_Count; Encoder_Count0; return Temp; } //中断函数 void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0) SET) { 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_0)0) { Encoder_Count; } EXTI_ClearITPendingBit(EXTI_Line1); } } 注意下面两个中断PB0和PB1的优先级不可以搞反否则就会相互抵消而且注意EXTI要调整为下降沿触发否则会导致正反搞错主函数代码#include stm32f10x.h // Device header #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) { NumEncoder_Get(); OLED_ShowSignedNum(1,5,Num,5); } }