STM32入门教程,第12课(上),对射式红外传感器计次
目录1 接线图2 工程文件3 代码3.1 封装对射式红外传感器3.2 配置外部中断3.2.1 配置RCC3.2.2 配置GPIO3.2.3 配置AFIO3.2.4 配置EXTI3.2.5 配置NVIC3.3 测试中断函数3.4 调试3.5 计次代码3.6 本节课代码1 接线图本小节我们来写一下外部中断的代码。先看一下接线图我们打开接线图的文件夹。\资料\STM32入门\程序源码\程序源码\STM32Project-有注释版\1-1 接线图\5-1 对射式红外传感器计次右上角这一部分和上一节oled显示屏的电路是一样把oled插好用于显示程序的现象。左下角这里接了一个对射式红外传感器的模块vcc、GND分别接电源的正负极DO数字输出端随便选一个GPIO口接上就行在这里接到了B14的端口。当我们的挡光片或者编码盘在对射式红外传感器中间经过时这个DO就会输出电平跳变的信号然后这个电平跳变的信号触发STM32 PB14号口的中断我们在中断函数里执行变量的程序然后主循环里调用OLED显示这个变量这样第一个程序就完成了。那我们看一下实际电路目前这个面包板已经插好了OLED部分的电路接线图和OLED那节是一样的。然后我们拿出这个对射式红外传感器模块。用三根公对母的杜邦线把引脚引出来然后VCC接电源正GND接电源负DO接STM32的PB14。这样就完成了。我们可以随便找一个挡光片挡一下传感器中间这个槽可以看到这个输出指示灯也跟随亮灭的这就说明高低电平输出是没问题的。对射式红外传感器正常2 工程文件复制工程文件——【4-1 OLED显示屏】粘贴重命名为【5-1 对射式红外传感器计次】。打开【5-1 对射式红外传感器计次】中的【Project.uvprojx】。删掉【main.c】中多余代码只留下显示【HelloWrold!】的代码。上面其他文件也都关掉。编译没有问题。下载OLED显示【HelloWrold!】。3 代码3.1 封装对射式红外传感器开始写新的代码先封装对射式红外传感器。右键Hardware添加新的文件。选择【C File】,Name为【CountSensor】Location加上【Hardware】Add。再次右键Hardware添加新的文件。选择【Header File】,Name为【CountSensor】Location加上【Hardware】Add。【CountSensor.c】文件中空白处右键选择【#include stm32f10x.h 】。添加头文件。【CountSensor.h】中写下固定部分。这样模块的基本结构就写好了。3.2 配置外部中断先在【CountSensor.c】中写初始化函数。在这里面我们就要进行外部中断的配置了。那如何配置外部中断呢我们可以看一下ppt的这个外部中断整体结构图看一下这个图使用外部中断要配置哪些东西就很清晰了。简单来说我们只需要把这个外部中断从GPIO到NVIC这一路中出现的外设模块都配置好把这条信号电路给打通就行了。具体步骤就是第一步配置RCC把我们这里涉及的外设的时钟都打开。第二步配置GPIO选择我们的端口为输入模式。第三步配置AFIO选择我们用的这一路GPIO连接到后面的EXTI。第四步配置EXTI选择边沿触发方式比如上升沿、下降沿或者双边沿。还有选择触发响应方式可以选择中断响应和事件响应。当然我们一般都是中断响应。第五步配置NVIC给我们这个中断选择一个合适的优先级最后通过NVIC外部中断信号就能进入CPU了这样CPU才能收到中断信号才能跳转到中断函数里执行中断程序。这五步就是外部中断的配置流程这里涉及的外设比较多有RCC、GPIO、AFIO、EXTI、NVIC。大家不要慌我们一个个来看我们回到Keil软件。3.2.1 配置RCC首先第一步是RCC开启时钟这个我们之前讲过主要就是三个开启时钟的函数。那我们在这里直接写RCC_APB2PeriphClockCmdRCC_APB2外设时钟控制。首先是RCC_APB2外设开启GPIOB的时钟。这个注意一下GPIOB是APB2的外设。这个参数是RCC_APB2Periph_GPIOB的这个函数也要用APB2的这个开启时钟函数。如果这里用APB1或者AHB的函数然后填上RCC_APB2Periph_GPIOB的参数。这样程序也是不会报错的。因为语法上没有错误后面这个参数只是一个宏定义。右键【RCC_APB2Periph_GPIOB】转到定义。确定。只是一个数的替换而已所以编译器也不知道你写错了。但实际运行的话这个程序肯定是有问题的所以这里要细心一点。注意函数和参数的这个APB2、APB1和AHB要对应起来。接下来开AFIO的时钟。AFIO也是APB2的外设。复制一下RCC_APB2PeriphClockCmd这个函数然后这里把GPIOB改成AFIO。你如果不确定哪个外设是接在哪个总线上的可以转到RCC_APB2PeriphClockCmd这个函数的定义看一下上面这个参数列表有的话就是这个总线上的外设。确定。有这个RCC_APB2Periph_AFIO所以这里就可以调用APB2开启时钟的函数然后填AFIO的这个参数开启时钟了。接着还有EXTI和NVIC两个外设这两个外设的时钟是一直都打开着的不需要我们再开启时钟。EXTI作为一个独立外设按理说应该是需要开启时钟但是寄存器里面却没有EXTI的控制位。这个原因手册里没找到网上也没得到确切的答案。个人推测可能是和EXTI唤醒有关或者是其他的一些电路设计上的考虑。那我们暂时就不用管了知道它不需要开启时钟就行了。另外NVIC也不需要开启时钟因为NVIC是内核的外设内核的外设都是不需要开启时钟的。人家跟CPU一起都是住在“皇宫”里的。而RCC管的都是内核外的外设所以RCC管不着NVIC。到这里时钟就配置完了。3.2.2 配置GPIO第二步配置GPIO。这一部分我们之前也学过还是一样的流程。首先定义结构体然后复制一下结构体名字把结构体成员都引出来接着填一下参数第一个GPIO_Mode对于外部中断来说要选择浮空输入、上拉输入或者下拉输入这其中的一个模式。当然像这种其他外设使用GPIO的情况如果你不清楚该配置为什么模式可以看一下这个参考手册。STM32入门\参考文档\STM32F10xxx参考手册中文.pdf在GPIO这一章找一下有一个外设的GPIO配置表里面有写每个外设的各个零件都需要配置为什么模式。这个我们之前也看过。在最后就有EXTI输入线它给的推荐配置就是浮空、上拉或者下拉。所以在这里我就给一个GPIO_Mode_IPU上拉输入默认为高电平的输入方式。然后GPIO_Pin我们用的P14号口所以就GPIO_Pin_14。GPIO_Speed这个不是很重要我们就还是选择GPIO_Speed_50MHz。最后调用GPIO_Init函数初始化一下GPIOB外设这样GPIO部分我们就配置好了。3.2.3 配置AFIO第三步配置AFIO。这个AFIOST公司并没有给它分配专门的库函数文件。它的库函数是和GPIO在一个文件里的那我们找一下Library——stm32f10x_gpio.h文件看一下最后的库函数。我们之前学习GPIO的时候讲了这上面的一些函数那还剩下一些函数没讲这些就是和AFIO有关的库函数。上面这里有一个GPIO_AFIODeInit这个函数是用来复位AFIO外设的调用一下这个函数AFIO外设的配置就会全部清除。下面这个GPIO_PinLockConfig这个函数是用来锁定GPIO配置的调用这个函数参数指定某个引脚这个引脚的配置就会被锁定防止意外更改。这个也GPIO外设的函数之前没有讲这里提一下用的不多大家了解一下即可。然后是GPIO_EventOutputConfig和GPIO_EventOutputCmd这两函数是用来配AFIO的事件输出功能的。用的也不多大家了解一下。GPIO_PinRemapConfig和GPIO_EXTILineConfig这两个函数就比较重要了。GPIO_PinRemapConfig可以用来进行引脚重映射第一个参数可以选择你要重映射的方式第二个参数是新的状态。使用还是非常简单的但是由于目前我们还没有学习到需要映射引脚的外设所以实际调用的话我们之后再演示。GPIO_EXTILineConfig就是我们本节外部中断需要用的函数调用这个函数就可以配置AFIO的数据选择器来选择我们想要的中断引脚。最后一个GPIO_ETH_MediaInterfaceConfig这个是和以太网有关的。我们这个芯片没有以太网外设所以也用不到。现在想要配置AFIO外部中断引脚选择那就直接复制这个GPIO_EXTILineConfig函数。放到CountSensor.c这里来然后右键跳转到定义看一下。这个函数虽然GPIO开头但实际上里面操作的是AFIO的寄存器。所以这个函数实际上是AFIO的函数。这个知道一下。上面简介写的是选择GPIO pin作为外部中断线。第一个参数是GPIO_PortSource选择某个gpu外设作为外部中断源。这个参数可以是GPIO_PortSourceGPIOx其中x可以是A到G。那我们就复制一下GPIO_PortSourceGPIOx这个参数放到这里。我们用的是PB14号引脚所以把x改成B就行了。第二个参数是GPIO_PinSource指定要配置的外设中断线。这个参数可以是GPIO_PinSourcex其中x可以是0到15。那我们再复制一下GPIO_PinSourcex这个参数放到第二个参数这里然后x改成14代表连接PB14号口的第14个中断线路。到这里AFIO外设中断引脚选择配置就完成了。当执行完这个函数后AFIO的第14个数据选择器就拨好了,其中输入端被拨到了GPIOB外设上对应的就是PB14号引脚输出端固定连接的是EXTI的第14个中断线路。这样PB14号引脚的电平信号就可以顺利通过AFIO进入后级EXTI电路了。3.2.4 配置EXTI第四步配置EXTI。我们先看一下EXTI的库函数文件看一下EXTI都有哪些库函数可以用。我们在Library里找到stm32f10x_exti.h文件打开拖到最后。这些就是EXTI的所有库函数了库函数总共就只有这些。第一个EXTI_DeInit调用它就可以把EXTI的配置都清除恢复成上电默认的状态。第二个EXTI_Init调用这个函数就可以根据这个结构体里的参数配置EXTI外设。初始化EXTI主要用的就是这个函数使用方法和GPIO_Init是一样的。第三个EXTI_StructInit调用这个函数可以把参数传递的结构体变量赋一个默认值。像这前面的三个函数基本所有的外设都有。就像是库函数的模板函数一样基本每个外设都需要这些类型的函数这些模板函数使用方法和意思也都是一样的。会使用一个之后再见到这种函数就能很容易的上手。所以当你学GPIO的时候你会觉得为啥要用结构体来初始化模块呢还得定义结构体结构体赋值然后再传递结构体的地址。简直太麻烦了是吧但当你继续学习其他外设之后你就会发现外部中断也是使用结构体初始化的方式、定时器也是、ADC也是、串口也是都是一个套路。而且结构体可以看到参数的名字参数也都是复制粘贴来的。根本不用查看寄存器随便选选参数就配置好了。从这个角度看STM32的库函数是不是比寄存器方便多了这就是库函数的好处。EXTI_GenerateSWInterrupt这个函数是用来软件触发外部中断的。调用这个函数参数给一个指定的中断线就能软件触发一次这个外部中断。这个如果你程序中需要用到这个功能的话可以使用这个函数。如果你只需要外部引脚触发中断那就不需要用这个函数了。剩下的这四个函数也是库函数的模板函数。很多模块都有这四个函数。因为在外设运行的过程中会产生一些状态标志位。比如外部中断来了会有一个挂起寄存器置了一个标志位。对于其他外设比如串口收到数据会置标志位。定时器时间到也会置标志位。这些标志位都是放在状态寄存器的当成越想要看这些标志位时就可以用到这四个函数。其中这前两个函数EXTI_GetFlagStatus可以获取指定的标志位是否被置1了。EXTI_ClearFlag可以对置1的标志位进行清除。那对于这些标志位有的比较紧急在置标志位后会触发中断。在中断函数里如果你想查看标志位和清除标志位。那就用下面两个函数EXTI_GetITStatus获取中断标志位是否被置1了。EXTI_ClearITPendingBit清除中断挂起标志位。所以总结一下如果你想在主程序里查看和清除标志位就用上面这两个函数。如果你想在中断函数里查看和清除标志位就用下面这两个函数。其实本质上这四个函数都是对状态寄存器的读写。上面两个和下面两个都是类似的功能都是读写状态寄存器。只不过下面这两个函数只能读写与中断有关的标志位并且对中断是否允许做出了判断。而上面的这两个函数只是一般的读写标志位没有额外的处理能不能触发中断的标志位都能读取。所以建议在主程序里用上面两个中断程序里用下面两个。当然你如果非要在中断里用上面两个那其实也是没问题的只不过是库函数针对这两种场景区分了这两类读取函数。对于EXTI的初始化配置很明显用这个EXTI_Init函数就行了。回到CountSensor.c程序里来在这里直接先调一个EXTI_Init函数里面只有一个参数就是EXTI初始化的结构体。因为EXTI只有一个所以不需要像GPIO那样先指定要配置哪个EXTI。那我们右键跳转定义看一下说明。函数简介写的是初始化EXTI外设根据结构体里面指定的参数第一个参数是EXTI_InitStruct它是一个EXTI_InitTypeDef类型的结构体指针包含了对于EXTI外设的配置信息。我们复制一下结构体类型的名字EXTI_InitTypeDef。回到CountSensor.c粘贴类型名然后起个变量名叫EXTI_InitStructure。接着复制变量名在下面粘贴先把所有的结构体成员都用点儿引出来放在这里最后把结构体变量放在EXTI_Init的参数里前面加上取地址符号。接着我们来依次看一下参数第一个参数EXTI_Line这个是指定我们要配置的中断线。右键EXTI_Line跳转到定义。下面的这个选member双击。然后看一下右边的注释。这个参数可以是EXTI_Lines的任意组合。我们选中EXTI_LinesCTRLF 搜索一下。Find Next。这里可以看到下面这些就是EXTI_Lines这个参数的取值。我们需要用PB14所在的第14个线路所以选择EXTI_Line14复制。然后放到EXTI_Line这里第一个参数就ok了。接着第二个EXTI_LineCmd跳到定义看一下注释。这里写的是指定选择的中断线的新状态。这个参数可以是ENABLE或DISABLE。我们肯定要开启中断所以复制ENABLE。放到EXTI_LineCmd这里第二个参数就ok了。第三个EXTI_Mode右键跳到定义。看一下注释这里写的是指定外部中断线的模式。这个参数可以是EXTIMode_TypeDef里面的一个值。还是一样选中EXTIMode_TypeDefCTRLF搜索一下。Find Next。可以看到这是一个枚举。第一个是中断模式第二个是事件模式。我们要用中断模式所以复制上面这个参数EXTI_Mode_Interrupt。放到EXTI_Mode这里这样第三个参数也ok了。接着看一下第四个参数EXTI_Trigger跳到定义。看一下注释。这里写的是指定触发信号的有效边缘。这个参数可以是EXTIMode_TypeDef定义里的一个值。这个定义它这里写错了这里应该是EXTITrigger_TypeDef而不是EXTIMode_TypeDef。它现在搞得和上面这个一样了这应该是个小疏忽。往上稍微翻找一下第67行可以找到这个EXTITrigger_TypeDef的定义。里面可以选择EXTI_Trigger_Rising上升沿触发、EXTI_Trigger_Falling下降沿触发、EXTI_Trigger_Rising_Falling上升沿和下降沿都触发。这个选择根据你的实际需求来我选EXTI_Trigger_Falling下降沿触发。复制EXTI_Trigger_Falling放到EXTI_Trigger这里这样我们的外部中断就配置完成了。我们当前的配置是将EXTI的第14个线路配置为中断模式下降沿触发然后开启中断。这样PB14的电平信号就能通过EXTI通向下一级NVIC了。3.2.5 配置NVIC第五步配置NVIC先看一下库函数文件里的函数。因为NVIC是内核外设所以它的库函数是被ST发配到杂项这里来了。我们打开Library的misc.h。翻到最后这里NVIC的四个函数和SysTick的一个函数。第一个NVIC_PriorityGroupConfig这个函数是用来中断分组的参数是中断分组的方式。第二个NVIC_Init根据结构体里面指定的参数初始化NVIC。第三个NVIC_SetVectorTable设置中断向量表。第四个NVIC_SystemLPConfig系统低功耗配置。第三、四个这两个函数用的不多大家可以先不看。只需要用上面的两个函数就行了。在配置中断之前先指定一下中断的分组然后使用NVIC_Init初始化一下NVIC就行了。我们复制一下NVIC_PriorityGroupConfig这个函数回到配置这里来放在这里。然后右键跳转到定义。上面这个简介是配置优先级分组先占优先级和从占优先级。这里先占优先级就是抢占优先级从占优先级就是响应优先级。这个先占优先级pre-emption priority和从占优先级subpriority的英文熟悉一下。这个参数可以取这个列表里的一个值。每个参数的解释这里可以看一下几位抢占几位响应。和上节讲的都是一样的。这个具体要选哪个其实是根据我们的实际需求来的一般的话中断不多很难导致中断冲突对优先级分组来说就比较随意了哪个都行。我选择第二个分组2位抢占2位响应。这个比较平均一些。复制NVIC_PriorityGroup_2参数。放到NVIC_PriorityGroupConfig这里来这样分组就完成了。另外注意一下这个分组方式整个芯片只能用一种所以按理说这个分组的代码整个工程只需要执行一次就行了。如果你把它放在模块里面进行分组那你要确保每个模块分组都选的是同一个要不你也可以把这个代码放在主函数的最开始这样模块你就不用再进行分组了。我这里就直接放在模块里进行分组了。这样分组就完成了。接下来直接调用NVIC_Init函数右键跳转到定义。简介就不用看了直接复制NVIC_InitTypeDef结构体。放到这里然后结构体变量名叫NVIC_InitStructure。接着复制变量名NVIC_InitStructure把成员都引出来最后把结构体变量的地址放在NVIC_Init函数里。然后依次看一下参数第一个NVIC_IRQChannel右键跳转定义。看一下解释指定中断通道来开启或关闭。这个参数可以是IRQn_Type里面的一个值。下面还有一个括号写的是对于完整的STM32中断通道列表请参考stm32f10x.h文件。这个意思是这个IRQn_Type的定义不在这个文件你要到stm32f10x.h里面去找。那我们还是选择IRQn_TypeCTRLF搜索一下如果直接在这里搜索是搜索不到的。那我们把下面的搜索范围由当前文件换成当前工程然后再搜索。可以看到现在是跳到了stm32f10x.h文件里这个定义就在这里我们可以在这上面的这个列表选择可以看到这里有非常多的中断通道。因为这个库函数可以兼容所有F1系列芯片但是不同的芯片中断通道列表是不一样的所以这里有很多条件编译用来选择你使用芯片的中断通道列表。我们可以点一下这左边的减号把一部分条件编译都折叠起来。我们的芯片用的是MD中等密度的所以只需要展开这个MD的条件编译即可其他的就不用看了那在这个表里找到EXTI15_10_IRQnSTM32的EXTI10到EXTI15 都是合并到了这个通道里。所以我们复制EXTI15_10_IRQn。然后放到NVIC_IRQChannel这里通道就指定好了。NVIC_IRQChannelCmd右键跳转到定义。右边的解释是指定中断通道是使能还是失能参数可以是ENABLE或者DISABLE。选择ENABLE放在NVIC_IRQChannelCmd这里。接着下面两个参数就是指定所选通道的抢占优先级和响应优先级了。跳到NVIC_IRQChannelPreemptionPriority定义看一下这两个参数的解释。上面的是指定抢占优先级下面的是指定响应优先级。这个值可以是0到15具体的值可以参考这个NVIC_Priority_Table表里的描述。那我们选中这个NVIC_Priority_Table表CTRLF搜索一下。Find Next。可以看到这里给出了每个分组对应抢占优先级和响应优先级的取值范围这个表和我们ppt里的那个表是一样的。我们选择了分组2那抢占优先级和响应优先级的取值范围就都是0~3。那我们回到这里因为我们这个程序的中断只有一个所以中断优先级的配置也是非常随意的。那我这里就给抢占优先级和响应优先级都设置为1优先级是在多个中断源同时申请产生拥挤时才有作用这只有一个中断优先级就随便了。那到这里NVIC就配置好了整个外部中断的配置也就结束了。外部中断的信号从GPIO到AFIO再到EXTI再到NVIC最终通向CPU这样才能让CPU由主程序跳到中断程序执行。那中断程序应该放在哪里呢这就需要我们写一个中断函数了。在STM32中中断函数的名字都是固定的每个中断通道都对应一个中断函数。中断函数的名字我们可以参考一下启动文件。我们找到Start——startup_stm32f10x_md.s启动文件打开看一下在这里找一下可以看到这里定义的中断向量表。这里面以IRQHandler结尾的字符串就是中断函数的名字。我们可以找到这个EXTI15_10_IRQHandler这一项这就是EXTI15_10的中断函数复制一下。回到这里在下面写void粘贴一下中断函数的名字然后参数也是void这就是中断函数的格式。中断函数都是无参无返回值的。中断函数的名字不要写错了写错了就进不了中断了最好是直接从启动文件复制过来这样就不会有问题了。然后在中断函数里一般都是先进行一个中断标志位的判断确保是我们想要的中断源触发的这个函数。因为这个函数EXTI10到EXTI15 都能进来所以要先判断一下是不是我们想要的EXTI14进来的。这时候我们就需要到stm32f10x_exti.h里看一下复制一下这个EXTI_GetITStatus。放到这里来右键EXTI_GetITStatus跳到定义看一下参数。第一个参数复制一下这个EXTI_Linex。放到EXTI_GetITStatus这里然后x改成14。看一下EXTI14的中断标志位是不是为1。然后右键EXTI_GetITStatus跳到定义再看一下。返回值是SET或RESET。回到程序我们就用if把这函数括起来判断一下返回值是不是等于等于SET。如果是的话我们就可以执行中断程序了。最后中断程序结束后一定要再调用一下清除中断标志位的函数。因为只要中断标志位置1了程序就会跳到中断函数。如果你不清除中断标志位那它就会一直申请中断这样程序就会不断响应中断执行中断函数那程序就卡死在中断函数里了。所以我们每次中断程序结束后都应该清除一下中断标志位。我们打开stm32f10x_exti.h复制一下EXTI_ClearITPendingBit放到if这里参数也是EXTI_Line14将中断标志位清除。这样中断的全部逻辑我们就写好了。3.3 测试中断函数接下来我们就来测试一下能不能进入中断函数我们复制一下这个初始化函数的第一行。放在头文件里声明一下。这个中断函数就不用声明了因为中断函数不需要调用它是自动执行的。那我们编译一下看一下没有问题我们写好模块之后最好编译一下要不然一会儿代码提示可能显示不出来我们新写的函数。然后回到main.c里面来,在最上面#include CountSensor.h然后在主循环前调用一下CountSensor_Init这样就把外部中断配置好了。然后就可以编译了没有问题。3.4 调试我们使用一下这个调试模式看一下程序能不能跳转到中断函数那我们点击这个调试模式的按钮进入调试模式。然后找一下中断函数在if这里打一个红色断点。然后全速运行看一下硬件我们用挡光片遮挡一下这个传感器这时我们看到程序成功跳转到中断函数里来了停在了这个断点处。那我们继续全速运行再挡一下传感器可以看到程序又停到中断里来了。这说明我们中断函数的配置和触发都是没问题的。那接下来的事情就简单了我们退出调试模式。3.5 计次代码我们想要一个数字来统计中断触发的次数。那么就在这个模块上定义一个变量类型可以是uint16_t名字可以是CountSensor_Count然后在中断函数里写CountSensor_Count 这样就行了。最后再在这个模块写一个get函数返回一下这个变量。然后我们把这个函数也放在头文件声明一下。编译看一下没有问题。然后在main函数里先改一下这个OLED_ShowString让它在一行一列显示一个Count:在主循环里我们循环调用OLED_ShowNum在一行七列显示CountSensor_Get返回值长度为5这样就是显示计次的数据了。我们试一下编译下载。这是oled显示的是count 00000然后我们挡一下传感器就加1再挡一下就再加1再挡一下就再加1这就是第一个程序的现象了。对射式红外传感器计次【注意】如果一次加2或者更多在CountSensor_Count ;前面加400毫秒的延时即可。记得先引用头文件。我们现在用的是下降沿触发在移开挡光片的时候触发中断。可以改一下EXTI_Trigger这里变为EXTI_Trigger_Rising上升沿触发。再试一下编译下载。现在就是遮挡的时候数字加1。对射式红外传感器_遮挡时数字1这里改成上升沿、下降沿都触发。编译下载。遮挡和移开的时候都加1。对射式红外传感器_遮挡和离开都1这就是各种触发方式的现象至此第一个程序就写完了。3.6 本节课代码CountSensor.c#include stm32f10x.h // Device header #include Delay.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);//AFIO外设中断引脚选择配置 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_Rising_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) { Delay_ms(400); 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); #endifmain.c#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); } }