我的内核学习笔记15:海思Hi3516平台GPIO使用记录

本文介绍海思平台 Hi3516 的 GPIO 使用,包括手册介绍及用户空间控制。

一、GPIO概览

Hi3516 芯片支持 12 组GPIO(General Purpose Input/Output),即GPIO0~GPIO11。每组 GPIO 提供 8 个可编程的输入输出管脚(GPIO11 只有 4个) 。
每个管脚可以配置为输入或者输出。这些管脚用于生成特定应用的输出信号或采集特定应用的输入信号。作为输入管脚时,GPIO 可作为中断源;作为输出管脚时,每个 GPIO 都可以独立地清 0 或置 1。

对于引脚复用的说明,海思在 SDK 包中提供了一个 excel 表说明,该表十分重要,包括了引脚复用地址及对应比特的说明。由于内容较多,可进行搜索定位。

GPIO 寄存器对应基地址如下:
| GPIO控制器 | 基地址
| - | - |
| GPIO11 | 0x120D_B000
| GPIO10 | 0x120D_A000
| GPIO9 | 0x120D_9000
| GPIO8 | 0x120D_8000
| GPIO7 | 0x120D_7000
| GPIO6 | 0x120D_6000
| GPIO5 | 0x120D_5000
| GPIO4 | 0x120D_4000
| GPIO3 | 0x120D_3000
| GPIO2 | 0x120D_2000
| GPIO1 | 0x120D_1000
| GPIO0 | 0x120D_0000

从表格中看出,寄存器的基地址分布是十分有规律的。不同的寄存器,其操作的偏移量均相同。如 GPIO_DIR 偏移量为 0x400,不同 GPIO 寄存基地址加上该偏移量即为对应的 GPIO_DIR。

二、GPIO 控制

控制 GPIO 步骤分三步:先复用引脚为 GPIO 功能,再设置方向(输出或输入),寄存器为 GPIO_DIR,最后设置高低电平或读取引脚值,寄存器为 GPIO_DATA。
注意,不同平台实现的机制不同,但大体上都有这三个步骤,。

注意:

1
2
3
4
当GPIO_DIR相应的比特配置为输入时,有效读取的结果将返回管脚的值;当配置为输出的时候,有效读取的结果将返回写入的值。   
GPIO_DATA 寄存器利用PADDR[9:2]实现了读写寄存器比特的屏蔽操作。该寄存器对应256个地址空间。PADDR[9:2]分别对应 GPIO_DATA[7:0],当相应的bit为高时,则可以对相应的位进行读写操作;反之,若对应bit为低则不能进行操作。例如:
若地址为 0x3FC(0b11_1111_1100),则对GPIO_DATA[7:0]这8bit操作全部有效。
若地址为 0x200(0b10_0000_0000),则仅对GPIO_DATA[7]的操作有效。

GPIO_DIR 偏移值为 0x400,可单独设置某一引脚,也可同时设置多个 GPIO 引脚。GPIO_DATA 范围为 0~0x3fc。
根据手册和经验,得到下表:
| GPIO控制器 | 偏移量 | 值
| - | - | - |
| GPIOX_0 | 0x004 | 0x01
| GPIOX_1 | 0x008 | 0x02
| GPIOX_2 | 0x010 | 0x04
| GPIOX_3 | 0x020 | 0x08
| GPIOX_4 | 0x040 | 0x10
| GPIOX_5 | 0x080 | 0x20
| GPIOX_6 | 0x100 | 0x40
| GPIOX_7 | 0x200 | 0x80

上表中X表示不同的 GPIO 组(bank)。后面数字为其具体的引脚。

下面为演示示例。
红灯 高电平亮 GPIO2_7:

1
2
himm 0x120D2400 0x80  0x120D2000为GPIO2基地址,400为方向,0x80表示bit7为1 
himm 0x120D2200 0x80 接上,200表示GPIO7数据寄存器,80表示bit7为1

复用GPIO示例:

1
2
3
4
GPIO8_0 himm 0x112F0020 0x604
GPIO8_1 himm 0x112F0024 0x604
GPIO8_2 himm 0x112F0028 0x504
GPIO8_3 himm 0x112F002C 0x404

设置 GPIO8 指定引脚的方向:

1
himm 0x120D8400 0x1F

单独设置各个 GPIO 的方向及电平:

1
2
3
4
5
6
7
8
9
10
11
GPIO8_0:
himm 0x120D8400 0x1
himm 0x120D8004 0x1

GPIO8_1:
himm 0x120D8400 0x2
himm 0x120D8008 0x2

GPIO8_2:
himm 0x120D8400 0x4
himm 0x120D8010 0x4

三、himm工具

前面只是介绍寄存器及操作的值,SDK 提示了名为 himm 的命令行工具,可以使用该工具直接对寄存器地址进行设置。 该工具的原理细节不展开,只描述大概涉及的点:

1
2
3
void * memmap(unsigned long phy_addr, unsigned long size)
fd = open ("/dev/mem", O_RDWR | O_SYNC);
addr = mmap((void *)0, size_in_page, PROT_READ|PROT_WRITE, MAP_SHARED, fd, phy_addr_in_page);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#define DEFAULT_MD_LEN 128

void himm_value(unsigned long ulAddr, unsigned long ulValue)
{
void* pMem = NULL;
pMem = memmap(ulAddr, DEFAULT_MD_LEN);
*(unsigned int*)pMem = ulValue;
}


void gpio_output(int type, int value)
{
switch (type)
{
case GPIO8_0:
if (value == 1)
himm_value(0x120D8004, 1);
else if (value == 0)
himm_value(0x120D8004, 0);
break;
case GPIO8_1:
if (value == 1)
himm_value(0x120D8008, 2);
else if (value == 0)
himm_value(0x120D8008, 0);
break;
case GPIO8_2:
if (value == 1)
himm_value(0x120D8010, 4);
else if (value == 0)
himm_value(0x120D8010, 0);
break;
default:
break;
}
}

四、小结

由上述可知,海思平台的 GPIO 控制还是比较直观的,至少在代码层面可以与手册直接关联起来。另外,可以直接在用户空间进行操作(需其实现的内存映射模块)。当然也可以自己写驱动,只是实现相同的功能,但其底层的操作方式,是一样的。
一般嵌入式 Linux 系统的 GPIO 操作,需要计算某个引脚属于系统的第几个 GPIO 号,需要进行换算。海思平台不再需要,不过,是否也可实现传统的那种方式,还没研究。

五、未完事宜

笔者实际上接触海思仅几天时间,该平台的使用和之前做的方法不太一样,有必须记录下来。本文没有涉及 GPIO 的输入、中断,等。

六、参考资料

海思 Hi3516DV300 SoC 用户指南。
SDK 源码。

李迟 2020.6.21 周日 晚