本帖最后由 lulugl 于 2024-2-29 07:19 编辑

【学习目的】
1、了解GD32H7xx系列GPIO的基本情况。
2、熟悉GPIO的寄存器。
3、掌握基于寄存器、库函数的GPIO的输出配置、使用GPIO点亮开发板上的LED灯。
【学习资料】
1、《GD32H7xx系列软件开发指南》
2、《GigaDevice Semiconductor》
3、《GD32H759I-EVAL评估板使用指南 Rev1.0》
4、示例《01_GPIO_Running_LED》
【简介】
GD32H7xx最多可支持 135 个通用 I/O 引脚(GPIO),分别为 PA0 ~ PA10,PA13 ~ PA15,PB0 ~ PB15,PC0 ~ PC15,PD0 ~ PD15,PE0 ~ PE15,PF0 ~ PF15,PG0 ~ PG15,PH0 ~ PH15,PJ8~ PJ11,PK0 ~ PK2。各片上设备用其来实现逻辑输入/输出功能。每个 GPIO 端口有相关的控制和配置寄存器以满足特定应用的需求。片上设备 GPIO 引脚的外部中断由 EXTI 模块的寄存器控制和配置。
GPIO 端口和其他的备用功能(AFs)备用引脚,在特定的封装下获得最大的灵活性。GPIO 引脚通过配置相关的寄存器可以用作备用功能引脚,备用功能输入/输出都可以。每个 GPIO 引脚可以由软件配置为输出(推挽或开漏)、输入、外设备用功能或者模拟模式。每个 GPIO 引脚都可以配置为上拉、下拉或无上拉/下拉。除模拟模式外,所有的 GPIO 引脚都具备大电流驱动能力。
 输入/输出方向控制;
【GPIO的特征】
  • 施密特触发输入功能使能控制;
  • 每个引脚都具有弱上拉/下拉功能;
  • 推挽/开漏输出使能控制;
  • 置位/复位输出使能;
  • 可编程的边沿触发外部中断-由 EXTI 寄存器配置;
  • 模拟输入/输出配置;
  • 备用功能输入/输出配置;
  • 端口锁定配置;
  • 单周期输出翻转功能

【功能说明】
每个通用I/O端口都可以通过32位控制寄存器(GPIOx_CTL)配置为GPIO输入,GPIO输出,AF功能或模拟模式。引脚AFIO输入/输出是通过AFIO功能使能来选择。当端口配置为输出(GPIO输出或AFIO输出)时,可以通过GPIO输出模式寄存器(GPIOx_OMODE)配置为推挽或开漏模式。输出端口的最大速度可以通过GPIO输出速度寄存器(GPIOx_OSPD)配置。每个端口可以通过GPIO上/下拉寄存器(GPIOx_PUD)配置为浮空(无上拉或下拉),上拉或下拉功能。
【学习心得1】
GD32H759EVAL开发板是这系列处理最齐全,容量最大的一款。他的IO当然也是最多的一个。从《开发指南》其对GPIO的配置的功能描述中,如果需要使用GPIO,需要对他的四个寄存器进行配置即:
1、控制寄存器(GPIOx_CTL):它是控制GPIO工作在输入、输出、AF或者是模拟模式,点用2个bit来配置。
2、模式寄存器(GPIOx_OMODE):配置为推挽或开漏模式。
3、输出速度寄存器(GPIOx_OSPD):输出端口的最大速度设置。
4、上/下拉寄存器(GPIOx_PUD): 配置为浮空(无上拉或下拉),上拉或下拉功能。
跟其他的MCU的GPIO的外设置是一样的,只是他的命名方式不一样而已。在表10-1GPIO配置表中详细的列出了16种配置方式,对应的控制寄存器、模式寄存器、下/下寄存器的配置列表。如果我们使用寄存器编程,这里就是查找配置之处。
在10.3.9中描述了GPIO锁定功能,这个是我第一次接触到这个功能,其文档描述如下:
GPIO的锁定机制可以保护I/O端口的配置。
被 保 护 的 寄 存 器 有 : GPIOx_CTL , GPIOx_OMODE , GPIOx_OSPD , GPIOx_PUD 和
GPIOx_AFSELy(y=0..1)
。通过配置32位锁定寄存器(GPIOx_LOCK)可以锁定I/O端口的配置。
当特定LOCK序列写到位于GPIOx_LOCK寄存器的LKK位上,并且LKy被置位,那么对应的端
口配置直到下一次复位前将不能改变。建议在电源驱动模块驱动的配置时使用锁定功能。
还有就是I/O补偿单元,这个可以在实际的应用来观察其效果。
【寄存器】
GPIO共有14个寄存器,我们如果只用个输出控制LED的话,配置只需要学习个寄存器即可即:
1、端口控制寄存器(GPIOx_CTL)选择输出 配置为:01:GPIO输出模式,即如果设置PA0 为输出模式就是将GPIOA_CTL 第1、0bit位置一:GPIOA_CTL |= 0x01;
2、端口输出模式寄存器(GPIOx_OMODE)选择推挽输出,配置为即如果设置PA0 为推挽输出,则将GPIOA_OMODE第bit0设置为0;
3、输出速度寄存器(GPIOx_OSPD)GD32H7xx可以选择4例输出速度,我们选择PA0最大速度为100M/时配置为11,即GPIOA_OSPD |= 0x03;
在指南中有如下描述:
1:0OSPD0[1:0]
Pin 0输出最大速度位
该位由软件置位和清除。
00:输出最大速度12M(复位值)
01:输出最大速度60M
10:输出最大速度85M
11:输出最大速度100M/220M
4、上拉/下拉寄存器(GPIOx_PUD),如果PA0配置为上拉输出,则:GPIOA_PUD |= 0x01;
其描述为:
PUD0[1:0] Pin 0上拉或下拉位
该位由软件置位和清除。
00:悬空模式,无上拉和下拉(复位值)
01:端口上拉模式
10:端口下拉模式
11:保留
到此,我们的配置就完成。需要控制IO输出高低电平,还需要用到输出控制寄存器(GPIOx_OCTL)
如果将PA0输出高电平,配置为:GPIOA_OCTL |= 0x01;
PA0 输出低电平,配置为: GPIOA_OCTL &= 0x00;
15:0
OCTLy
端口输出控制位(y=0..15)
该位由软件置位和清除。
0:引脚输出低电平
1:引脚输出高电平
到此,我们学习使用GPIO的输出功能就基本完成,如果想使用翻翻、位操作,还可以学习端口位操作寄存器(GPIOx_BOP)或者 位翻转寄存器(GPIOx_TG)。
【代码实验】
通过上面的学习,我们可以把示例的相关代码修改为如下:
  1. int main(void)
  2. {
  3.     /* enable the CPU Cache */
  4.     cache_enable();
  5.     /* configure systick */
  6.     systick_config();

  7.     /* enable the LED GPIO clock */
  8.     rcu_periph_clock_enable(RCU_GPIOF);
  9.     rcu_periph_clock_enable(RCU_GPIOA);
  10.     /* configure LED1 GPIO pin */
  11. //   gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_10);
  12. GPIO_CTL(GPIOF) &= ~(0x03<<20); //由于复位后为11,所以需要先清零
  13.   GPIO_CTL(GPIOF) |= (0x01<<20);
  14.   //  gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_10);
  15.    GPIO_OMODE(GPIOF) &= (uint32_t)(~GPIO_PIN_10);
  16.     /* configure LED2 GPIO pin */
  17.   //  gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_6);
  18. GPIO_CTL(GPIOA) &= ~(0x03<<12); //由于复位后为11,所以需要先清零
  19.   GPIO_CTL(GPIOA) |= (0x02<<12);
  20.    // gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_6);
  21.   GPIO_OMODE(GPIOA) &= (uint32_t)(~GPIO_PIN_6);
  22.     /* reset LED1 GPIO pin */
  23.    // gpio_bit_reset(GPIOF, GPIO_PIN_10);
  24.   GPIO_OCTL(GPIOF) &= (uint32_t)(~GPIO_PIN_10);
  25.     /* reset LED2 GPIO pin */
  26.    // gpio_bit_reset(GPIOA, GPIO_PIN_6);
  27.   GPIO_OCTL(GPIOA) &= (uint32_t)(~GPIO_PIN_6);
  28.     while(1) {
  29.         /* turn on LED1, turn off LED2 */
  30.        // gpio_bit_set(GPIOF, GPIO_PIN_10);
  31.     GPIO_OCTL(GPIOF) |= (uint32_t)(GPIO_PIN_10);
  32.        // gpio_bit_reset(GPIOA, GPIO_PIN_6);
  33.     GPIO_OCTL(GPIOA) &= (uint32_t)(~GPIO_PIN_6);
  34.         delay_1ms(500);
  35.          
  36.         /* turn on LED2 and LED1 */
  37.         gpio_bit_set(GPIOA, GPIO_PIN_6);
  38.         gpio_bit_set(GPIOF, GPIO_PIN_10);
  39.         delay_1ms(500);

  40.         /* turn off LED1 and LED2 */
  41.         gpio_bit_reset(GPIOA, GPIO_PIN_6);
  42.         gpio_bit_reset(GPIOF, GPIO_PIN_10);
  43.         delay_1ms(500);
  44.     }
  45. }
到此学习GPIO的输出功能就到这里了,期待拿到开发板进行验证。
其实如果需要再精减代码,可以直接操作内存,如GPIOA_CTL的基础为:
(*(volatile uint32_t *)(uint32_t)0x5802 0000) |= (0x2<<6)
等效于: GPIO_CTL(GPIOA) |= (0x02<<6);
这样运行效率高。。。但是阅读效果不好。。。