学习完江科协的STM32教程的GPIO+EXTI部分利用套件实现了一个小的功能。
这款套件包含了F103c8t6最小系统版、面包板、LED灯、按键、OLED显示屏、对射式红外传感器、蜂鸣器以及杜邦线等元件。
这个智能人数控制系统的工作原理非常简单明了。当人们依次刷卡进入区域时,系统会限制每次只能进入10人。当人数未超过10人时,绿灯会闪烁,伴随着蜂鸣器的短促鸣叫声,OLED显示屏上的人数会增加一。当刚好达到10人时,黄灯会闪烁,并伴随着蜂鸣器的短促鸣叫声。此时,如果有人再次刷卡,红灯将持续亮起,蜂鸣器会发出长鸣声,作为警示。
当有人出去后,只需通过按键操作,即可减少人数,恢复正常通行。此时,红灯将熄灭,系统恢复到正常状态。
完成本次功能需要用到的知识点:
从图中我们可以看出GPIO是在APB2的总线上的
一、一般操作GPIO的步骤
1.定义一个GPIO_InitTypeDef 类型的结构体,用于配置GPIO相关参数
2.开启外设时钟(RCC)对于使用总线APB2来说的话是RCC APB2PeriphClockCmd()具体参数看源码
3.进行结构体参数设置:
(1)GPIO_Speed:端口速度(50HZ)
(2)GPIO_Pin;端口号(如果是初始化所有引脚,GPIO_Pin_All)
(3)GPIO_Mode:端口模式(stm32有8种模式)
以下是8种工作模式:
-
- GPIO_Mode_AIN(模拟输入模式):引脚用作模拟输入,用于模拟信号的采集。使用场景:模拟信号采集、传感器接口等。
- GPIO_Mode_IN_FLOATING(浮空输入模式):引脚作为浮空输入接收外部信号。使用场景:读取外部信号、按键输入等。
- GPIO_Mode_IPD(带下拉输入模式):引脚作为带下拉电阻的输入接收外部信号。使用场景:读取外部信号,当外部信号未连接时,引脚会被拉低。
- GPIO_Mode_IPU(带上拉输入模式):引脚作为带上拉电阻的输入接收外部信号。使用场景:读取外部信号,当外部信号未连接时,引脚会被拉高。
- GPIO_Mode_Out_OD(开漏输出模式):引脚作为开漏输出发送信号给外部设备。使用场景:与其他器件共享总线,如I2C总线。
- GPIO_Mode_Out_PP(推挽输出模式):引脚作为推挽输出发送信号给外部设备。使用场景:控制外部设备的状态、输出指示信号等。
- GPIO_Mode_AF_OD(复用开漏输出模式):引脚作为复用开漏输出发送信号给外部设备。使用场景:使用特定的功能,如串口通信。
- GPIO_Mode_AF_PP(复用推挽输出模式):引脚作为复用推挽输出发送信号给外部设备。使用场景:使用特定的功能,如PWM输出。
{
推挽输出和开漏输出的主要区别在于输出电平的能力和连接方式。
- 输出电平能力:推挽输出可以主动拉高(VDD)或拉低(GND)引脚上的电平,因此可以提供高电平和低电平输出。开漏输出只能主动拉低(GND)引脚上的电平,无法提供高电平输出。要获得高电平,需要通过外部上拉电阻连接到VDD。
- 连接方式:推挽输出可以直接连接到负载,例如LED、电机等,因为它可以提供稳定的高电平和低电平输出。开漏输出通常被用于与其他电路共享总线的情况,如I2C、SPI、UART等。在这种情况下,多个设备共享同一条信号线,每个设备都可以通过开漏输出将线拉低,但只有一个设备能够将线拉高,因为其他设备的开漏输出只能被拉低。
总结起来,推挽输出适用于需要提供高电平和低电平输出的情况,而开漏输出适用于需要多设备共享总线的情况,其中只有一个设备能够提供高电平输出。
}
{
模拟输入和浮空输入是电子电路中两种不同的输入模式。
- 模拟输入模式(Analog Input Mode):在模拟输入模式下,输入信号是连续的、可变的模拟信号。模拟输入通常用于测量和处理连续变化的物理量,如声音、温度、压力等。模拟输入信号通常是通过模拟传感器从外部环境中采集的,并传入电子电路中进行处理和转换。模拟输入信号可以在一定范围内连续变化,可以是电压、电流或其他模拟量。
- 浮空输入模式(Floating Input Mode):在浮空输入模式下,输入信号处于非确定性状态,没有明确的高电平或低电平定义。浮空输入通常指的是未连接到有效信号源或未连接到电源或地的输入引脚。在浮空输入状态下,输入引脚可能会受到电磁干扰或环境噪声的影响,导致输出不确定或不可预测的结果。
总结来说,模拟输入是指接收并处理连续变化的模拟信号,而浮空输入是指输入引脚处于未连接有效信号源的状态。
}
二、GPIO的相关库函数:
- GPIO_DeInit(GPIO_TypeDef* GPIOx)
:复位指定的GPIO端口,将其所有寄存器恢复为默认值。
- GPIO_AFIODeInit()
:复位AFIO模块,将其所有寄存器恢复为默认值。
- GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
:初始化GPIO端口的引脚功能和模式,通过传递一个结构体参数来配置引脚的详细设置。
- GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct)
:将GPIO初始化结构体的成员变量初始化为默认值。
- GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
:读取指定引脚的输入电平(高电平或低电平)。
- GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
:读取整个GPIO端口的输入电平。
- GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
:读取指定引脚的输出电平(高电平或低电平)。
- GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)
:读取整个GPIO端口的输出电平。
- GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
:将指定引脚的输出电平设置为高电平。
- GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
:将指定引脚的输出电平设置为低电平。
- GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
:将指定引脚的输出电平设置为指定的BitVal(高电平或低电平)。
- GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)
:将整个GPIO端口的输出电平设置为指定的PortVal。
- GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
:对指定引脚进行锁定,防止意外更改其配置。
- GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
:配置GPIO事件输出选择。
- GPIO_EventOutputCmd(FunctionalState NewState)
:使能或禁用GPIO事件输出。
- GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState)
:配置GPIO引脚的重映射。
- GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
:配置外部中断引脚的GPIO端口源和引脚源。
- GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface)
:配置以太网媒体接口的GPIO引脚。
EXIT外部中断基础知识:
NVIC中断控制器:
STM32 的中断如此之多,配置起来并不容易,因此,我们需要一个强大而方便的中断控制器 NVIC (Nested Vectored Interrupt Controller)。NVIC 是属于Cortex 内核的器件,不可屏蔽中断 (NMI)和外部中断都由它来处理,而SYSTICK 不是由 NVIC 来控制的。
对于cpu来说直接将多个中断通道输出到cpu会影响效率和占用资源,所以会通过NVIC中断控制器来管理这些中断的执行顺序,每个中断都会有优先级,所以NVIC会根据他们的优先级的高低进行向cpu输出cpu每次执行一个。
抢占优先级和响应优先级:
STM32 的中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。
抢占,是指打断其它中断的属性,即因为具有这个属性,会出现嵌套中断(在执行中断服务函数 A 的过程中被中断 B 打断,执行完中断服务函数 B 再继续执行中断服务函数 A),抢占属性由 NVIC_IRQChannelPreemptionPriority 的参
数配置。
而响应属性则应用在抢占属性相同的情况下,当两个中断向量的抢占优先级相同时,如果两个中断同时到达,则先处理响应优先级高的中断,响应属性由 NVIC_IRQChannelSubPriority 的参数配置
NVIC优先级组:
EXIT的优点和使用场景:
EXIT外部中断的优点:(简单说就是当中断不触发时是不需要cpu维持状态的所以不会占用cpu的资源,如果触发中断cpu需要马上放下当前的工作来处理中断事件,可以保证其实时性)
- 实时性:外部中断可以立即中断程序的执行并处理相应的事件。这种实时性使得外部中断特别适用于需要快速响应的应用,例如传感器数据的实时采集和处理。
- 节省资源:外部中断可以有效地节省处理器的资源。相比于使用轮询方式不断检测特定事件的状态,外部中断只在事件发生时触发中断,不需要持续的处理器占用,从而节省了处理器的运算能力。
- 简化编程:外部中断的触发可以直接调用预定义的中断服务程序(ISR),简化了编程的复杂性。开发者只需编写ISR以响应中断事件,而不需要在主循环中持续检测特定事件的状态。
- 多任务处理:外部中断可以与多任务处理相结合,实现多个任务之间的切换和共享资源。在多任务系统中,外部中断可以作为任务切换的触发源,实现任务的调度和切换。
- 灵活性:外部中断可以配置为不同的触发方式,例如上升沿触发、下降沿触发、边沿触发等。这种灵活性使得外部中断适用于不同的应用场景,可以根据具体需求选择合适的触发方式。
总而言之,EXTI外部中断具有快速实时响应、节省资源、简化编程、支持多任务处理和灵活配置等优点,使其在许多嵌入式系统和实时应用中得到广泛应用。
EXIT外部中段的使用场景:
- 按键输入:外部中断可以用于检测和响应按键的按下或释放事件。当按键被按下或释放时,外部中断可以立即中断程序的执行,并调用相应的中断服务程序来处理按键事件。
- 传感器信号:许多传感器(如光敏传感器、温度传感器、加速度传感器等)会产生外部信号来指示特定事件的发生。外部中断可以用于检测这些信号,并及时处理传感器数据。
- 硬件状态变化:外部中断可以用于检测硬件状态的变化,并及时采取相应的措施。例如,当输入电压达到或超过某个阈值时,可以触发外部中断来执行相应的操作。
- 通信接口:外部中断可以用于检测和响应与其他设备的通信事件,例如UART、SPI或I2C等接口的数据传输完成或接收到特定数据等。
- 定时器事件:外部中断可以用于定时器的溢出或计数器的比较事件。当定时器或计数器满足预设条件时,外部中断可以触发相应的中断服务程序来执行定时任务。
- 外部事件触发:外部中断可以用于检测和响应其他外部事件的发生,例如外部信号的上升沿或下降沿触发、外部中断信号的触发等。
总体而言,EXTI外部中断适用于需要实时响应外部事件的各种嵌入式系统和实时应用场景,包括按键输入、传感器信号处理、硬件状态变化、通信接口、定时任务和其他外部事件触发等。
EXIT外部中断具体使用:
第一步,配置RCC,把使用到的外部时钟都开启(开启两个)
GPIO和AFIO都是在APB2总线上,所以开启APB2时钟
NVIC和EXIT外设的时钟是一直打开的不需要手动去开启时钟
第二步: 配置GPIO,选择端口为输入模式(不清楚外设的输出模式可以参考数据手册系统推荐)
具体步骤看GPIO笔记
第三步:配置AFIO,选择用到的一路GPIO,连接到后面的EXIT
AFIO的库函数文件和GPIO是在同一个文件中
配置中断通道:
GPIO_EXTILineConfig(GPIO_PortSource,GPIO_PinSource)
参数说明:
- GPIO_PortSource:GPIO引脚所在的GPIO端口编号,取值范围为GPIO_PortSource_GPIOx,x为A、B、C等。
- GPIO_PinSource:GPIO引脚的引脚编号,取值范围为GPIO_PinSource_x,x为0~15。
例如,如果要将GPIOA的Pin0与外部中断线路0关联,可以使用以下代码:GPIO_EXTILineConfig(GPIO_PortSource_GPIOA, GPIO_PinSource_0)
通过这个函数的调用,将指定的GPIO引脚与指定的外部中断线路关联起来,以便在产生外部中断时能够触发相应的中断处理程序
第四步:配置EXIT选择边沿触发方式,比如上升沿、下降沿或者双边沿,选择触发响应方式,中断响应或者事件响应。
EXTI_InitTypeDef EXTI_InitStructure; //EXIT初始化结构体
EXTI_Line:指定要使用的外部中断线路。可以使用逻辑或运算符(|)将多个中断线路组合在一起。取值可以是以下之一:
-
- EXTI_Line0 ~ EXTI_Line15:分别对应外部中断线路0 ~ 15。
- EXTI_LineNone:表示不使用任何外部中断线路。
EXTI_Mode:指定外部中断的工作模式。可以取值为以下之一
-
- EXTI_Mode_Interrupt:配置为中断模式。当外部中断触发时,会产生中断请求。
- EXTI_Mode_Event:配置为事件模式。当外部中断触发时,不会产生中断请求,但会触发事件。
EXTI_Trigger:指定外部中断触发的方式。可以取值为以下之一:
-
- EXTI_Trigger_Rising:上升沿触发。
- EXTI_Trigger_Falling:下降沿触发。
- EXTI_Trigger_Rising_Falling:上升沿和下降沿都触发。
EXTI_LineCmd:指定是否使能外部中断线路。可以取值为以下之一:
-
- ENABLE:使能外部中断线路。
- DISABLE:禁用外部中断线路。
EXTI_Init(&EXTI_InitStructure);
第五步:配置NVIC,配置中断的合适优先级
设置优先级分组:NVIC_PriorityGroupConfig();
- NVIC_PriorityGroup_0:优先级分组为0,不分组,4位抢占优先级,没有子优先级。
- NVIC_PriorityGroup_1:优先级分组为1,2位抢占优先级,2位子优先级。
- NVIC_PriorityGroup_2:优先级分组为2,3位抢占优先级,1位子优先级。
- NVIC_PriorityGroup_3:优先级分组为3,4位抢占优先级,没有子优先级。
配置NVIC:
NVIC_InitTypeDef NVIC_InitStructure;
- NVIC_IRQChannel:指定要配置的中断通道。取值范围为0到239。通道号可以通过查看芯片的参考手册或者使用HAL库提供的宏定义来确定。
- NVIC_IRQChannelPreemptionPriority:指定中断抢占优先级。取值范围为0到15。数值越小,优先级越高。
- NVIC_IRQChannelSubPriority:指定中断子优先级。取值范围为0到15。数值越小,优先级越高。
- NVIC_IRQChannelCmd:指定中断使能状态。可以为ENABLE或DISABLE。
NVIC_Init(&NVIC_InitStructure);
最后编写中断函数(中断函数的命名于使用的通道有关需要查看库函数定义)返回值和参数都是void
学习完这些就可轻松完成本次的功能,我是一名刚刚学习嵌入式的小白,欢迎大家一起交流学习讨论。