前言
后续驱动需要需要使用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 = ® 				    /*要发送的数据,也就是寄存器地址*/
	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实验得到的结果是一致的。







![[MySQL][深入理解隔离性][上][MVCC]详细讲解](https://i-blog.csdnimg.cn/direct/5b7e85adf41b4fa684639ccb64a405df.png)










