请问GPIO的默认电平在哪儿?是参考手册的复位电平吗? - STM32 - 意法半导体STM32/STM8技术社区
后使用快捷导航没有帐号?
查看: 5239|回复: 6
请问GPIO的默认电平在哪儿?是参考手册的复位电平吗?
在线时间81 小时
该用户从未签到主题帖子精华
高级会员, 积分 819, 距离下一级还需 181 积分
LED& &配置了上拉&&,配置完默认是亮的
KEY上面那个wakeup配置了下拉,另外三个配置上拉,
为什么这么配置啊,反过来配置行不
网上查了一下,
上拉表示引脚内部接了一个电阻,然后接高电平
所以灯亮了
可是我给LED配置下拉也会默认亮的,
我查到资料说GPIO有默认电平。。。
请问一下,应该怎么解释呢?
QQ图片13.jpg (12.33 KB, 下载次数: 0)
22:43 上传
            
      
在线时间43 小时
该用户从未签到主题帖子精华
高级会员, 积分 609, 距离下一级还需 391 积分
STM32关键初始化后默认是低电平。其次,你回去好好复习下电路基础知识。你图上的LED要点亮还是熄灭,只是跟你LED0标号的地方是高电平还是低电平决定的。
在线时间286 小时
ST金币2882
该用户从未签到主题帖子精华
金牌会员, 积分 4786, 距离下一级还需 214 积分
MCU在复位后GPIO的状态一般为高阻态,电平高低由外部电路决定
在对GPIO初始化后其状态即确定
            
      
在线时间81 小时
该用户从未签到主题帖子精华
高级会员, 积分 819, 距离下一级还需 181 积分
MCU在复位后GPIO的状态一般为高阻态,电平高低由外部电路决定
在对GPIO初始化后其状态即确定 ...
谢谢回复,我是跨行业的,之前没接触过电路,只是会C,
请问一下,我发的图是否可以看出来?
在线时间862 小时
ST金币2344
该用户从未签到主题帖子精华
配置完后,将IO设置书输出高。
            
      
在线时间5 小时
该用户从未签到主题帖子精华
初级会员, 积分 99, 距离下一级还需 101 积分
默认初始化的电平为0
在线时间3 小时
该用户从未签到主题帖子精华
初级会员, 积分 65, 距离下一级还需 135 积分
复位默认低电平吧
            
      
STM32粉丝勋章Ⅳ
狂欢节专属(智多星)
STM32粉丝勋章Ⅱ
狂欢节专属(研讨会问答)
站长推荐 /2
Tel: 3-8064
备案号: 苏ICP备号-2
|||意法半导体STM32/STM8技术社区
Powered by对于STM32 GPIO模式不理解造成的问题解释
今天在编写Flash的程序时候,在最后,所有的程序都已经编写完成,经过测试,没有问题。接下来就是整理程序,自己将一些函数封装,可以便于自己以后移植,但是问题就出现在了这儿。
我在封装的过程中,突然发现自己如果将SPI模式初始化函数放置在 CS引脚初始化后面,接下来直接读取 Flash芯片ID的时候使用Jlink读取的数据就不能显示正常,显示数据为0xFFFF,但是,如果我将SPI模式初始化函数放置在 CS引脚初始化前面,整个程序运行是正常的。
真的是百思不得其解,我当时在想,不应该是CS初始化的影响,但是自己真的是没找见问题,最后,就到21IC上面去求助,当时我写完问题之后,接下来做什么?
接着自己找问题呗,还能做什么。
当时我又重新阅读我写的程序,突然发现我好像在初始化CS引脚的时候,没有设置输出速率(当时的我是这么认为 端口x的模式位的),唉,那就初始化呗,结果我初始化之后,我再将SPI模式初始化函数放置在 CS引脚初始化后面。结果程序正常运行!
当时就傻眼了,自己觉的这个输出速率不应该影响这么大吧,自己查找数据手册,仔细阅读关于GPIO设置这块的资料。结果才发现,原来自己以前真的是想错了。
下面上图,数据手册中关于GPIO模式初始化函数的说明:
看到这张表的时候我就知道自己是错了,原来,这个MODE是GPIO的输出模式位,只有选择了这个位,才能确定GPIO的输入输出特性。只有选择为输出模式,才能接下来配置为推挽、开漏、推挽复用、开漏复用模式。同样,配置为输入模式,才能配置为模拟、浮空、上拉、下拉输入模式。
STM32在上电复位之后,默认的输出模式时 00(也就是输入模式),所以,我上面没有设置CS引脚的输出模式,才造成了CS引脚不起作用。
在仔细阅读关于STM32库函数实现方法之后,我发现,STM32库函数中对于GPIO使用的是一种更为精妙的方法。
先上源码:首先是看一下关于STM32 GPIO结构体内部:
分为三个结构体变量:GPIO_Pin,GPIO_Speed,GPIO_Mode。
其中,GPIO_Speed和GPIO_Mode同时也是一个结构体。分别初始化GPIO引脚的输出速率和模式。
接下来,看一下GPIO_Speed可以选择的取值,明显发现这个的参数的设置,就是关于STM32 GPIO引脚的输出速率设置。
typedef enum
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeD
接下来,看一下GPIO_Mode的取值:
typedef enum
{ GPIO_Mode_AIN = 0x0,
GPIO_Mode_IN_FLOATING = 0x04,
GPIO_Mode_IPD = 0x28,
GPIO_Mode_IPU = 0x48,
GPIO_Mode_Out_OD = 0x14,
GPIO_Mode_Out_PP = 0x10,
GPIO_Mode_AF_OD = 0x1C,
GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeD
在阅读数据手册的时候发现,如果是直接才做寄存器,对于GPIO模式的控制位只有2位,所以,是不会出现0x14,0x10,0x1C,0x18这些取值的。这里,如果想知道为什么库函数中会这样的设置,就必须接着看GPIO_Init函数。
currentmode = ((uint32_t)GPIO_InitStruct-&GPIO_Mode) & ((uint32_t)0x0F);
if ((((uint32_t)GPIO_InitStruct-&GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
/* Check the parameters */
assert_param(IS_GPIO_SPEED(GPIO_InitStruct-&GPIO_Speed));
/* Output mode */
currentmode |= (uint32_t)GPIO_InitStruct-&GPIO_S
对于GPIO_Init函数,传递进来的参数,先通过mode参数,设置了GPIO模式参数,再接着根据传递的mode的值,判断此GPIO设置为输入模式还是输出模式,如果是输出模式,才会进行设置GPIO的速率,如果是输入模式,忽略掉速率的设置。
没有更多推荐了,一歩ずつ歩み続けてはじめて千里の遠くに行き着けるし
STM32 GPIO各种模式的深入理解
GPIO的各种模式
GPIO管脚可以被配置为多种工作模式,其中有3种比较常用:高阻输入、推挽输出、开漏输出
1. 高阻输入(Input)
11:00:57 上传
图1.1 GPIO高阻输入模式结构示意图
为减少信息传输线的数目,大多数计算机中的信息传输线采用总线形式,即凡要传输的同类信息都在同一组传输线,且信息是分时传送的。在计算机中一般有三组总线,即数据总线、地址总线和控制总线。为防止信息相互干扰,要求凡挂到总线上的寄存器或存储器等,它的输入输出端不仅能呈现0、1两个信息状态,而且还应能呈现第三个状态----高阻抗状态,即此时好像它们的输出被开关断开,对总线状态不起作用,此时总线可由其他器件占用。三态缓冲器即可实现上述功能,它除具有输入输出端之外,还有一控制端。
如图1.1所示,为GPIO管脚在高阻输入模式下的等效结构示意图。这是一个管脚的情况,其它管脚的结构也是同样的。输入模式的结构比较简单,就是一个带有施密特触发输入(Schmitt-triggered input)的三态缓冲器(U1),并具有很高的输入等效阻抗。施密特触发输入的作用是能将缓慢变化的或者是畸变的输入脉冲信号整形成比较理想的矩形脉冲信号。执行GPIO管脚读操作时,在读脉冲(Read Pulse)的作用下会把管脚(Pin)的当前电平状态读到内部总线上(Internal Bus)。在不执行读操作时,外部管脚与内部总线之间是隔离的。
2. 推挽输出(Output)
11:00:58 上传
图1.2 GPIO推挽输出模式结构示意图
推挽输出原理:在功率放大器电路中大量采用推挽放大器电路,这种电路中用两只三极管构成一级放大器电路,两只三极管分别放大输入信号的正半周和负半周,即用一只三极管放大信号的正半周,用另一只三极管放大信号的负半周,两只三极管输出的半周信号在放大器负载上合并后得到一个完整周期的输出信号。
推挽放大器电路中,一只三极管工作在导通、放大状态时,另一只三极管处于截止状态,当输入信号变化到另一个半周后,原先导通、放大的三极管进入截止,而原先截止的三极管进入导通、放大状态,两只三极管在不断地交替导通放大和截止变化,所以称为推挽放大器(armjishu.com)。
如图1.2所示,为GPIO管脚在推挽输出模式下的等效结构示意图。U1是输出锁存器,执行GPIO管脚写操作时,在写脉冲(Write Pulse)的作用下,数据被锁存到Q和/Q。T1和T2构成CMOS反相器,T1导通或T2导通时都表现出较低的阻抗,但T1和T2不会同时导通或同时关闭,最后形成的是推挽输出。在推挽输出模式下,GPIO还具有回读功能,实现回读功能的是一个简单的三态门U2。注意:执行回读功能时,读到的是管脚的输出锁存状态,而不是外部管脚Pin的状态。
3. 开漏输出(OutputOD)
11:00:58 上传
图1.3 GPIO开漏输出结构示意图
如图1.3所示,为GPIO管脚在开漏输出模式下的等效结构示意图。开漏输出和推挽输出相比结构基本相同,但只有下拉晶体管T1而没有上拉晶体管。同样,T1实际上也是多组可编程选择的晶体管。开漏输出的实际作用就是一个开关,输出“1”时断开、输出“0”时连接到GND(有一定内阻)。回读功能:读到的仍是输出锁存器的状态,而不是外部管脚Pin的状态。因此开漏输出模式是不能用来输入的。
开漏输出结构没有内部上拉,因此在实际应用时通常都要外接合适的上拉电阻(通常采用4.7~10kΩ)。开漏输出能够方便地实现“线与”逻辑功能,即多个开漏的管脚可以直接并在一起(不需要缓冲隔离)使用,并统一外接一个合适的上拉电阻,就自然形成“逻辑与”关系。开漏输出的另一种用途是能够方便地实现不同逻辑电平之间的转换(如3.3V到5V之间),只需外接一个上拉电阻,而不需要额外的转换电路。典型的应用例子就是基于开漏电气连接的I2C总线。
4. 钳位二极管
GPIO内部具有钳位保护二极管,如图1.4所示。其作用是防止从外部管脚Pin输入的电压过高或者过低。VDD正常供电是3.3V,如果从Pin输入的信号(假设任何输入信号都有一定的内阻)电压超过VDD加上二极管D1的导通压降(假定在0.6V左右),则二极管D1导通,会把多于的电流引到VDD,而真正输入到内部的信号电压不会超过3.9V。同理,如果从Pin输入的信号电压比GND还低,则由于二极管D2的作用,会把实际输入内部的信号电压钳制在-0.6V左右。
11:00:58 上传
图1.4 GPIO钳位二极管示意图
假设VDD=3.3V,GPIO设置在开漏模式下,外接10kΩ上拉电阻连接到5V电源,在输出“1”时,我们通过测量发现:GPIO管脚上的电压并不会达到5V,而是在4V上下,这正是内部钳位二极管在起作用。虽然输出电压达不到满幅的5V,但对于实际的数字逻辑通常3.5V以上就算是高电平了(armjishu.com)。
11:00:58 上传
图1.5 解决开漏模式上拉电压不足的方法
如果确实想进一步提高输出电压,一种简单的做法是先在GPIO管脚上串联一只二极管(如1N4148),然后再接上拉电阻。参见图1.5,框内是芯片内部电路。向管脚写“1”时,T1关闭,在Pin处得到的电压是3.3+VD1+VD3=4.5V,电压提升效果明显;向管脚写“0”时,T1导通,在Pin处得到的电压是VD3=0.6V,仍属低电平
漏极开路的分析
11:00:58 上传
11:00:58 上传
电力场效应管又名电力场效应晶体管分为结型和绝缘栅型,通常主要指绝缘栅型中的型(),简称电力(),结型电力场效应晶体管一般称作静电感应晶体管()。
A:我们先来说说集电极开路输出的结构。集电极开路输出的结构如图所示,右边的那个三极管集电极什么都不接,所以叫做集电极开路(左边的三极管为反相之用,使输入为“”时,输出也为“”)。对于图,当左端的输入为“”时,前面的三极管截止(即集电极跟发射极之间相当于断开),所以电源通过电阻加到右边的三极管上,右边的三极管导通(即相当于一个开关闭合);当左端的输入为“”时,前面的三极管导通,而后面的三极管截止(相当于开关断开)。
我们将图简化成图的样子。图中的开关受软件控制,“”时断开,“”时闭合。很明显可以看出,当开关闭合时,输出直接接地,所以输出电平为。而当开关断开时,则输出端悬空了,即高阻态。这时电平状态未知,如果后面一个电阻负载(即使很轻的负载)到地,那么输出端的电平就被这个负载拉到低电平了,所以这个电路是不能输出高电平的。
再看图三。图三中那个的电阻即是上拉电阻。如果开关闭合,则有电流从电阻及开关上流过,但由于开关闭和时电阻为(方便我们的讨论,实际情况中开关电阻不为,另外对于三极管还存在饱和压降),所以在开关上的电压为,即输出电平为。如果开关断开,则由于开关电阻为无穷大(同上,不考虑实际中的漏电流),所以流过的电流为,因此在电阻上的压降也为,所以输出端的电压就是了,这样就能输出高电平了。但是这个输出的内阻是比较大的(即Ω),如果接一个电阻为的负载,通过分压计算,就可以算得最后的输出电压为伏,即伏。所以,如果要达到一定的电压的话,就不能太小。如果真的太小,而导致输出电压不够的话,那我们只有通过减小那个的上拉电阻来增加驱动能力。但是,上拉电阻又不能取得太小,因为当开关闭合时,将产生电流,由于开关能流过的电流是有限的,因此限制了上拉电阻的取值,另外还需要考虑到,当输出低电平时,负载可能还会给提供一部分电流从开关流过,因此要综合这些电流考虑来选择合适的上拉电阻。
如果我们将一个读数据用的输入端接在输出端,这样就是一个口了(的口就是这样的结构,其中口内部不带上拉,而其它三个口带内部上拉),当我们要使用输入功能时,只要将输出口设置为即可,这样就相当于那个开关断开,而对于口来说,就是高阻态了。
对于漏极开路()输出,跟集电极开路输出是十分类似的。将上面的三极管换成场效应管即可。这样集电极就变成了漏极,就变成了,原理分析是一样的。
另一种输出结构是推挽输出。推挽输出的结构就是把上面的上拉电阻也换成一个开关,当要输出高电平时,上面的开关通,下面的开关断;而要输出低电平时,则刚好相反。比起或者来说,这样的推挽结构高、低电平驱动能力都很强。如果两个输出不同电平的输出口接在一起的话,就会产生很大的电流,有可能将输出口烧坏。而上面说的或输出则不会有这样的情况,因为上拉电阻提供的电流比较小。如果是推挽输出的要设置为高阻态时,则两个开关必须同时断开(或者在输出口上使用一个传输门),这样可作为输入状态,单片机的一些口就是这种结构。
11:00:59 上传
在平时的电路设计时我们会遇到开漏()和开集()的概念,可能大家在念书时就知道其基本的用法,而且在设计中也并未遇到过问题。但是我忽然觉得自己也对这个概念了解的并不系统。于是进行了以下总结: 所谓开漏电路的概念里提到的“漏”就是指的漏极,同理,开集电路中的“集”就是指三极管的集电极,开漏电路就是指以的漏极为输出的电路。一般的常规用法是会在漏极外部的电路添加一个上拉电阻。完整的开漏电路应该由开漏器件和开漏的上拉电阻组成。如下图中所示:
11:00:59 上传
组成开漏形式的电路有以下几个特点:
1. 利用外部电路的驱动能力,减少内部的驱动。当内部导通时,驱动电流是从外部的流经,到。内部仅需很下的栅极驱动电流。如图。
2. 可以将多个开漏输出的,连接到一条线上。形成“与逻辑”关系。如图,当、、任意一个变低后,开漏线上的逻辑就为了。这也是,等总线判断总线占用状态的原理。
3. 可以利用改变上拉电源的电压,改变传输电平。如图的逻辑电平由电源决定,而输出高电平则由决定。这样我们就可以用低电平逻辑控制输出高电平逻辑了。
4. 开漏不连接外部的上拉电阻,则只能输出低电平。
5. 标准的开漏脚一般只有输出的能力。添加其它的判断电路,才能具备双向输入、输出的能力。
应用中需注意:
1. 开漏和开集的原理类似,在许多应用中我们利用开集电路代替开漏电路。例如,某输入要求由开漏电路驱动。则我们常见的驱动方式是利用一个三极管组成开集电路来驱动它,即方便又节省成本。如图。
11:00:59 上传
2. 上拉电阻的阻值决定了逻辑电平转换的沿的速度。阻值越大,速度越低功耗越小。反之亦然!
11:00:59 上传
(1)GPIO_Mode_AIN 模拟输入
(2)GPIO_Mode_IN_FLOATING 浮空输入
(3)GPIO_Mode_IPD 下拉输入
(4)GPIO_Mode_IPU 上拉输入
这三种输入电路是用那一种,要根据外围电路来决定。
所谓高阻,可以简单理解为输出端处于浮空状态没有电流流动,其电平随外部电平高低而定,即门电路放弃对输出端电路的控制。而上拉就是将不确定的信号通过一个电阻嵌位在高电平,电阻同时起限流作用。下拉同理,只不过上拉是对器件注入电流,下拉是输出电流。至于弱上拉和强上拉,只是上拉电阻的阻值不同,没有什么严格区分。简言之,上拉就是在端口没有输入的情况下,将端口的电平稳定在高电平。
(5)GPIO_Mode_Out_OD 开漏输出
(6)GPIO_Mode_Out_PP 推挽输出
(7)GPIO_Mode_AF_OD 复用开漏输出
(8)GPIO_Mode_AF_PP 复用推挽输出
GPIO_Speed_10MHz 最高输出速率10MHz
GPIO_Speed_2MHz 最高输出速率2MHz
GPIO_Speed_50MHz 最高输出速率50MHz
1.1I/O口的输出模式下,有3种输出速度可选(2MHz、10MHz和50MHz),这个速度是指I/O口驱动电路的响应速度而不是输出信号的速度,输出信号的速度与程序有关(芯片内部在I/O口 的输出部分安排了多个响应速度不同的输出驱动电路,用户可以根据自己的需要选择合适的驱动电路)。通过选择速度来选择不同的输出驱动模块,达到最佳的噪声 控制和降低功耗的目的。高频的驱动电路,噪声也高,当不需要高的输出频率时,请选用低频驱动电路,这样非常有利于提高系统的EMI性能。当然如果要输出较高频率的信号,但却选用了较低频率的驱动模块,很可能会得到失真的输出信号。
关键是GPIO的引脚速度跟应用匹配(推荐10倍以上?)。比如:
? 对于串口,假如最大波特率只需115.2k,那么用2M的GPIO的引脚速度就够了,既省电也噪声小。
? 对于I2C接口,假如使用400k波特率,若想把余量留大些,那么用2M的GPIO的引脚速度或许不够,这时可以选用10M的GPIO引脚速度。
? 对于SPI接口,假如使用18M或9M波特率,用10M的GPIO的引脚速度显然不够了,需要选用50M的GPIO的引脚速度。
1.2 GPIO口设为输入时,输出驱动电路与端口是断开,所以输出速度配置无意义。
1.3在复位期间和刚复位后,复用功能未开启,I/O端口被配置成浮空输入模式。
1.4所有端口都有外部中断能力。为了使用外部中断线,端口必须配置成输入模式。
1.5GPIO口的配置具有上锁功能,当配置好GPIO口后,可以通过程序锁住配置组合,直到下次芯片复位才能解锁。
2、推挽输出与开漏输出的区别
推挽输出:可以输出高,低电平,连接数字器件;开漏输出:输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行. 适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内).
推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止.
要实现 线与 需要用OC(open collector)门电路.是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小,效率高。输出既可以向负载灌电流,也可以从负载抽取电流
11:00:59 上传
当端口配置为输出时:
开漏模式:输出 0 时,N-MOS 导通,P-MOS 不被激活,输出0。
输出 1 时,N-MOS 高阻, P-MOS 不被激活,输出1(需要外部上拉电路);此模式可以把端口作为双向IO使用。
推挽模式:输出 0 时,N-MOS 导通,P-MOS 高阻
输出 1 时,N-MOS 高阻,P-MOS 导通,输出1(不需要外部上拉电路)。
简单来说开漏是0的时候接GND 1的时候浮空 推挽是0的时候接GND 1的时候接VCC
3、在STM32中选用IO模式
(1) 浮空输入_IN_FLOATING —浮空输入,可以做KEY识别,RX1
(2)带上拉输入_IPU—IO内部上拉电阻输入
(3)带下拉输入_IPD—IO内部下拉电阻输入
(4) 模拟输入_AIN —应用ADC模拟输入,或者低功耗下省电。
(5)开漏输出_OUT_OD —IO输出0接GND,IO输出1,悬空,需要外接上拉电阻,才能实现输出高电平。当输出为1时,IO口的状态由上拉电阻拉高电平,但由于是开漏输出模式,这样IO口也就可以由外部电路改变为低电平或不变 。可以读IO输入电平变化,实现C51的IO双向功能。
(6)推挽输出_OUT_PP ——IO输出0-接GND, IO输出1 -接VCC,读输入值是未知的。
(7)复用功能的推挽输出_AF_PP ——片内外设功能(I2C的SCL,SDA)
(8)复用功能的开漏输出_AF_OD——片内外设功能(TX1,MOSI,MISO.SCK.SS)
实例总结:
(1)模拟I2C使用开漏输出_OUT_OD,接上拉电阻,能够正确输出0和1;读值时先
GPIO_SetBits(GPIOB, GPIO_Pin_0);拉高,然后可以读IO的值;使用
GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0);
(2)如果是无上拉电阻,IO默认是高电平;需要读取IO的值,可以使用
带上拉输入_IPU和浮空输入_IN_FLOATING和 开漏输出_OUT_OD;
4、IO低功耗:
关于模拟输入&低功耗,根据STM32的低功耗AN(AN2629)及其源文件,在STOP模式下,为了得到尽量低的功耗,确实把所有的IO(包括非A/D输入的GPIO)都设置为模拟输入
没有更多推荐了,STM32系列第3篇--GPIO初始化
11:51:29来源: eefocus 关键字:&&
使能和初始化IO口:&&&&GPIO_InitTypeDef&GPIO_InitS
&&&&RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
&&&&RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
&&&&GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
&&&&GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;
&&&&GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
&&&&GPIO_Init(GPIOD,&GPIO_InitStructure);
&&&&GPIO_SetBits(GPIOD,GPIO_Pin_2);
&&&&GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
&&&&GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;
&&&&GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
&&&&GPIO_Init(GPIOA,&GPIO_InitStructure);
&&&&GPIO_SetBits(GPIOA,GPIO_Pin_8);11213操作IO口:&库函数:GPIO_SetBits(GPIOD,GPIO_Pin_2);//置位GPIO_ResetBits(GPIOD,GPIO_Pin_2);//复位12位操作:PAout(8)=1;
PAout(8)=0;
关键字:&&
编辑:什么鱼
引用地址:
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。
关注eeworld公众号快捷获取更多信息
关注eeworld服务号享受更多官方福利
STM32之USART串口通信
STM32之USART 232串口通信
STM32之USART串口通信
热门关键词
大学堂最新课程
汇总了TI汽车信息娱乐系统方案、优质音频解决方案、汽车娱乐系统和仪表盘参考设计相关的文档、视频等资源
热门资源推荐
频道白皮书
何立民专栏
北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。大白带你重玩STM32系列(九) ------按键检测与GPIO口的输入-电子产品世界论坛
大白带你重玩STM32系列(九) ------按键检测与GPIO口的输入
一场大雨的到来,给最近的连天酷暑降了降温。这么凉爽的好天气,怎能不用来继续搞搞32?开个玩笑,言归正传。 今天我们来讲一讲STM32 GPIO口的输入。在第六讲()中,我们了解了GPIO口的输出,并点亮了一个彩色LED发光二极管。那么对于STM32的GPIO口来说,它的输入输出功能的使用和51单片机相比是有些差别的。51单片机的IO口,不需要经过配置,便可直接使用它的输入输出功能。而STM32的输入和输出是需要配置的,并且它有多达8种不同的输入输出模式,他们分别是:输入浮空,输入上拉,输入下拉,模拟输入;开漏输出,推挽输出,推挽复用输出,开漏复用输出这8种模式。何时使用哪种模式,要根据具体的情况而定。 在点亮LED发光二极管的时候,我们采用的是推挽输出,来增加GPIO口输出强度。那么在这一节中,对于按键这种输入设备,我们则应该采用GPIO口的输入模式,因为,在按键被按下时,与按键相关的GPIO口的状态会根据按键所接的电路结构发生相应的变化,STM32要想捕获到这种变化,就要开放GPIO口,让GPIO口能够把这个信号传递到芯片内部去。因此,需要将GPIO口配置成输入模式。这里我们将其配置成浮空输入。这样更容易检测到外部信号产生的变化。 下面我们先来了解一下按键的特点。 机械按键是目前见得较多的按键。而我们开发板上使用的是下图中第一行第一个,这种规格的轻触机械按键。如图。
这种按键的机械触点在断开、闭合时,由于触点的物理上存在的弹性作用,按键开关不会马上稳定接通或一下子断开,使用按键时会产生图中的带波纹信号,需要用软件消抖处理滤波,见下图。一般来讲是通过程序上延时10ms的方式进行消抖,而这种方法是不方便进行按键的输入检测的,同时,因为需要原地延时等待10ms,因此在一些快速响应的场合,还是略显低效。
因此我们推荐硬件电路进行按键的消抖。例如我使用的开发板,它的按键电路部分就带有硬件消抖的功能,现将其电路见下图,它利用电容充放电的延时,消除了波纹,从而简化软件的处理,软件只需要直接检测引脚的电平即可。这样一来,就不用在程序里再写10ms的消抖延时了。
从上面的原理图可知,按键在没有被按下的时候, GPIO 引脚的输入状态为低电平(按键所在的电路不通,引脚接地),当按键按下时, GPIO 引脚的输入状态为高电平(按键所在的电路导通,引脚接到电源)。只要我们检测引脚的输入电平,即可判断按键是否被按下。GPIO口的工作模式选择输入浮空就OK! 现在,我们就从程序方面来分析一下,应该怎样使用按键。 程序将要完成的要求如下:使用一个按键,来改变LED彩灯的色彩。每按一次,改变一种颜色。单片机上电复位后,LED灯发红光,当按键第一次按下,由红变绿;当按键第二次按下,由绿变蓝;当按键第三次按下,由蓝再变为红色。以此往复。 最终实现之后的效果,请摸下方视频链接观看:
通过以上电路原理图我们可以看出Key1按键接在STM32的PA0端口上。而在第六讲()中我们了解了LED彩灯的使用方法。并知道LED彩灯的红绿蓝三极分别接在PB5、PB0、PB1三个GPIO口上。了解了硬件电路之后,我们就开始构造程序框架了。 程序怎么写,在我们的脑海中一定要形成一个清晰的大概流程之后,再开始动手。切忌边写边想,这样会多走很多弯路。对于本节中要完成的要求,我们捋出来编程要点如下,还是三大步骤: (1)使能按键Key1和LED灯对应的 GPIO 端口时钟; (2)初始化按键Key1和LED灯对应的 GPIO 目标引脚的工作模式(按键Key1为输入浮空,LED灯为推挽输出,50MHz); (3)编写简单测试程序,检测按键的状态,实现按键控制 LED 灯。
照例,还是分别对三部分的主要代码进行解释说明。 在程序的最开始还是宏定义相关的代码。现将代码贴出并简要说明: /* 定义KEY连接的GPIO端口 */
#define &&&KEY1_GPIO_CLK &&&&RCC_APB2Periph_GPIOA #define &&&KEY1_GPIO_PORT &&&GPIOA
#define &&&KEY1_GPIO_PIN
GPIO_Pin_0
/* 定义LED连接的GPIO端口 */ // R-红色 #define LED1_GPIO_PORT
/* GPIO端口 */ #define LED1_GPIO_CLK
RCC_APB2Periph_GPIOB
/* GPIO端口时钟 */ #define LED1_GPIO_PIN
GPIO_Pin_5
/* 连接到SCL时钟线的GPIO */
// G-绿色 #define LED2_GPIO_PORT
/* GPIO端口 */ #define LED2_GPIO_CLK
RCC_APB2Periph_GPIOB
/* GPIO端口时钟 */ #define LED2_GPIO_PIN
GPIO_Pin_0
/* 连接到SCL时钟线的GPIO */
// B-蓝色 #define LED3_GPIO_PORT
/* GPIO端口 */ #define LED3_GPIO_CLK
RCC_APB2Periph_GPIOB
/* GPIO端口时钟 */ #define LED3_GPIO_PIN
GPIO_Pin_1
/* 连接到SCL时钟线的GPIO */
以上代码根据按键的硬件连接, 把检测按键输入和LED彩灯的 GPIO 端口、 GPIO 引脚号以及GPIO 端口时钟使用宏定义封装起来了。在之后的程序中,我们可以用宏定义中我们定义的特殊名称来替代这些端口和引脚号。更直观更方便同时也增强了程序的可读性。下面就是相关的代码段的编写:
(1)使能按键Key1和LED灯对应的 GPIO 端口时钟。 /*开启按键端口的时钟*/
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK,ENABLE); /*开启LED相关的GPIO外设时钟*/
RCC_APB2PeriphClockCmd( LED1_GPIO_CLK | LED2_GPIO_CLK | LED3_GPIO_CLK, ENABLE); 以上两行代码,分别打开了Key1和LED灯对应的 GPIO 端口时钟。在进行GPIO口等外设的使用时,必须首先打开该外设所对应时钟线上的时钟信号才行。 (2)初始化按键Key1和LED灯对应的 GPIO 目标引脚的工作模式(按键Key1为输入浮空,LED灯为推挽输出,50MHz); 按键GPIO初始化函数代码如下: void Key_GPIO_Config(void) {
GPIO_InitTypeDef GPIO_InitS
/*开启按键端口的时钟*/
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK,ENABLE);
//选择按键的引脚
GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;
// 设置按键的引脚为浮空输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//使用结构体初始化按键
GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure); } 函数执行流程如下: (1) 使用 GPIO_InitTypeDef 定义 GPIO 初始化结构体变量,以便下面用于存储 GPIO 配置。 (2) 调用库函数 RCC_APB2PeriphClockCmd 来使能按键的 GPIO 端口时钟,调用时我们使用“|”操作同时配置两个按键的时钟。 (3) 向 GPIO 初始化结构体赋值,把引脚初始化成浮空输入模式,其中的 GPIO_Pin 使用宏“KEY1_GPIO_PIN”来赋值,使函数的实现方便移植。 由于引脚的默认电平受按键电路影响,所以设置成浮空输入。 (4) 使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的初始化,这里的 GPIO 端口使用“KEYx_GPIO_PORT”宏来赋值,也是为了程序移植方便。 (5) 使用同样的初始化结构体,只修改控制的引脚和端口,初始化其它按键检测时使用的GPIO 引脚。
LED彩灯对应的GPIO口的初始化,在第六讲()中已经介绍过,在此不再讲解。有需要的小伙伴请摸上方蓝字部分链接,移步第六讲查看。
(3)编写简单测试程序,检测按键的状态,实现按键控制 LED 灯。 首先,要在程序中进行的是按键状态的检测,也就是说我们需要写程序分析,什么时候按键被按下了。我们可以通过检测对应引脚的电平来判断按键状态,看是否有按键被按下。代码如下:
/* 按键按下标置宏
* &按键按下为高电平,设置 KEY_ON=1, KEY_OFF=0
* &若按键按下为低电平,把宏设置成KEY_ON=0 ,KEY_OFF=1 即可
*/ #define KEY_ON 1 #define KEY_OFF 0
* 函数名:Key_Scan
* 描述 &:检测是否有按键按下
* 输入 &:GPIOx:x 可以是 A,B,C,D或者 E
GPIO_Pin:待读取的端口位
* 输出 &:KEY_OFF(没按下按键)、KEY_ON(按下按键)
*/ uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin) {
/*检测是否有按键按下 */
if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON )
/*等待按键释放 */
while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);
return KEY_OFF; }
在这里我们定义了一个 Key_Scan 函数用于扫描按键状态。 GPIO 引脚的输入电平可通过 读 取 IDR 寄 存 器 对 应 的 数 据 位 来 感 知 , 而 STM32 标 准 库 提 供 了 库 函 数GPIO_ReadInputDataBit 来获取位状态,该函数输入 GPIO 端口及引脚号,函数返回该引脚的电平状态,高电平返回 1,低电平返回 0。 Key_Scan 函数中以 GPIO_ReadInputDataBit 的返回值与自定义的宏“KEY_ON”对比,若检测到按键按下,则使用 while 循环持续检测按键状态,直到按键释放,按键释放后 Key_Scan 函数返回一个“KEY_ON”值;若没有检测到按键按下,则函数直接返回“KEY_OFF”。若按键的硬件没有做消抖处理,需要在这个 Key_Scan 函数中做软件滤波,防止波纹抖动引起误触发。如果对固件库中的函数不熟的同学,建议可以参照《STM32f10固件库使用手册中文版》进行学习。 再接下来,我们就开始写主函数,在主函数中实现我们想要的效果了。首先我们在主函数main()中初始化 LED 灯及按键后,在 while 函数里不断调用 Key_Scan 函数,并判断其返回值,若返回值表示按键按下,则改变&LED 灯的状态。 现将整个程序的代码贴出如下: #include&&stm32f10x.h&
/*&定义KEY连接的GPIO端口&*/
#define&&&&KEY1_GPIO_CLK&&&&&RCC_APB2Periph_GPIOA
#define&&&&KEY1_GPIO_PORT&&&&GPIOA
#define&&&&KEY1_GPIO_PIN
&GPIO_Pin_0
&/*&按键按下标置宏
*&&按键按下为高电平,设置&KEY_ON=1,&KEY_OFF=0
*&&若按键按下为低电平,把宏设置成KEY_ON=0&,KEY_OFF=1&即可
#define&KEY_ON 1
#define&KEY_OFF 0
/*&定义LED连接的GPIO端口&*/
#define&LED1_GPIO_PORT&&&& GPIOB
&&&&&&&&&&&&&&/*&GPIO端口&*/
#define&LED1_GPIO_CLK& &&&&RCC_APB2Periph_GPIOB
/*&GPIO端口时钟&*/
#define&LED1_GPIO_PIN
GPIO_Pin_5
&&&&&&&&/*&连接到SCL时钟线的GPIO&*/
#define&LED2_GPIO_PORT&&&& GPIOB
&&&&&&&&&&&&&&/*&GPIO端口&*/
#define&LED2_GPIO_CLK& &&&&RCC_APB2Periph_GPIOB
/*&GPIO端口时钟&*/
#define&LED2_GPIO_PIN
GPIO_Pin_0
&&&&&&&&/*&连接到SCL时钟线的GPIO&*/
#define&LED3_GPIO_PORT&&&& GPIOB
&&&&&&&&&&&&&&/*&GPIO端口&*/
#define&LED3_GPIO_CLK& &&&&RCC_APB2Periph_GPIOB
/*&GPIO端口时钟&*/
#define&LED3_GPIO_PIN
GPIO_Pin_1
&&&&&&&&/*&连接到SCL时钟线的GPIO&*/
&&*&@brief&&配置按键用到的I/O口
&&*&@param&&无
&&*&@retval&无
void&Key_GPIO_Config(void)
GPIO_InitTypeDef&GPIO_InitS
/*开启按键端口的时钟*/
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK,ENABLE);
//选择按键的引脚
GPIO_InitStructure.GPIO_Pin&=&KEY1_GPIO_PIN;&
//&设置按键的引脚为浮空输入
GPIO_InitStructure.GPIO_Mode&=&GPIO_Mode_IN_FLOATING;&
//使用结构体初始化按键
GPIO_Init(KEY1_GPIO_PORT,&&GPIO_InitStructure);
&*&函数名:Key_Scan
&*&描述&&:检测是否有按键按下
&*&输入&&:GPIOx:x&可以是&A,B,C,D或者&E
&&&&&GPIO_Pin:待读取的端口位&
&*&输出&&:KEY_OFF(没按下按键)、KEY_ON(按下按键)
uint8_t&Key_Scan(GPIO_TypeDef*&GPIOx,uint16_t&GPIO_Pin)
/*检测是否有按键按下&*/
if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin)&==&KEY_ON&)&&
/*等待按键释放&*/
while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin)&==&KEY_ON);&&&
return& KEY_ON; &
return&KEY_OFF;
void&LED_GPIO_Config(void)
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef&GPIO_InitS
/*开启LED相关的GPIO外设时钟*/
RCC_APB2PeriphClockCmd(&LED1_GPIO_CLK&|&LED2_GPIO_CLK&|&LED3_GPIO_CLK,&ENABLE);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin&=&LED1_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode&=&GPIO_Mode_Out_PP;&&&
/*设置引脚速率为50MHz&*/&&&
GPIO_InitStructure.GPIO_Speed&=&GPIO_Speed_50MHz;&
/*调用库函数,初始化GPIO*/
GPIO_Init(LED1_GPIO_PORT,&&GPIO_InitStructure);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin&=&LED2_GPIO_PIN;
/*调用库函数,初始化GPIO*/
GPIO_Init(LED2_GPIO_PORT,&&GPIO_InitStructure);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin&=&LED3_GPIO_PIN;
/*调用库函数,初始化GPIOF*/
GPIO_Init(LED3_GPIO_PORT,&&GPIO_InitStructure);
/*&关闭所有led灯 */
GPIO_SetBits(LED1_GPIO_PORT,&LED1_GPIO_PIN);
/*&关闭所有led灯 */
GPIO_SetBits(LED2_GPIO_PORT,&LED2_GPIO_PIN); &
&&&&/*&关闭所有led灯 */
GPIO_SetBits(LED3_GPIO_PORT,&LED3_GPIO_PIN);
void&Led_Turn_on_R(void)&&
&&&&/*&Turn&On&LED1&*/&&
&&&&GPIO_ResetBits(LED1_GPIO_PORT,&LED1_GPIO_PIN);&&
void&Led_Turn_on_G(void)&&
&&&&/*&Turn&On&LED2&*/&&
&&&&GPIO_ResetBits(LED2_GPIO_PORT,&LED2_GPIO_PIN&);&&
void&Led_Turn_on_B(void)&&
&&&&/*&Turn&On&LED3&*/&&
&&&&GPIO_ResetBits(LED3_GPIO_PORT,&LED3_GPIO_PIN);&&
void&Led_Turn_off_all(void)&&
&&&&/*&Turn&Off&All&LEDs&*/&&
&&&&GPIO_SetBits(GPIOB&,&LED1_GPIO_PIN|LED2_GPIO_PIN|LED3_GPIO_PIN);&&
&&*&@brief&&主函数
&&*&@param&&无
&&*&@retval&无
int&main(void)
u8&KEY_FLAG&=&0;&
/*&LED端口初始化&*/
LED_GPIO_Config();
/*&按键端口初始化&*/
Key_GPIO_Config();
/*&轮询按键状态,若按键按下则反转LED&*/
while(1)&&&&&&&&&&&&&&&&&&&&&&&&&&&&
if(&Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN)&==&KEY_ON&&)
/*改变按键Key1的标志位,用以控制LED灯的色彩改变*/
KEY_FLAG&++;
if&(KEY_FLAG&==&3)
KEY_FLAG&=&0;
switch(KEY_FLAG)&
case&0&:&Led_Turn_off_all();&Led_Turn_on_R();break&;&//发红光
case&1&:&Led_Turn_off_all();&Led_Turn_on_G();break&;&//发绿光
case&2&:&Led_Turn_off_all();&Led_Turn_on_B();break&;&//发蓝光
}好了,本节就到此为止。在这一讲当中,我们学会了使用GPIO口的输入模式,以及轻触按键的使用。希望大家下去勤总结勤练习,多写程序才是学好STM32的王道。
“温故而知新,可以为师矣”。
不错的硬件消抖电路,学习了。
硬件消抖比软件消抖好用多了
帖子好详细,这么多按键,大开眼界。
继续啊 & 等着看各种接口的使用
一眨眼两年了
我觉得软件消抖是必须要有的。
好久没更新了?
匿名不能发帖!请先 [
Copyright (C) 《电子产品世界》杂志社 版权所有}