32单片机基础:对射式红外传感器计次

news2024/10/4 14:20:42

接线如下图: 

在HardWare建立两个文件:如图

COuntSensor.c

如何配置外部中断,根据下面图,我们需要把外部中断从GPIO到NVIC这一路出现的外设模块都配置好。把这条信号打通就OK了。

1.配置RCC:把我们这里涉及的外设时钟都打开,不打开时钟,外设是没法工作的

2.配置GPIO,选择我们的端口为输入模式

3.配置AFIO,选择我们用的这一路的GPIO,连接到后面的EXTI

4.配置EXTI,选择边沿触发方式,比如上升沿,下降沿,或者双边沿,选择触发响应方式,可以选择中断响应和事件响应,

5.配置NVIC,给我们的中断设置一个合适的优先级,

最后,通过NVIC,外部中断信号就能进入CPU了,这样CPU才能收到中断信号,才能跳转到中断函数里执行中断程序。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启GPIOB的时钟

 这里注意一点,GPIOB是APB2的外设,这里的参数是APB2Periph_GPIOB,函数也要用APB2的这个开启时钟函数,如果用了APB1或者AHB的函数,然后填上APB2Periph_GPIOB的参数,程序不会报错,所以这里细心一点,注意函数和参数的这个APB2,APB1和AHB要对应起来

AFIO也是APB2的外设,也是相同的配置。你如果不确定哪一个外设是接在哪个总线上的,可以转到这个函数的定义,看一下参数列表

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//配置AFIO

然后是EXTI和NVIC两个外设,这两个外设的时钟一直都是打开的,不需要我们再开启时钟了。

NVIC是内核的外设,内核的外设都是不需要开启时钟的,人家跟CPU住在一起,都是住在皇宫里的,而RCC管的是内核外的外设,所以RCC管不着NVIC,

ok,第一步配置时钟完成,下一步配置GPIO

    GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

第三步:配置AFIO外设,

这个AFIO外设,ST公司并没有给它分配专门的库函数文件,他的库函数是与GPIO在一个文件里的

这些就是与AFIO有关的库函数

void GPIO_AFIODeInit(void);图片没给出,是在上面几行,

这个函数是 用来复位AFIO外设的,调用一下这个函数,AFIO外设的配置就会全部清除,

void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

这个函数是用来锁定GPIO配置的,调用这个函数,参数指定某个引脚,那这个引脚的配置就会被锁定,防止意外更改,这个也是GPIO的函数,用的不多,了解即可

void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);

这两个函数是用来配置AFIO的事件输出功能的,用的不多,了解即可

void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);(重要)

这个函数可以用来进行引脚重映射,第一个参数选择你要重映射的方式,第二个参数是新的状态,使用还是非常简单的。但是我们目前还是没有学习到需要重映射引脚的外设,所以实际调用的话,我们之后博文见。

void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);(重要)

这个是我们本节要用到的外部中断函数调用这个函数,就可以配置AFIO的数据选择器,来选择我们想要的中断引脚

void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);

这个函数是和以太网相关的,我们这个芯片没有以太网外设,所以也用不到。

好,那这个AFIO库函数,我们就了解差不多了。我们操作一下,调用函数需要的参数,

 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//配置AFIO

 上面的代码,AFIO外部中断引脚选择配置我们就完成了,就这一个函数就OK了

当执行完这个函数后,AFIO的第14个数据选择器就拔好了,跟下面这个图对应起来。

第四步:配置EXTI

我们先看一下EXTI的库函数文件,看一下EXTI都有哪一些库函数可以用。 

void EXTI_DeInit(void);

调用它,就可以把EXTI的配置都清楚,恢复成上电默认的状态

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

调用这个函数,就可以根据这个结构体里的参数配置EXTI外设。我们初始EXTI主要用的就是这个函数,使用方法与GPIO_Init也是一样的,这个应该好理解

void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);

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

像前面三个函数,基本所以外设都有,就像是库函数的模版函数一样,基本每一个外设都需要这些类型的函数,这些模版函数使用的方法和意思也都是一样的,会使用一个之后,再见到这种函数,就会很容易上手,所以,当你学GPIO的时候,你会觉得为啥要用结构体来初始化模块呢。还得定义结构体,结构体赋值,然后再传递结构体的地址。简直太麻烦了,当你继续学习其他外设之后,你会发现,外部中断也是使用结构体初始化的方式,定时器也是,ADC也是,串口也是。

都是一个套路,而且结构体可以看到参数的名字,参数也是可以复制粘贴来的,根本不用查看寄存器,随便选选参数就配置好了,从这个角度看,STM32的库函数是不是比寄存器方便多了。这就是库函数的好处

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);
剩下这四个函数,也是库函数的模版函数,很多模块都有这四个函数因为在外设运行的过程中,会产生一些状态标志位,比如外部中断来了,串口收到数据定时器时间到,都会置标志位,这些标志位都是放在状态寄存器的,当程序想要看这些标志位时,就可以用到这四个函数,

其中FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);就可以获取指定的标志位是否被置1了。

void EXTI_ClearFlag(uint32_t EXTI_Line);可以对置1的标志位进行清除

那对于这些标志位,有的比较紧急,在置标志位后触发中断。在中断函数里,如果你想查看标志位和清除标志位,那就用

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);//获取中断标志位是否被置1,
void EXTI_ClearITPendingBit(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;

 EXTI_Init这个函数需要什么参数呢,跳转到函数定义。

可以看出,里面只需要一个参数,就是EXTI初始化的结构体,因为EXTI只有一个,所以不需要像GPIO那样,先指定要配置的哪个EXTI了,

看上面,需要一个指针结构体,所以我们定义一个结构体,按上图所示起名称,然后把成员引入。

     EXTI_InitTypeDef  EXTI_InitStructure;//EXTI配置
	 EXTI_InitStructure.EXTI_Line= EXTI_Line14;
	 EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	 EXTI_InitStructure.EXTI_Mode= EXTI_Mode_Interrupt;
	 EXTI_InitStructure.EXTI_Trigger= EXTI_Trigger_Falling;
	 EXTI_Init( &EXTI_InitStructure);

 我们再跳转到成员变量的定义,如下图

ctrl+f搜索一下EXTI_lines,如下图,我们需要14的线路, 

 同理,找到各个参数要的。

 第五步:配置NVIC

因为NVIC是内核外设,所以它的库函数是被ST发配到杂项这里来了,我们打开misc.h文件,

我们来看一下,第一个

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

这个函数是用来中断分组的,参数是中断分组的方式,

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

根据结构体里面指定的参数初始化NVIC,

void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);

设置中断向量表(用的不多)

void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);

系统低功耗配置(用的不多)

所以只需要用上面的两个函数就OK了。在配置中断之前,先指定一下中断的分组。

然后使用NVIC_Init初始化一下NVIC就行了。

看一下void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);这个函数怎么用,

下面写到,配置优先寄存器:先占优先级和从占优先级,这里先占优先级就是抢占优先级,从占优先级就是响应优先级。这个参数可以取下面这个列表里的一个值。

这个具体要选哪个,其实看我们的实际需求来的,一般的话,中断不多,很难导致中断冲突。对优先级分钟来说,就比较随意了,哪个都行。那这里我就选择第二个分组,2位抢占,2位响应,

 注意一下,这个分组方式整个芯片只能用一种,所以按理说这个分组的代码整个工程只需要执行一次就行了。如果你把它放到模块里面进行分组,那你要确保每个模块分组都选择的是同一个。

同理,跳转每个函数了解一下

 

最上面提示我们不在我们这个文件里,我们按CTRL+F选着下图所示 

 我们芯片是MD的。直接展开MD,看一下。

 选择EXTI15_10_IRQn ,STM32的EXTI10到15都是合并到了这个通道里

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC配置
	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);

最后初始化的程序:

void CountSensor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//配置AFIO
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//配置AFIO
	
	 EXTI_InitTypeDef  EXTI_InitStructure;//EXTI配置
	 EXTI_InitStructure.EXTI_Line= EXTI_Line14;
	 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);//NVIC配置
	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_Init

OK,下面写中断的函数,在STM32中,中断函数的名字都是固定的。

每个中断通道都对应一个中函数,中断函数的名字我们可以参考一下启动文件

以IRQHandler结尾的字符串就是我们中断函数的名字

   DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10

这个就是  EXTI15_10的中断函数,我们复制一下

中断函数都是无参五返回值的,

void EXTI15_10_IRQHandler (void)
 {
	 
 }

然后在中断函数里,一般都是先进行一个中断标志位的判断,确保是我们想要的中断源触发的这个函数,因为这个函数EXTI10到15都能进来,所以要先判断一下是不是我们想要的EXTI14进来的。

 if(EXTI_GetITStatus(EXTI_Line14)==SET)//看一下EXTI14中断标志位是不是为1,返回值是SET和RESET
	 {
		  EXTI_ClearITPendingBit(EXTI_Line14);//清除标志位
	 }

判断一下,看看是不是,如果是的话,我们就可以进入执行中断程序了。

最后,中断程序结束后,一定再调用一下清除中断标志位的函数。

因为中断标志位置1了,程序就会跳转到中断函数。

如果不清除中断标志位,那它就一直申请中断。这样程序就会不断响应中断,执行中断函数,那程序就卡死在中断函数里了。

中断函数不需要声明,因为中断函数不需要调用它是自动执行的。

最后,代码如下:

CountSensor.c

#include "stm32f10x.h"                  // Device header              

uint16_t CountSensor_Count;

void CountSensor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//配置AFIO
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//配置AFIO
	
	 EXTI_InitTypeDef  EXTI_InitStructure;//EXTI配置
	 EXTI_InitStructure.EXTI_Line= EXTI_Line14;
	 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);//NVIC配置
	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)//看一下EXTI14中断标志位是不是为1,返回值是SET和RESET
	 {
		  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()
{
	
	OLED_Init();
	CountSensor_Init();
	OLED_ShowString(1,1,"Count:");
	
	while(1)
	{
      OLED_ShowNum(1,7,CountSensor_Get(),5);
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1469686.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用什么软件制作电子杂志

想要制作高大上的电子杂志?别再烦恼啦!今天给大家推荐一款超级实用的软件,让你轻松制作出专业水准的电子杂志! 这款软件功能强大,操作简单,适合所有对设计感兴趣的小伙伴们。无论是新手还是专业设计师&…

20.scala视图界定

目录 概述实践代码执行 结束 概述 scala 中的视图界定 实践 代码 /*** 视图界定*/ object Genericity03 {def main(args: Array[String]): Unit {println(new MaxInt(1,2).compare)println(new MaxLong(1L,2L).compare)// 不行 // println(new MaxValue(1,2).compare)// …

[c++] 深拷贝和浅拷贝,拷贝构造、赋值运算符

1 拷贝构造和赋值运算符 1.1 拷贝构造 拷贝构造在如下场景会被调用: (1)函数调用时,函数参数是对象的值传递 (2)声明对象同时初始化的时候(而不是声明和初始化分开,因为声明的时候就创建了对…

游戏配置内存“瘦身”策略

背景 游戏配置数据绝对是游戏服务器进程的内存大头,有些游戏服务器单纯数据配置的容量就超过一个G。因此,这部分内存优化也就放在首要位置了。 优化策略 在《服务器进程如何降低内存》一文中,我们讲述了可以通过“优化游戏配置缓存”来降低游戏服务器进程的内存使用量。本…

【电子通识】认识FMEA(失效模式和影响分析)

FMEA是Failure Mode and Effect Analysis的英文缩写,中文名称为失效模式和影响分析。主要应用于航空航天、食品、汽车和核电等行业。 FMEA讨论的是事先策划以及执行措施,预防问题的发生或控制问题的发展,降低设计和过程的风险。由于问题还没…

C语言------操作符的巧妙使用

1.计算一个数字二进制补码里面1的个数 (1)方法一 根据这个10进制的整数,对这个数进行%10,/10不断地进行下去, %10得到最后一位,/10得到舍去最后一位之后剩余的数; 同理得到:二进…

深入理解 CSS 定位与布局高级技巧

更多web开发知识欢迎访问我的专栏>>> CSS高级 目标:掌握定位的作用及特点;掌握 CSS 高级技巧 01-定位 作用:灵活的改变盒子在网页中的位置 实现: 1.定位模式:position 2.边偏移:设置盒子的位…

构造百万测试数据五大方法!

在测试的工作过程中,很多场景是需要构造一些数据在项目里的,方便测试工作的进行。比如下面的场景: 项目需要做性能测试,需要大量的数据就算是功能测试,比如测试搜索功能,需要有数据做搜索测试需要检查数据…

Windows Server 2012 IIS中发布ASP.NET CORE项目

服务器安装IIS: 微软官网下载SDK: 下载Runtime官网:https://dotnet.microsoft.com/download/dotnet-core 安装成功重启IIS: VS发布项目:

js滑动窗口算法

滑动窗口算法(Sliding Window Algorithm)是一种用于解决数组或字符串的子串问题的有效算法。其核心思想是通过维护一个窗口,根据问题的要求移动窗口的左右边界,从而在窗口内部找到符合条件的子串。 一般步骤如下: 初…

acwing算法学习笔记 ------ 双链表

1、定义 这里可以做一个投机取巧,我们不再像单链表去用head去存头和尾,直接让r[0] 1,l[1] 0; idx 2.进行初始化, 解释一下l[N] 和 r[N] l[N]:是表示指向左面下一个节点下标, r[N]:表示指向下一个节点的下标。大家不用担心i…

学习 LangChain 的 Passing data through

学习 LangChain 的 Passing data through 1. Passing data through2. 示例 1. Passing data through RunnablePassthrough 允许不改变或添加额外的键来传递输入。这通常与 RunnableParallel 结合使用,将数据分配给映射中的新键。 RunnablePassthrough() 单独调用&…

【Java程序员面试专栏 算法思维】一 高频面试算法题:排序算法

一轮的算法训练完成后,对相关的题目有了一个初步理解了,接下来进行专题训练,以下这些题目就是汇总的高频题目,本篇主要聊聊排序算法,包括手撕排序算法,经典的TOPK问题以及区间合并,所以放到一篇Blog中集中练习 题目关键字解题思路时间空间快速排序双指针+递归+基准值分…

U盘乱码与文件丢失:恢复指南与预防策略

U盘乱码文件丢失是一种常见的技术问题,通常表现为存储在U盘中的文件名显示为不可识别的字符或文件无法正常打开,有时甚至文件会完全消失。这种情况可能由多种原因引起,包括但不限于文件系统损坏、不正确的拔插操作、病毒感染、兼容性问题等。…

Linux学习之vi/vim详细介绍

目录 ​编辑 1. 什么是 vim? 2. vi/vim 的使用 2.1 命令模式 2.2 输入模式 2.3 底线命令模式 3. vi/vim 使用实例 3.1 使用 vi/vim 进入一般模式 3.2 按下 i 进入输入模式(也称为编辑模式),开始编辑文字 3.3 按下 ESC 按钮回到一般模式…

相信未来:技术的进步意味着重构

十年以来,呼声最高:AI、BigData、Cloud Service。 以本人看来,仅AI技术的进步和应用,整个软件行业,所有软件将被重构。 提醒:非大学毕业、非计算机及相关专业,在IT这个行业,特别是…

大数据开发项目--音乐排行榜

环境:windows10,centos7.9,hadoop3.2、hbase2.5.3和zookeeper3.8完全分布式; 环境搭建具体操作请参考以下文章: CentOS7 Hadoop3.X完全分布式环境搭建 Hadoop3.x完全分布式环境搭建Zookeeper和Hbase 1. 集成MapReduce…

安装 WSL 报错 Error code: Wsl/WININET_E_NAME_NOT_RESOLVED 问题解决

问题描述 在执行 wsl --install 安装Windows子系统Linux WSL (Windows Subsystem for Linux) 时报错: 无法从“https://raw.githubusercontent.com/microsoft/WSL/master/distributions/DistributionInfo.json”中提取列表分发。无法解析服务器的名称或地址 Error…

代码随想录算法训练营第60天 | 647.回文子串 516.最长回文子序列

回文子串 这道题主要难在dp数组的定义以及递推关系的构建。如果直接用 dp[i] 表示[0,i]子串中包含的回文串的数目,是无法找到递推关系的。通过回文串的性质可以构造这样的递推关系:对于判断[i,j]是否是回文串,如果s[i] s[j],只需…

网络攻防之ARP欺骗和DNS劫持实验

目录 ARP单向欺骗 ARP双向欺骗 DNS劫持 实验环境: 攻击主机:kali2023虚拟机,IP地址为192.168.133.141 靶机:Windows10虚拟机,IP地址为192.168.133.129 网关地址:192.168.133.2 (1)ARP协议介绍 在以…