我们前面已经知道该如何让LED点亮,那么接下来开始指导大家如何正确操作我们芯片的引脚去控制其亮灭。
配置流程
一般我们使用GPIO的端口,都需要有以下几个步骤。
开启GPIO的端口时钟
配置GPIO的模式
配置GPIO的输出
从LED介绍那一章节我们了解到LED1接的是单片机的PC13,我们要使用LED就需要配置GPIOC端口
。
开启GPIO的端口时钟
GD32的所有外设资源时钟默认都是关闭的,所以在配置外设之前,需要先开启对应的时钟。
要使能LED1,就要先开启GPIOC的时钟,从用户手册的第27页我们了解到GPIOC挂载在AHB1总线上,那操作GPIOC的时钟肯定要配置AHB1使能寄存器。
在用户手册的第110页,AHB1使能寄存器如图:
可以看到地址偏移量为0x30,那这个基地址是多少呢?因为AHB1使能寄存器是在RCU外设的地址范围内,所以RCU的外设基地址就是AHB1使能寄存器的基地址,在用户手册的第95页有明确说明,RCU基地址:0x4002 3800
。
那我们RCU_AHB1EN寄存器的地址就是基地址加上偏移量,0x4002 3800 + 0x30 = 0x4002 3830
。找到要操作的地址了,那我们怎么去配置这个值呢?我们继续看这个寄存器的说明,在用户手册的第115页有寄存器说明:
可以了解到,RCU_AHB1EN寄存器的第 2 位就是 GPIOC 端口时钟使能,我们要开启GPIOC端口时钟使能,就需要往RCU_AHB1EN的第2位写1,然后为了保持其他位不变,我们可以使用一个或运算
,也就是:
RCU_AHB1EN |= 0x00000004;
也就是相当于拉高RCU_AHB1EN寄存器的第2位,其他位保持不变。其实也可以写为
RCU_AHB1EN |= (1 << 2);
是一样的效果,这里的2就是寄存器的第2位,如果是3就是寄存器的第3位,依此类推。
配置GPIO模式
GPIO的模式配置可分为两步。
第一步就是通过控制寄存器(GPIOx_CTL)配置为
输入功能
/输出功能
/复用功能
或者模拟功能
。第二步就是通过 GPIO 上/下拉寄存器(GPIOx_PUD)配置GPIO的
上下拉模式
或者浮空
。
- 配置为输出功能
我们要控制LED,自然是配置GPIO为输出模式。找到控制寄存器(GPIOx_CTL):
可以看到GPIOX_CTL的地址偏移是0x00,我们要使能的是GPIOC的引脚,所以要加上GPIOC的基地址0x4002 0800
,即GPIOC_CTL寄存器的地址为0x4002 0800 + 0x00
。我们接着看这个寄存器的介绍说明
从图3-1-5可以看到每一个引脚都由2位控制,我们要操作的是PC13引脚,也就是pin13,也就是GPIOC_CTL寄存器的第27位和第26位。我们要配置为输出模式,也就是把这两位配置为01,我们用二进制来表示一下,
//从左到右,31~0
0000 0100 0000 0000 0000 0000 0000 0000
2
转换成十六进制就是0x04000000
。所以我们将GPIOC_CTL寄存器里面写入0x04000000
,就是把PC13这个引脚配置为了输出模式,为了保持其它位的数据不发生变化,我们先清空这两位,然后再往这两位里面写值。操作为
GPIO_CTL(GPIOC) &= 0xf3ffffff;
这句代码是清空第27位和第26位。下面代码就是把第27位和第26位配置为01,这样操作保证了其它位不发生变化。
GPIO_CTL(GPIOC) |= 0x04000000;
其实也可以这样配置
GPIO_CTL(GPIOC) &= ~(0x03 << (26*));*
这句代码就是把GPIOC_CTL寄存器的第27位和第26位清零
GPIO_CTL(GPIOC) |= (0x01 << (26));
这句代码就是把第27位和第26位配置为01。
- 配置为浮空模式,无上拉下拉
一般输出模式我们都配置为浮空模式,输入模式我们才需要考虑上拉还是下拉,根据默认电平状态进行判断。 第一步配置好为输出模式之后,我们还需要进行第二步配置,配置PC13为浮空模式,无上拉和下拉。关于端口上下拉寄存器如图
可以看到端口上下拉寄存器的地址偏移是0x0C,那GPIOC_PUD的地址就是0x4002 0800 + 0x0C
。我们接着往下看这个寄存器的介绍
可以看到,每一个引脚都由2位进行控制,和GPIOD_CTL寄存器的配置一样,也是先清空这两位,然后再配置这两位为00。转化为下面这两句代码。
GPIO_PUD(GPIOC) &= ~(0x03 << (2*13));
GPIO_PUD(GPIOC) |= (0x00 << (2*13));
2
其实我们发现,第二句代码其实不用写,因为第一句就把这两位清零了。
配置GPIO的输出
配置GPIO的输出也分为两步,第一步配置端口输出模式寄存器GPIOx_OMODE,第二步配置端口速度寄存器GPIOx_OSPD。
- 配置为推挽输出
当我们配置为输出模式的时候,可以有两种输出模式选择,一种是推挽输出模式,一种是开漏输出模式。
- 推挽输出: 可以输出高低电平,推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。
- 开漏输出: 输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。开漏输出可以很方便的调节输出的电平,因为输出电平完全是由上拉电阻连接的电源电平决定。开漏输出可以实现"线与"功能,就是可以把多个信号线连接在一起,只有当所有信号全部为高电平时,合在一起的总线为高电平,否则就为低电平。
开漏是需要外接上拉电阻才可以输出高电平,这里并不适合,所以需要设置为推挽输出。
端口输出模式寄存器如图:
可以看到端口输出模式寄存器的地址偏移为0x04,那GPIOC_OMODE寄存器的地址就是0x4002 0800 + 0x04
。我们接着往下看这个寄存器的介绍,如图:
可以看到每一个引脚由1位进行控制,要配置为推挽输出,只需要往对应的寄存器写0即可。转化为代码为
GPIO_OMODE(GPIOC) &= ~(0x01 << 13);
- 配置速度
我们配置引脚输出模式之后,我们还要选择这个引脚对应的速度,端口输出速度寄存器如图:
可以看到端口输出速度寄存器的地址偏移是0x08,那么GPIOC_OSPD寄存器的地址就是0x4002 0800 + 0X08
。我们接着往下看这个寄存器的介绍,如图:
可以看出每一个引脚由2位进行控制,要配置为多少等级,只需要往对应的位写入对应的值即可。从官方提供的代码中查找到了速度等级对应的频率关系,如下所示。
/* GPIO output max speed level */
#define OSPD_OSPD(regval) (BITS(0,1) & ((uint32_t)(regval) << 0))
#define GPIO_OSPEED_LEVEL0 OSPD_OSPD(0) /*!< output max speed level 0 */
#define GPIO_OSPEED_LEVEL1 OSPD_OSPD(1) /*!< output max speed level 1 */
#define GPIO_OSPEED_LEVEL2 OSPD_OSPD(2) /*!< output max speed level 2 */
#define GPIO_OSPEED_LEVEL3 OSPD_OSPD(3) /*!< output max speed level 3 */
/* GPIO output max speed value */
#define GPIO_OSPEED_2MHZ GPIO_OSPEED_LEVEL0 /*!< output max speed 2MHz */
#define GPIO_OSPEED_10MHZ GPIO_OSPEED_LEVEL1 /*!< output max speed 10MHz */
#define GPIO_OSPEED_25MHZ GPIO_OSPEED_LEVEL2 /*!< output max speed 25MHz */
#define GPIO_OSPEED_MAX GPIO_OSPEED_LEVEL3 /*!< output max speed MAX */
2
3
4
5
6
7
8
9
10
11
12
我们这里选择配置为25MHZ,所以需要配置为等级2,需要往对应的位写入二进制的10。转化为代码就是
GPIO_OSPD(GPIOC) &= ~(0x03 << (2 * 13));
GPIO_OSPD(GPIOC) |= (0x02 << (2 * 13));
2
通过这两句我们就可以把PC13引脚的速度配置为25MHZ。
到此,我们关于GPIO的配置就完成了。
配置GPIO输出高电平
配置好GPIO之后,我们就可以进行点灯了。其实也就是让GPIO引脚输出高低电平,到我们的开发板上就是让PC13输出高电平。
如何让PC13输出高电平呢?通过查阅用户手册7.4章节的GPIO寄存器,这里为大家总结了几种操作的方式。
端口输出控制寄存器
关于端口输出控制寄存器如图:
可以看到端口输出控制寄存器的地址偏移为0x14,那么对应的GPIOC_OCTL寄存器的地址为0x4002 0800 + 0X14
。我们接着往下看这个寄存器的介绍,如图
可以了解到GPIOC_OCTL寄存器的低16位有效,每一个引脚对应一位,往对应的位写0就是输出低电平,写1就输出高电平。
//输出低电平转换为代码为
GPIO_OCTL(GPIOC) &= ~ (0x01 << 13);
//输出高电平转换为代码为
GPIO_OCTL(GPIOC) |= (0x01 << 13);
2
3
4
端口位操作寄存器
关于端口位操作寄存器如图
可以看到端口位操作寄存器的地址偏移为0x18,那么对应的GPIOC_BOP寄存器的地址为0x4002 0800 + 0X18
。我们接着往下看这个寄存器的介绍,如图
可以了解到GPIOC_BOP寄存器的低16位是置1位,高16位是清0位。对于低16位每一个引脚对应一位,往对应的位写1就是输出高电平,写0电平状态不改变。对于高16位每一个引脚对应一位,往对应的位写1就是输出低电平,写0电平状态不改变。
//输出低电平转换为代码为
GPIOC_BOP |= (0x01 << (13 + 16));
//输出高电平转换为代码为
GPIOC_BOP |= (0x01 << 13);
2
3
4
端口位翻转寄存器
使用端口位翻转寄存器也有类似的功能,从名称就可以知道这个寄存器是把对应的位电平进行翻转,这是一个很不错的操作,使用起来也很方便。但前提是我们要知道当下的引脚电平是一个什么状态,然后我们翻转之后又是一个什么状态。关于这一部分就不过多说明,大家可以自行研究一下。
实验现象
如果用上面的代码需要注意一下,像RCU_BASE这种宏定义其实在GD32的头文件中已经定义了,可以直接使用原来定义的,这样我们就不用再编写,也可以把我们编写的名称稍微修改一下,比如在它们前面加上一个LED或者BSP等等都可。
关于这一章节的代码,可以在开发板资料/03 - 软件资料/代码例程/里面的001寄存器点灯
。
下载中心跳转📦
资料下载中心:点击跳转🚀
烧写我们的代码之后,可以看到开发板的LED这个灯将被点亮: