当前位置: 首页 > ai >正文

28.成功解决i2c_transfer返回-6的问题并linux驱动mpu6050(适合一切linux学习者)

开发板:正点原子STM32MP157
环境:ubuntu
驱动目标:mpu6050
解决思路:就是换等长的杜邦线就行!
正点原子的STM32MP157板子上并没有mpu6050的模块!
所以只能外接!
涉及总代码会放在后面!
对于代码的编写不会的宝子可以按照我之前的文章看看I2C的驱动知识和编程,评论区或者后台问我,会一一回答!

1、电路原理图

在这里插入图片描述

这块板子只提供I2C4的外设接口并引脚引出:看上图就是I2C4 SCL PZ4和I2C4 SDA PZ5
看过我之前的同学就知道:我的pcf8563也是这个总线接口!但不影响!
所以我们驱动mpu6050只能利用I2C4总线来驱动。

2、修改设备树

设备树里面几乎不用改,比如:arch/arm/boot/dts/stm32mp151.dtsi、arch/arm/boot/dts/stm32mp15-pinctrl.dtsi不用改,除了不一样的板子,那么就要改一些接口重复的。
打开arch/arm/boot/dts/stm32mp157d-atk.dts文件
在这里插入图片描述

提供给大家复制:

&i2c4 {clock-frequency = <100000>;pinctrl-names = "default", "sleep";pinctrl-0 = <&i2c4_pins_a>;pinctrl-1 = <&i2c4_pins_sleep_a>;status = "okay";#address-cells = <1>;#size-cells = <0>;mpu6050@68{compatible = "alientek,mpu6050";reg = <0x68>;status = "okay"; };
};

同时也提供stm32mp15-pinctrl.dtsi和stm32mp151.dtsi关于I2C4的代码:
stm32mp151.dtsi

i2c4: i2c@5c002000 {compatible = "st,stm32mp15-i2c";reg = <0x5c002000 0x400>;interrupt-names = "event", "error";interrupts-extended = <&exti 24 IRQ_TYPE_LEVEL_HIGH>,<&intc GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;clocks = <&scmi0_clk CK_SCMI0_I2C4>;resets = <&scmi0_reset RST_SCMI0_I2C4>;#address-cells = <1>;#size-cells = <0>;dmas = <&mdma1 36 0x0 0x40008 0x0 0x0 0>,<&mdma1 37 0x0 0x40002 0x0 0x0 0>;dma-names = "rx", "tx";power-domains = <&pd_core>;st,syscfg-fmp = <&syscfg 0x4 0x8>;wakeup-source;status = "disabled";};

stm32mp15-pinctrl.dtsi

i2c4_pins_a: i2c4-0 {pins {pinmux = <STM32_PINMUX('Z', 4, AF6)>, /* I2C4_SCL */<STM32_PINMUX('Z', 5, AF6)>; /* I2C4_SDA */bias-disable;drive-open-drain;slew-rate = <0>;};};i2c4_pins_sleep_a: i2c4-1 {pins {pinmux = <STM32_PINMUX('Z', 4, ANALOG)>, /* I2C4_SCL */<STM32_PINMUX('Z', 5, ANALOG)>; /* I2C4_SDA */};};

当然板子不一样,涉及的很多配置都不一样,大家可以看网上的资料!

3、驱动代码会放在后面!

当我改好设备树和驱动代码后,烧录启动开发板:
发现modprobe mpu6050.ko后发现:
在这里插入图片描述

总是返回的是-6;这个是由i2c_transfer传输失败返回的!
我继续查找原因!
发现:
1、在sys/bus/i2c/devices下有68,也就是mpu6050
在这里插入图片描述

这里没有i2c-3不知道为什么?如果有知道的同学可以在评论区回复!
2、在dev下也有mpu6050
在这里插入图片描述

3、在proc/device-tree/soc/i2c@5c002000也有mpu6050@68
在这里插入图片描述

正如设备树的配置:
在这里插入图片描述

同时我们看看文件里的内容:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

都是okay:说明i2c4开启了!
这都是正常的:
最后我们查查I2C总线上有没有68这个地址:
我们利用:i2cdetect -y 或者i2cdetect -r -y来测试通信:
在这里插入图片描述

还是没有68!
其中0和1是之前看到的:
在这里插入图片描述

这里我也不知道为什么是通过I2C1和I2C2来。但是i2cdetect -r -y 3或4都没用。
之后只能换杜邦线了!
我换的是:
在这里插入图片描述

然后就行了!
在这里插入图片描述

出现68了!
同时只能用开发板上的3.3v和5V还有GND才可以。
如果不换杜邦线,用零散的会发生抢占总线等异常等现象!
比如:这是用零散的杜邦线!
在这里插入图片描述

结果测试:
在这里插入图片描述

返回-110或-16!
所以我们还是用等长的杜邦线!
结果可以了!
在这里插入图片描述

测试:
在这里插入图片描述

随便截一段数据:
在这里插入图片描述
当我把手放在芯片上温度就上来!
在这里插入图片描述
mpu6050.c

#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include "mpu6050reg.h"
#include <linux/gpio.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>#define MPU6050_CNT  1
#define MPU6050_NAME "mpu6050"
#define MPU_SELF_TEST_X 0x0D
#define MPU_SELF_TEST_Y 0x0E
#define MPU_SELF_TEST_Z 0x0F
#define MPU_SELF_TEST_A 0x10/* mpu6050设备结构体 */
struct mpu6050_dev {dev_t devid;            /* 设备号 */int major;              /* 主设备号 */int minor;              /* 次设备号 */struct cdev cdev;       /* cdev */struct class *class;    /* 类 */struct device *device;  /* 设备 */struct i2c_client *client; /* i2c设备 */signed int gyro_x_adc;  /* 陀螺仪X轴原始值 */signed int gyro_y_adc;  /* 陀螺仪Y轴原始值 */signed int gyro_z_adc;  /* 陀螺仪Z轴原始值 */signed int accel_x_adc; /* 加速度计X轴原始值 */signed int accel_y_adc; /* 加速度计Y轴原始值 */signed int accel_z_adc; /* 加速度计Z轴原始值 */signed int temp_adc;    /* 温度原始值 */
};/* 从mpu6050读取多个寄存器数据 */
static int mpu6050_read_regs(struct mpu6050_dev *dev, u8 reg, void *val, int len)
{int ret=-1;struct i2c_msg msg[2];struct i2c_client *client = dev->client;/* 第一个i2c_msg结构体,发送要读取的寄存器首地址 */msg[0].addr = client->addr;        /* i2c设备地址 */msg[0].flags = 0;                  /* 标记为发送数据 */msg[0].buf = &reg;                /* 要发送的数据 */msg[0].len = 1;                   /* 要发送的数据长度 *//* 第二个i2c_msg结构体,读取数据 */msg[1].addr = client->addr;        /* i2c设备地址 */msg[1].flags = I2C_M_RD;          /* 标记为读取数据 */msg[1].buf = val;                 /* 读取数据缓冲区 */msg[1].len = len;                 /* 要读取的数据长度 *///printk("msg[0].addr=%#x,msg[1].addr=%#x,reg_addr=%#x\r\n",msg[0].addr//,msg[1].addr,reg);printk("msg[0].addr=%#x\r\n",msg[0].addr);ret=i2c_transfer(client->adapter, msg, 2);if(ret == 2) {ret = 0;} else {printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);ret = -EREMOTEIO;}return ret;
}/* 向mpu6050多个寄存器写入数据 */
static int mpu6050_write_regs(struct mpu6050_dev *dev, u8 reg, u8 *buf, u8 len)
{int ret=-1;u8 b[256];struct i2c_msg msg;struct i2c_client *client = dev->client;b[0] = reg;                      /* 寄存器首地址 */memcpy(&b[1], buf, len);         /* 将要写入的数据拷贝到数组b里面 */msg.addr = client->addr;          /* i2c设备地址 */msg.flags = 0;                    /* 标记为写入数据 */msg.buf = b;                      /* 要写入的数据缓冲区 */msg.len = len + 1;                /* 要写入的数据长度 */ret = i2c_transfer(client->adapter, &msg, 1);if(ret == 1) {ret = 0;} else {printk("i2c write failed=%d reg=%06x len=%d\n",ret, reg, len);ret = -EREMOTEIO;}return ret;
}/* 读取mpu6050指定寄存器值,读取一个寄存器 */
static unsigned char mpu6050_read_reg(struct mpu6050_dev *dev, u8 reg)
{u8 data = 0;mpu6050_read_regs(dev,reg,&data, 1);return data;
}/* 向mpu6050指定寄存器写入指定的值,写一个寄存器 */
static void mpu6050_write_reg(struct mpu6050_dev *dev, u8 reg, u8 value)
{u8 buf=0;buf = value;mpu6050_write_regs(dev, reg, &buf, 1);
}/* 读取MPU6050的数据,读取原始数据,包括三轴陀螺仪、三轴加速度计和内部温度 */
void mpu6050_read_data(struct mpu6050_dev *dev)
{unsigned char data[14];mpu6050_read_regs(dev, MPU_ACCEL_XOUT_H, data, 14);dev->accel_x_adc = (signed short)((data[0] << 8) | data[1]); dev->accel_y_adc = (signed short)((data[2] << 8) | data[3]); dev->accel_z_adc = (signed short)((data[4] << 8) | data[5]); dev->temp_adc = (signed short)((data[6] << 8) | data[7]);dev->gyro_x_adc = (signed short)((data[8] << 8) | data[9]);dev->gyro_y_adc = (signed short)((data[10] << 8) | data[11]);dev->gyro_z_adc = (signed short)((data[12] << 8) | data[13]);
}/* MPU6050内部寄存器初始化函数 */
void mpu6050_self_test(struct mpu6050_dev *dev) {u8 self_test[4];signed int self_test_results[4];int i=0;// 读取自检寄存器mpu6050_read_regs(dev, MPU_SELF_TEST_X, self_test, 4);// 计算自检结果for ( i=0 ; i < 4; i++) {self_test_results[i] = (self_test[i] - 1) * 100 / 255; // 示例计算}// 打印自检结果printk("Self Test Results: X=%d, Y=%d, Z=%d, A=%d\n",self_test_results[0], self_test_results[1],self_test_results[2], self_test_results[3]);
}void mpu6050_reginit(struct mpu6050_dev *dev)
{u8 value = 0;/* 复位设备 */mpu6050_write_reg(dev, MPU_PWR_MGMT_1, 0x80);mdelay(50);/* 唤醒设备 */mpu6050_write_reg(dev, MPU_PWR_MGMT_1, 0x00);mdelay(50);mpu6050_write_reg(dev, MPU_PWR_MGMT_2, 0x00);mdelay(50);value = mpu6050_read_reg(dev,MPU_WHO_AM_I);printk("MPU6050 ID = %#X\r\n", value);mpu6050_write_reg(dev, MPU_SMPLRT_DIV, 0x09);     /* 采样率分频,1000/(1+7)=125Hz */mpu6050_write_reg(dev, MPU_CONFIG, 0x06);                           /* 数字低通滤波器,截止频率5Hz */mpu6050_write_reg(dev, MPU_GYRO_CONFIG, 0x18);    /* 陀螺仪量程,±2000°/s */mpu6050_write_reg(dev, MPU_ACCEL_CONFIG, 0x18);   /* 加速度计量程,±16g */mpu6050_write_reg(dev, 0x38, 0x00);		//关闭所有中断mpu6050_write_reg(dev, 0x6A, 0x00);		//I2C主模式关闭mpu6050_write_reg(dev, 0x23, 0x00);			//关闭FIFOmpu6050_write_reg(dev, MPU_PWR_MGMT_1, 0x01);mpu6050_write_reg(dev, MPU_PWR_MGMT_2, 0x00);// 进行自检mpu6050_self_test(dev);
}static int mpu6050_open(struct inode *inode, struct file *filp)
{filp->private_data = container_of(inode->i_cdev, struct mpu6050_dev, cdev);return 0;
}static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{signed int data[7];long err = 0;struct mpu6050_dev *dev = filp->private_data;mpu6050_read_data(dev);data[0] = dev->gyro_x_adc;data[1] = dev->gyro_y_adc;data[2] = dev->gyro_z_adc;data[3] = dev->accel_x_adc;data[4] = dev->accel_y_adc;data[5] = dev->accel_z_adc;data[6] = dev->temp_adc;err = copy_to_user(buf, data, sizeof(data));return 0;
}static int mpu6050_release(struct inode *inode, struct file *filp)
{return 0;
}/* mpu6050操作函数 */
static const struct file_operations mpu6050_ops = {.owner = THIS_MODULE,.open = mpu6050_open,.read = mpu6050_read,.release = mpu6050_release,
};/* i2c驱动的probe函数 */
static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{int ret;struct mpu6050_dev *mpu6050dev;/* 分配内存 */mpu6050dev = devm_kzalloc(&client->dev, sizeof(*mpu6050dev), GFP_KERNEL);if (!mpu6050dev)return -ENOMEM;/* 注册字符设备驱动 *//* 1、申请设备号 */mpu6050dev->major = 0;if (mpu6050dev->major) {mpu6050dev->devid = MKDEV(mpu6050dev->major, 0);ret = register_chrdev_region(mpu6050dev->devid, MPU6050_CNT, MPU6050_NAME);} else {ret = alloc_chrdev_region(&mpu6050dev->devid, 0, MPU6050_CNT, MPU6050_NAME);mpu6050dev->major = MAJOR(mpu6050dev->devid);mpu6050dev->minor = MINOR(mpu6050dev->devid);}if (ret < 0) {goto fail_devid;}printk("major=%d,minor=%d\r\n",mpu6050dev->major, mpu6050dev->minor);	/* 2、初始化cdev */mpu6050dev->cdev.owner = THIS_MODULE;cdev_init(&mpu6050dev->cdev, &mpu6050_ops);/* 3、添加cdev */ret = cdev_add(&mpu6050dev->cdev, mpu6050dev->devid, MPU6050_CNT);if (ret < 0)goto fail_cdev;/* 4、创建类 */mpu6050dev->class = class_create(THIS_MODULE, MPU6050_NAME);if (IS_ERR(mpu6050dev->class)) {ret = PTR_ERR(mpu6050dev->class);goto fail_class;}/* 5、创建设备 */mpu6050dev->device = device_create(mpu6050dev->class, NULL, mpu6050dev->devid, NULL, MPU6050_NAME);if (IS_ERR(mpu6050dev->device)) {ret = PTR_ERR(mpu6050dev->device);goto fail_device;}/* 6、保存i2c_client */mpu6050dev->client = client;/* 7、保存mpu6050dev结构体 */i2c_set_clientdata(client, mpu6050dev);/* 8、初始化MPU6050内部寄存器 */mpu6050_reginit(mpu6050dev);return 0;fail_device:class_destroy(mpu6050dev->class);
fail_class:cdev_del(&mpu6050dev->cdev);
fail_cdev:unregister_chrdev_region(mpu6050dev->devid, MPU6050_CNT);
fail_devid:return ret;
}/* i2c驱动的remove函数 */
static int mpu6050_remove(struct i2c_client *client)
{struct mpu6050_dev *mpu6050dev = i2c_get_clientdata(client);/* 注销设备 */device_destroy(mpu6050dev->class, mpu6050dev->devid);/* 注销类 */class_destroy(mpu6050dev->class);/* 删除cdev */cdev_del(&mpu6050dev->cdev);/* 注销设备号 */unregister_chrdev_region(mpu6050dev->devid, MPU6050_CNT);return 0;
}/* 传统匹配方式ID列表 */
static const struct i2c_device_id mpu6050_id[] = {{"alientek,mpu6050", 0},  {}
};/* 设备树匹配列表 */
static const struct of_device_id mpu6050_of_match[] = {{ .compatible = "alientek,mpu6050" },{ /* Sentinel */ }
};/* i2c驱动结构体 */
static struct i2c_driver mpu6050_driver = {.probe = mpu6050_probe,.remove = mpu6050_remove,.driver = {.owner = THIS_MODULE,.name = "mpu6050",.of_match_table = mpu6050_of_match,},.id_table = mpu6050_id,
};/* 驱动注册与卸载 */
module_i2c_driver(mpu6050_driver);MODULE_LICENSE("GPL");
MODULE_AUTHOR("chensir");
MODULE_INFO(intree, "Y"); 

mpu6050reg.h

#ifndef MPU6050_H
#define MPU6050_H#define MPU6050_ID            0x68    /* ID值 *//* MPU6050寄存器 *复位后所有寄存器地址都为0,除了*Register 107(0X6B) Power Management 1  = 0x40*Register 117(0X75) WHO_AM_I           = 0x68*/
/* 陀螺仪和加速度自测 */
#define MPU_SELF_TEST_X      0x0D
#define MPU_SELF_TEST_Y      0x0E
#define MPU_SELF_TEST_Z      0x0F
#define MPU_SELF_TEST_A      0x10#define MPU_SMPLRT_DIV       0x19    /* 采样率分频,典型值:0x07(125Hz) */
#define MPU_CONFIG           0x1A    /* 配置寄存器 */
#define MPU_GYRO_CONFIG      0x1B    /* 陀螺仪配置 */
#define MPU_ACCEL_CONFIG     0x1C    /* 加速度计配置 *//* 加速度输出 */
#define MPU_ACCEL_XOUT_H     0x3B
#define MPU_ACCEL_XOUT_L     0x3C
#define MPU_ACCEL_YOUT_H     0x3D
#define MPU_ACCEL_YOUT_L     0x3E
#define MPU_ACCEL_ZOUT_H     0x3F
#define MPU_ACCEL_ZOUT_L     0x40/* 温度输出 */
#define MPU_TEMP_OUT_H       0x41
#define MPU_TEMP_OUT_L       0x42/* 陀螺仪输出 */
#define MPU_GYRO_XOUT_H      0x43
#define MPU_GYRO_XOUT_L      0x44
#define MPU_GYRO_YOUT_H      0x45
#define MPU_GYRO_YOUT_L      0x46
#define MPU_GYRO_ZOUT_H      0x47
#define MPU_GYRO_ZOUT_L      0x48/* 电源管理 */
#define MPU_PWR_MGMT_1       0x6B    /* 电源管理1 */
#define MPU_PWR_MGMT_2       0x6C    /* 电源管理2 */#define MPU_WHO_AM_I         0x75    /* WHO AM I *//* SPI特定设置 */
#define MPU_SPI_READ         0x80    /* SPI读标志 */#endif 

mpu6050App.c

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>int main(int argc, char *argv[])
{int fd;char *filename;signed int databuf[7];signed int gyro_x_adc, gyro_y_adc, gyro_z_adc;signed int accel_x_adc, accel_y_adc, accel_z_adc;signed int temp_adc;float gyro_x_act, gyro_y_act, gyro_z_act;float accel_x_act, accel_y_act, accel_z_act;float temp_act;int ret = 0;if (argc != 2) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if(fd < 0) {printf("can't open file %s\r\n", filename);return -1;}while (1) {ret = read(fd, databuf, sizeof(databuf));if(ret == 0) { /* 数据读取成功 */gyro_x_adc = databuf[0];gyro_y_adc = databuf[1];gyro_z_adc = databuf[2];accel_x_adc = databuf[3];accel_y_adc = databuf[4];accel_z_adc = databuf[5];temp_adc = databuf[6];/* 计算实际值 */gyro_x_act = (float)(gyro_x_adc) / 16.4;    /* 16.4 LSB/(°/s) */gyro_y_act = (float)(gyro_y_adc) / 16.4;gyro_z_act = (float)(gyro_z_adc) / 16.4;accel_x_act = (float)(accel_x_adc) / 2048;  /* 2048 LSB/g */accel_y_act = (float)(accel_y_adc) / 2048;accel_z_act = (float)(accel_z_adc) / 2048;temp_act = ((float)(temp_adc) - 521 ) / 340 + 35; /* 温度转换公式 */printf("\r\n原始值:\r\n");printf("gx = %d, gy = %d, gz = %d\r\n", gyro_x_adc, gyro_y_adc, gyro_z_adc);printf("ax = %d, ay = %d, az = %d\r\n", accel_x_adc, accel_y_adc, accel_z_adc);printf("temp = %d\r\n", temp_adc);printf("实际值:\r\n");printf("act gx = %.2f°/S, act gy = %.2f°/S, act gz = %.2f°/S\r\n", gyro_x_act, gyro_y_act, gyro_z_act);printf("act ax = %.2fg, act ay = %.2fg, act az = %.2fg\r\n",accel_x_act, accel_y_act, accel_z_act);printf("act temp = %.2f°C\r\n", temp_act);}usleep(100000); /*100ms */}close(fd);return 0;
}

makefile

KERNELDIR := /home/chensir/linux/atk-mp1/linux/my_linux/linux-5.4.31
CURRENT_PATH := $(shell pwd) 
obj-m := mpu6050.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

对于代码的编写不会的宝子可以按照我之前的文章看看I2C的驱动知识和编程,评论区或者后台问我,会一一回答!

http://www.xdnf.cn/news/4464.html

相关文章:

  • OpenCV 中用于背景分割(背景建模)的一个类cv::bgsegm::BackgroundSubtractorCNT
  • 【HarmonyOS 5】鸿蒙中常见的标题栏布局方案
  • Oracle 开窗函数
  • 高组装导轨的特点
  • Java中字符转数字的原理解析 - 为什么char x - ‘0‘能得到对应数字
  • 《Python星球日记》 第43天:机器学习概述与Scikit-learn入门
  • 旧版谷歌浏览器Chrome v116.0.5845.141下载
  • 38.机壳间接缝的处理
  • 27、移除元素
  • 加速页面加载的全流程优化策略
  • 日常知识点之随手问题整理(虚函数 虚函数表 继承的使用场景)
  • 【Linux 系统调试】Linux 调试工具strip使用方法
  • Kubernetes生产级资源管理实战:从QoS策略到OOM防御体系
  • C 语言网络编程问题:E1696 无法打开 源 文件 “sys/socket.h“
  • ubuntu安装Go SDK
  • linux 怎么把trex-core-2.65用 crosstool-ng-1.27.0/编译
  • chili调试笔记13 工程图模块 mesh渲染 mesh共享边显示实现
  • FlyEnv:优雅直观的跨平台开发环境管理工具
  • VUE+ElementUI 使用el-input类型type=“number” 时,取消右边的上下箭头
  • Nginx 搭建支持多版本和前端路由的静态网站
  • 高斯牛顿法 梯度下降法 LM算法的区别
  • 【ARM AMBA AHB 入门 3 -- AHB 总线介绍】
  • Postman中https和http的区别是什么?
  • Linux 下MySql主从数据库的环境搭建
  • 什么是回调 钩子 Hook机制 钩子函数 异步编程
  • 【Prometheus】业务指标与基础指标的标签来源差异及设计解析
  • 4大主流行业CRM需求精解:精准匹配业务痛点与选型策略
  • 数据结构与算法-单链表的应用
  • C语言学习之字符函数和字符串函数
  • 【Python】让Selenium 像Beautifulsoup一样,用解析HTML 结构的方式提取元素!