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

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值