STM32基本GPIO操作:按键输入(扫描+外部中断)
(涉及专有名词较多,难免解释不到位,若有错误还请指出,谢谢!)
硬件连接图如下:
一、扫描
思路是在main函数中通过死循环来扫描端口电平状态检测,以此判断按键是否按下。实现较为简单。
1.初始化(注意C语言中变量声明需放在函数开头)
以下是初始化PB5端口(LED灯)的代码,每一条语句的含义在我另一篇博客里
GPIO_InitTypeDef GPIO_Init1;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_Init1.GPIO_Pin = GPIO_Pin_5;
GPIO_Init1.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init1.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_SetBits(GPIOB, GPIO_Pin_5); //先熄灯
GPIO_Init(GPIOB, &GPIO_Init1);
以下是初始化PE3端口(按键)的代码
GPIO_InitTypeDef GPIO_Init2;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
GPIO_Init2.GPIO_Pin = GPIO_Pin_3; // 设置GPIO端口号为5
GPIO_Init2.GPIO_Mode = GPIO_Mode_IPU; // 设置端口模式为输入上拉
// 设置为输入端口时不需要指定GPIO_Speed参数
GPIO_Init(GPIOE, &GPIO_Init2);
输入上拉与输入下拉的区别:
输入上拉(GPIO_Mode_IPU):端口与VCC通过一个电阻串连,因此没有输入或输入高电平时端口为高电平,输入低电平时端口为低电平
输入下拉(GPIO_Mode_IPD):端口与GND通过一个电阻串连,因此没有输入或输入低电平时端口为低电平,输入高电平时端口为高电平
从硬件图上得知按键与GND相连,如果端口设置为输入上拉,那么松开按键时端口为高电平,按下按键时端口为低电平,可以区分两种状态
如果端口设置为输入下拉,那么无论是按下还是松开按键时端口总为低电平,无法区分两种状态
类似地,如果按键与VCC相连,则端口需要设置为输入下拉才能区分按下/松开两种状态
2.扫描
读取PE3端口的状态:
GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)
返回值为SET则端口为高电位,返回值为RESET则端口为低电位
在main函数中放入以下死循环代码以实现扫描PE3端口并点灯的功能
while (1)
{
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==SET) // 如果按键对应端口为高电平
{
GPIO_SetBits(GPIOB, GPIO_Pin_5); // 熄灯(LED负极连接PB5,LED正极连接VCC,PB5高电平熄灯)
}
else // 否则
{
GPIO_ResetBits(GPIOB, GPIO_Pin_5); // 亮灯
}
delay_ms(10); // 一些开发板例程当中提供了delay函数,需要通过delay_init()初始化后才可使用
// 若无现成delay函数,可通过一定次数的for循环来代替
}
3.例程
代码如下:
int main(void)
{
GPIO_InitTypeDef GPIO_Init1, GPIO_Init2;
delay_init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_Init1.GPIO_Pin = GPIO_Pin_5;
GPIO_Init1.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init1.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_Init1);
GPIO_SetBits(GPIOB, GPIO_Pin_5); //先熄灯
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
GPIO_Init2.GPIO_Pin = GPIO_Pin_3;
GPIO_Init2.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOE, &GPIO_Init2);
delay_ms(200);
while (1)
{
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==SET)
{
GPIO_SetBits(GPIOB, GPIO_Pin_5);
}
else
{
GPIO_ResetBits(GPIOB, GPIO_Pin_5);
}
delay_ms(10);
}
}
二、中断
0.相关概念
中断:程序运行过程中,系统外部、系统内部或者现行程序本身若出现紧急事件,处理机立即中止现行程序的运行,自动转入相应的处理程序(中断服务程序),待处理完后,再返回原来的程序运行
简而言之就是触发某一事件可以使得MCU跳转执行该事件的处理程序,而按键按下或放开(GPIO口电平改变)则可作为一个外部中断,通过编写这一事件的处理程序从而达到改变灯亮灭状态的目的
(这里提到的“事件”并不是STM32当中的专有名词“事件”,而是泛指发生了某一件事)
使用扫描方式获得按键输入的思路如下:
主函数()
{
初始化()
死循环
{
如果(按键按下)
{……}
否则
{……}
}
}
而使用中断获得按键输入的思路如下:
主函数()
{
初始化()
其它操作()
}
中断处理函数()
{
如果(按键按下)
{……}
否则
{……}
}
对比可知使用扫描方式将使得芯片无法(难以)处理其它事务
NVIC:全名为“内嵌向量中断控制器”,主要用来控制芯片中各个中断的优先级,在很多地方都会使用(串口通信、SPI通信、定时器、I?C通信等涉及到实时处理的功能都会与中断有关)
EXTI(不是EXIT):全名为“外部中断/事件控制器”,可以实现输入信号的上升沿检测和下降沿的检测
1.初始化(注意C语言中变量声明需放在函数开头)
1.1 NVIC
需要用到的初始化语句如下:
NVIC_InitTypeDef NVIC_I; //定义初始化结构体
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置整个系统的中断优先级分组
NVIC_I.NVIC_IRQChannel=EXTI3_IRQn; //设置初始化哪个中断
NVIC_I.NVIC_IRQChannelPreemptionPriority=0x02; //设置中断抢占优先级
NVIC_I.NVIC_IRQChannelSubPriority=0x02; //设置中断响应优先级(子优先级)
NVIC_I.NVIC_IRQChannelCmd=ENABLE; //中断使能(启动)
NVIC_Init(&NVIC_I); //初始化
中断优先级分组、抢占优先级和子优先级的关系:
STM32系列的芯片当中一般会有很多的中断,而当多个中断同时发生时就需要一个调度机制来控制它们的执行顺序,因此有了中断的优先级的概念。优先级遵循以下几点:
1.优先级的数字越小优先级越高
2.抢占优先级高的中断会先执行,它也可以打断抢占优先级低的中断
3.当抢占优先级相同时,响应优先级高的中断会先执行,但它不可以打断响应优先级低的中断
4.当两个中断的抢占优先级和响应优先级都相同时,先产生的中断先执行(按照时间顺序)
举个例子,现在有三个中断:
中断1:抢占优先级为2,响应优先级为1
中断2:抢占优先级为3,响应优先级为0
中断3:抢占优先级为2,响应优先级为0
则3个中断的优先级顺序是中断3>中断1>中断2,同时中断1、3可以打断中断2,中断3不能打断中断1
而两类优先级可以设置成哪些值呢?这取决于整个系统的中断优先级分组。通过
NVIC_PriorityGroupConfig();
可以设置整个系统的中断优先级分组,其参数可以是NVIC_PriorityGroup_0、NVIC_PriorityGroup_1、NVIC_PriorityGroup_2、NVIC_PriorityGroup_3、NVIC_PriorityGroup_4之一。具体关系如下:
NVIC_PriorityGroup_0:0位抢占优先级(无效)+4位响应优先级(0~15)
NVIC_PriorityGroup_1:1位抢占优先级(01)+3位响应优先级(07)
NVIC_PriorityGroup_2:2位抢占优先级(03)+2位响应优先级(03)
NVIC_PriorityGroup_3:3位抢占优先级(07)+1位响应优先级(01)
NVIC_PriorityGroup_4:4位抢占优先级(0~15)+0位响应优先级(无效)
例如中断分组设置为3,则所有中断的抢占优先级可以被设置为0~7,响应优先级可以被设置为0、1
需要注意的是如果程序中用到了二次封装的一些库(比如开发板例程中厂商为初学者写的串口库等),则NVIC_PriorityGroupConfig()可能已经被调用过了,此时再次修改可能会带来其它问题
1.2 EXTI
需要用到的初始化语句如下:
EXTI_InitTypeDef EXTI_I; //定义初始化结构体
EXTI_I.EXTI_Line=EXTI_Line3; //设置初始化哪条中断/事件线
EXTI_I.EXTI_Mode = EXTI_Mode_Interrupt; //设置为产生中断(EXTI_Mode_Event为产生事件)
EXTI_I.EXTI_Trigger = EXTI_Trigger_Falling; //设置为下降沿触发
EXTI_I.EXTI_LineCmd = ENABLE; //使能
EXTI_Init(&EXTI_I); //初始化
中断/事件的区别:
中断产生后经由NVIC交给MCU进行处理(软件层面)
事件最终作为一个脉冲信号直接触发其它硬件(硬件层面)
附一张EXTI的框图便于理解,蓝色是中断,红色是事件
EXTI、NVIC与GPIO的对应关系:
如图所示
上升/下降沿:
低电平跳到高电平为上升沿,高电平跳到低电平为下降沿
1.3 GPIO
与扫描方式的初始化代码相同
GPIO_InitTypeDef GPIO_Init1, GPIO_Init2;
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_Init1.GPIO_Pin = GPIO_Pin_5;
GPIO_Init1.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init1.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_Init1);
GPIO_SetBits(GPIOB, GPIO_Pin_5); //先熄灯
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
GPIO_Init2.GPIO_Pin = GPIO_Pin_3;
GPIO_Init2.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOE, &GPIO_Init2);
1.4 其它
目前不明确这两条语句的作用
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //开启端口复用,涉及到GPIO口做外部中断时都需要这一条语句
2.中断处理函数
函数名与中断/事件线有着对应关系,可参照上一张表
void EXTI3_IRQHandler(void)
{
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==SET)
{
GPIO_SetBits(GPIOB, GPIO_Pin_5);
}
else
{
GPIO_ResetBits(GPIOB, GPIO_Pin_5);
}
EXTI_ClearITPendingBit(EXTI_Line3);
}
最后的EXTI_ClearITPendingBit()用于清除中断标志位,避免对之后的中断造成影响
3.例程
代码如下:
int main(void)
{
GPIO_InitTypeDef GPIO_Init1, GPIO_Init2;
NVIC_InitTypeDef NVIC_I;
EXTI_InitTypeDef EXTI_I;
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
NVIC_I.NVIC_IRQChannel=EXTI3_IRQn;
NVIC_I.NVIC_IRQChannelPreemptionPriority=0x02;
NVIC_I.NVIC_IRQChannelSubPriority=0x02;
NVIC_I.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_I);
EXTI_I.EXTI_Line=EXTI_Line3;
EXTI_I.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_I.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_I.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_I);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_Init1.GPIO_Pin = GPIO_Pin_5;
GPIO_Init1.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init1.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_Init1);
GPIO_SetBits(GPIOB, GPIO_Pin_5); //先熄灯
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
GPIO_Init2.GPIO_Pin = GPIO_Pin_3;
GPIO_Init2.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOE, &GPIO_Init2);
}
void EXTI3_IRQHandler(void)
{
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==SET)
{
GPIO_SetBits(GPIOB, GPIO_Pin_5);
}
else
{
GPIO_ResetBits(GPIOB, GPIO_Pin_5);
}
EXTI_ClearITPendingBit(EXTI_Line3);
}
2019.12.22
STM32基本GPIO操作:按键输入(扫描+外部中断)的更多相关文章
- STM32之GPIO操作
啊哈.没办法.外国人的芯片就喜欢用英文来命名,所以中文的:通用输入/输出 就用GPIO来代替..谁叫哥们都不是外国人呢.好啦.胡扯了一下,借用唐伯虎点秋香的话:小小书童,可笑可笑... 知道了GPI ...
- STM32基本GPIO操作:点灯(库函数+寄存器)
社团作业=_= 开发版上的LED灯负极连接在PB5口,正极串联一510Ω电阻后与3.3V相连 若开发板不带LED灯则需要自行连接,务必串联一个合适的电阻防止LED灯烧坏 零.一个有趣的延时函数 来自于 ...
- STM32 常用GPIO操作函数记录
STM32读具体GPIOx的某一位是1还是0 /** * @brief Reads the specified input port pin. * @param GPIOx: where x can ...
- STM32外部中断具体解释
一.基本概念 ARM Coetex-M3内核共支持256个中断,当中16个内部中断,240个外部中断和可编程的256级中断优先级的设置.STM32眼下支持的中断共84个(16个内部+68个外部), ...
- STM32(3)——外部中断的使用
1 .简介 ARM Coetex-M3内核共支持256个中断,其中16个内部中断,240个外部中断和可编程的256级中断优先级的设置.STM32目前支持的中断共84个(16个内部+68个外部),还有1 ...
- stm32 外部中断学习
今天我们看看STM32的外部中断实验. STM32 供 IO 口使用的中断线只有 16 个,但是 STM32 的 IO 口却远远不止 16 个,那么 STM32 是怎么把 16 个中断线和 IO 口一 ...
- STM32外部中断初理解
PA0,PB0...PG0--->EXTI0 PA1,PB1...PG1--->EXTI1 ....... PA15,PB15...PG15--->EXTI15 以上为GPIO和中断 ...
- STM32学习笔记(九) 外部中断,待机模式和事件唤醒
学会知识只需要不段的积累和提高,但是如何将知识系统的讲解出来就需要深入的认知和系统的了解.外部中断和事件学习难度并不高,不过涉及到STM32的电源控制部分,还是值得认真了解的,在本文中我将以实际代码为 ...
- stm32学习笔记——外部中断的使用
stm32学习笔记——外部中断的使用 基本概念 stm32中,每一个GPIO都可以触发一个外部中断,但是,GPIO的中断是以组为一个单位的,同组间的外部中断同一时间只能使用一个.比如说,PA0,PB0 ...
随机推荐
- 机器学习回顾篇(11):支持向量机(SVM)
.caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px so ...
- 正则grep 使用介绍
第6周第3次课(4月25日) 课程内容: 9.1 正则介绍_grep上9.2 grep中9.3 grep下扩展把一个目录下,过滤所有*.php文档中含有eval的行grep -r --include= ...
- openssl的移植
下载openssl1.1并解压,进入openssl根目录,执行配置命令 ./Configure linux-armv4 --prefix=$(pwd)/__install 这里使用当前目录下的__in ...
- 《Windows内核安全与驱动开发》 7.1&7.2&7.3 串口的过滤
<Windows内核安全与驱动开发>阅读笔记 -- 索引目录 <Windows内核安全与驱动开发> 7.1&7.2&7.3 串口的过滤 一.设备绑定的内核API ...
- Python列表中的字典按照该字典下的键值进行排序
列表中的字典按照该字典下的键值进行排序 这算是排序中比较复杂的一种情况吧,多重嵌套,按照某种规则进行排序.如下面这个json(注:这里这是该列表中的一个项): [ { "stat" ...
- Android多图选择
多图选择是Android中一个常用的功能,用户可以拍照或者批量选择图片上传,还是国际惯例,先看下效果图,demo地址我会放到文章末尾. 经过对比,这里我选择了一个第三方开源库PictureSelect ...
- Statistics : Data Distribution
1.Normal distribution In probability theory, the normal (or Gaussian or Gauss or Laplace–Gauss) dist ...
- HDU2767 Proving Equivalences(加边变为强联通图)
Proving Equivalences Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth ...
- HDU5919 Sequence II(主席树)
Mr. Frog has an integer sequence of length n, which can be denoted as a1,a2,⋯,ana1,a2,⋯,anThere are ...
- BZOJ2440完全平方数(莫比乌斯反演)
Description 小 X 自幼就很喜欢数.但奇怪的是,他十分讨厌完全平方数.他觉得这些数看起来很令人难受.由此,他也讨厌所有是完全平方数的正整数倍的数.然而这丝毫不影响他对其他数的热爱. 这天是 ...