GPIO的概念
GPIO指的是通用的输入输出端口,可以由用户通过软件配置的方式来进行控制,GPIO引脚需要和外设连接在一起的,就可以和外设实现通信以及采集数据等功能。
GPIO的定义
指的是对芯片的某个引脚进行高低电平的输出,以及可以去检测某个引脚的电平状态。
一般电平采用都是TTL电平信号.
TTL电平信号规定:+5V等价于逻辑“1”,0V等价于逻辑“0”。
TTL电平有一个电平范围:
- 对于输出电路:电压大于等于(≥)2.4V为逻辑1;电压小于等于(≤)0.4V为逻辑0;
- 对于输入电路:电压大于等于(≥)2.0V为逻辑1;电压小于等于(≤)0.8V为逻辑0;
高电平(2.4~5V) 低电平(0~0.8V)
另外常见的是RS232电平。
RS232是美国电子工业协会于1962年发布的串行通信接口标准,其中RS为英文“Recomend Standard”的缩写,中文翻译为“推荐标准”,232为标示号。该标准对串行通信的物理接口及逻辑电平都做了规定。
最简单的RS232通信由三条数据线组成,即TxD、RxD和GND。RS232采用负逻辑电平,即-15V~-3V代表逻辑"1",+3V~+15V代表逻辑"0"。这里的电平,是TxD线(或者RxD线)相对于GND的电压。
引脚的说明
电源引脚 :VCC、VDD、VSS、VDDA、VSSA、VREF+等属于电源引脚
晶振引脚 :PC14、PC15、PH0、PH1就属于晶振引脚,也可以作为其他的功能使用
复位引脚 :NRST属于复位引脚
BOOT引脚 :BOOT0是专用引脚,BOOT1就属于功能引脚 (设置芯片的自举模式)
GPIO引脚 :芯片一共有144引脚,但是GPIO引脚有114个
下载引脚 :PA13、PA14、PB3、PB4等都属于下载引脚(JTAG、SWD)
引脚的命名规则
一般是由P开头,分为很多组,以字母A~H来命名,每个组有16个引脚,引脚的编号为0~15,所以如 PA0 ~ PA15 PB0~PB15........
引脚的功能
需要注意:芯片的引脚有很多的功能,一般默认的功能都是作为GPIO(输入输出),但是引脚也有其他的功能,被称为“第二功能”,只有在使用ADC通道和DAC需要把引脚设置模拟模式,其他情况下想要把引脚当做别的功能使用,需要把引脚设置复用模式。
需要注意:引脚的功能必须通过查阅芯片的数据手册,才可以进行配置 如下图所示
注意:带FT标志的IO口,引脚内部有保护二极管,可以容忍5V电压(芯片的标准电压为3.3V)
GPIO外设的使用
GPIO外设的使用一般在实际开发过都是使用函数库开发,提高开发效率,以GPIO外设为例,讲解一下函数库开发的流程。以点亮LED为例
分析原理图,找到外设连接的GPIO引脚 LED0 --- PF9
一般硬件工程师在设计开发板的过程中,先设计原理图(实际上就是对硬件的抽象),一般外设都需要通过导线连接在芯片的引脚上(不简洁),都会利用网络标号实现导线的连接,网络编号其实就是一个电气连接点,一般是以字母+数字命名,如果多个连接点都是用同样的网络编号,就说明导线是连接在一起的。
分析原理图,理解硬件的控制原理 GPIO输出高电平 灯灭
对于一些简单的外设通过原理图就可以分析,但是一些复杂的传感器还需要结合对应的数据手册。
根据函数库的接口进行开发
参考ST公司提供的函数库帮助手册
ST公司的代码例程
移植
GPIO外设详解
根据官方文档中的说明
定义一个外设的结构体变量
变量命名规则 PPP_InitTypeDef PPP_InitStructure;
每个外设都有对应的结构体,结构体的定义一般都是存放在每个外设的头文件内,比如GPIO外设的初始化结构体就定义在stm32f4xx_gpio.h中
在配置外设结构体之前,需要调用库函数打开外设的时钟
如RCC_AHB1PeriphClockCmd
任何一个外设都是由时钟控制的,时钟就相当于外设的开关,因为底层寄存器一般内部结构中都是由触发器组成,而触发器需要触发信号,而触发信号是由时钟提供的。
STM32低功耗的根本原因就是在芯片复位之后,所有的外设的时钟(开关)都是默认关闭,需要用到哪个外设就打开哪个外设的时钟,不需要的就关闭,有利于降低功耗。
函数库中提供了多个可以打开外设时钟的函数
STM32中文参考手册的2.3章节 存储器映射
可以看到,内核中的外设都会挂载在不同的总线下面,AHB总线叫做高级高性能总线,APB总线叫做高级外设总线(APB1和APB2)。每个总线的时钟频率都不同,因为不同类型的外设需要的时钟频率不同,所以为了降低STM32的功耗,把不同性能的外设挂载在不同频率的总线下。
函数库中提供的时钟使能的函数在stm32f4xx_rcc.h中声明,在stm32f4xx_rcc.c定义的
函数原型
void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState)
函数参数
参数一:RCC_AHB1Periph 指的是外设的时钟 如 RCC_AHB1Periph_GPIOF
参数二:NewState 指的是打开或者关闭 如 ENABLE or DISABLE
返回值:None
需要注意:在调用函数库的时候,大家不需要关注函数内部的实现过程,只需要掌握函数的传参和返回值即可。
打开外设时钟后,需要对外设的结构体成员进行赋值操作(引脚、模式、速率、类型....)
GPIO外设的结构体成员有5个,可以参考stm32f4xx_gpio.h
需要指定配置的引脚 GPIO_Pin 定义在GPIO_pins_define (跳转 or 查找)
一个端口有16个引脚,在操作端口的某个引脚的时候,需要说清楚用哪个引脚,这样才可以对寄存器的某些位进行操作。
需要指定引脚的模式 GPIO_Mode 定义在GPIOMode_TypeDef (跳转 or 查找)
GPIO的引脚模式可以参考中文参考手册的7.3章节
可以看到每个引脚内部都有保护二极管,这也是STM32的安全机制,但是需要注意STM32引脚不是万能的,一般是作为控制使用,而不是作为驱动使用。
每个引脚内部都有上拉电阻和下拉电阻,但是上拉电阻是一个弱上拉(驱动能力较弱),如果想要提供给引脚一个确定的电平,建议外接上拉电阻。
建议大家在没有使用引脚的时候给引脚一个默认的电平状态,可以消除引脚不定值的影响。
GPIO提供了3种输入模式(浮空输入、上拉输入、下拉输入),参考GPIO内部结构示意图
GPIO提供了4种输出模式(上拉+下拉推挽输出、上拉+下拉开漏输出),参考GPIO内部结构示意图
PMOS管和NMOS管循环依次导通,好处是可以提高电路的负载能力和切换速度,并且可以降低功耗。
设置引脚的输出速率 GPIO_Speed 定义在GPIOSpeed_TypeDef
指的是IO驱动电路的输出能力(其实就是在不失真的情况下能输出的最大速率),引脚的输出速率影响GPIO的引脚的功耗和噪声,一般大家可以根据实际需要来选择,速率越高,则功耗越高,噪声越大,如果对产品的实时性要求不高,则选择一个低速的即可,有利于降低EMI(电磁干扰),一般指的是影响引脚在切换高低电平的过程中上升沿和下降沿的陡度。如果自己也不知道选哪个,就选高的。
设置引脚的输出类型 GPIO_OType 定义在GPIOOType_TypeDef
推挽输出:可以利用驱动器直接输出高电平和低电平
开漏输出:可以利用驱动器直接输出低电平,但是不能直接输出高电平(需要外接上拉电阻)
设置引脚的上下拉电阻 GPIO_PuPd 定义在GPIOPuPd_TypeDef
一般都会在引脚未使用的情况给引脚一个默认的电平状态,防止引脚出现电平不定(高组态)的情况。
在对结构体成员赋值之后,调用初始化函数把结构体成员的值写入到对应外设的寄存器
函数原型
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
函数参数
参数一:GPIOx 指的是想要初始化的GPIO外设的端口 A~K 如 GPIOF
参数二:GPIO_InitStruct 指的是配置好的GPIO结构体变量的地址 记得取地址 &
返回值 None
其他函数
设置引脚输出高电平(输出模式有效) 调用 GPIO_SetBits()
函数原型
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
函数参数
参数一:GPIOx 想要输出高电平的引脚端口 如 GPIOx A~K 如 GPIOF
参数二:GPIO_Pin 想要输出高电平的引脚位置 GPIO_Pin_x where x can be (0..15)
返回值 None
设置引脚输出低电平(输出模式有效) 调用 GPIO_ResetBits()
函数原型
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
函数参数
参数一:GPIOx 想要输出高电平的引脚端口 如 GPIOx A~K 如 GPIOF
参数二:GPIO_Pin 想要输出高电平的引脚位置 GPIO_Pin_x where x can be (0..15)
返回值 None
获取引脚的电平状态(输入模式有效) 调用GPIO_ReadInputDataBit()
函数原型
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
函数参数
参数一:GPIOx 想要读取的引脚端口 如 GPIOx A~K 如 GPIOF
参数二:GPIO_Pin 想要读取的引脚位置 GPIO_Pin_x where x can be (0..15)
返回值 返回读取到的引脚的电平状态 0或者1
GPIO寄存器开发流程
不管是图形界面还是函数库开发,本质都是对底层寄存器的使用,一般在实时性要求不高的情况,就使用前两种方式,但是对实时性要求比较高的情况下,就需要使用寄存器开发。所以就说明一下使用流程。
- 分析原理图,找到外设连接的芯片的引脚
- 分析原理图,理解硬件的控制原理
- 分析中文参考手册或者芯片数据手册,了解需要使用的寄存器都有哪些
- 理解寄存器的工作原理 参考中文参考手册 STM32的寄存器是32bit
- 找到寄存器的地址,根据地址写入数据 (数据手册+中文参考手册)
- 根据寄存器地址写入数据
位操作:
想要对地址进行操作,需要利用C语言的位操作符,位运算符有6种:
~ | & ^ << >>
~ 按位取反 0变1 1变0
& 按位与 两位同时为1,则结果为1 0 & x = 0
| 按位或 两位中有1位为1,则结果为1 1 | x = 1
^ 按位异或 两位相同,结果为0,不同为1
<< 左移运算符 高位舍弃,低位补0 0110 1101 << 3 ----- 0110 1000
>> 右移运算符 原理同左移
把某一位清零 a &= ~(1<<3); 把某一位置1 a |= (1<<3);
需要把地址先转换为指针,然后再进行操作 *(volatile unsigned int *)0x12345678 = 1234;
外设的寄存器的地址都定义在stm32f4xx.h这个头文件中,不需要自己定义寄存器的地址
举例:利用寄存器的方式点亮LD0 代码如下