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

news2024/9/20 20:30:10

前言

后续驱动需要需要使用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实验得到的结果是一致的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1940551.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【效率提升】程序员常用Shell脚本

文章目录 常用Shell脚本一. 定期更新分区数据二、获取系统资源的使用情况 常用Shell脚本 一. 定期更新分区数据 在某些场景下&#xff0c;我们需要对N年前某一分区的数据进行删除&#xff0c;并添加今年该对应分区的数据&#xff0c;实现数据的流动式存储。 #!/bin/bash dt$…

基于FPGA的数字信号处理(18)--半加器和全加器

前言 在数字系统中&#xff0c;加法运算是最常见的算术运算&#xff0c;同时它也是进行各种复杂运算的基础。 半加器 最简单的加法器叫做 半加器&#xff08;Half Adder&#xff09;&#xff0c;它将2个输入1bit的数据相加&#xff0c;输出一个2bits的和&#xff0c;和的范围为…

若依前后端获取当前用户

后端 Autowired private TokenService tokenService;LoginUser loginUser tokenService.getLoginUser(); sysInquiry.setCreateBy(loginUser.getUsername()); sysInquiry.setCreateTime(DateUtils.getNowDate()); 前端 获取使用 const nickName this.$store.state.user.nick…

最大似然估计模型及 Stata 具体操作步骤

目录 一、引言 二、理论原理 三、准备数据 四、定义似然函数 五、进行最大似然估计 六、代码解释 七、代码运行结果 八、模型评估与诊断 一、引言 最大似然估计&#xff08;Maximum Likelihood Estimation&#xff0c;MLE&#xff09;是一种在统计学中广泛应用的参数估计…

linux下磁盘分区工具GParted

最近发现安装的redhat机器部分磁盘大小分配不合理 使用gpated对磁盘重新分区 1、使用U盘制作一个启动盘 下载启动盘制作工具Index of /downloads 使用非常简单&#xff0c;选择gparted-live-1.1.0-3-i686.iso包即可 2、制作完成后&#xff0c;重启机器&#xff0c;选择U盘…

黑马点评-Postman卡住sending Requst原因解决

不知道为什么&#xff0c;用这个c1e1d5的token就会一直卡死&#xff0c;但是换了一个token就解决了&#xff0c;目前不知道为什么 解决了&#xff0c;原来是这个请求下面的函数发生了死循环&#xff01;&#xff01;太瓜皮了我超&#xff01; 把num写成了count&#xff0c;导…

Docker启动PostgreSql并设置时间与主机同步

在 Docker 中启动 PostgreSql 时&#xff0c;需要配置容器的时间与主机同步。可以通过在 Dockerfile 或者 Docker Compose 文件中设置容器的时区&#xff0c;或者使用宿主机的时间来同步容器的时间。这样可以确保容器中的 PostgreSql 与主机的时间保持一致&#xff0c;避免在使…

【iOS】Tagged Pointer

目录 前言什么是Tagged Pointer&#xff1f;引入Tagged Pointer技术之前引入Tagged Pointer之后总结 Tagged Pointer原理&#xff08;TagData分析&#xff09;关闭数据混淆MacOS分析NSNumberNSString iOS分析 判断Tagged PointerTagged Pointer应用Tagged Pointer 注意点 Tagge…

[MySQL][深入理解隔离性][上][MVCC]详细讲解

目录 0.铺垫1.初识MVCC2.三个记录隐藏列字段1.是什么&#xff1f;2.示例 3.undo日志4.模拟MVCC5.思考 0.铺垫 在RR级别的时候&#xff0c;多个事务的update&#xff0c;多个事务的insert&#xff0c;多个事务的delete&#xff0c;是否会有加锁现象&#xff1f; 现象结果是&…

【C++】deque以及优先级队列

容器适配器 deque的介绍deque的原理介绍 priority_queue的介绍与使用priority_queue的介绍priority_queue的使用constructor&#xff08;构造函数&#xff09;emptypushpoptopsize priority_queue的模拟实现 仿函数何为适配器容器适配器deque的缺陷选择deque作为适配器的理由ST…

保姆级教程:十分钟快速上手Coze自定义插件

&#x1f43c; 关注我, 了解更多 AI 前沿资讯和玩法&#xff0c;AI 学习之旅上&#xff0c;我与您一同成长&#xff01; 我已经不止一次听到这样的话&#xff1a;未来的 AI 应用属于各行各业的智能体 (Agent) &#xff5e; 无论是国内的 扣子Coze、Dify&#xff0c;还是国外的…

网易易盾图标点选验证码识别代码

简介 网易图标点选一直都是一个大难题&#xff0c;如上图所示。难点之一是图标变幻莫测&#xff0c;很难刷出有重复的图标&#xff0c;所以使用传统等等方式去标注、识别具有较大的难度。 经过我们大量的数据标注&#xff0c;终于完成了这款验证码的识别。 目前我们提供两种识…

FPGA:频闪灯设计

1、需求 若在FPGA上实现LED灯一秒闪烁一次&#xff0c;先进行计算&#xff0c;1秒闪烁一次&#xff0c;即周期为1秒&#xff0c;开发板XC7A35TFFG-2的基本时钟输入由板载 50MHz 有源晶振提供&#xff0c;即频率为f 50MHz 。 则一个周期为 T 1 f 1 50 M H z 20 n s T\frac{…

C++初阶:模版初阶【范式编程】【函数模板】【类模板】

一.范式编程 我们在写C函数重载的时候&#xff0c;可能会写许多同一类的函数。 比如交换函数&#xff1a; void Swap(int& left, int& right) {int temp left;left right;right temp; }void Swap(double& left, double& right) {double temp left;left …

多线程.下

目录 1.线程等待 2.join&#xff08;&#xff09;介绍 3.获取当前对象引用 4.线程的状态 5.线程安全 6.synchronized()关键字 7.synchronized关键字底层介绍 1.线程等待 对于操作系统而言&#xff0c;内部多个线程的执行是“随机调度&#xff0c;抢占式执行”的。简而言…

孟浩然,山水田园一山人

孟浩然&#xff0c;字浩然&#xff0c;号孟山人&#xff0c;生于唐睿宗永昌元年&#xff08;公元689年&#xff09;&#xff0c;卒于唐玄宗开元二十八年&#xff08;公元740年&#xff09;&#xff0c;享年51岁。他出生于盛唐时期的襄州襄阳&#xff08;今湖北省襄阳市&#xf…

网络安全常见错误及解决办法(更新中)

# 开启代理&#xff0c;无法连接网络 把代理关掉 # 上一秒还在安装tree&#xff0c;下一秒xshell就连接不上了 —》sshd服务的key这个文件权限过高&#xff0c;跟装tree没有关系&#xff0c;装一个epel 源&#xff0c;epel-release​ 部分命令&#xff1a;chmod 600 /etc/ssh…

QXlsx读写excel

QXlsx读写excel 安装 QXlsx使用 qmake使用 CMake 基本用法1. 写入 Excel 文件2. 读取 Excel 文件 详细用法1. 设置单元格样式2. 合并单元格3. 创建图表4. 设置列宽和行高 完整示例 QXlsx 是一个用于在 Qt 应用中读写 Excel 文件的第三方库。它提供了丰富的 API&#xff0c;可以…

人工智能与社交变革:探索Facebook如何领导智能化社交平台

在过去十年中&#xff0c;人工智能&#xff08;AI&#xff09;技术迅猛发展&#xff0c;彻底改变了我们与数字世界互动的方式。Facebook作为全球最大的社交媒体平台之一&#xff0c;充分利用AI技术&#xff0c;不断推动社交平台的智能化&#xff0c;提升用户体验。本文将深入探…

昇思25天学习打卡营第23天|ResNet50图像分类

课程打卡凭证 ResNet网络 ResNet&#xff08;Residual Networks&#xff0c;残差网络&#xff09;是一种深度神经网络结构&#xff0c;它的核心思想是引入了“残差学习”来解决深度网络中的退化问题。在深度神经网络中&#xff0c;当网络层数增加到一定程度时&#xff0c;网络…