• EXTI外部中断


    中断系统介绍

    中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得 CPU 暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
    中断优先级:当有多个中断源同时申请中断时, CPU 会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
    中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断, CPU 再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回

    STM32中断

    68 个可屏蔽中断通道,包含 EXTI TIM ADC USART SPI I2C RTC 等多个外设
    使用 NVIC 统一管理中断,每个中断通道都拥有 16 个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级

     NVIC

    NVIC优先级分组 

    NVIC 的中断优先级由优先级寄存器的 4 位( 0~15 )决定,这 4 位可以进行切分,分为高 n 位的抢占优先级和低 4-n 位的响应优先级
    抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队

     响应优先级:即当有中断正在执行,执行完毕后,响应优先级高的,先执行。(排队排第一)

    抢占优先级:即当有中断正在执行,无论有没有执行完,抢占优先级高的立马执行。(插队)

    EXTI简介

    EXTI Extern Interrupt )外部中断
    EXTI 可以监测指定 GPIO 口的电平信号,当其指定的 GPIO 口产生电平变化时, EXTI 将立即向 NVIC 发出中断申请,经过 NVIC 裁决后即可中断 CPU 主程序,使 CPU 执行 EXTI 对应的中断程序
    支持的触发方式:上升沿 / 下降沿 / 双边沿 / 软件触发
    支持的 GPIO 口:所有 GPIO 口,但相同的 Pin不能同时触发中断(即PA1和PB1)
    通道数: 16 GPIO_Pin ,外加 PVD 输出、 RTC 闹钟、 USB 唤醒、以太网唤醒
    触发响应方式:中断响应 / 事件响应(不会触发中断,而是触发别的外设操作,是外设之间的联合工作)

     

    AFIO中断引脚选择: 它可以在前面的GPIO外设的16个引脚中选择其中一个连接到后面的EXTI的通道里,所以相同的Pin不可以同时触发中断。

    AFIO复用IO口

    AFIO 主要用于引脚复用功能的选择和重定义
    STM32 中, AFIO 主要完成两个任务:复用功能引脚重映射、中断引脚选择

    EXTI框图

    到底什么样的设备需要用到外部中断呢,外部中断又有什么好处呢?

    对于STM32来说,想要获取的信号是外部驱动的很快的突发信号,就应该用到外部中断。

    比如旋转编码器的输出信号,当我们不使用旋转编码器时,这时我们不需要STM32做出任何反应,但是一旦我们拧动了旋转编码器,就会发出很多脉冲波形需要被接收,这个信号是突发的,STM32是不知道什么时候会来的,且它是外部驱动的,STM32只能被动读取,信号也非常快,当STM32稍微晚接收一点点时间都会造成数据的丢失。这种情况就应该使用外部中断。

    旋转编码器

    旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向
    类型:机械触点式 / 霍尔传感器式 / 光栅式

     

    硬件电路 

    实操部分

    5-1 对射式红外传感器计次

    首先先编写初始化函数

    这里我们需要搞懂需要配置哪些函数

    由上图可知:

    初始化函数

    1、配置RCC时钟(即涉及到的外设需要用到的时钟),再配置GPIO,选择为输入模式;
    1. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    2. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

    当不知道配置GPIO为什么输入模式时,可以查看参考手册中的GPIO介绍中的外设GPIO配置

     这里我们选择上拉输入

    1. GPIO_InitTypeDef GPIO_InitStructure;
    2. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    3. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    4. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    5. GPIO_Init(GPIOB, &GPIO_InitStructure);
    2、配置AFIO,选择我们将会用到的GPIO口,连接到EXTI;

    这和配置对射式红外传感器的GPIO口时不一样,需要用到新的函数,在GPIO的头文件中可以找到

    首先是

    void GPIO_AFIODeInit(void);
    

     会把AFIO外设的配置全部清除

    第二个:

    void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
    

     这个函数可以配置AFIO的数据选择器,来选择我们想要的中断引脚。

    打开函数定义就可得知参数应该填什么

    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
    3、配置EXTI,选择边沿触发方式(上升沿、下降沿和双边沿),选择触发响应方式(中断响应和事件响应)

    打开EXTI头文件,查看EXTI有什么函数

    a、清除EXTI的配置

    void EXTI_DeInit(void);
    

    b、 初始化EXTI

    void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
    

    c、 调用这个函数,可以把参数传递的结构体变量附一个默认值

    void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
    

     d、软件触发外部中断,调用这个函数,参数给一个指定的中断线,就能外部软件触发一次中断

    void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
    

     在外设运行过程中,会产生一些状态标志位(比如外部中断来了,会置标志位;串口受到数据,会置标志位;定时器时间到,也会置标志位;这些标志位都是存放在标志寄存器上的,当我们想要阅读这些标志位时就可以使用到以下四个函数)

     e、第一个:可以获取指定标志位是否被置1,;第二个:可以对置1的标志位进行清除(倾向于在主程序中使用)

    1. FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
    2. void EXTI_ClearFlag(uint32_t EXTI_Line);

    f、在中断函数中,使用以下两个函数

    第一个:获取中断标志位是否被置1,;第二个:清除中断挂起标志位(倾向于在中断函数中使用)

    1. ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
    2. void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

    初始化

    void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
    

    需要用到结构体,右键查看结构体成员

    1. EXTI_InitTypeDef EXTI_InitStructure;
    2. EXTI_InitStructure.EXTI_Line = ;
    3. EXTI_InitStructure.EXTI_Mode = ;
    4. EXTI_InitStructure.EXTI_Trigger = ;
    5. EXTI_InitStructure.EXTI_LineCmd = ;
    6. EXTI_Init(&EXTI_InitStructure);

     然后依次右键查看各成员的定义

    在注释中搜索关键词的位置,例如EXTI_Lines;

    1. //配置EXTI
    2. EXTI_InitTypeDef EXTI_InitStructure;
    3. //指定要配置的中断线
    4. EXTI_InitStructure.EXTI_Line = EXTI_Line14;
    5. //指定外部中断线的模式(枚举类型的中断模式和事件模式)
    6. EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    7. //指定触发信号的有效边缘(上升沿,下降沿,双向沿)
    8. EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    9. //指定选择中断线的新状态(ENABLE/DISABLE)
    10. EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    11. EXTI_Init(&EXTI_InitStructure);
     4、配置NVIC,给中断选择一个合适的优先级,再通过NVIC就可以进入到CPU了。

    NVIC的函数被发配到了杂项头文件中misc.h

    1、用于中断分组,参数是中断分组的方式

    void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
    

    2、初始化

    void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
    

     在配置中断之前,先指定中断的分组,再初始化NVIC即可。

    中断分组:

     在中断不多的情况下,不用太在意如何分组,这里我们选择比较平均的第二个分组,两位抢占(pre-emption),两位响应(subpriority)。

    1. //配置NVIC
    2. //分组方式整个芯片只能用一种,则这个分组的代码只要执行一次即可
    3. //如果是放在模块内使用该分组函数,则一定要保证每个模块的分组方式相同
    4. //或许可以直接放在main函数中
    5. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

     NVIC初始化

    1. NVIC_InitTypeDef NVIC_InitStructure;
    2. NVIC_InitStructure.NVIC_IRQChannel = ;
    3. NVIC_InitStructure.NVIC_IRQChannelCmd = ;
    4. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = ;
    5. NVIC_InitStructure.NVIC_IRQChannelSubPriority = ;
    6. NVIC_Init(&NVIC_InitStructure);

    再一个个仔细的查找该填入的值

    第一个是选择中断通道,我们芯片时MD中等密度的,所以只需要展开MD的条件编译即可

    STM32的EXTI10到EXTI15都合并到了这个通道里。

    再根据下图赋值

    1. //配置NVIC
    2. //NVIC的响应优先级和抢断优先级分组
    3. //分组方式整个芯片只能用一种,则这个分组的代码只要执行一次即可
    4. //如果是放在模块内使用该分组函数,则一定要保证每个模块的分组方式相同
    5. //或者可以直接放在main函数中
    6. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    7. NVIC_InitTypeDef NVIC_InitStructure;
    8. //中断通道
    9. NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
    10. //指定中断通道是使能还是失能
    11. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    12. //抢断优先级和响应优先级
    13. //根据之前NVIC的分组函数,可以确定一下两个元素的范围
    14. //因为选择了分组2,则范围是0~3,且该工程只有这一个中断,所以随意设置为1
    15. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    16. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    17. NVIC_Init(&NVIC_InitStructure);

    中断函数

    名字是固定的,可以在启动文件中参考

    这些以IRQHandler结尾的都是中断函数的名字

    其中

    DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10
    

    就是我们EXTI的中断函数

    在我们编写的中断函数的下面写下中断函数的基本框架

    1. void EXTI15_10_IRQHandler(void)
    2. {
    3. }

    一定是无返回值和输入值的!

    到这步我们还需要添加一个判断语句——是否是PB14口引起的中断,这就需要用到我们之前所解释过的EXTI函数,读取中断标志位的函数

    ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
    

    同时还需要在判断后加上清除中断标志位的函数

    void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
    

    把中断状态清除,以防一直卡在中断函数中

    整体

    1. void EXTI15_10_IRQHandler(void)
    2. {
    3. if(EXTI_GetITStatus(EXTI_Line14) == SET)//判断是否是指定端口产生的中断
    4. {
    5. //内容写在这
    6. //清除中断标志位,回归主函数,以防一直卡在中断函数中
    7. EXTI_ClearITPendingBit(EXTI_Line14);
    8. }
    9. }

    记得一定要在中断函数执行完后清除中断标志位,否则中断标志位一直为SET,程序就会一直响应中断,一直执行中断函数。

    这样一来,再在main函数中初始化中断函数就写好代码了,可以用Keil自带的调试模式查看能不能跳到中断函数中。

    在中断函数中设置一个停止点,然后全速运行,再试着使PB14产生中断(实验中是挡住红外传感器的光)

     当出现

    时,就说明中断函数写得没错 

    我们就可以定义一个数来计算中断触发的次数,即挡光片挡住光线的次数。

    1. uint16_t Counts;
    2. void EXTI15_10_IRQHandler(void)
    3. {
    4. if(EXTI_GetITStatus(EXTI_Line14) == SET)//判断是否是指定端口产生的中断
    5. {
    6. //内容写在这
    7. Counts++;
    8. //清除中断标志位,回归主函数,以防一直卡在中断函数中
    9. EXTI_ClearITPendingBit(EXTI_Line14);
    10. }
    11. }
    12. uint16_t GetCounts(void)
    13. {
    14. return Counts;
    15. }

    在主函数中:

    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "CountSenor.h"
    5. uint16_t Sensor_Counts;
    6. int main(void)
    7. {
    8. OLED_Init();
    9. CountSensor_Init();
    10. while(1)
    11. {
    12. Sensor_Counts = GetCounts();
    13. OLED_ShowString(1,1,"Counts:");
    14. OLED_ShowNum(1,8,Sensor_Counts,2);
    15. }
    16. }

    就可以实现我们想要的功能了

    总体:

    CountSensor.c

    1. #include "stm32f10x.h" // Device header
    2. uint16_t Counts;
    3. void CountSensor_Init(void)
    4. {
    5. //配置时钟
    6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    8. //EXTI和NVIC的时钟是一直开着的,不用使用函数配置
    9. //配置对射式红外传感器的GPIO口
    10. GPIO_InitTypeDef GPIO_InitStructure;
    11. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    12. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    13. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    14. GPIO_Init(GPIOB, &GPIO_InitStructure);
    15. //配置AFIO
    16. GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
    17. //配置EXTI
    18. EXTI_InitTypeDef EXTI_InitStructure;
    19. //指定要配置的中断线
    20. EXTI_InitStructure.EXTI_Line = EXTI_Line14;
    21. //指定外部中断线的模式(枚举类型的中断模式和事件模式)
    22. EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    23. //指定触发信号的有效边缘(上升沿,下降沿,双向沿)
    24. EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    25. //指定选择中断线的新状态(ENABLE/DISABLE)
    26. EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    27. EXTI_Init(&EXTI_InitStructure);
    28. //配置NVIC
    29. //NVIC的响应优先级和抢断优先级分组
    30. //分组方式整个芯片只能用一种,则这个分组的代码只要执行一次即可
    31. //如果是放在模块内使用该分组函数,则一定要保证每个模块的分组方式相同
    32. //或者可以直接放在main函数中
    33. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    34. NVIC_InitTypeDef NVIC_InitStructure;
    35. //中断通道
    36. NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
    37. //指定中断通道是使能还是失能
    38. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    39. //抢断优先级和响应优先级
    40. //根据之前NVIC的分组函数,可以确定一下两个元素的范围
    41. //因为选择了分组2,则范围是0~3,且该工程只有这一个中断,所以随意设置为1
    42. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    43. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    44. NVIC_Init(&NVIC_InitStructure);
    45. }
    46. void EXTI15_10_IRQHandler(void)
    47. {
    48. if(EXTI_GetITStatus(EXTI_Line14) == SET)//判断是否是指定端口产生的中断
    49. {
    50. //内容写在这
    51. Counts++;
    52. //清除中断标志位,回归主函数,以防一直卡在中断函数中
    53. EXTI_ClearITPendingBit(EXTI_Line14);
    54. }
    55. }
    56. uint16_t GetCounts(void)
    57. {
    58. return Counts;
    59. }

    main.c

    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "CountSenor.h"
    5. uint16_t Sensor_Counts;
    6. int main(void)
    7. {
    8. OLED_Init();
    9. CountSensor_Init();
    10. while(1)
    11. {
    12. Sensor_Counts = GetCounts();
    13. OLED_ShowString(1,1,"Counts:");
    14. OLED_ShowNum(1,8,Sensor_Counts,2);
    15. }
    16. }

    还用到了OLED模块

    这里是移开挡光片+1,只需更改

    1. //指定触发信号的有效边缘(上升沿,下降沿,双向沿)
    2. EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;

    即可改为挡住+1,或者挡住+1和移开+1 

    5-2 旋转编码器计次

    在之前的代码上稍作修改即可,把向左转和向右转改为两个EXTI中断。

    配置时钟不用改,配置GPIO口时

    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_0;

    配置APIO需要分开配置

    1. //配置AFIO
    2. GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
    3. GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);

    EXTI中断可以一起配置

    	EXTI_InitStructure.EXTI_Line = EXTI_Line1 | EXTI_Line0;

    配置NVIC时需要分开配置

    1. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    2. //EXTI0
    3. NVIC_InitTypeDef NVIC_InitStructure;
    4. //中断通道
    5. NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    6. //指定中断通道是使能还是失能
    7. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    8. //抢断优先级和响应优先级
    9. //根据之前NVIC的分组函数,可以确定一下两个元素的范围
    10. //因为选择了分组2,则范围是0~3,且该工程只有这一个中断,所以随意设置为1
    11. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    12. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    13. NVIC_Init(&NVIC_InitStructure);
    14. //EXTI1
    15. NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
    16. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    17. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    18. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    19. NVIC_Init(&NVIC_InitStructure);

    这里需要稍稍把EXTI1的中断优先级调后

    再分别写出两个EXTI的中断函数

     

    1. void EXTI0_IRQHandler(void)
    2. {
    3. if (EXTI_GetITStatus(EXTI_Line0) == SET)
    4. {
    5. if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
    6. {
    7. Counts++;
    8. }
    9. EXTI_ClearITPendingBit(EXTI_Line0);
    10. }
    11. }
    12. void EXTI1_IRQHandler(void)
    13. {
    14. if (EXTI_GetITStatus(EXTI_Line1) == SET)
    15. {
    16. if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
    17. {
    18. Counts--;
    19. }
    20. EXTI_ClearITPendingBit(EXTI_Line1);
    21. }
    22. }

     这样再把原先显示数字的Counts改为有符号的就行了

    主体

    CountSensor.c

    1. #include "stm32f10x.h" // Device header
    2. int16_t Counts;
    3. void CountSensor_Init(void)
    4. {
    5. //配置时钟
    6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    8. //EXTI和NVIC的时钟是一直开着的,不用使用函数配置
    9. //配置旋转编码器的GPIO口
    10. GPIO_InitTypeDef GPIO_InitStructure;
    11. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    12. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_0;
    13. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    14. GPIO_Init(GPIOB, &GPIO_InitStructure);
    15. //配置AFIO
    16. GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
    17. GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
    18. //配置EXTI
    19. EXTI_InitTypeDef EXTI_InitStructure;
    20. //指定要配置的中断线
    21. EXTI_InitStructure.EXTI_Line = EXTI_Line1 | EXTI_Line0;
    22. //指定外部中断线的模式(枚举类型的中断模式和事件模式)
    23. EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    24. //指定触发信号的有效边缘(上升沿,下降沿,双向沿)
    25. EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    26. //指定选择中断线的新状态(ENABLE/DISABLE)
    27. EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    28. EXTI_Init(&EXTI_InitStructure);
    29. //配置NVIC
    30. //NVIC的响应优先级和抢断优先级分组
    31. //分组方式整个芯片只能用一种,则这个分组的代码只要执行一次即可
    32. //如果是放在模块内使用该分组函数,则一定要保证每个模块的分组方式相同
    33. //或者可以直接放在main函数中
    34. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    35. //EXTI0
    36. NVIC_InitTypeDef NVIC_InitStructure;
    37. //中断通道
    38. NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    39. //指定中断通道是使能还是失能
    40. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    41. //抢断优先级和响应优先级
    42. //根据之前NVIC的分组函数,可以确定一下两个元素的范围
    43. //因为选择了分组2,则范围是0~3,且该工程只有这一个中断,所以随意设置为1
    44. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    45. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    46. NVIC_Init(&NVIC_InitStructure);
    47. //EXTI1
    48. NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
    49. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    50. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    51. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    52. NVIC_Init(&NVIC_InitStructure);
    53. }
    54. //旋转编码器旋转时会产生两道波形,设为A和B,
    55. //正转时,B相比A,慢90°,反转则快90°
    56. //以此特性,只有A处于低电平,B处于下降沿时,判断为正转
    57. //A为下降沿,B为低电平时判断为反转
    58. 所以以判断旋转编码器的两个输出端的值,来设置两个旋转方向的中断函数
    59. void EXTI0_IRQHandler(void)
    60. {
    61. if (EXTI_GetITStatus(EXTI_Line0) == SET)
    62. {
    63. if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
    64. {
    65. Counts++;
    66. }
    67. EXTI_ClearITPendingBit(EXTI_Line0);
    68. }
    69. }
    70. void EXTI1_IRQHandler(void)
    71. {
    72. if (EXTI_GetITStatus(EXTI_Line1) == SET)
    73. {
    74. if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
    75. {
    76. Counts--;
    77. }
    78. EXTI_ClearITPendingBit(EXTI_Line1);
    79. }
    80. }
    81. int16_t GetCounts(void)
    82. {
    83. return Counts;
    84. }

    main.c

    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "CountSenor.h"
    5. int16_t Sensor_Counts;
    6. int main(void)
    7. {
    8. OLED_Init();
    9. CountSensor_Init();
    10. while(1)
    11. {
    12. Sensor_Counts = GetCounts();
    13. OLED_ShowString(1,1,"Counts:");
    14. OLED_ShowSignedNum(1,8,Sensor_Counts,2);
    15. }
    16. }

  • 相关阅读:
    网络安全笔记2——单钥密码体制
    [论文精读]U-Net: Convolutional Networks for BiomedicalImage Segmentation
    交叉验证太重要了!
    MySQL常见的性能优化方法技巧以及示例
    Spring Boot如何配置CORS支持
    不用Swagger,那我用啥?
    SpringBoot事件发布监听
    图算融合使能不同优化等级尝试网络性能调优
    【完全攻略】畅游NLP海洋:HuggingFace的快速入门
    C语言习题练习4--函数递归
  • 原文地址:https://blog.csdn.net/m0_74460550/article/details/132918179