Jeston Orin IIC 驱动测试 —— 以MPU6050为例

前言

后续驱动需要需要使用IIC作为通讯的协议,但是做的板子还没来,因此,在开发板驱动加载真正的之前,我们需要确保IIC能够正常通信。
网上的博客基本都是教怎么使用i2c-tools进行通信的,但是这种方法只是在用户空间下进行实现的,做驱动的肯定不满足需求,因此才写下这篇文章。

一、i2c-tools的下载和使用

1、i2c-tools的下载
i2c-tools是一个i2c探测与数据收发工具,可以帮助我们在用户空间下快速进行i2c通信,可以帮我们提前排查与规避问题,如i2c设备从地址的探测与确定,比如:
N4芯片的原理图中,标识的设备从地址是0x62,但实际上地址是0x31,如果直接写驱动程序,以0x62地址收发数据,肯定无法正常进行通信。

因此,我们可以先下载i2c-tools,进行iic设备地址的探测与通信测试。

sudo apt-get update
sudo apt-get install -y i2c-tools

检测i2c-tools是否安装成功

apt-cache policy i2c-tools

若出现i2c-tools版本相关信息,则说明安装成功。

i2c-tools:
    Installed : 4.0 - 2
    Candidate : 4.0 - 2

2、i2c从设备地址探测
(1)查看i2c总线列表

sudo i2cdetect -l

在这里插入图片描述

  • 测试选择用jeston agx orin的外部40Pin的i2c引脚,因此此处选用的是i2c-1总线,即c240000.i2c
  • 实际使用的是jeston agx orin的camera interface的i2c引脚,选用的是i2c-2,即3180000.i2c

(2)i2cdetect - i2c从设备地址扫描
我们在40pin引脚外接了一个MPU6050,该器件的i2c addr固定位0x68。现在进行扫描验证:

sudo i2cdetect -a -r -y 1

在这里插入图片描述

可以看见在MPU6050插入之后,多出了一个0x68的从设备地址。

3、i2cdump -查看i2c从设备的寄存器表

sudo i2cdump -y -f 1 0x68

在这里插入图片描述

4、i2cset / i2cget - i2c设备读写
(1)读取i2c寄存器

sudo i2cget -f -y 1 0x68 0x01

在这里插入图片描述
在这里插入图片描述

可以看到,MPU6050的0x01地址的寄存器的数值为0x3
(2)设置i2c寄存器

sudo i2cget -f -y 1 0x68 0x6b
sudo i2cset -f -y 1 0x68 0x6b 0x07
sudo i2cget -f -y 1 0x68 0x6b

在这里插入图片描述

我们在下方会自己编写驱动程序,根据上述获取的数值,进行验证。

二、硬件连接

开发板为Jeston Agx Orin,测试用的IIC器件为MPU6050,引脚连接如下:

VCC ——> PIN2 :5V
GND ——> PIN6 :GND
SCL ——> PIN28:I2C2_CLK
SDA ——> PIN27:I2C2_DAT

三、设备树修改

Orin外接的40PIN的I2C对应的设备树节点是@c240000,我们需要修改设备树,在其下面添加一个0x68的设备节点。

cd you_path/sources/hardware/nvidia/soc/t23x/kernel-dts
sudo vi tegra234-soc.dtsi

在i2c@c240000设备树下挂接i2节点。

gen2_i2c: i2c@c240000 {
        ......
		mpu6050@68{
			reg = <0x68>;
			compatible = "nvidia,mpu6050";
		};
	};

之后进行设备树编译,以及开发板上设备树文件的替换。

四、i2c测试驱动的编写

由于直接在驱动probe()函数中进行i2c信号收发会导致信号难以被示波器抓取,写read/write接诶口又需要再写一个应用程序,所以为了方便测试,使用sysfs接口进行驱动程序的编写。

/*
 * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @Date: 2024-07-21 14:42:12
 * @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @LastEditTime: 2024-07-22 11:45:45
 * @FilePath: /15-iic-test/iic-test.c
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>

#include <linux/types.h>

#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/io.h>

#include <linux/platform_device.h> //包含 platform 函数
#include <linux/of.h> //包含设备树相关函数
#include <linux/i2c.h>
#include <linux/kobject.h> //包含 sysfs 文件系统对象类
#include <linux/sysfs.h> //包含 sysfs 操作文件函数

struct i2c_client *mpu6050_dev;



/*读取寄存器的数值*/
static int mpu6050_read_regs(u8 reg,char *val,int len)
{

	struct i2c_msg msg[2];
    int ret;
    // char buf[2] = { 0x00 };
    // buf[0] = (reg >> 8) & 0xFF;
    // buf[1] = reg & 0xFF;
    // printk(KERN_ERR "read - reg = 0x%04X ;buf = 0x%02X 0x%02X\n", reg, buf[0],buf[1]);

	/*msg[0]发送要读取的寄存器首地址*/
	msg[0].addr =  mpu6050_dev->addr;	/*从机地址*/
	msg[0].flags = 0;				    /*要发送的数据*/
	msg[0].buf = &reg; 				    /*要发送的数据,也就是寄存器地址*/
	msg[0].len = 1;					    /*要发送的寄存器长度为1*/

    // printk(KERN_ERR "slave  addr :0x%x \n", mpu6050_dev->addr);

	/*msg[1]读取数据*/
	msg[1].addr =  mpu6050_dev->addr;	/*从机地址*/
	msg[1].flags = I2C_M_RD;		    /*表示读数据*/
	msg[1].buf = val; 				    /*接收到的从机的数据*/
	msg[1].len = len;				    /*要读取的寄存器长度*/
	ret = i2c_transfer(mpu6050_dev->adapter,msg,ARRAY_SIZE(msg));
    if(ret == ARRAY_SIZE(msg)){
        ret = 0; 
    }else{
        printk(KERN_ERR "i2c read failed = %d len = %d \n", ret, reg, len);
    }
    return ret;
}

/*读取一个寄存器*/
static unsigned char mpu6050_read_reg(u8 reg)
{
	char data = 0;
    int ret;
    ret = mpu6050_read_regs(reg,&data,1);
    if(ret==0)
        printk(KERN_ERR "i2c read addr : 0x%x , data : %x  \n",reg , data);
	return data;
}

/*写入N个寄存器的数据*/
static int mpu6050_write_regs(u8 reg,char *buf,int len)
{
	struct i2c_msg msg;

    u8 b[256];

	// char data[128];
    int err;
    // data[0] = (reg >> 8) & 0xFF;
	// data[1] = reg & 0xFF;
	// data[2] = *buf;

    b[0] = reg; // 首字节是寄存器地址
    memcpy(&b[1],buf,len);  //构建要发送的数据,也就是寄存器首地址+实际的地址。
    

	msg.addr =  mpu6050_dev->addr;	/*从机地址*/
	msg.flags = 0;				/*要发送的数据*/
	msg.buf = b; 				/*要发送的数据,也就是寄存器地址+实际数据*/
	msg.len = len+1;			/*要发送的数据长度:寄存器地址+1*/
    // printk(KERN_ERR "I2C ready write 0x%04x = (data[0])0x%02x (data[1])0x%02x  (data[2])0x%02x\n", 
	// 	 reg , data[0], data[1], data[2]);

	err = i2c_transfer(mpu6050_dev->adapter,&msg,1);
    if (err != 1) {
		printk( KERN_ERR "%s: writing register 0x%x from 0x%x failed\n", __func__,
		    reg, mpu6050_dev->addr);
	} 
	return 0;


}

/*向一个寄存器写数据*/
static void mpu6050_write_reg(u8 reg,char data)
{
	char buf = 0;
	buf = data;
	mpu6050_write_regs(reg,&buf,1);
    printk(KERN_ERR "i2c write addr : 0x%x , data : 0x%x  \n",reg , buf);

}





//定义一个 i2c 设备结构体指针
static ssize_t mpu6050_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    int ret;
    char addr = 0x01; // 要读取的寄存器地址
    char recv[1]; // 用于存储读取到的数据

    recv[0] = mpu6050_read_reg(addr);
    // printk(KERN_ERR "read addr:%d err :%d \n",addr,  ret);
    addr = 0x6B;
    recv[0] = mpu6050_read_reg(addr);


    return sprintf(buf, "data should be 0x03 , read data = 0x%x\n", recv[0]);
}

static ssize_t mpu6050_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{

    mpu6050_write_reg(0x6B,0x07);
    mpu6050_read_reg(0x6B);

    return count;
}

static DEVICE_ATTR(mpu6050, 0660, mpu6050_show, mpu6050_store); //定义文件属性

static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret;
    ret = device_create_file(&client->dev, &dev_attr_mpu6050); //创建属性文件
    if (ret != 0)
    {
        printk(KERN_INFO "create mpu6050_dev file failed!\n");
        return -1;
    }
    printk(KERN_INFO "create mpu6050_dev file succeed!\n");
    mpu6050_dev = client; //初始化 i2c 设备结构体指针
    return 0;
}

static int mpu6050_remove(struct i2c_client *client)
{
    device_remove_file(&client->dev, &dev_attr_mpu6050); //删除属性文件
    printk(KERN_INFO "exit sysfs mpu6050!\n");
    return 0;
}

static const struct i2c_device_id mpu6050_id[] = {
    { "nvidia,mpu6050", 0 },
    // { "nvidia,xs9922", 0 },
    { },
};

static const struct of_device_id mpu6050_of_match[] = {
    { .compatible = "nvidia,mpu6050" },
    // { .compatible = "nvidia,xs9922" },
    { },
};

MODULE_DEVICE_TABLE(of, mpu6050_of_match);

static struct i2c_driver mpu6050_driver = {
    .driver = {
        .name = "nvidia,mpu6050",
        .owner = THIS_MODULE,
        .of_match_table = mpu6050_of_match,
    },
    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
    .id_table = mpu6050_id,
};

module_i2c_driver(mpu6050_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("csx");
MODULE_VERSION("0.1");
MODULE_DESCRIPTION("mpu6050_driver"); 

进行驱动程序的编译以及加载到Orin开发板上。

五、驱动测试

1、文件路径
创建的设备属性文件在/sys/devices目录下

cd /sys/devices/platform/c240000.i2c/i2c-1/1-0068
sudo chmod 777 ./mpu6050

2、cat指令查看寄存器数值
测试cat命令

cat mpu6050

cat指令,如驱动代码功能为读取0x01地址以及0x06b地址的寄存器数值,结果如下:
在这里插入图片描述

使用逻辑分析仪抓取的信号如下:
在这里插入图片描述

可见0x01寄存器数值为0x03,0x6b寄存器数值为0x4b,与之前使用i2c-tools查看到的寄存器数值是一致的。

3、echo指令修改寄存器数值并查看

echo > mpu6050

如前面的驱动代码,echo指令的功能为修改0x6b寄存器数值为0x07,并查看其数值。
在这里插入图片描述

使用逻辑分析仪抓取的信号如下:
在这里插入图片描述
可见0x6b寄存器数值从0x4b修改为0x07,与之前使用i2c-tools实验得到的结果是一致的。

  • 26
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
VL53L0X是一种集成式TOF(Time of Flight)测距传感器,具有高度精度和快速测量速度。它支持IIC(Inter-Integrated Circuit)通信协议,这是一种常用的串行通信协议,在嵌入式系统中广泛应用。 VL53L0X通过IIC总线与主控设备进行通信。IIC总线由两条信号线组成:串行数据线(SDA)和串行时钟线(SCL)。数据的传输是基于主从模式,主设备(主控器)控制总线的节奏和数据传输的方向,而从设备(VL53L0X)按照主设备的指令进行响应。 在IIC通信中,主控设备起始一个传输周期,发送器件地址和读/写位,然后VL53L0X对主控设备进行响应。主控器根据需要向VL53L0X发送读或写的数据,或者从VL53L0X接收数据。 VL53L0X的设备地址由其硬件引脚决定,可以通过实际物理连接来设置VL53L0X的地址与主控器匹配,确保正确识别和通信。通信过程中,主控器可以向VL53L0X发送指令以控制其测距、校准和其他功能。 使用IIC通信协议,VL53L0X与主控设备之间的数据传输可靠高效。该协议的优势是可以使用相对较少的引脚和线路连接多个器件,从而节省了系统资源。并且IIC通信协议具有简单易用、灵活可扩展的特点,方便开发人员对VL53L0X进行控制和集成。 总而言之,VL53L0X通过IIC通信协议与主控设备进行通信,利用其高精度测距和快速测量速度,可以广泛应用于不同领域的测量和控制系统中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值