linux应用层读写i2c设备

一般情况下,linux读写i2c设备需要在内核编写一个i2c驱动,该驱动实现一个字符驱动,然后在字符驱动里面使用i2c框架读写和操作对应的设备。其实linux对于这种规范化的驱动是可以直接在linux应用层进行读写操作的,原理是基于对i2c主机驱动的操作,当然前提是要在设备树上面把对应的i2c设备挂好,让i2c总线驱动知道你这个设备的存在。

首先在设备树i2c-5上面挂载了ap3216c这个设备,且设备地址为0x1e:

&i2c5 { 
    ap3216c@1e { 
              compatible = "alientek,ap3216c"; 
              reg = <0x1e>; 
    }; 
};

烧录后看看在不在,很明显在i2c-5下面是能找到这个设备了:

root@ATK-DLRK3568:/# ls /sys/bus/i2c/devices/
0-001c  1-0014  4-001a    5-001e  5-0036-1  i2c-0  i2c-3  i2c-5
0-0020  4-0010  4-001a-1  5-0036  5-0051    i2c-1  i2c-4  i2c-6
root@ATK-DLRK3568:/# ls /sys/bus/i2c/devices/5-001e/
modalias  name  of_node  power  subsystem  uevent
root@ATK-DLRK3568:/# ls /sys/bus/i2c/devices/5-001e/name
/sys/bus/i2c/devices/5-001e/name
root@ATK-DLRK3568:/# cat /sys/bus/i2c/devices/5-001e/name
ap3216c

 咱现在不想写内核驱动,就想在应用层操作他,写一个应用程序test_i2c.c:

#include <stdio.h> 
#include <linux/types.h> 
#include <stdlib.h> 
#include <stdint.h>
#include <stdbool.h>
#include <fcntl.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/ioctl.h> 
#include <errno.h> 
#include <assert.h> 
#include <string.h> 
#include <linux/i2c.h> 
#include <linux/i2c-dev.h>


static int i2c_write_bytes(int fd, uint8_t slave_addr, uint8_t reg_addr, uint8_t *values, uint8_t len)
{
    uint8_t *outbuf = NULL;
    struct i2c_rdwr_ioctl_data packets;
    struct i2c_msg messages[1];

    outbuf = malloc(len + 1);
    if (!outbuf) {
        printf("Error: No memory for buffer\n");
        return -1;
    }

    outbuf[0] = reg_addr;
    memcpy(outbuf + 1, values, len);
    
    messages[0].addr = slave_addr;
    messages[0].flags = 0;
    messages[0].len = len + 1;
    messages[0].buf = outbuf;
    
    /* Transfer the i2c packets to the kernel and verify it worked */
    packets.msgs = messages;
    packets.nmsgs = 1;
    if(ioctl(fd, I2C_RDWR, &packets) < 0)
    {
        printf("Error: Unable to send data");
        free(outbuf);
        return -1;
    }

    free(outbuf);
    
    return 0;
}

static int i2c_read_bytes(int fd, uint8_t slave_addr, uint8_t reg_addr, uint8_t *values, uint8_t len)
{
    uint8_t outbuf[1];
    struct i2c_rdwr_ioctl_data packets;
    struct i2c_msg messages[2];

    outbuf[0] = reg_addr;
    messages[0].addr = slave_addr;
    messages[0].flags = 0;
    messages[0].len = sizeof(outbuf);
    messages[0].buf = outbuf;
    
    /* The data will get returned in this structure */
    messages[1].addr = slave_addr;
    messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/;
    messages[1].len = len;
    messages[1].buf = values;
    
    /* Send the request to the kernel and get the result back */
    packets.msgs = messages;
    packets.nmsgs = 2;
    if(ioctl(fd, I2C_RDWR, &packets) < 0)
    {
        printf("Error: Unable to send data");
        return -1;
    }
    
    return 0;
}


int main(int argc, char *argv[])
{
    int fd;
    bool cmdIsRd = false;
    char *arg_ptr = NULL;
    unsigned long len;
    unsigned int slave_addr, reg_addr;
    uint8_t buffer[1024];

    /* 1.判断命令行参数是否合法 */
    if(argc < 5){
        printf("Usage:\n");
        printf("%s /dev/i2c-x {r|w}length salve_addr reg_addr [value]\n", argv[0]);
        return -1;
    }

    /* 2.打开I2C总线 */
    fd = open(argv[1], O_RDWR); 
    if (fd < 0)
    { 
        printf("can not open file %s\n", argv[0]);
        return -1; 
    }

    /* 3.解析命令的方向 */
    arg_ptr = argv[2];
    switch (*arg_ptr) {
    case 'r': cmdIsRd = true; break;
    case 'w': cmdIsRd = false; break;
    default:
        printf("Error: Invalid direction\n");
        return -1; 
    }

    /* 4.解析value的长度 */
    arg_ptr++;
    len = strtoul(arg_ptr, NULL, 0);

    /* 5.解析从机地址和寄存器地址 */
    slave_addr = strtoul(argv[3], NULL, 0);
    reg_addr = strtoul(argv[4], NULL, 0);

    // printf("%c len=%d, salve_addr=0x%02X, reg_addr=0x%02X\n", cmdIsRd?'r':'w', len, slave_addr, reg_addr);

    /* 6.读写数据 */
    if(cmdIsRd)
    {
        i2c_read_bytes(fd, slave_addr, reg_addr, buffer, len);

        printf("read data =");
        for(int i = 0; i < len; i++)
        {
            printf("0x%02X ", buffer[i]);
        }
        printf("\n");
    }
    else if(argc > 5)
    {
        for(int i = 0; i < len; i++)
        {
            buffer[i] = strtoul(argv[5 + i], NULL, 0);
        }
    
        i2c_write_bytes(fd, slave_addr, reg_addr, buffer, len);
    }

    return 0;
}

编译一下,推进文件系统里面:

/opt/atk-dlrk356x-toolchain/bin/aarch64-buildroot-linux-gnu-gcc test_i2c.c -o test_i2c

adb push test_i2c /home

adb shell

root@ATK-DLRK3568:/# ls /home
ap3216c.ko  ftp  i2c_test  test_ap3216

操作一下看看能否正常读写,结果显示,是可以的:

root@ATK-DLRK3568:/home# ./i2c_test /dev/i2c-5 w1 0x1e 0x00 0x04
root@ATK-DLRK3568:/home# ./i2c_test /dev/i2c-5 w1 0x1e 0x00 0x03
root@ATK-DLRK3568:/home# ./i2c_test /dev/i2c-5 r20 0x1e 0x00
read data =0x03 0x00 0x03 0x00 0x03 0x00 0x03 0x00 0x03 0x00 0x03 0x00 0x03 0x00 0x03 0x00 0x03 0x00 0x03 0x00

 参考:【I2C】基于/dev/i2c-x应用层读写I2C设备_c语言应用层读写i2c-CSDN博客

### 回答1: 在Linux应用层,我们可以使用相应的库函数来进行读写i2c设备的操作。下面是一个简单的示例代码: 首先,我们需要打开i2c设备文件,可以使用open函数来实现: ```c int fd = open("/dev/i2c-0", O_RDWR); if (fd < 0) { perror("Failed to open i2c device"); return -1; } ``` 接下来,我们需要设置要访问的i2c设备的地址,可以使用ioctl函数来实现: ```c int addr = 0x50; // 替换为你要访问的i2c设备地址 if (ioctl(fd, I2C_SLAVE, addr) < 0) { perror("Failed to set i2c device address"); return -1; } ``` 然后,我们可以使用read函数来读取i2c设备的数据: ```c unsigned char buffer[10]; int length = 5; // 要读取的字节数 if (read(fd, buffer, length) != length) { perror("Failed to read from i2c device"); return -1; } ``` 类似地,我们可以使用write函数来向i2c设备写入数据: ```c unsigned char data[10] = {0x00, 0x01, 0x02, 0x03, 0x04}; // 要写入的数据 int length = 5; // 要写入的字节数 if (write(fd, data, length) != length) { perror("Failed to write to i2c device"); return -1; } ``` 最后,我们需要关闭i2c设备文件,可以使用close函数来实现: ```c close(fd); ``` 通过以上的代码,我们可以在Linux应用层进行i2c设备读写操作。请注意,示例中使用的设备文件路径和i2c设备地址可能需要根据实际情况进行修改。 ### 回答2: 在Linux中,要在应用层读写I2C设备,可以使用Linux提供的I2C工具以及编程接口。 1. 使用I2C工具:Linux提供了一些命令行工具来读写I2C设备,最常用的是`i2c-tools`包中的`i2cget`和`i2cset`命令。可以通过安装`i2c-tools`来获得这些工具。使用时,需要知道目标I2C设备的地址以及要读写的寄存器地址,然后可以使用`i2cget`命令读取该寄存器的值,或使用`i2cset`命令向该寄存器写入数据。 2. 使用编程接口:在应用程序中使用编程接口可以更灵活地读写I2C设备。在Linux中,可以使用标准的Linux编程接口,如`ioctl`和`open`等函数来操作I2C设备。首先需要使用`open`函数打开I2C设备文件,然后使用`ioctl`函数设置I2C设备的地址、通信速率等参数。接下来可以使用`read`和`write`函数来读取和写入I2C设备的数据。 这些方法都能实现在Linux应用层中对I2C设备进行读写操作。使用哪种方法取决于具体的需求和场景。如果只是简单的读写操作,可以选择使用I2C工具;如果需要更复杂的控制和处理逻辑,可以选择使用编程接口来实现。 ### 回答3: 在Linux应用层读写I2C,我们可以使用内核提供的i2c-dev驱动来实现。以下是一种基本的方法: 1. 打开I2C设备 首先,我们需要在应用程序中打开I2C设备文件。设备文件的路径通常为"/dev/i2c-N",其中N为I2C控制器的编号。通过调用open()函数打开设备文件,可以获得一个文件描述符(file descriptor)。 2. 设置I2C设备的从属地址 在进行I2C通信之前,我们需要设置I2C设备的从属地址(slave address)。通过ioctl()函数调用I2C_SLAVE命令,将从属地址传递给i2c-dev驱动。 3. 发送和接收数据 在已设置好从属地址的前提下,我们可以通过write()函数向I2C设备发送数据。数据应该是一个字节数组,可以包含多个字节。通过调用read()函数,可以从I2C设备中接收数据,同样以字节数组的形式返回。 4. 关闭I2C设备 在完成I2C通信后,我们应该关闭I2C设备文件,释放资源。通过调用close()函数,可以关闭文件描述符。 需要注意的是,读写I2C设备需要具有对应的权限。通常情况下,我们需要以超级用户或者具有I2C访问权限的用户身份运行应用程序。 以上是一个在Linux应用层读写I2C的简单示例。实际应用中,可能需要更复杂的数据处理和通信协议实现,但基本的读写操作是类似的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值