gpio模式,gpio配置

  

  在本章中,我们将向您展示如何使用STM32的外部输入中断。在前几章中,我们已经掌握了STM32的io口的最基本操作。在本章中,我们将介绍如何使用STM32的IO端口作为外部中断输入。在本章中,我们将使用中断模式来实现控制LED开关的功能。本章分为以下学习目标:   

  

  1.了解STM32的中断模式NVIC。   

  

  2.了解外部中断的使用。   

  

  1.1 中断的概念   

  

  如果学生有单片机基础,就会知道中断的概念。所谓中断,是指在CPU执行当前程序的过程中,由于某种随机的外设请求或CPU内部的异常事件,CPU挂起正在执行的程序,转而执行相应的服务处理程序;当服务处理程序结束时,CPU返回暂停状态,继续执行原来的程序。虽然某些中断可能会被其他高优先级中断中断,但这种情况也称为中断嵌套。   

  

  1.2 STM32 的中断   

  

  1) STM32 中断分组   

  

  STM32的中断总共有68个屏蔽中断通道(不包括16个Cortex-M3中断线路);16个可编程优先级;什么是可编程级别?首先,我们来看看STM32的中断级模式。STM32使用一个4位寄存器来设置中断级别。4位长度可以设置16个优先级(2的4次方等于16);这个优先级是可以设置的,所以称为可编程级别。而且这个4位的优先级可以分为两部分,一部分叫抢占优先级,一部分叫响应优先级(有些材料也叫次优先级)(比如4位中设置了1位抢占优先级和3位响应优先级)。)这里大家可能有点疑惑。分成抢占优先级和响应优先级有什么用?在STM32的NVIC中,规定抢占优先级较高的中断函数可以中断抢占优先级较低的中断函数,但是抢占优先级相同的中断函数不能互相中断。但是,响应优先级不能相互中断。有些人可能不会被打断,那么响应优先级的意义是什么?如果两个中断同时到达,中断控制器根据其响应优先级决定先处理哪一个;如果他们的抢占优先级和响应优先级相等,他们会根据自己在中断表中的排名顺序决定先处理哪一个。STM32的中断包有五种类型:   

  

     

  

  因此,当我们使用中断时,我们应该首先设置NVIC的分组。一般来说,分组是在配置中断NVIC变量之前设置的。   

  

  2) STM32 中断分组库函数。   

  

  从上面的学习中,我们知道当使用中断时,我们应该首先设置NVIC的分组。以及如何设置?在V3.5的库中,定义了集合分组函数nvic _ prioritygroupconfig();   

  

     

  

  NVIC优先级组该参数用于选择和设置组。我们知道它总共可以分为五组。因此,在V3.5的库中,这五个组也有助于您定义它们的名称:   

  

     

  

  1.3 EXTI 外部中断   

  

  1) EXTI 外部中断的结构   

  

  与51不同,STM32只有两个外部中断IO,STM32的每个IO口都可以作为一个外部中断。它的外部中断结构是什么样的?我们先来看一张图:   

  

     

  

     

  

     

  

  从图中我们可以清楚的看到STM32有16个外部中断通道,对应每组IO口的Px0到Px15。从图中我们还可以知道,虽然STM32的每一个IO口都可以作为外部中断,但是一次只能使用16个外部中断,同时做外部中断的IO口的序号不能相同。例如,如果使用PA0进行外部中断,则不能同时使用PB0进行外部中断。但是,同时使用16个外部中断对我们来说已经足够了。   

  

  外部中断的设置步骤:   

  

  1)   

配置时钟。外设时钟及端口复用时钟。

  

2)GPIO端口配置。GPIO初始化、输入模式以及是上拉模式,还是下拉模式。

  

3)外部中断IO口指定。选择要用作外部中断的 IO 口作为输入、可以认为是启用IO端口的复用功能。

  

4) 设置中断分组。 (一般在系统开机之后开始设置,而且一个程序中只 设置一次。 )

  

5) 中断初始化。(比如你要使用下降沿触发,那么就选择上拉输入 模式,要使用上升沿触发,那么就选择下拉输入模式。)

  

5) 开启要使用的中断通道并设置外部中断的参数(如:中断优先级、中 断触发模式)

  

6)编写中断处理函数。

  

1.4 V3.5 库函数介绍

  

外部中断的函数是放在 stm32f10x_exti.c 中的,而 NVIC 它是放在 misc.c中。 透过上面的操作步骤,如果我们使用 V3.5 库函数的话,一般会使用的库函数可以有:

  

1) RCC_APB2PeriphClockCmd()函数

  

我们要打开 GPIOE 及管脚复用的时钟。

  

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);

  

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

  

2) GPIO_Init()函数

  

IO 模式设置为上拉输入:

  

GPIO_InitStructure.GPIO_Pin=k_left;

  

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;

  

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

  

GPIO_Init(GPIOE,&GPIO_InitStructure);

  

3) GPIO_EXTILineConfig()函数

  

这个函数用来设置作为输入的 IO 口,它有两个输入参数: 第一个参数是使

  

用选择 GPIO 的组别,我们要使用的是 PE2,所以设 置为:

  

GPIO_PortSourceGPIOE。 第二个参数是设置 IO 的序号,我们使用的是 PE2,

  

所以我们为:

  

GPIO_PinSource2 所以设置代码为:

  

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);

  

4) NVIC_Init()函数:

  

用来设置中断的优先级和打开总中断。这个要输入一个结构体指针。 这个结构

  

体的参数分别有四个成员:

  

第一个成员是 NVIC_IRQChannelPreemptionPriority,表示抢占优先级的 等级,

  

我们设置为 0。第二个成员是 NVIC_IRQChannelSubPriority,表示响应优先级的等级, 我们也

  

设置为 0。

  

第三个成员是 NVIC_IRQChannel,表示选择你要设置的全局中断,我们 要设置

  

的中断是外部中断 0,所以我们设置为:EXTI0_IRQn。

  

第四个成员是 NVIC_IRQChannelCmd,表示要设置的状态,我们是要打 开中断的,

  

所以我们设置为:ENABLE。

  

所以最后的设置如下:

  

/* 设置 NVIC 参数 */

  

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

  

NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;

  

//打开 EXTI2

  

的全局中断

  

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优

  

先级为 0

  

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

  

//响应

  

优先级为 0

  

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  

//使能

  

NVIC_Init(&NVIC_InitStructure);

  

5)

  

EXTI_Init()函数:

  

这个函数是用来初始化外部中断的,它有一个参数,用来传递一个结构 体,这

  

个结构体有 4 个成员。

  

第一个成员:EXTI_Line,表示外部中断的通道。我们要使用的通道 0, 所以设

  

置为:EXTI_Line0。

  

第二个成员:EXTI_LineCmd,表示要设置的状态,我们是要打开使能, 所以设

  

置为:ENABLE。

  

第三个成员:EXTI_Mode,表示要使用的模式,我们设置为中断模式, 所以设置

  

为:EXTI_Mode_Interrupt。

  

第四个成员:EXTI_Trigger,表示中断触发模式,我们这里使用上升沿 触发,

  

所以设置为:EXTI_Trigger_Rising。

  

设置代码为:/* 设置外部中断的模式 */

  

EXTI_InitStructure.EXTI_Line=EXTI_Line2;

  

EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;

  

EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;

  

EXTI_InitStructure.EXTI_LineCmd = ENABLE;

  

EXTI_Init(&EXTI_InitStructure);

  

6)外部中断的中断函数

  

在库函数中,每个外部中断它都帮你定义好了中断名字,只要我们 使 用 就 可以 了 , 它 一 般 定 义 在 启 动 文 件 中 , 大 家 可 以 打 开startup_stm32f10x_hd.s 查看 264 行之后,都是帮起好的中断函数。 而外部中断 0 的中断函数是 void EXTI0_IRQHandler(void);在这里 大家要注意的是,外部中断虽然同时可以使用 16 个外部中断,但是它并 没有 16 个中断函数。它们分别是:

  

EXTI0_IRQHandler

  

EXTI1_IRQHandler

  

EXTI2_IRQHandler

  

EXTI3_IRQHandler

  

EXTI4_IRQHandler

  

EXTI9_5_IRQHandler

  

EXTI15_10_IRQHandler

  

从上面可以看出,只有从外部中断 0 到外部中断 4 有独立的中断函数, 而从外部中断 5 到外部中断 9 是共用一个中断函数,而外部中断 10 到外部 中断15 共用一个中断函数。而我们如何区分呢? 在进入中断函数之后,我们可以先检测一下你想要的中断标志,如果中 断标志设置了,那么就是你想要的中断设置了,如果没有那么就是不是你想 要的中断。

  

7) EXTI_GetITStatus()函数

  

这个函数可以用来读取你想要的中断标志。它有一个参数,用来选择你 要读取的中断标志位。比如你想要读取 EXTI0 那么就设置为 EXTI_Line0。 同时它还会返回一个参数。 SET (非零): 表示标志位已设置; RESET (0): 表示标志位未设置。 所以我们要读取外部中断 0 的代码为:

  

if (EXTI_GetITStatus(EXTI_Line0))

  

{

  

}

  

8) EXTI_ClearITPendingBit()函数

  

这个函数的作用是用来清除中断标志,外部中断的中断标志要自己软件 手动清除。它有一个输入参数,用来选择你要清除的外部中断。它的参数其 实跟上面的 EXTI_GetITStatus()函数是一样的。

  

1.5 例程程序

  

/******************************************************************************** 函 数 名 : exti_init 1) 初始化函数* 函数功能 : 外部中断 2 端口初始化函数* 输 入 : 无* 输 出 : 无*******************************************************************************/void exti_init() //外部中断初始化{GPIO_InitTypeDef GPIO_InitStructure;EXTI_InitTypeDef EXTI_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;/* 开启 GPIO 时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);GPIO_InitStructure.GPIO_Pin=k_left;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOE,&GPIO_InitStructure);GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);//选择GPIO 管脚用作外部中断线路//此处一定要记住给端口管脚加上中断外部线路/* 设置外部中断的模式 */EXTI_InitStructure.EXTI_Line=EXTI_Line2;EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);/* 设置 NVIC 参数 */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;//打开 EXTI2的全局中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级为 0NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //响应优先级为 0NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能NVIC_Init(&NVIC_InitStructure);}按键端口定义#define k_left GPIO_Pin_2 //K1 PE2

  

2) 中断函数

  

void EXTI2_IRQHandler() //外部中断 2 中断函数{if(EXTI_GetITStatus(EXTI_Line2)==SET){EXTI_ClearITPendingBit(EXTI_Line0);//清除 EXTI 线路挂起位delay_ms(10);//消抖处理if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)==Bit_RESET)//k_left 按键按下{if(GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_0)==Bit_RESET){//LED 熄灭 GPIO_SetBits(GPIOC,GPIO_Pin_0);}else{ //LED 发光GPIO_ResetBits(GPIOC,GPIO_Pin_0);}}while(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)==0);}}

  

这个函数是 K_left 的外部中断函数,在进入中断函数之后先检测相应的中 断的标志是否设置,有人可能觉得这个有点多余,但是从上面我们讲到 中断的时候,我们知道有些时候是几个外部中断共用一个外部中断函数的,所以 我们要读取清楚。做完中断处理,离开中断函数之前,记得要清除相应的中断标 志。

相关文章