【海思篇】【Hi3516DV300】十三、添加GPIO管理接口和手动实现一个GPIO调试工具

目的:应用层快捷、方便的配置gpio

1 gpio工作方式-通用输入输出

gpio获取步骤:
1 管脚复用
2 获取gpio方向和值
gpio配置步骤:
1 管脚复用
2 配置gpio方向
3 如果是输出方向,可以配置gpio值

2 接口设计

typedef struct _tagPINMUX_REG_S
{
    HI_U32 u32Reg;
    HI_U32 u32Val;
}PINMUX_REG_S;

HI_S32 HI_GPIOMNG_Init(PINMUX_REG_S *paPinmuxReg, HI_U8 u8PinmuxRegSize);
HI_S32 HI_GPIOMNG_Denit();
HI_S32 HI_GPIOMNG_SetDir(HI_U8 u8Gpio, HI_U8 u8Dir);
HI_S32 HI_GPIOMNG_GetDir(HI_U8 u8Gpio);
HI_S32 HI_GPIOMNG_SetVal(HI_U8 u8Gpio, HI_U8 u8Val);
HI_S32 HI_GPIOMNG_GetVal(HI_U8 u8Gpio);

HI_GPIOMNG_Init:管脚复用和gpio寄存器地址映射到应用层
HI_GPIOMNG_Denit:关闭gpio寄存器地址映
HI_GPIOMNG_SetDir:配置gpio方向(0输入,1输出)
HI_GPIOMNG_GetDir:获取gpio方向(0输入,1输出)
HI_GPIOMNG_SetVal:配置gpio值(0 低,1 高)
HI_GPIOMNG_GetVal:获取gpio值(0 低,1 高)
u8Gpio:海思gpio号计算公式:u8Gpio=GpioGroup*8+GpioBit

3 接口实现

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "hi_type.h"
#include "hi_gpiomng.h"

#define GPIO_BASE_ADDR                            (0x120D0000)
#define GPIO_GRP(group_id)                        (g_pGpioBaseAddr+0x1000*group_id)
#define GPIO_DATA(group_reg_base, gpio_offset)    (((group_reg_base) + 0x000) + (1 << ((gpio_offset) + 2)))
#define GPIO_DIR(group_reg_base)                  ((group_reg_base) + 0x400)
#define GPIO_IS(group_reg_base)                   ((group_reg_base) + 0x404)
#define GPIO_IBE(group_reg_base)                  ((group_reg_base) + 0x408)
#define GPIO_IEV(group_reg_base)                  ((group_reg_base) + 0x40C)
#define GPIO_IE(group_reg_base)                   ((group_reg_base) + 0x410)
#define GPIO_RIS(group_reg_base)                  ((group_reg_base) + 0x414)
#define GPIO_MIS(group_reg_base)                  ((group_reg_base) + 0x418)
#define GPIO_IC(group_reg_base)                   ((group_reg_base) + 0x41C)

#define PAGE_SIZE_MASK (~(0xfff))
#define PAGE_SIZE 0x1000
#define GPIO_MAP_MEM_SIZE 0xC000

static const char dev[]="/dev/mem";
static HI_VOID *g_pGpioBaseAddr = NULL;
static HI_BOOL g_bGpioInit = HI_FALSE;

HI_VOID * COMM_MMAP(HI_U32 u32RetAddr, HI_U32 u32MapMemSize)
{
	HI_S32 fd = open (dev, O_RDWR | O_SYNC);
	if (fd < 0)
	{
	    printf("open %s error!\n", dev);
	    return NULL;
	}
    
	/* addr align in page_size(4K) */
	unsigned long phy_addr_in_page;
	unsigned long page_diff;
	phy_addr_in_page = u32RetAddr & PAGE_SIZE_MASK;
	page_diff = u32RetAddr - phy_addr_in_page;

	/* size in page_size */
	unsigned long size_in_page;
	unsigned long size = u32MapMemSize;
	size_in_page =((size + page_diff - 1) & PAGE_SIZE_MASK) + PAGE_SIZE;

	void *addr = mmap((void *)0, size_in_page, PROT_READ|PROT_WRITE, MAP_SHARED, fd, phy_addr_in_page);
	if (addr == MAP_FAILED)
	{
	    printf("mmap @ 0x%x error!\n", phy_addr_in_page);
	    close(fd);
	    return NULL;
	}

  close(fd);
  
	return addr+page_diff;
}

HI_VOID COMM_MUNMAP(HI_VOID *pRegAddr, HI_U32 u32MapMemSize)
{
	munmap(pRegAddr, u32MapMemSize);
}


HI_S32 HI_GPIOMNG_Init(PINMUX_REG_S *paPinmuxReg, HI_U8 u8PinmuxRegSize)
{
  HI_S32 s32Ret;
  HI_S32 i;
  // PINMUX
  for(i = 0; i < u8PinmuxRegSize; i++)
  {
    HI_VOID *pRegAddr = COMM_MMAP(paPinmuxReg[0].u32Reg, PAGE_SIZE);
    if(pRegAddr)
    {
      // 判断功能位, 这样可以保护以前已经复用过且配置好的管脚高低状态
      if((*((HI_U32 *)pRegAddr)&0x0f) != (paPinmuxReg[0].u32Val&0x0f))
      {
        *((HI_U32 *)pRegAddr) = paPinmuxReg[0].u32Val;
      }
      COMM_MUNMAP(pRegAddr, PAGE_SIZE);
    }
    else
    {
      printf("gpio pinmux failed!");
      return HI_FAILURE;
    }
  }

  // MAP GPIO_BASE
  g_pGpioBaseAddr = COMM_MMAP(GPIO_BASE_ADDR, GPIO_MAP_MEM_SIZE);
  if(g_pGpioBaseAddr == NULL)
  {
     printf("gpio map failed!");
     return HI_FAILURE;
  }

  g_bGpioInit = HI_TRUE;
  return HI_SUCCESS;
}

HI_S32 HI_GPIOMNG_Denit()
{
  if(g_bGpioInit)
  {
    COMM_MUNMAP(g_pGpioBaseAddr, GPIO_MAP_MEM_SIZE);
    g_bGpioInit = HI_FALSE;
  }

  return HI_SUCCESS;
}

HI_S32 HI_GPIOMNG_SetDir(HI_U8 u8Gpio, HI_U8 u8Dir)
{
  if(!g_bGpioInit) return HI_FAILURE;
  HI_U8 u8GpioGroup;
  HI_U8 u8GpioBit;
  HI_VOID *pGroupAddr;
  HI_VOID *pDirAddr;

  u8GpioGroup = u8Gpio/8;
  u8GpioBit = u8Gpio%8;
  pGroupAddr = GPIO_GRP(u8GpioGroup);
  pDirAddr = GPIO_DIR(pGroupAddr);
  if(u8Dir)
  {
    *(HI_U32 *)pDirAddr |= (0x1<<u8GpioBit);
  }
  else
  {
    *(HI_U32 *)pDirAddr &= ~(0x1<<u8GpioBit);
  }

  return HI_SUCCESS;
}

HI_S32 HI_GPIOMNG_GetDir(HI_U8 u8Gpio)
{
  if(!g_bGpioInit) return HI_FAILURE;
  HI_U8 u8GpioGroup;
  HI_U8 u8GpioBit;
  HI_VOID *pGroupAddr;
  HI_VOID *pDirAddr;

  u8GpioGroup = u8Gpio/8;
  u8GpioBit = u8Gpio%8;
  pGroupAddr = GPIO_GRP(u8GpioGroup);
  pDirAddr = GPIO_DIR(pGroupAddr);
  if((*(HI_U32 *)pDirAddr)&(0x1<<u8GpioBit))
  {
    return 1;
  }
  else
  {
    return 0;
  }
}

HI_S32 HI_GPIOMNG_SetVal(HI_U8 u8Gpio, HI_U8 u8Val)
{
  if(!g_bGpioInit) return HI_FAILURE;
  HI_U8 u8GpioGroup;
  HI_U8 u8GpioBit;
  HI_VOID *pGroupAddr;
  HI_VOID *pDataAddr;

  u8GpioGroup = u8Gpio/8;
  u8GpioBit = u8Gpio%8;
  pGroupAddr = GPIO_GRP(u8GpioGroup);
  pDataAddr = GPIO_DATA(pGroupAddr, u8GpioBit);
  if(u8Val)
  {
    *(HI_U32 *)pDataAddr |= (0x1<<u8GpioBit);
  }
  else
  {
    *(HI_U32 *)pDataAddr &= ~(0x1<<u8GpioBit);
  }

  return HI_SUCCESS;
}

HI_S32 HI_GPIOMNG_GetVal(HI_U8 u8Gpio)
{
  if(!g_bGpioInit) return HI_FAILURE;
  HI_U8 u8GpioGroup;
  HI_U8 u8GpioBit;
  HI_VOID *pGroupAddr;
  HI_VOID *pDataAddr;

  u8GpioGroup = u8Gpio/8;
  u8GpioBit = u8Gpio%8;
  pGroupAddr = GPIO_GRP(u8GpioGroup);
  pDataAddr = GPIO_DATA(pGroupAddr, u8GpioBit);
  if((*(HI_U32 *)pDataAddr)&(0x1<<(u8GpioBit+2)))
  {
    return 1;
  }
  else
  {
    return 0;
  }

  return HI_SUCCESS;
}

测试(有LED等的测试结果可以验证):

/app # ./hi_debug -r -g 4
Get: GPIO[4] DIR[OUT] DATA[0]
/app # ./hi_debug -w -g 4 -D 0
Set Dir: GPIO[4] DIR[IN]
/app # ./hi_debug -w -g 4 -D 1
Set Dir: GPIO[4] DIR[OUT]
/app # ./hi_debug -w -g 4 -d 0
Set Data: GPIO[4] DIR[OUT] DATA[0]
/app # ./hi_debug -w -g 4 -d 1
Set Data: GPIO[4] DIR[OUT] DATA[1]

改进:
可以添加一个参数-c/–config, 用于提前配置管脚复用信息,可以更加灵活。

4 接口应用

准备基于gpio接口函数实现一个调试小工具,该工具具备以下功能:

  1. 可以读取gpio信息(方向和值)
  2. 可以配置gpio方向
  3. 可以配置gpio值

准备工作:
根据Hi35xx_PINOUT_CN.xls,列出开发板上应用会用到的gpio,用于HI_GPIOMNG_Init函数初始化gpio管脚复用。

其他方向:

  1. gpio按键驱动(我会在另一篇文章里实现)
  2. led灯管理(我会在另另一篇文章里实现)
#include 
#include 
#include 
#include 

#include "hi_type.h"
#include "hi_gpiomng.h"

static const char *short_options = "hrwg:D:d:";
static struct option long_options[] = {
	{"help", 0, 0, 'h'},
	{"read", 0, 0, 'r'},	// 
	{"write", 0, 0, 'w'},	// 
	{"gpio", 1, 0, 'g'},	// 
	{"dir", 1, 0, 'D'},	// 
	{"data", 1, 0, 'd'},	// 
	{0, 0, 0, 0}
};
static PINMUX_REG_S pinux_regs[] = {
  {0x112f0098, 0x0500}, // GPIO0_4
  {0x112F0008, 0x0500}, // GPIO6_0
  {0x112F000C, 0x0500}, // GPIO6_1
  {0x112F0010, 0x0500}, // GPIO6_2
};
static void usage(int argc, char *argv[])
{
  printf("The program is uesed to read/write gpio[ build by wei on %s ].\n", __DATE__);
	printf("usage: %s [OPTION]...\n", argv[0]);
	printf("\t-h, --help		help\n"
		 "\t-r, --read		read gpio\n"
		 "\t-w, --write		write gpio\n"
		 "\t-g, --gpio		gpio number(gpio_group*8+gpio_bit)\n"
		 "\t-D, --dir		gpio direction(0[in] or 1[out]\n"
		 "\t-d, --data		gpio data(0 or 1)\n");
	printf("Example:\n");
	printf(" --- Default: %s -r -g 4\n", argv[0]);
	printf("              %s -w -g 4 -d 1\n", argv[0]);
  printf("              %s -w -g 4 -D 0\n", argv[0]);
	printf("\n");

	
}
int main(int argc, char *argv[])
{
  int option_index, ch;
  int read = 0;
  int write = 0;
  int gpio = 0;
  int data = 0;
  int data_flag = 0;
  int dir = 0;
  int dir_flag = 0;
  while ((ch = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) {
		switch (ch) {
		case 'h':
			usage(argc, argv);
			return 0;
		case 'r':
			read = 1;
			break;
		case 'w':
			write = 1;
			break;
		case 'g':
			if(strchr(optarg, 'X') || strchr(optarg, 'x'))
			{
				gpio = strtol(optarg, NULL, 16);
			}
			else
			{
				gpio = strtol(optarg, NULL, 10);
			}
			break;
    case 'D':
      dir_flag = 1;
			if(strchr(optarg, 'X') || strchr(optarg, 'x'))
			{
				dir = strtol(optarg, NULL, 16)?1:0;
			}
			else
			{
				dir = strtol(optarg, NULL, 10)?1:0;
			}
			break;  
    case 'd':
      data_flag = 1;
			if(strchr(optarg, 'X') || strchr(optarg, 'x'))
			{
				data = strtol(optarg, NULL, 16)?1:0;
			}
			else
			{
				data = strtol(optarg, NULL, 10)?1:0;
			}
			break;
    default:
			printf("Try '--help' for more information.\n");
			return 1;
		}
	}

  if(optind <= argc-1);

  if(read)
  {
    HI_GPIOMNG_Init(pinux_regs, sizeof(pinux_regs)/sizeof(pinux_regs[0]));
    int gpioDir = HI_GPIOMNG_GetDir(gpio);
    int gpioDat = HI_GPIOMNG_GetVal(gpio);
    printf("Get: GPIO[%d] DIR[%s] DATA[%d]\n", gpio, gpioDir?"OUT":"IN", gpioDat);
    HI_GPIOMNG_Denit();
  }
  else if(dir_flag)
  {
    HI_GPIOMNG_Init(pinux_regs, sizeof(pinux_regs)/sizeof(pinux_regs[0]));
    HI_GPIOMNG_SetDir(gpio, dir);
    if(data_flag) 
    {
      HI_GPIOMNG_SetVal(gpio, data);
      printf("Set Dir: GPIO[%d] DIR[%s] DATA[%d]\n", gpio, dir?"OUT":"IN", data);
    }
    else
    {
      printf("Set Dir: GPIO[%d] DIR[%s]\n", gpio, dir?"OUT":"IN");
    }
    HI_GPIOMNG_Denit();
  }
  else if(write)
  {
    HI_GPIOMNG_Init(pinux_regs, sizeof(pinux_regs)/sizeof(pinux_regs[0]));
    HI_GPIOMNG_SetDir(gpio, 1);
    HI_GPIOMNG_SetVal(gpio, data);
    printf("Set Data: GPIO[%d] DIR[%s] DATA[%d]\n", gpio, "OUT", data);
    HI_GPIOMNG_Denit();
  }
  else
  {
    usage(argc, argv);
  }
  
  return 0;
}

5 其他gpio实现方式

  1. himm原始配置
#比如配置gpio0_4为输出,拉高
himm 0x112f0098 0x0500
himm 0x120d0400 0x10
himm 0x120d0040 0x10
  1. 通过/sys/class/gpio配置
#比如配置gpio0_4为输出,拉高
echo 4 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio4/direction
echo 1 > /sys/class/gpio/gpio4/value
echo 4 > /sys/class/gpio/unexport

6 优势

  1. 比himm调用方便、规整,可以封装库,调用简洁,使用方便;
  2. 比/sys/class/gpio快,封装成工具后比它更方便;

7 代码传送门

通用所有海思(有linux内核的)
hi_gpiomng

你可能感兴趣的:(【海思篇】【Hi3516DV300】十三、添加GPIO管理接口和手动实现一个GPIO调试工具)