stm32和arm的区别 按键实验,我明明上拉输入 ,为什么一烧入就两个小灯一起亮。

现在的位置:
零死角玩转stm32-初级篇之EXTI之按键中断实验
    8、EXTI之按键中断实验
   EXTI (External interrupt) 就是指外部中断,通过GPIO检测输入脉冲,引起中断事件,打断原来的代码执行流程,进入到中断服务函数中进行处理,处理完后,再返回到中断之前的代码中执行。
前面提到,STM32的所有GPIO都可以用作外部中断源的输入端,利用这个特性,我们可以把按键轮询检测 改为由中断 来处理,大大提高软件执行的效率。
<span style="color: # STM32的中断和异常
Cortex内核具有强大的异常响应系统,它把能够打断当前代码执行流程的事件分为异常(exception)和中断(interrupt),并把它们用一个表管理起来,编号为0~15的称为内核异常,而16以上的则称为外部中断(外,相对内核而言),这个表就称为中断向量表。
而STM32对这个表重新进行了编排,把编号从-3至6的中断向量定义为系统异常,编号为负 的内核异常不能被设置优先级,如复位(Reset)、不可屏蔽中断 (NMI)、硬错误(Hardfault)。从编号7开始的为外部中断,这些中断的优先级都是可以自行设置的。详细的STM32中断向量表见图 81,STM32中断向量表。
图 81中断向量表
   这个表可以从《STM32中文参考手册》找到,但野火一般是从启动文件startup_stm32f10x_hd.s中查找的,因为不同型号的STM32芯片,中断向量表稍微有点区别,在启动文件中,已经有相应芯片可用的全部中断向量。而且在编写中断服务函数时,需要从启动文件中定义的中断向量表查找中断服务函数名。
  8.2 NVIC中断控制器
STM32的中断如此之多,配置起来并不容易,因此,我们需要一个强大而方便的中断控制器NVIC (Nested Vectored Interrupt Controller)。NVIC是属于Cortex内核的器件,不可屏蔽中断 (NMI)和外部中断都由它来处理,而SYSTICK不是由NVIC来控制的。
图82 NVIC在内核中的位置
   8.2.1 NVIC结构体成员
当我们要使用NVIC来配置中断时,自然想到ST库肯定也已经把它封装成库函数了。查找库帮助文档,发现在Modules-&STM32F10x_StdPeriph_Driver-&misc 查找到一个NVIC_Init() 函数,对NVIC初始化,首先要定义并填充一个NVIC_InitTypeDef 类型的结构体。
这个结构体有四个成员
前面两个结构体成员都很好理解,首先要用NVIC_IRQChannel参数来选择将要配置的中断向量,用NVIC_IRQChannelCmd参数来进行使能(ENABLE)或关闭(DISABLE)该中断。在NVIC_IRQChannelPreemptionPriority成员要配置中断向量的抢占优先级,在NVIC_IRQChannelSubPriority需要配置中断向量的响应优先级。对于中断的配置,最重要的便是配置其优先级,但STM32的同一个中断向量为什么需要设置两种优先级?这两种优先级有什么区别?
 8.2.2 抢占优先级和响应优先级
STM32的中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。
抢占,是指打断其它中断的属性,即因为具有这个属性,会出现嵌套中断(在执行中断服务函数A的过程中被中断B打断,执行完中断服务函数B再继续执行中断服务函数A),抢占属性由NVIC_IRQChannelPreemptionPriority的参数配置。
而响应属性则应用在抢占属性相同的情况下,当两个中断向量的抢占优先级相同时,如果两个中断同时到达,则先处理响应优先级高的中断,响应属性由NVIC_IRQChannelSubPriority的参数配置。
例如,现在有三个中断向量:
若内核正在执行C的中断服务函数,则它能被抢占优先级更高的中断A打断,由于B和C的抢占优先级相同,所以C不能被B打断。但如果B和C中断是同时到达的,内核就会首先响应响应优先级别更高的B中断。
 8.2.3 NVIC的优先级组
在配置优先级的时候,还要注意一个很重要的问题,中断种类的数量。NVIC只可以配置16种 中断向量的优先级,也就是说,抢占优先级和响应优先级的数量由一个4位的数字来决定,把这个4位数字的位数 分配成抢占优先级部分和响应优先级部分。有5组分配方式:
第0组: 所有4位用来配置抢占优先级,即NVIC配置的24 =16种中断向量都是只有抢占属性,没有响应属性。
第1组:最高1位用来配置抢占优先级,低3位用来配置响应优先级。表示有21=2种级别的抢占优先级(0级,1级),有23=8种响应优先级,即在16种中断向量之中,有8种中断,其抢占优先级都为0级,而它们的响应优先级分别为0~7,其余8种中断向量的抢占优先级则都为1级,响应优先级别分别为0~7。
第2组:2位用来配置抢占优先级,2位用来配置响应优先级。即22=4种抢占优先级,22=4种响应优先级。
第3组:高3位用来配置抢占优先级,最低1位用来配置响应优先级。即有8种抢占优先级,2种响应2优先级。
第4组:所有4位用来配置响应优先级。即16种中断向量具有都不相同的响应优先级。
要配置这些优先级组,可以采用库函数NVIC_PriorityGroupConfig(),可输入的参数为NVIC_PriorityGroup_0 ~ NVIC_PriorityGroup_4,分别为以上介绍的5种分配组。
于是,有读者觉得疑惑了,如此强大的STM32,所有GPIO都能够配置成外部中断,USART、ADC等外设也有中断,而NVIC只能配置16种中断向量,那在某个工程中使用了超过16个的中断怎么办呢?注意NVIC能配置的是16种 中断向量,而不是16个,当工程之中有超过16个中断向量时,必然有2个以上的中断向量是使用相同的中断种类,而具有相同中断种类的中断向量不能互相嵌套。
STM2 单片机的所有I/O端口都可以配置为EXTI中断模式,用来捕捉外部信号,可以配置为下降沿中断,上升沿中断和上升下降沿中断这三种模式。它们以下图的方式连接到16个外部中断/事件线上
 8.3 EXTI外部中断
STM32的所有GPIO都引入到EXTI外部中断线上,使得所有的GPIO都能作为外部中断的输入源。GPIO与EXTI的连接方式见图 03
图 03 EXTI与GPIO连接图
   观察这个图知道,PA0~PG0 连接到EXTI0 、PA1~PG1连接到EXTI1、 ……、 PA15~PG15连接到EXTI15。这里大家要注意的是:PAx~PGx端口的中断事件都连接到了EXTIx,即同一时刻EXTx只能相应一个端口的事件触发,不能够同一时间响应所有GPIO端口的事件,但可以分时复用。它可以配置为上升沿触发,下降沿触发或双边沿触发。EXTI 最普通的应用就是接上一个按键,设置为下降沿触发,用中断来检测按键。
 8.4 中断检测按键实验分析
8.4.1实验描述及工程文件清单
8.4.2 配置工程环境
本中断检测按键实验照例使用了GPIO和RCC片上外设,由于还使用到了中断,所以比上一个按键实验要多使用两个库文件,分别为FWlib/stm32f10x_exti.c和FWlib/misc.c,必须把这两个文件也添加到工程之中。其中stm32f10x_exti.c文件包含了支持exti配置和操作的相关库函数;而misc.c文件则包含了NVIC的配置函数。本实验中我们还会在stm32f10x_it.c文件中编写中断服务函数。
添加了所需要的库文件、用户文件之后,要在stm32f10x_conf.h文件中配置使用到的头文件。
**********************************************************
Project/STM32F10x_StdPeriph_Template/stm32f10x_conf.h
MCD Application Team
* @version V3.5.0
08-April-2011
Library configuration file.
*************************************************/
#include "stm32f10x_exti.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "misc.h" /
   8.4.5 main文件
我们从main函数开始分析:
* 函数名:main
int main(void)
/* config the led */
LED_GPIO_Config();
LED1( ON );
/* exti line config */
EXTI_PE5_Config();
/* wait interrupt */
使用LED_GPIO_Config() 配置好LED用到的I/O后,调用LED1()点亮一盏LED灯。这两个函数的具体讲解可参考前面的教程。
  8.4.6 配置外部中断
现在我们重点分析下EXTI_PE5_Config() 这个函数,这是一个在用户文件exti.c中实现的函数,它完成了一般配置一个I/O为EXTI中断的步骤,主要为功能:
1、使能EXTIx线的时钟和第二功能AFIO时钟
2、配置EXTIx线的中断优先级
3、配置EXTI 中断线I/O
4、选定要配置为EXTI的I/O口线和I/O口的工作模式
5、EXTI 中断线工作模式配置
EXTI_PE5_Config()代码:
* 函数名:EXTI_PE5_Config
:配置 PE5 为线中断口,并设置中断优先级
:外部调用
void EXTI_PE5_Config(void)
GPIO_InitTypeDef GPIO_InitS
EXTI_InitTypeDef EXTI_InitS
/* config the extiline(PE5) clock and AFIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO,ENABLE);
/* config the NVIC(PE5) */
NVIC_Configuration();
/* EXTI line gpio config(PE5) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
// 上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure);
/* EXTI line(PE5) mode config */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource5);
EXTI_InitStructure.EXTI_Line = EXTI_Line5;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_I
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_F //下降沿中断
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
   8.4.7 AFIO时钟
EXTI_PE5_Config()代码的第14行,调用RCC_APB2PeriphClockCmd() 时还输入了参数RCC_APB2Periph_AFIO,表示开启AFIO的时钟。见图 04。
 AFIO (alternate-function I/O),指GPIO端口的复用功能,GPIO除了用作普通的输入输出(主功能),还可以作为片上外设的复用输入输出,如串口,ADC,这些就是复用功能。大多数GPIO都有一个默认复用功能,有的GPIO还有重映射功能, 重映射功能是指把原来属于A引脚的默认复用功能,转移到了B引脚进行使用,前提是B引脚具有这个重映射功能
当把GPIO用作EXTI外部中断 或使用重映射功能的时候,必须开启AFIO时钟,而在使用默认复用功能的时候,就不必开启AFIO时钟了。
图 04 GPIO引脚功能说明
   8.4.8 NVIC初始化配置
在EXTI_PE5_Config()代码的第17行调用了NVIC_Configuration(),这是用户编写的用来配置NVIC控制器的函数。其实现如下:
* 函数名:NVIC_Configuration
:配置嵌套向量中断控制器NVIC
:内部调用
static void NVIC_Configuration(void)
NVIC_InitTypeDef NVIC_InitS
/* Configure one bit for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* 配置P[A|B|C|D|E]5为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
本代码的第13行调用了 NVIC_PriorityGroupConfig()库函数,把NVIC中断优先级分组设置为第1组。接下来开始向NVIC初始化结构体写入参数 .NVIC_IRQChannel = EXTI9_5_IRQn,表示要配置的为EXTI第5~9线的中断向量。因为按键PE5对应的EXTI线为EXTI5,而从EXTI5~EXTI9线,由于它们是使用同一个中断向量的,所以只能写入EXTI9_5_IRQn这个参数。这些可写入的参数可以在stm32f10x.h文件的IRQn类型定义中查找到。
然后配置抢占优先级和响应优先级,因为这个工程简单,就直接把它设置为最高级中断。填充完结构体,别忘记最后要调用NVIC_Init() 函数来向寄存器写入参数。
8.4.9 EXTI初始化配置
回到EXTI_PE5_Config()代码中,配置好NVIC后,还要对GPIOE进行初始化,这部分和按键轮询的设置类似。
接下来,调用GPIO_EXTILineConfig()函数把GPIOE,Pin5设置为EXTI输入线。
图 05 EXTI中断源配置函数
   选择好了GPIO,开始填写EXTI的初始化结构体。从这些参数的名字,读者就已经可以知道野火是如何把它应用到按键检测中了吧?
.EXTI_Line = EXTI_Line5;
给EXTI_Line成员赋值。选择EXTI_Line5线进行配置,因为按键的PE5连接到了EXTI_Line5。
.EXTI_Mode = EXTI_Mode_I
给EXTI_Mode成员赋值。把EXTI_Line5的模式设置为为中断模式EXTI_Mode_Interrupt。这个结构体成员也可以赋值为事件模式EXTI_Mode_Event ,这个模式不会立刻触发中断,而只是在寄存器上把相应的事件标置位置1,应用这个模式要不停地查询相应的寄存器。
.EXTI_Trigger = EXTI_Trigger_F
给EXTI_Trigger成员赋值。把触发方式(EXTI_Trigger)设置为下降沿触发(EXTI_Trigger_Falling)。
.EXTI_LineCmd = ENABLE;
给EXTI_LineCmd成员赋值。把EXTI_LineCmd设置为使能。
最后调用EXTI_Init()把EXTI初始化结构体的参数写入寄存器。
 8.4.10 编写中断服务函数
在这个EXTI设置中我们把PE5连接到内部的EXTI5,GPIO配置为上拉输入,工作在下降沿中断。在外围电路上我们将PE5接到了key1上。当按键没有按下时,PE5始终为高,当按键按下时PE5变为低,从而PE5上产生一个下降沿跳变,EXTI5会捕捉到这一跳变,并产生相应的中断,中断服务程序在stm32f10x_it.c中实现。
 stm32f10x_it.c文件是专门用来存放中断服务函数的。文件中默认只有几个关于系统异常的中断服务函数,而且都是空函数,在需要的时候自已进行编写。那么中断服务函数名是不是可以自己定义呢?不可以。中断服务函数的名字必须要跟启动文件startup_stm32f10x_hd.s中的中断向量表定义一致。以下为启动文件中定义的部分向量表:
EXTI0_IRQH EXTI Line 0
EXTI1_IRQH EXTI Line 1
EXTI2_IRQH EXTI Line 2
EXTI3_IRQH EXTI Line 3
EXTI4_IRQH EXTI Line 4
DMA1_Channel1_IRQH DMA1 Channel 1
DMA1_Channel2_IRQH DMA1 Channel 2
DMA1_Channel3_IRQH DMA1 Channel 3
DMA1_Channel4_IRQH DMA1 Channel 4
DMA1_Channel5_IRQH DMA1 Channel 5
DMA1_Channel6_IRQH DMA1 Channel 6
DMA1_Channel7_IRQH DMA1 Channel 7
ADC1_2_IRQH ADC1 & ADC2
USB_HP_CAN1_TX_IRQH USB High Priority or CAN1 TX
USB_LP_CAN1_RX0_IRQH USB Low
Priority or CAN1 RX0
CAN1_RX1_IRQH CAN1 RX1
CAN1_SCE_IRQH CAN1 SCE
EXTI9_5_IRQH EXTI Line 9..5
TIM1_BRK_IRQH TIM1 Break
TIM1_UP_IRQH TIM1 Update
TIM1_TRG_COM_IRQH TIM1 Trigger and Commutation
TIM1_CC_IRQH TIM1 Capture Compare
TIM2_IRQH TIM2
TIM3_IRQH TIM3
TIM4_IRQH TIM4
I2C1_EV_IRQH I2C1 Event
I2C1_ER_IRQH I2C1 Error
I2C2_EV_IRQH I2C2 Event
I2C2_ER_IRQH I2C2 Error
SPI1_IRQH SPI1
SPI2_IRQH SPI2
USART1_IRQH USART1
USART2_IRQH USART2
USART3_IRQH USART3
EXTI15_10_IRQH EXTI Line 15..10
第18行,为EXTI9_5IRQHandler,表示为EXTI9~EXTI5中断向量的服务函数名。
于是,我们就可以在stm32f10x_it.c文件中加入名为EXTI9_5_IRQHandler()的函数:
/* I/O线中断,中断线为PE5 */
void EXTI9_5_IRQHandler(void)
if(EXTI_GetITStatus(EXTI_Line5) != RESET) //确保是否产生了EXTI Line中断
// LED1 取反
GPIO_WriteBit(GPIOC, GPIO_Pin_3,
(BitAction)((1-GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_3))));
EXTI_ClearITPendingBit(EXTI_Line5);
//清除中断标志位
其内容比较容易理解,进入中断后,调用库函数EXTI_GetITStatus() 来重新检查是否产生了EXTI_Line中断,接下来把LED取反,操作完毕后,调用EXTI_ClearITPendingBit() 清除中断标置位再退出中断服务函数。这两个函数的解释见图 06及图 07。
图 06EXTI状态检查函数
图 07 EXTI清除标志位函数
   这两种函数在ST库函数非常见,当我们要读取某外设的状态时,可调用该外设的XXX_GetFlagStatus()函数来获取该状态。一般也有XXX_ClearFlag()库函数可供调用,进行相应的标志位清除。
中断服务程序比较简单,很容易读懂,但我们在写中断函数入口的时候要注意函数名的写法,函数名只有两种命名方法:
1-& EXTI0_IRQH EXTI Line 0
EXTI1_IRQH EXTI Line 1
EXTI2_IRQH EXTI Line 2
EXTI3_IRQH EXTI Line 3
EXTI4_IRQH EXTI Line 4
2-& EXTI9_5_IRQH EXTI Line 9..5
EXTI15_10_IRQH EXTI Line 15..10
只要是中断线在5之后的就不能像0~4那样单独一个函数名,都必须写成EXTI9_5_IRQHandler和EXTI15_10_IRQHandler。假如写成EXTI5_IRQHandler、EXTI6_IRQHandler……EXTI15_IRQHandler这样子的话编译器是不会报错的,只是中断服务程序不能工作罢了。如果你不知道的话,会让你搞半天也不知问题出现在哪。
 8.4.11实验现象
将野火STM32开发板供电(DC5V),插上JLINK,将编译好的程序下载到开发板,LED1亮,按下按键时LED1灭,再按下按键时LED1亮,如此循环。
【上篇】【下篇】
横跨多重电子应用领域、全球领先的半导体供应商意法半导体(STMicroelectronics,简称ST;纽约证券交易所代码:STM)新推出的两款立即可用的原型板大幅削减LoRaWAN(TM) 、Sigfox、WM-Bus、6LoWP...
横跨多重电子应用领域、全球领先的半导体供应商意法半导体(STMicroelectronics,简称ST)宣布,其STM32 微控制器开发套件出货量超过100万套,同时STM8微控制器开发套件出货量也亦逾10万套,为...
虽然Python在国外是一门非常火的语言,在黑客界更是赫赫有名,然而中国的大学却极少开设 Python 课程,故而国内 Python 程序员多属自学。而一个没有MCU编程经验的初学者,要想让芯片跑起来,...
虽然Python在国外是一门非常火的语言,在黑客界更是赫赫有名,然而中国的大学却极少开设 Python 课程,故而国内 Python 程序员多属自学。而一个没有MCU编程经验的初学者,要想让芯片跑起来,...
横跨多重电子应用领域、全球领先的半导体供应商意法半导体(STMicroelectronics,简称ST)推出一套价格亲民的基于意法半导体STM32微控制器生态系统的开发工具,设计工程师利用新开发套件能够开...
您必须才能发表留言!STM32学习笔记——按键输入
隔了好久才写这第二篇,期间重感冒,身体难受大约十天,今天重新写,又踩了好多的坑。
1:首先,按键实验是GPIO口的另一应用,上一次的跑马灯实验是将GPIO口作为输出,此次按键实验是将GPIO口作为输入。
和跑马灯实验实验一样,要启用GPIO口,第一步要做的就是使能GPIO所在的时钟总线,具体库函数操作为:RCC_APB2PeriphClockCmd(GPIOA,ENABLE);
2:接下来说说GPIO作为输入时和输入的不同:
(1):首先需要了解按键是低电平有效还是高电平有效,因为此处需要配置GPIO口的输入模式。大家都知道GPIO口有四种输入模式,四种输出模式(具体请看我的第一篇笔记)。我使用的是STM32F103C8T6,按键s2为低电平有效,故,此处配置输出模式时应为上拉输入模式,即GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
注:关于为什么低电平有效就用上拉输入,这里多讲几句。
首先,上拉电阻是为了保证在没有信号输入的时候,IO口保持高电平,若按键为低电平有效,则没有信号的时候,IO应呈现高电平,所以,此处应设置为上拉输入。
同理,下拉电阻是为了保证在没有信号输入的时候,IO口保持低电平,若按键为高电平有效,则没有信号的时候,IO应呈现低电平。
然后再配置一下IO口引脚,初始化PA0即可。作为输入时,不需要配置速度。
(2)GPIO口各项功能配置完毕之后就是编写按键函数。库函数操作为:GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);若是很简单要实现按键控制LED灯的功能,只需如下的代码:
这里led灯的转换是通过位带操作的,具体操作是在LED.h里写如下代码:#define LED0 PAout(0)即可
考虑道按键防抖,写了如下的按键扫描函数:
这里在编写主函数的时候还有一个大坑,我很好奇,但是不知道为什么。
这里的按键扫描函数还需要在主函数中声明一次,不然会出现warning : function "KEY_Scan()"declared implaitility.可我明明在头文件里添加了key.h了。。。。
STM32学习笔记:单片机按键单击、双击、长按功能实现
stm32f103学习笔记(三)按键输入(IO口输入)
STM32F103C8T6按键中断功能实现
STM32 嵌入式学习入门(3)——STM32F103 按键输入控制LED灯
STM32入门开发--按键模块实现按键点灯
嵌入式系统学习——STM32之按键输入
STM32开发板按键实验
STM32之GPIO按键实验
STM32实例之I/O控制中的按键实验
没有更多推荐了,STM32 嵌入式学习入门(3)——STM32F103 按键输入控制LED灯
STM32 嵌入式学习入门(3)—— STM32F103 按键输入控制LED灯
按键是单片机上一个很重要的输入设备,也很容易掌握,只要明白了IO口最基本的使用,就可以操作按键了。
我们的目的是控制开发板上板载的三个按键来操作开发板上板载的两个LED灯实现亮或灭(按键第一次按下时灯亮,再按下时灯灭,以此类推)。
博主所用的开发板是正点原子的mini板(STM32F103RCT6)和战舰板(STM32F103ZET6),因此下面的内容的例子以这两款开发板为例,但是相关的原理对任何开发板来说都是一样的,只要自己的开发板上板载了按键和LED灯(这两个资源应该是所有开发板上都有的资源吧),然后查看自己开发板的数据手册和硬件电路图、原理图,找到各个按键对应的IO口就好了。
在开发板上,板载硬件资源(比如我们这里用到的按键和LED灯)都是和确定的GPIO相连的,要使用这些硬件资源,实际上可以理解成对这些IO口的操作。
下面先大致说一下整个流程。
1.首先要使用这些硬件资源,就要对其进行初始化。初始化包括使能IO口时钟和初始化IO口参数两个部分,这两点在上一篇博文中有提到了。
2.初始化完成后就可以操作IO口了,就是写相关的逻辑代码。这里是按键控制LED灯,所以我们首先要扫描与按键相连的IO口的电平。如果IO电平发生变化,那么说明按键被按下了,我们就要让灯的状态发生反转。如果IO电平没有发生变化,那么说明按键没有被按下,我们可以过一个很小的时间间隔再去扫描与按键相连的IO口看是否发生变化,这样形成了一个死循环。
下面上一段代码,是正点原子Mini STM32F103RCT6开发板(mini板)按键实验的主函数部分:
#define LED0 PAout(8) // PA8
#define LED1 PDout(2) // PD2
#define KEY0_PRES 1 //KEY0
#define KEY1_PRES 2 //KEY1
#define WKUP_PRES 3 //WK_UP
int main(void)
delay_init();
//延时函数初始化
LED_Init();
//初始化与LED连接的硬件接口
KEY_Init();
//初始化与按键连接的硬件接口
//点亮LED0
t=KEY_Scan(0);
//得到键值 KEY_Scan(0);即不支持连续有效。如果需要支持连续按,这里参数设置为1.
switch(t){
case KEY0_PRES:
LED0=!LED0;
case KEY1_PRES:
LED1=!LED1;
case WKUP_PRES:
LED0=!LED0; LED1=!LED1;
delay_ms(10);
}//while(1)
代码比较长,但整体逻辑还是很容易理解的,首先解释一下前两行的宏定义,这里是通过位操作,实现了对PA8/PD2的电平输出的操作的,这一行代码其实挺复杂的,准确说是博主也讲不出原理(如果你去查看stm32的官方库函数,会发现这里的PAout(n)是通过很多层宏定义得到的),但是我会调用。总之,这样定义了以后,下面的代码你对LED0赋1对应着设置IO口为高电平,赋0就是设置IO口为低电平。下面的三个宏定义是为下面switch-case服务的,增强了代码的可读性。
接下来就是主函数了,首先是调用三个初始化函数,这个等下后面具体说。初始化完成后,然后LED0=0;这一句,点亮LED0。接着就是while(1)循环了,死循环里面先调用KEY_Scan(0);函数对按键进行检测,该函数的返回值有四种情况,即KEY0_PRES、KEY1_PRES、WKUP_PRES、0。前三个返回值表明相应的按键被按下了,返回0表明当前没有按键按下。然后是switch-case结构了,如果有按键按下,执行相应分支,对LED上的电平进行反转,实现按一次亮,再按一次灭,以此类推,这样的效果。如果没有按键按下,那么延时10ms,然后继续检测有没有按键按下。整体程序的执行过程就是这样。
下面的问题就是三个初始化函数和KEY_Scan(0);函数是怎么实现的了。
1.延时函数
这里用到了delay_init();和delay_ms(10);两个函数。延时函数要想完全搞清楚其中的原理需要了解STM32内核中定时器的知识,不是三两句可以解释清楚的,以后有时间再写一篇详细介绍一下延时函数,写完后贴到这里来。对于刚刚接触STM32的同学来说,我建议刚开始就会调用这个函数就好了,不要深究其实现原理,因为涉及其它内容比较多,零基础刚开始接触STM32,去看那些,如果看不懂挺打击人积极性的。这两个函数的调用方法:delay_ms(10);调用时候传递一个整形参数n,表示要延时n毫秒,如果程序中用到了delay_ms(n);函数,那么在调用延时函数前要调用延时初始化函数delay_init();就是这样无参调用就好了。另外,delay_ms(n);函数调用时候n的值也不能过大,n的值是有一个上限的,这就像int所能表示的最大值也是有上限的一样。具体的这个值是多少,是和时钟频率有关的,对时钟频率为72M的条件下(这算是个默认条件了),n&=1864。
上面说的两个延时函数都不是自己写的,正点原子的每个实验代码的SYSTEM文件夹的delay.c文件中都有这个代码,所以博主就拿来主义了,很方便。如果是别的开发板,可以查查手册看怎样能实现延时的功能,实在不行,下面的代码也能实现延时……
void Delay(u32 count)
for(;i&i++);
2.LED_Init();和KEY_Init();函数
这两个函数是自己写的,就是对IO口的相关参数进行配置,先上代码:
void LED_Init(void)
GPIO_InitTypeDef
GPIO_InitS
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE);
//使能PA,PD端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
//LED0--&PA.8 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);
//根据设定参数初始化GPIOA.8
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
//LED1--&PD.2 端口配置, 推挽输出
GPIO_Init(GPIOD, &GPIO_InitStructure);
//推挽输出 ,IO口速度为50MHz
}void KEY_Init(void)
GPIO_InitTypeDef GPIO_InitS
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);//使能PORTA,PORTC时钟
GPIO_InitStructure.GPIO_Pin
= GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
//设置成上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//初始化GPIOA15
GPIO_InitStructure.GPIO_Pin
= GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
//设置成上拉输入
GPIO_Init(GPIOC, &GPIO_InitStructure);
//初始化GPIOC5
GPIO_InitStructure.GPIO_Pin
= GPIO_Pin_0;//PA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0
关于GPIO口的初始化,可以参考博主的上一篇文章,
至于这里配置为什么是这些参数,是根据开发板硬件连接确定的,上面的代码是正点原子MiniSTM32F103RCT6开发板的代码,这款开发板的原理图如下图:
从这里可以看出来按键对应的IO口以及它们是与高电平相连还是低电平相连。比如两个LED,硬件上,它们都是与高电平相连的,所以你把另一端设置为低电平的时候它们就被点亮,如果另一端设置为高电平,那么它们就灭。另外IO口的模式也是从硬件连接上确定的。
3.KEY_Scan(0);函数的实现
这个函数的实现其实不难,和STM32的知识没多大关系,就是C语言的逻辑问题,代码如下:
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//返回值:
//0,没有任何按键按下
//KEY0_PRES,KEY0按下
//KEY1_PRES,KEY1按下
//WKUP_PRES,WK_UP按下
//注意此函数有响应优先级,KEY0&KEY1&WK_UP!!
u8 KEY_Scan(u8 mode)
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1;
//支持连按
if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
delay_ms(10);//去抖动
if(KEY0==0)
return KEY0_PRES;
if(KEY1==0)
return KEY1_PRES;
if(WK_UP==1)
return WKUP_PRES;
if(KEY0==1&&KEY1==1&&WK_UP==0)
return 0;// 无按键按下
STM32_外部中断之按键控制点亮LED灯
Stm32按键中断使LED灯闪烁
STM32F103程序设计-6-引脚输入功能-按键(查询)
STM32(二)之GPIO操作(2)——通过按键控制LED灯的开关
stm32按键输入
嵌入式系统学习——STM32之按键输入
STM32之GPIO按键实验
STM32实例之I/O控制中的按键实验
总结写的stm32的KEY控制LED
Stm32按键输入控制LED灯
没有更多推荐了,

我要回帖

更多关于 stm32按键输入实验报告 的文章

 

随机推荐