墨宝.docx
《墨宝.docx》由会员分享,可在线阅读,更多相关《墨宝.docx(10页珍藏版)》请在冰豆网上搜索。
![墨宝.docx](https://file1.bdocx.com/fileroot1/2023-7/24/8c12a232-95c1-4537-97ad-149f77e033f3/8c12a232-95c1-4537-97ad-149f77e033f31.gif)
墨宝
1.GPIO_Mode的作用?
一、GPIO配置
(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倍以上?
)。
比如:
1.1.1对于串口,假如最大波特率只需115.2k,那么用2M的GPIO的引脚速度就够了,既省电也噪声小。
1.1.2对于I2C接口,假如使用400k波特率,若想把余量留大些,那么用2M的GPIO的引脚速度或许不够,这时可以选用10M的GPIO引脚速度。
1.1.3对于SPI接口,假如使用18M或9M波特率,用10M的GPIO的引脚速度显然不够了,需要选用50M的GPIO的引脚速度。
1.2GPIO口设为输入时,输出驱动电路与端口是断开,所以输出速度配置无意义。
1.3在复位期间和刚复位后,复用功能未开启,I/O端口被配置成浮空输入模式。
1.4所有端口都有外部中断能力。
为了使用外部中断线,端口必须配置成输入模式。
1.5GPIO口的配置具有上锁功能,当配置好GPIO口后,可以通过程序锁住配置组合,直到下次芯片复位才能解锁。
2、推挽输出与开漏输出的区别
推挽输出:
可以输出高,低电平,连接数字器件;开漏输出:
输出端相当于三极管的集电极.要得到高电平状态需要上拉电阻才行.适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内).
推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止.
要实现线与需要用OC(opencollector)门电路.是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小,效率高。
输出既可以向负载灌电流,也可以从负载抽取电流
当端口配置为输出时:
开漏模式:
输出0时,N-MOS导通,P-MOS不被激活,输出0。
输出1时,N-MOS高阻,P-MOS不被激活,输出1(需要外部上拉电路);此模式可以把端口作为双向IO使用。
推挽模式:
输出0时,N-MOS导通,P-MOS高阻,输出0。
输出1时,N-MOS高阻,P-MOS导通,输出1(不需要外部上拉电路)。
简单来说开漏是0的时候接GND1的时候浮空推挽是0的时候接GND1的时候接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)都设置为模拟输入
5、程序
(1)时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|
RCC_APB2Periph_GPIOC,ENABLE);
(2)IO配置:
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;//IR输入
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;
GPIO_Init(GPIOC,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_Init(GPIOB,&GPIO_InitStructure);
(3)输出输入:
输出0:
GPIO_ResetBits(GPIOB,GPIO_Pin_0)
输出1:
GPIO_SetBits(GPIOB,GPIO_Pin_0)
输入:
GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)
12MAY,2011
GPIO的重新学习
凡是学习都应该是不断深化的,这一部分GPIO的重新学习,相对于刘总的理论就是先知道是个啥,再深入研究它为啥。
1.1普通管脚、复用功能、重映射功能的区别
之所以涉及到这方面,完全是因为挑战杯的小系统提供到第三层的管脚有限,不能用他默认的管脚进行配置PWM。
而对于一个STM32芯片而言,每个管脚的功能定义是多重的,这大大的方便了我们实际的应用操作需求。
如图所示,对于PB5这个管脚,他的主功能为普通管脚即为PB5,默认复用功能为I2C1_SMBA1,重映射定义功能为TIM3_CH1/SPI1_MOSI。
对于主功能,GPIO配置的时候,声明他的基本类型就行,如:
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//基本管脚推挽输出
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
这时PB5的功能为基本GPIO
对于复用功能,进行GPIO配置的时候,更改配置GPIO_Mode的值就行了,如
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
这时PB5的功能为I2C1_SMBAI
对于重映射定义功能,在配置GPIO前,需对APB2总线时钟进行使能配置:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE
这是对重映射管脚时钟的初始化,加完这句后再进行重映射管脚的配置:
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
先是重映射声明,然后就类似于复用功能的声明。
理解上很容易理解,关键就是Remap函数参数的选择。
assert_param问题
我们在学STM32的时候函数assert_param出现的几率非常大,上网搜索一下,网上一般解释断言机制,做为程序开发调试阶段时使用。
下面我就谈一下我对这些应用的看法,学习东西抱着知其然也要知其所以然。
4 断言机制函数assert_param
我们在分析库函数的时候,几乎每一个函数的原型有这个函数assert_param();下面以assert_param(IS_GPIO_ALL_PERIPH(GPIOx));为例说一下我的理解,函数的参数IS_GPIO_ALL_PERIPH(GPIOx),我们可以寻找到原型
#define IS_GPIO_ALL_PERIPH(PERIPH) (((*(uint32_t*)&(PERIPH)) == GPIOA_BASE) || \ ((*(uint32_t*)&(PERIPH)) == GPIOB_BASE) || \ ((*(uint32_t*)&(PERIPH)) == GPIOC_BASE) || \ ((*(uint32_t*)&(PERIPH)) == GPIOD_BASE) || \ ((*(uint32_t*)&(PERIPH)) == GPIOE_BASE) || \ ((*(uint32_t*)&(PERIPH)) == GPIOF_BASE) || \ ((*(uint32_t*)&(PERIPH)) == GPIOG_BASE))
这个宏定义的作用就是检查参数PERIPH,判断参数PERIPH是否为GPIOX(A...G)基址中的一个,只要有一个为真则其值为真,否则为假,不用多说,这是C语言中基本的逻辑运算。
当然这个库函数也用的很有意思,看:
首先对PERIPH进行取址,也就是求地址,&PERIPH,然后对这个地址强制转化为32位的指针,即前面加(uint32_t *),然后通过*进行访问这个地址(指针)中的内容。
不多说了,看几遍就能明白。
下面我们再回到assert_param这个函数,这个函数是哪里的呢?
在stm32f10x_conf.h寻找到原型如下:
#ifdef USE_FULL_ASSERT
#define assert_param(expr) ((expr) ?
(void)0 :
assert_failed((uint8_t *)__FILE__, __LINE__))
void assert_failed(uint8_t* file, uint32_t line); #else
#define assert_param(expr) ((void)0) #endif
这是一个预编译文件,若是定义了USE_FULL_ASSERT这个文件,则执行后面的文件,我们在程序中一般都没什么定义,即执行后面这个语句((void)0),这个语句不用多想,没有定义USE_FULL_ASSERT就是什么也不执行。
说的明白点,对上面的那个语句IS_GPIO_ALL_PERIPH(GPIOx)不执行任何操作。
若是定义了USE_FULL_ASSERT它,我们调用这个函数assert_param时,及对参数
IS_GPIO_ALL_PERIPH(GPIOx)的正确性进行检查,通过一个C语言中的双目运算符来判断,若是返回1,执行语句(void)0,跟上面一样,若是返回0,则执行后面的函数assert_failed((uint8_t *)__FILE__, __LINE__),函数的作用在库函数中有解释,用来指示出错的行数和文件。
注意:
__FILE__, __LINE__是标准库函数中的宏定义!
切记
void assert_failed(uint8_t* file, uint32_t line);刚开始没看明白为什么加在这里,仔细一想是在头文件的函数声明。
至于函数实体呢?
我们从官方文件的模板中main.c中可以找到。
如下:
void assert_failed(u8* file, u32 line)
{ /* User can add his own implementation to report the file name and line number, ex:
printf("Wrong parameters value:
file %s on line %d\r\n", file, line) */
名人堂:
众名人带你感受他们的驱动人生马云任志强李嘉诚柳传志史玉柱
/* Infinite loop */ while
(1) { }
} 英文注释也说明了怎么应用,通过输入参数来确定位置,最简单的方法就是串口打印了,这个函数的主要思想是在输入参数有问题的时候,但是有编译不出来,它可以帮你检查参数的有效性,好处不必多言,自己领悟就行。
继续说明如下:
assert_param是怎样包含进去的呢?
我们在stm32f10x_conf.h这个头文件中定义的函数声明还是宏定义,怎么在其它文件中都能应用呢?
也很多网上朋友在刚开始学习的时候都遇到编译不过去的问题出现,最后通过在文件中添加USE_STDPERIPH_DRIVER来解决的:
我们可以在整个工程中进行搜索USE_STDPERIPH_DRIVER,通过头文件可以看出,是使用标准外设文件。
在stm32f10x.h文件中我们可以搜索到如下情况:
#if !
defined USE_STDPERIPH_DRIVER /**
* @brief Comment the line below if you will not use the peripherals drivers. In this case, these drivers will not be included and the application code will be based on direct access to peripherals registers */
#define USE_STDPERIPH_DRIVER #endif
#ifdef USE_STDPERIPH_DRIVER #include "stm32f10x_conf.h" #endif
可以很容易看出来,我们不在那里添加,这个头文件中也给我们设置了开关,只要把第一个的注释去掉,就不用在配置中添加USE_STDPERIPH_DRIVER了,在第二个文件中我们可以知道怎样包含这个控制开关文件了,呵呵。
我们也明白为什么我们在写程序的时候只要包含stm32f10x.h就能很容易的包含所有的文件文件了吧,我们只要在stm32f10x_conf.h配置一下就能包含所需要的库文件了。
通过以上可以看出,通过头文件的相互包含,来控制外设以及调试文件的调用,这样我们理清思路,理解起来就好多了。
当然在学习中可能有些C语言问题还没有理解透彻,多上网搜一下,或者多看书,很快就搞明白的。
#include"stm32f10x.h"与#include"stm32f10x_lib.h"问题
stm32f10x_lib.h文件是老版本的库函数所用到的,现在新版本的库函数都不用了
关于位操作的方法
经常会看到一些位操作。
1、将char型变量a的第七位(bit6)清0,其它位不变。
a&=~(1<<6);//括号内1左移6位,得二进制数:
01000000
//按位取反,得10111111,所得的数与a作”位与&”运算,
//a的第7位(bit6)被置零,而其它位不变。
2、同理,将变量a的第七位(bit6)置1,其它位不变的方法如下。
a|=(1<<6);//把第七位(bit6)置1,其它为不变
3、将变量a的第七位(bit6)取反,其它位不变。
a^=(1<<6);//把第七位(bit6)取反,其它位不变
第3点用了异或,异或有以下特点:
(1)按位异或可以用来使某些特定的位翻转,如对数10100001的第2位和第3位翻转,可以将数与00000110进行按位异或运算:
10100001^00000110=10100111
(2)通过按位异或运算,可以实现两个值的交换,而不必使用临时变量。
例如交换两个整数a,b的值,可通过下列语句实现:
a=10100001,b=00000110
a=a^b; //a=10100111
b=b^a; //b=10100001
a=a^b; //a=00000110
(3)数a两次异或同一个数b(a=a^b^b)仍然为原值a.