本节主要介绍GPIO的系统函数以及如何使用
GPIO介绍
GPIO是通用输入输出端口的缩。GPIO口是一个可编程的引脚,我们通过对GPIO口里的寄存器进行配置,让CPU对特定的引脚输出高电平或低电平。GPIO口是由引脚、功能寄存器组成,不同的架构中的GPIO封装不同,所使用的引脚数与寄存器数不同。GPIO口的作用是用来控制连接在此GPIO口上的外设,我们一般通过原理图找到当前板子的GPIO口引出在哪个口上或者排针上,我们把我们的外设接到上面去就可以通过GPIO与这个外设进行交互控制,在驱动层我们通过读写GPIO口中的功能寄存器来改变连接在此GPIO上的外设状态。GPIO口的应用非常广泛,比如LED灯、按键、蜂鸣器、数码管等等,都可以通过GPIO口来控制。
每个 GPIO 引脚可以由软件配置为输出(推挽或开漏)、输入、外设备用功能或者模拟模式。每个 GPIO 引脚都可以配置为上拉、下拉或浮空。除模拟模式外,所有的 GPIO 引脚都具备大电流驱动能力。
每个通用 I/O端口都可以通过两个32 位的控制寄存器(GPIOx_CTL0/ GPIOx_CTL1)和一个32位的数据寄存器(GPIOx_OCTL)配置为8 种模式:模拟输入,浮空输入,上拉输入,下拉输入,GPIO 推挽输出,GPIO 开漏输出,AFIO 推挽输出和 AFIO 开漏输出。下图是标准I/O端口位的基本结构:
下面简单介绍一下GPIO为输入引脚时的上拉模式和下拉模式,以及GPIO为输出引脚时的推挽模式和开漏模式。
输入引脚上拉:开启上拉时引脚默认电压为高电平。
输入引脚下拉:开启下拉时引脚默认电压为低电平,这样就可以消除引脚不定状态的影响。
将上拉和下拉的开关都关断,这种状态我们称为浮空模式,一旦配置成这个模式,引脚的电压是不确定的,如果用万用表测量此模式下管脚电压时会发现只有1v左右,而且还不时改变,所以一般情况下我们都会给引脚设置成上拉或者下拉模式,使它有一个默认状态。
输出引脚开漏:自身没有高电平输出能力,需要借助外部上拉电阻才能真正输出高电平,优势就是可以很方便的调节输出的电平,因为输出电平完全由上拉电阻连接的电源电平决定。所以在需要进行电平转换的地方,非常适合使用开漏输出。
输出引脚推挽:自身具有高低电平输出能力,不需要借助外部上拉电阻才能真正输出高电平。能真正的输出高电平和低电平,在两种电平下都具有驱动能力。所谓的驱动能力,就是指输出电流的能力。对于驱动大负载(即负载内阻越小,负载越大)时,例如IO输出为5V,驱动的负载内阻为10ohm,于是根据欧姆定律可以正常情况下负载上的电流为0.5A(推算出功率为2.5W)。显然一般的IO不可能有这么大的驱动能力,也就是没有办法输出这么大的电流。于是造成的结果就是输出电压会被拉下来,达不到标称的5V。如果只是数字信号的传递驱动能力则完全够用。
各个工作模式对应的电气原理图感兴趣可以自行搜索。
GD32库函数介绍
下面主要介绍一些核心库函数:
/* 函数名 rcu_periph_clock_enable(rcu_periph_enum periph)
* 输入值 periph RCU外设
* 返回值 无
* 功能 启用外设时钟
* 使用举例 rcu_periph_clock_enable(RCU_GPIOA);//使能PA端口时钟
*/
/* 函数名 gpio_init(uint32_t gpio_periph, uint32_t mode, uint32_t speed, uint32_t pin)
* 输入值 gpio_periph GPIO端口 mode GPIO工作模式 speed GPIO最大输出速度 pin GPIO引脚
* 返回值 无
* 功能 初始化相关GPIO引脚,配置工作模式和最大输出速度
* 使用举例 gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);//LED1 PB2 输出初始化 推挽模式 速度50Mhz
*/
/* 函数名 gpio_bit_set(uint32_t gpio_periph,uint32_t pin)
* 输入值 gpio_periph GPIO端口 pin GPIO引脚
* 返回值 无
* 功能 输出引脚电平拉高
* 使用举例 gpio_bit_set(GPIOB, GPIO_PIN_2);//PB2 输出高电平
*/
/* 函数名 gpio_bit_reset(uint32_t gpio_periph,uint32_t pin)
* 输入值 gpio_periph GPIO端口 pin GPIO引脚
* 返回值 无
* 功能 输出引脚电平拉低
* 使用举例 gpio_bit_reset(GPIOB, GPIO_PIN_2);//PB2 输出低电平
*/
以GPIO输出模式为例,主要分为三个步骤:
- 配置GPIO端口时钟,如果需要使用PA7引脚,需要先初始化PA端口的时钟。配置代码如下:
/* 函数名 rcu_periph_clock_enable(rcu_periph_enum periph)
* 输入值 periph RCU外设
* 返回值 无
* 功能 启用外设时钟
* 使用举例 rcu_periph_clock_enable(RCU_GPIOA);//使能PA端口时钟
*/
rcu_periph_clock_enable(RCU_GPIOA); //使能PA端口时钟
- 配置GPIO工作模式,这里以输出推挽模式为例,配置代码如下:
/* 函数名 gpio_init(uint32_t gpio_periph, uint32_t mode, uint32_t speed, uint32_t pin)
* 输入值 gpio_periph GPIO端口 mode GPIO工作模式 speed GPIO最大输出速度 pin GPIO引脚
* 返回值 无
* 功能 初始化相关GPIO引脚,配置工作模式和最大输出速度
* 使用举例 gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
*/
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
// PA7 输出初始化 推挽模式 速度50Mhz
- 如果选择输出模式,根据需要设置电平状态,这里初始化低电平为例,配置代码如下:
/* 函数名 gpio_bit_set(uint32_t gpio_periph,uint32_t pin)
* 输入值 gpio_periph GPIO端口 pin GPIO引脚
* 返回值 无
* 功能 输出引脚电平拉高
* 使用举例 gpio_bit_set(GPIOB, GPIO_PIN_2);//PB2 输出高电平
*/
gpio_bit_set(GPIOA, GPIO_PIN_7); //PA7输出高电平
库函数使用实例
下面以LED的初始化举例,下面是对应代码:
//LED 初始化配置
/* 函数名 LED_config(void)
* 输入值 无
* 返回值 无
* 功能 初始化LED相关GPIO引脚,初始状态 LED都熄灭
* 使用举例 LED_config();
*/
void LED_config()
{
//使能对应引脚时钟
rcu_periph_clock_enable(RCU_GPIOA); //使能PA端口时钟
rcu_periph_clock_enable(RCU_GPIOB); //使能PB端口时钟
//设置对应工作模式
gpio_init(LED1_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, LED1_PIN);//LED1 PB2 输出初始化 推挽模式 速度50Mhz
gpio_init(LED2_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, LED2_PIN);//LED2 PB1 输出初始化 推挽模式 速度50Mhz
gpio_init(LED3_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, LED3_PIN);//LED3 PB0 输出初始化 推挽模式 速度50Mhz
gpio_init(LED4_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, LED4_PIN);//LED4 PA7 输出初始化 推挽模式 速度50Mhz
//初始化输出状态
gpio_bit_set(LED1_PORT, LED1_PIN); //输出高电平 LED灭
gpio_bit_set(LED2_PORT, LED2_PIN); //输出高电平 LED灭
gpio_bit_set(LED3_PORT, LED3_PIN); //输出高电平 LED灭
gpio_bit_set(LED4_PORT, LED4_PIN); //输出高电平 LED灭
}
这里自行编写了一个GPIO的电平翻转函数:
//引脚翻转函数
/* 函数名 gpio_bit_toggle(uint32_t gpio_periph,uint32_t pin)
* 输入值 gpio_periph GPIO端口 pin GPIO引脚
* 返回值 无
* 功能 输出引脚电平翻转 如果是高变成低,如果是低变成高
* 使用举例 gpio_bit_toggle(GPIOB, GPIO_PIN_2);//翻转PB2 输出电平,
*/
void gpio_bit_toggle(uint32_t gpio_periph,uint32_t pin)
{
if(gpio_output_bit_get(gpio_periph, pin)==SET) //如果当前输出是高电平
gpio_bit_reset(gpio_periph, pin); //翻转为低电平
else //如果当前输出是低电平
gpio_bit_set(gpio_periph, pin); //翻转为高电平
}
根据LED的电气原理图编写点亮代码
代码如下:
//开LED
/* 函数名 LED_on(uint8_t LED)
* 输入值 LED 范围为1-4,代表4个LED
* 返回值 无
* 功能 点亮对应LED
* 使用举例 LED_on(LED1);
*/
void LED_on(uint8_t LED)
{
switch (LED)
{
case LED1:
gpio_bit_reset(LED1_PORT, LED1_PIN); //输出低电平 LED1亮
break;
case LED2:
gpio_bit_reset(LED2_PORT, LED2_PIN); //输出低电平 LED2亮
break;
case LED3:
gpio_bit_reset(LED3_PORT, LED3_PIN); //输出低电平 LED3亮
break;
case LED4:
gpio_bit_reset(LED4_PORT, LED4_PIN); //输出低电平 LED4亮
break;
}
}