您的位置:首页 > 其它

STM32简单入门程序分析之GPIO,外部中断

2014-06-13 07:57 483 查看
首先来讲一讲GPIO的相关初始化:

相关程序是从上一篇文章摘取,做了简单的介绍

在做介绍之前我们先来做一个小小的有趣总结,STM32库文件的设计非常的人性化,比如你要是用IIC,那么一定有个函数叫做什么IIC_INIT之类的,这样你就可以在stm32f10x_iic.c里面去查找,当然具体文件我只是在这里举例子,

就像下面的GPIO,那必然有个函数叫做GPIO_Init();这个你用sourceInsight,一下就能够搜索出来,好了来看看下面的GPIO初始化吧。

首先,我们现在库函数里面找到GPIO_Init(。。。)这个函数,看看它需要我们给它什么参数,参数的范围是那些。

进入到GPIO_Init函数里面,我们会看到下面三个函数,这个就是STM32强大之一了,

assert_param(IS_GPIO_ALL_PERIPH(GPIOx));

assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));

assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));

第一个assert_param里面主要是限制的是GPIOA,还是GPIOB....,

点击一下进去看看:

#define IS_GPIO_ALL_PERIPH(PERIPH) (((PERIPH) == GPIOA) || \

((PERIPH) == GPIOB) || \

((PERIPH) == GPIOC) || \

((PERIPH) == GPIOD) || \

((PERIPH) == GPIOE) || \

((PERIPH) == GPIOF) || \

((PERIPH) == GPIOG))

所以可以很清晰的看到我们的参数只能够是里面的其中一个。

好了,看看下一个assert_param里面有什么,点击IS_GPIO_MODE,进去看看,

#define IS_GPIO_MODE(MODE) (((MODE) == GPIO_Mode_AIN) || ((MODE) == GPIO_Mode_IN_FLOATING) || \

((MODE) == GPIO_Mode_IPD) || ((MODE) == GPIO_Mode_IPU) || \

((MODE) == GPIO_Mode_Out_OD) || ((MODE) == GPIO_Mode_Out_PP) || \

((MODE) == GPIO_Mode_AF_OD) || ((MODE) == GPIO_Mode_AF_PP))

这里就看到了,这个是设置GPIO的模式,就是我们需要将我们将要使用的GPIO配制成什么模式,下面就简单介绍一下,网上也有很多相关介绍,笔者这里就小小抄袭一下,希望不要吐槽:


GPIO_Mode_AIN:模拟输入,用ADC的时候就需要将管脚配制成这种模式

GPIO_Mode_IN_FLOATING:浮空输入,什么是浮空,就是什么都没有,如果要检测关键外部电平的话,希望外部有上拉或者下拉电路来配合。

GPIO_Mode_IPD:下拉输入,也就是STM32内部将管教配置成这种模式,也就是外部没有电平的时候该管脚检测到的是低电平,因为被拉低了。

GPIO_Mode_IPU:上拉输入,意思和上面差不多,只是条件恰好相反。

GPIO_Mode_Out_OD:开漏输出

GPIO_Mode_Out_PP:推挽输出

GPIO_Mode_AF_OD:复用开漏输出

GPIO_Mode_AF_PP:复用推挽输出

那么来看看第三个assert_param里面有什么,点击IS_GPIO_PIN,很明显,是让我们设置具体哪一个引脚,我想这个我就不用说了吧,聪明的你肯定知道了。

来看看程序:
//定义管脚定义结构体

GPIO_InitTypeDef GPIO_INITStructure;

//定义为输出模式

GPIO_INITStructure.GPIO_Mode = GPIO_Mode_Out_PP;

//指定需要初始化哪个管脚

GPIO_INITStructure.GPIO_Pin = GPIO_Pin_8;

//指定IO口的速度

GPIO_INITStructure.GPIO_Speed = GPIO_Speed_50MHz;

//其实上面的所需要的变量都可以在下面这个

//函数里面找到答案

//assert_param(IS_GPIO_ALL_PERIPH(GPIOx));//限定需要初始化哪一组IO口

//assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));//定义管脚的模式

//assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin)); //定义是哪一个管脚

GPIO_Init(GPIOC, &GPIO_INITStructure);

好了,上面就完整滴设置了一个IO管脚,下面我们来看看其他有意思的东西。

现在笔者在这里提出一个问题,怎样使用外部中断,如果对于初学者,就像笔者这样的人来说,一头雾水,但是不要害怕,我们先大胆想一想,外部中断,不就是拿一个管脚来检测管脚的电平变化吗?对啊,那么我们需要做哪些事情呢:

①是外部中断,那么管脚肯定是输入啦

②打开中断,就像我们上面说的一样

好了,我们暂且在这里停下,因为对于初学者来说,大概也只能够想到这里,根本不会一下子想到什么NVIC这些玩意儿。那么笔者简单帮大家梳理下基本流程:

①管脚初始化,当然输输入,这里笔者用的是上拉输入

②将对应管脚配置成中断,并且配置对应的中断

③中断优先级配置,这个是STM32的特有

④编写中断处理函数

在这里很多人还是云里雾里,这里读者先介绍下STM32的中断,牛逼的STM32的每个管脚都可以作为中断管脚,但是很多人看了DATASHEET发现只有19个外部中断啊,下面是中断相关知识:

0~15:对应16个IO口输入中断

16:PVD中断,这个是主电掉电中断,后面介绍

17:RTC中断,后面介绍

18:USB唤醒中断,后面介绍

从上面可以看出,IO外部中断只有16个,但是刚刚说了,每个IO都可以作为中断啊,那是怎么回事呢,原来STM32可以将每个中断进行配置,也就是中断线的配置,比如我将外部中断3对应到GPIOA的PIN3去,就应该使用一个库函数叫做,GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource3);这样就完成了中断线的配置,待会儿我们看具体的程序,看看具体怎么做吧,我会从IO配置到NVIC都罗列出来供大家参考:

①IO初始化

GPIO_InitTypeDef GPIO_INITStructure;

GPIO_INITStructure.GPIO_Mode = GPIO_Mode_IPU;//配制成上拉输入

GPIO_INITStructure.GPIO_Pin = GPIO_Pin_8;

GPIO_INITStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB , &GPIO_INITStructure);

简单介绍:上面完成了IO的初始化,将GPIOB 的pin8配制成了上拉输入,这是外部中断第一个需要配置的地方

②对初学者很难理解的中断线初始化

EXTI_InitTypeDefEXTI_InitStructure;//定义中断所需要的结构体,就像串口初始化

EXTI_DeInit();//复位外部中断

//初始化外部中断 8

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource8);//将中断线8配置到GPIOB-8管脚

EXTI_InitStructure.EXTI_Line = EXTI_Line8;//指定配置外部中断8

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//配制成中断模式

EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//配制成下降沿处罚

EXTI_Init(&EXTI_InitStructure);//将结构体给EXTI_Init函数

/*****************************************************************/

这已是第二天早上七点十九分,现在继续写。。。世界杯。。。

/*****************************************************************/

③初始化NVIC

首先说明一下,只要使用到中断,不关你事PVD中断,还是USB中断,还是串口中断,嘿嘿,我们都不能忘掉NVIC中断优先级管理单元。

首先让我们看看上面这幅图,这就是STM32的中断优先级管理分组,首先我们来声明下:分组,只能够选择一次,所有的中断配置都必须在这个分组范围内:

举例:我们如果选择分组1,那么抢占优先级由一位表示,很明显,值为0或者1,响应优先级由三位表示,那么就是总共有8个,0~7,好了现在说说什么事抢占优先级和响应优先级,比如现在正在运行抢占优先级1的中断,现在抢占优先级为0的中断触发了,需要响应,那么正在运行的抢占优先级1中断将会靠边站,等到优先级0的中短线处理完之后继续运行,如果两个终端的抢占优先级相同,不同的是响应优先级,那么如果两个中断同时到来,那么将会优先响应响应优先级高的中断,如果抢占优先级和响应优先级都是相同的,那么只能够排队咯。

好了,说多了,现在看看程序:

NVIC_InitTypeDef
NVIC_INITStruture;//定义NVIC结构体

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//定义为组2,也就是2抢占位,2响应位

NVIC_INITStruture.NVIC_IRQChannel = USART1_IRQn;//这里同时配置了串口的优先级,注意这里的参数

NVIC_INITStruture.NVIC_IRQChannelPreemptionPriority = 1;//配置抢占优先级

NVIC_INITStruture.NVIC_IRQChannelSubPriority = 0;//配置响应优先级

NVIC_INITStruture.NVIC_IRQChannelCmd = ENABLE;//使能中断线

NVIC_Init(&NVIC_INITStruture);//初始化刚刚配置的串口中断

NVIC_INITStruture.NVIC_IRQChannel = EXTI9_5_IRQn;//初始化外部中断8,这里小小说明下, 可以从源码看到,中断8属于EXTI9_5_IRQn

NVIC_INITStruture.NVIC_IRQChannelPreemptionPriority = 2;

NVIC_INITStruture.NVIC_IRQChannelSubPriority = 0;

NVIC_INITStruture.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_INITStruture);

好了,该讲的都讲了,初始化我们就搞定了,如果去掉上面那些没用的文字,其实代码只有几行,其实这个不是很重要,重要的是我们能不能把我这个初始化流程。

上一篇文章并没有将中断的处理函数写出来,现在笔者将在这里将中断处理函数写出来,如下:

extern unsigned char key_value ;

//外部中断

void EXTI9_5_IRQHandler(void)

{

if(SET == EXTI_GetFlagStatus(EXTI_Line8)) {

EXTI_ClearFlag(EXTI_Line8);

key_value = 1;

}else if(SET == EXTI_GetFlagStatus(EXTI_Line9)) {

EXTI_ClearFlag(EXTI_Line9);

key_value = 2;

}

}

说明:

①EXTI9_5_IRQHandler这个中断入口函数是别人定义好了的,不要自己突发奇想,这个我们怎么找到呢

这个是在Startup_stm32f10x_hd.s里面有定义,如上图所示。可能有些初学者就疑惑了,给个名字不给入口参数不是白给??这里笔者小说一下,外部中断返回值和入口参数都是void。

刚刚说了,我们要使用的EXTI_8属于EXTI9_5_IRQHandler里面,也就是说这个函数包括了5-9这几个中断。那么我们怎么区分是我们要的中断8呢?

FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);就是它,我们用它实现选择判断,然后,就没有然后了,该说的都说了。

如果需要源代码的同志直接联系我,谢谢支持哦,由于要上班了,今天晚上更新用TICK做精确延时。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: