我的内核学习笔记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。
注意,不同平台实现的机制不同,但大体上都有这三个步骤,。

注意:

当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:

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

复用GPIO示例:

GPIO8_0 himm 0x112F0020 0x604
GPIO8_1 himm 0x112F0024 0x604
GPIO8_2 himm 0x112F0028 0x504
GPIO8_3 himm 0x112F002C 0x404

设置 GPIO8 指定引脚的方向:

himm 0x120D8400 0x1F

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

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 的命令行工具,可以使用该工具直接对寄存器地址进行设置。 该工具的原理细节不展开,只描述大概涉及的点:

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);

#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 源码。
https://blog.csdn.net/qingzhuyuxian/article/details/82981613

李迟 2020.6.21 周日  晚

你可能感兴趣的:(内核驱动学习笔记)