> 文章列表 > Linux操作GPIO口或I2C

Linux操作GPIO口或I2C

Linux操作GPIO口或I2C

GPIO口是一种非常简单的IO操作,也非常常用接口,一般用于设备控制外部接入或输出开关;

通常来说,控制GPIO可以通过主板上原生IO口,也可以通过I2C芯片扩展IO两种方式。原生IO比较好理解,一般来说,通过输出1或0就可以实现拉高拉低操作。I2C芯片扩展呢?主板这侧通过I2c通讯协议,操作芯片(例如PCA9555、PCA9534之类)寄存器的值,由芯片控制其外接IO,这种芯片逻辑结构非常简单,有扩展8路IO、也有16路的,像某些主板或开发板都是通过类似信息扩展然后支持触摸屏等外设,内核挂载个驱动即可,因此,如果我们自己要控制的话,就需要了解I2C通讯协议以及相关知识。

不管原生还是扩展的,都需要硬件原理图支持,可能还需要内核的支持,对于应用开发者,这需要多种角色配合或者你自己搞得定。

1、如何控制原生GPIO?

很简单,首先要确认IO对应number。一般来说,硬件会告诉你,xx外设接在GPIOx_Yz (例如:GPIO4_D2),那么用户态怎么read or write?

  1. 先计算number:
number = x * 32 + (Y - ‘A’) * 8 + z

GPIO4_D2  = 4 * 32 + 3 * 8 + 2 = 154

      2. 查看该io是否被扩展:

ls /sys/class/gpio/gpio154

     若不存在,需要export下

echo 154 > /sys/class/gpio/export

        3. 然后查看IO direction(输入or输出)

cat /sys/class/gpio/gpio154/direction

        若与实际不符

需要配置下:

//配置成 输出

echo out > /sys/class/gpio/gpio154/direction

//配置成 输入

echo in > /sys/class/gpio/gpio154/direction

        4. 操作value

//当io为 in时,获取IO值 

cat /sys/class/gpio/gpio154/value

//当Io为out,设置1,或设置0

echo 1 > /sys/class/gpio/gpio154/value

2. 扩展芯片访问IO口

       如果成熟点或厉害点,可以自己在dts整个驱动,将芯片IO映射成主板的IO,这个也是Linux下非常通用操作,这样之后,我们访问扩展芯片的IO就像访问本地IO一样,方法如上;

       但这个需要知道number,这个时候,我们如何查询io number呢?

        2.1 首先,要知道该芯片挂载到哪个dev下面

        一般硬件人员也会提供一个i2c的号,

ls  /dev/i2c-1     i2c-1 就是第一路i2c  

        也可以通过

/sys/class/gpio# ls -l

total 0

--w------- 1 root root 4096 Apr 14 09:36 export

 gpio11 -> ../../devices/platform/fdd60000.gpio/gpiochip0/gpio/gpio11

gpio154 -> ../../devices/platform/fe770000.gpio/gpiochip4/gpio/gpio154

gpiochip0 -> ../../devices/platform/fdd60000.gpio/gpio/gpiochip0

 gpiochip128 -> ../../devices/platform/fe770000.gpio/gpio/gpiochip128

gpiochip32 -> ../../devices/platform/fe740000.gpio/gpio/gpiochip32

gpiochip479 -> ../../devices/platform/fe5a0000.i2c/i2c-1/1-0022/gpio/gpiochip479

devices/platform/fe5a0000.i2c/i2c-1/1-0022/gpio/gpiochip479

“gpiochip”是指扩展芯片的,一般是一组

此处可知:i2c-1/1-0022     挂载地址0x22

     2.2 查看寄存器内容:

/sys/bus/i2c/devices/i2c-1# i2cdetect -r -y 1

也可以看哪些地址被使用(挂载)

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f

00:                         -- -- -- -- -- -- -- --

10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

20: -- UU UU -- -- -- -- -- -- -- -- -- -- -- -- --     //此时,0x21 0x22 以及 0x51 都是链接i2c

30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

50: -- UU -- -- -- -- -- -- -- -- -- -- -- -- -- --

60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

70: -- -- -- -- -- -- -- --           

 查看寄存器内容:

/sys/class/gpio# i2cdump -y -f 1 0x22

No size specified (using byte-data access)

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef

00: f5 fa ff ff 00 00 6b f5 XX XX XX XX XX XX XX XX    ??....k?XXXXXXXX  

10: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX

20: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX

30: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX

结合芯片手册,就可知道具体含义:

一般是 in out ... config 这样

in out是value的值,config是IO的方向

这些值都按位考虑的,即每个bit表示一个IO口

2.3 i2c与gpioxxx映射关系

对于芯片扩展的,我们需要知道base 以及 count

/sys/class/gpio/gpiochip479# cat base

479

/sys/class/gpio/gpiochip479# cat ngpio

16

那么此时,对应的芯片IO(0)是479这个号

通过(参考步骤1 就可完成每个IO定义,访问)

echo 479 > /sys/class/gpio/export

类似480是指第2个IO,类推

2.4 用户态代码操作IO口

直接上代码,前提要知道 I2C地址,寄存器地址(一般0x00开始)

#include "I2cDevice.h"
#include <stdio.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdlib.h>
#include "Base/Logger/Define.h"using namespace std;I2cDevice::I2cDevice() : mFile(-1)
{}I2cDevice::~I2cDevice()
{if(mFile != -1){Close();}
}bool I2cDevice::Open(const char *node)
{if(mFile != -1){Close();}mFile = open(node, O_RDWR);if(mFile < 0){mFile = -1;errorf("I2C device open [%s] failed.\\n",node);return false;}return true;
}bool I2cDevice::Close()
{if(mFile != -1){close(mFile);mFile = -1;}return true;
}int I2cDevice::Read(const uint8_t addr, I2cReg* info)
{if(mFile == -1){return -1;}struct i2c_rdwr_ioctl_data data;struct i2c_msg messages[2];uint8_t reg = info->reg;messages[0].addr = addr;        /*device address*/messages[0].flags = 0;          /*write*/messages[0].len = sizeof(reg);messages[0].buf = &reg;         /*data address*/messages[1].addr = addr;        /*device address*/messages[1].flags = I2C_M_RD;   /*read*/messages[1].len = sizeof(info->val);messages[1].buf = &(info->val);data.msgs = messages;data.nmsgs = 2;/*设置从机模式*/ioctl(mFile, I2C_SLAVE_FORCE, addr);if(ioctl(mFile, I2C_RDWR, &data) < 0){debugf("I2C read failed. addr=%02X, reg=%d, val=%d\\n",addr,info->reg,info->val);return -2;}return 0;
}int I2cDevice::Write(const uint8_t addr, const I2cReg &info)
{if(mFile == -1){return -1;}uint8_t buf[2];struct i2c_rdwr_ioctl_data data;struct i2c_msg messages;buf[0] = info.reg;buf[1] = info.val;messages.addr = addr;  /*device address*/messages.flags = 0;    /*write*/messages.len = 2;messages.buf = buf;    /*data address*/data.msgs = &messages;data.nmsgs = 1;/*设置从机模式*/ 必须设置,否则会失败ioctl(mFile, I2C_SLAVE_FORCE, addr);if(ioctl(mFile, I2C_RDWR, &data) < 0){debugf("I2C write failed. addr=%02X, reg=%d, val=%d\\n",addr,info.reg,info.val);return -2;}usleep(1000);return 0;
}