当前位置: 首页 > news >正文

STM32入门教程,第12课(上),对射式红外传感器计次

目录

1 接线图

2 工程文件

3 代码

3.1 封装对射式红外传感器

3.2 配置外部中断

3.2.1 配置RCC

3.2.2 配置GPIO

3.2.3 配置AFIO

3.2.4 配置EXTI

3.2.5 配置NVIC

3.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_APB2PeriphClockCmd(RCC_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。这个AFIO,ST公司并没有给它分配专门的库函数文件。它的库函数是和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_Lines,CTRL+F 搜索一下。Find Next。

这里可以看到,下面这些就是EXTI_Lines这个参数的取值。

我们需要用PB14所在的第14个线路,所以选择EXTI_Line14,复制。

然后放到EXTI_Line这里,第一个参数就ok了。

接着第二个EXTI_LineCmd,跳到定义,看一下注释。

这里写的是:指定选择的中断线的新状态。这个参数可以是ENABLE或DISABLE。我们肯定要开启中断,所以复制ENABLE。

放到EXTI_LineCmd这里,第二个参数就ok了。

第三个EXTI_Mode,右键跳到定义。

看一下注释,这里写的是:指定外部中断线的模式。这个参数可以是EXTIMode_TypeDef里面的一个值。

还是一样,选中EXTIMode_TypeDef,CTRL+F搜索一下。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_Type,CTRL+F搜索一下,如果直接在这里搜索,是搜索不到的。

那我们把下面的搜索范围,由当前文件换成当前工程,然后再搜索。

可以看到,现在是跳到了stm32f10x.h文件里,这个定义就在这里,我们可以在这上面的这个列表选择,可以看到这里有非常多的中断通道。

因为这个库函数可以兼容所有F1系列芯片,但是不同的芯片中断通道列表是不一样的,所以这里有很多条件编译,用来选择你使用芯片的中断通道列表。

我们可以点一下这左边的减号,把一部分条件编译都折叠起来。

我们的芯片用的是MD中等密度的,所以只需要展开这个MD的条件编译即可,其他的就不用看了,那在这个表里,找到EXTI15_10_IRQn,STM32的EXTI10到EXTI15 都是合并到了这个通道里。所以我们复制EXTI15_10_IRQn。

然后放到NVIC_IRQChannel这里,通道就指定好了。

NVIC_IRQChannelCmd,右键,跳转到定义。

右边的解释是:指定中断通道是使能还是失能,参数可以是ENABLE或者DISABLE。

选择ENABLE,放在NVIC_IRQChannelCmd这里。

接着下面两个参数,就是指定所选通道的抢占优先级和响应优先级了。

跳到NVIC_IRQChannelPreemptionPriority定义,看一下这两个参数的解释。

上面的是指定抢占优先级,下面的是指定响应优先级。这个值可以是0到15,具体的值可以参考这个NVIC_Priority_Table表里的描述。

那我们选中这个NVIC_Priority_Table表,CTRL+F搜索一下。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); #endif

main.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); } }
http://www.cnnetsun.cn/news/4290256.html

相关文章:

  • 数据挖掘笔试核心考点复盘:逻辑回归、贝叶斯与业务实战
  • 基于SpringBoot的码头船只货柜管理系统(源码+文档+部署讲解等)
  • 怎么压缩音频不超过3M?文件过大无法上传的本地压缩方案汇总
  • Delphi 13.1下KonopkaControls控件安装配置与排坑指南
  • MDmesh DM9超结MOSFET:快速恢复体二极管如何提升电源效率
  • 无代码内容工厂:不写一行代码的AI自动化生产实践
  • /codex:status与/codex:result:codex-plugin-cc后台任务追踪双雄
  • Edgi:开源AI阅读追踪工具,将PDF阅读转化为可视化学习仪表盘
  • AI 编码代理的 20 个生产级工程技能:agent-skills 从需求到上线的完整实战指南
  • 工业机器人产业链:核心零部件国产化趋势
  • LPS27HHW防水MEMS气压传感器:从硬件设计到产线避坑全解析
  • Mermaid 实践手册:五分钟上手文本驱动图表渲染
  • Home Assistant Home Connect 设备离线、状态不更新:5类典型故障一次修好
  • Godot 引擎 4 步上手:从零到发布你的第一个 2D/3D 游戏
  • DeepSeek涨价后:缓存命中率与模型路由驱动的API成本控制指南
  • 8款高效AI论文平台横向实测,本硕博避坑必备指南
  • OpenSEO新手教程:从创建项目到查看关键词数据的完整指南
  • C++ vector动态数组:从核心原理到高效使用指南
  • SSL 证书链不完整怎么修?cert-chain-resolver 一条命令补齐中间证书
  • Hermes Agent 金融分析实战:3 个场景跑通你的 AI 投资助手
  • Zvec vs Milvus vs Qdrant:3款向量数据库选型指南,谁是你的最优解
  • Bun 安装指南:三步玩转这款四合一 JavaScript 运行时
  • oMLX 快速上手教程:5 分钟在 Mac 上跑起第一个本地大模型
  • 【c语言】1.3 嵌入式中的位运算
  • 零基础写论文✅靠这一个工具,我全程零求助通关!
  • 如何在电脑上投屏并控制安卓手机:scrcpy 投屏工具完整教程
  • 2023工业峰会电机控制讲座:从PWM到FOC的实战干货笔记
  • LocalSend局域网传输实测:2.5GB压缩包40秒传完,新手三步就能上手
  • Deep-Live-Cam 实时人脸换脸上手指南:3 步出第一个结果
  • 数据管线的运行防线