gpio就是一个通用的输入输出口,每一个引脚都是一个gpio,单片机可以获取这个引脚上的电平的高低,也可以控制这个引脚的电平高低,往往一个gpio都会复用别的功能,例如I2C,SPI等用于通信的引脚
gpio的原理图如下所示
对于一般单片机来说,一个gpio口有以下几种工作模式
浮空输入、上拉输入、下拉输入、模拟输入和以及输出模式的推挽输出、开漏输出、复用输出,下面来一一讲一下这些工作模式
下图是一个浮空输入的链路,让我们依次从右往左看
1.保护二极管,引脚的输入就是两个保护二极管,当IO端口输入电平高于VDD,则上管导通,IO输入变为VDD;当输入电平低于VSS,则下管导通,输入电平变为VSS,这两个二极管起到了保护内部电路不被高电压烧毁;但如果持续给过高的输入,保护二极管也会烧毁,所以还是要注意外部的输入
2.再往里走,再TTL触发器和保护二极管之间,就是上下拉电阻,当把任意一个接通,则该IO变为上拉/下拉输入
3.TTL施密特触发器,这个器件起到了滤波的作用,假设你要去读取IO的电平状态,那么究竟什么时候是0,什么时候是1 我们用万用表可以测引脚上的电压假设为2V,那么究竟是1还是0 假设MCU认为高于2V为1,低于2V为0,那么外部一个2V的输入,就会因为电磁干扰不停的切换01的状态,TTL就能起到滤波的作用,当外部输入大于2.5V,从0切换至1;当外部输入低于1V,从1切换至0,那么输入再1V-2.5V之间的波动都会被过滤掉
4.模拟输入和复用输入,分别是单片机直接控制该引脚的电平高低,不依赖外部输入;以及把该IO复用为timer或i2c等功能使用,在STM32的初始化过程中,我们需要先配置GPIO,再把GPIO复用为某个功能,然后再初始化这个功能引脚;而HPM的芯片,如果我们复用某个引脚为GPIO,则直接选择功能复用,然后配置该功能引脚即可,省略了GPIO init的过程,同时也不需要根据不同功能开启不同时钟,再board_init中,已经做好了这一切
5.数据输入寄存器,没啥好说的,想知道里面的值就去读
接下来看一下输出的链路,从左往右看
1.输出这边有一些位设置/清楚寄存器,是因为当IO作为输出时,能够做翻转,置高置低等功能,所以会略微复杂一点,但暴露给软件的接口还是简单的,你想写高写低,还是翻转,都直接用函数去调用就可
2.复用功能输出就是把这个IO口当别的功能用
3.输出控制电路和右边的两个MOS,这决定了输出是什么模式,如果两个MOS都导通,那么这个输出就有输出高低电平的能力,也就是推挽输出;当PMOS截止,NMOS导通,那么这个IO的输出就只有输出低电平或浮空的能力,也就是开漏输出
4.从链路上可以看见,当这个GPIO被配置为输出,我们也能从输入数据读取寄存器中获取当前IO的状态
接下来HPM6300evk为例介绍一下用户手册中一些重要的寄存器,其余型号的板子在GPIO这块的功能都是一致的
PAD[FUNC_CTL]
共32个位域,该寄存器0-4位控制引脚的功能复用,8位选择模拟输入,16位控制是否回采,其余为预留位
PAD[PAD_CTL]
共32个位域,用于控制上下拉,中断使能等功能,下面列出一些重要的部分
24位用于控制中断使能
17位用于控制上下拉使能
18位用于选择上拉/下拉
通过以上两个寄存器,就可以实现所有工作模式的初始化
以下代码是gpio的example中对引脚初始化的部分
pad_ctl 中,将PE置1,把PS置1,对照上面的寄存器表,可以看到是上下拉使能,然后选择上拉
func_ctl则是引脚复用的选择
这部分代码让人困惑的是,尤其是对于习惯了STM32编程习惯的人来说,直接写寄存器来初始化IO,显得又不专业,也不优雅;
还有HPM_BIOC这一行代码,sample中注释写了PZ必须配置BIOC,这部分也让人很困惑
但这种配置的设计原因,是因为HPM同时提供了一种基于vscode的插件,可以一键生成这部分代码,使用该pimmux工具,可以通过UI的方式直接配置包括GPIO,I2C,SPI在内的一切功能,只要该引脚能复用这个功能,那么初始化部分就可以一键生成
生成代码如下
你只需要把把代码复制进工程,就能完成你引脚的初始化设置,从而专注于功能的实现上
最后讲一下常用的几个函数
1.引脚电平读取,对于外部可用的引脚,GPIO base address均为在hpm_soc.h内定义好的HPM_GPIO0
/* GPIO0 base pointer */
#define HPM_GPIO0 ((GPIO_Type *) HPM_GPIO0_BASE)
2.Port选择你是PX,PA就选A,PB就选B,可以在hom_gpio_regs.h内查看具体的值,本质上也是一个无符号整数,直接传值也可以
3.pin就是第几个引脚,PA09,那你就传个9进来,也就是如果要获取PA09的电平,你就这样
uint8_t pin_value = gpio_read_pin(HPM_GPIO0,GPIO_DI_GPIOA,9);
在初始化之后,如果还想改引脚的输入输出状态,可以调用这两个函数,当然你也可以用他们来初始化,本质都是写寄存器,不过初始化还是可以试试hpm的pinmux工具,非常好用
引脚电平翻转,以及写高写低
GPIO当然也支持中断,还有一个GPIOM管理器,用于控制多核之间的IO隔离
因为还不是很清楚,留着之后再讲