在学习了GPIO和SYSTICK之后,我来开始按键的操作,其中按键的控制可以分为两种情况:按键扫描和按键中断。按键扫描是用cpu对按键的情况进行不断扫描,检测到按键变化执行按键控制。按键中断是一但出现IO口的变化就触发中断,执行中断内的程序。相比按键扫描,按键中断可以大幅节省cpu效率。
下面我就开始进行按键实验。
首先,我们找到按键的位置,按照官方给的PCB文件可以容易的找到
按键是被挂在PC13管脚上的,根据以前我们的经验我们已经能控制LED灯的亮灭了,所以,今天控制按键就用LED的亮灭表示,按键时LED的亮灭反转。关于GPIO控制LED的方法:点这里。
- 按键扫描
按键扫描就只是多了一个检测按键的函数,一旦检测到按键变化就对其进行LED的的反转。关于按键的函数如下:
#include "KEY.h"
void Key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOC ,GPIO_Pin_13);
}
void Delay(uint32_t uCount)
{
for(;uCount>0;uCount--);
}
uint8_t KeyDown(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin)==0)//检测按键是否被按下
{ Delay(1000); //消抖
if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin)==0)
{
while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin)==0);//等待按键释放
return 0;
}
else
return 1;
}
else
return 1;
}
此函数可以对按键的IO口进行配置,并对按键状态不断扫描,检测按键的状态,通过主函数对其调用就可以实现按键的扫描并进行反转LED。
主函数:
#include "stm32f0xx.h"
#include "KEY.h"
#include "LED.h"
#include "main.h"
int main(void)
{
Led_Init();
Key_Init();
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
while (1)
{
if(KeyDown(GPIOC,GPIO_Pin_13)==0)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_5 ,
(BitAction)((1-GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5))));
}
}
}
这是实现按键反转LED的一种方法。
- 外部中断
中断是指由于接收到来自外围硬件(相对于中央处理器和内存)的异步信号或来自软件的同步信号,而进行相应的硬件/软件处理。发出这样的信号称为进行中断请求(interrupt request,IRQ)。硬件中断导致处理器通过一个上下文切换(context switch)来保存执行状态(以程序计数器和程序状态字等寄存器信息为主);软件中断则通常作为CPU指令集中的一个指令,以可编程的方式直接指示这种上下文切换,并将处理导向一段中断处理代码。中断在计算机多任务处理,尤其是实时系统中尤为有用,这样的系统,包括运行于其上的操作系统,也被称为“中断驱动的”。简单的来说就比如某个人正在做某事,突然来了个电话,他就要停下手中的事情去接电话,中断相当于这个电话。触发中断后跳出原来运行的程序去执行中断处理。
下面我们来写一下这个外部中断程序:
#include "exti.h"
void EXTI_KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC,ENABLE);
NVIC_InitStruct.NVIC_IRQChannel=EXTI4_15_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPriority=1;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStruct);
GPIO_InitStruct.GPIO_Pin=13;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_Level_2;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP;
GPIO_Init(GPIOC,&GPIO_InitStruct);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC,EXTI_PinSource13);
EXTI_InitStruct.EXTI_Line=EXTI_Line13;
EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStruct);
}
主函数:
#include "main.h"
#include "LED.h"
#include "exti.h"
int main(void)
{
SystemInit();
Led_Init();
GPIO_ResetBits(GPIOA ,GPIO_Pin_5);
EXTI_KEY_Init();
while (1)
{
}
}
stm32f0xx_it.c中的中断配置函数:
void EXTI4_15_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line13)!=RESET)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_5 ,
(BitAction)((1-GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5))));
EXTI_ClearFlag(EXTI_Line13);
}
}
现在让我们来看一下这个程序,在外部中断函数中,我们首先要对外部中断用到的按键所连接的GPIO端口和管理连接到 GPIO 口的外部中断的SYSCFG的时钟打开,然后对嵌套向量中断控制器 (NVIC)进行设置。
由于在STM32的库函数中有stm32f0xx_misc.h的第54行有如下定义:
typedef struct
{
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPriority;
FunctionalState NVIC_IRQChannelCmd;
} NVIC_InitTypeDef;
这个结构体对NVIC的配置通道和优先级及使能进行了定义,设置中断通道为4—15通道,优先级为1,使能通道。然后对按键的GPIO进行定义,设置为上拉输入,再对EXTI进行操作,和NVIC一样,在stm32f0x_exti.h的第79行也有如下定义:
typedef struct
{
uint32_t EXTI_Line;
EXTIMode_TypeDef EXTI_Mode;
EXTITrigger_TypeDef EXTI_Trigger;
FunctionalState EXTI_LineCmd;
}EXTI_InitTypeDef;
由图可知
配置为外部中断线程为13,模式为外部中断,下降沿中断,并且在SYSCFG中有管理连接到 GPIO 口的外部中断的功能,所以在函数:
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex)
{
uint32_t tmp = 0x00;
/* Check the parameters */
assert_param(IS_EXTI_PORT_SOURCE(EXTI_PortSourceGPIOx));
assert_param(IS_EXTI_PIN_SOURCE(EXTI_PinSourcex));
tmp = ((uint32_t)0x0F) << (0x04 * (EXTI_PinSourcex & (uint8_t)0x03));
SYSCFG->EXTICR[EXTI_PinSourcex >> 0x02] &= ~tmp;
SYSCFG->EXTICR[EXTI_PinSourcex >> 0x02] |= (((uint32_t)EXTI_PortSourceGPIOx) << (0x04 * (EXTI_PinSourcex & (uint8_t)0x03)));
}
中可以对EXTICR寄存器进行操作,打开对应管脚的外部中断。
主函数很简单。在此不多做解释。
在解释下stm32f0xx_it.c中的中断配置函数,中断配置函数必须和.s文件中的函数一致,不能改动,笔者在此吃了很大的亏,由于和.s文件中的函数不一样,不能进入中断,系统只能将其作为一个普通函数,不具有中断功能,切记!!!在函数中一旦读取中断通道变化,就会对LED进行反转,实现按键中断。