linux应用层读写i2c设备

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

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

cpp 复制代码
&i2c5 { 
    ap3216c@1e { 
              compatible = "alientek,ap3216c"; 
              reg = <0x1e>; 
    }; 
};

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

bash 复制代码
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:

cpp 复制代码
#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;
}

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

bash 复制代码
/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

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

bash 复制代码
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博客

相关推荐
pk_xz1234562 小时前
Shell 脚本中变量和字符串的入门介绍
linux·运维·服务器
小珑也要变强2 小时前
Linux之sed命令详解
linux·运维·服务器
Lary_Rock4 小时前
RK3576 LINUX RKNN SDK 测试
linux·运维·服务器
云飞云共享云桌面6 小时前
8位机械工程师如何共享一台图形工作站算力?
linux·服务器·网络
Peter_chq6 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
一坨阿亮7 小时前
Linux 使用中的问题
linux·运维
dsywws8 小时前
Linux学习笔记之vim入门
linux·笔记·学习
幺零九零零9 小时前
【C++】socket套接字编程
linux·服务器·网络·c++
小林熬夜学编程10 小时前
【Linux系统编程】第四十一弹---线程深度解析:从地址空间到多线程实践
linux·c语言·开发语言·c++·算法
程思扬11 小时前
为什么Uptime+Kuma本地部署与远程使用是网站监控新选择?
linux·服务器·网络·经验分享·后端·网络协议·1024程序员节