Example Device: DEBIX Model A + I/O Board
Overview
lThe MPU6050 module is a 6-axis motion processing component consisting of three-axis acceleration and three-axis gyroscope sensor.
lThe MPU6050 module contains a three-axis acceleration sensor, a three-axis angular velocity (gyroscope) sensor, and a temperature sensor.
Hardware installation
lMPU6050 module's VCC terminal is connected to J2 Pin2 of DEBIX Model A I/O board
lMPU6050 module's SDA terminal is connected to J2 Pin3 of DEBIX Model A I/O board
lMPU6050 module's SCL terminal is connected to J2 Pin5 of DEBIX Model A I/O board
lMPU6050 module's GND terminal is connected to J2 Pin39 of DEBIX Model A I/O board
lThe remaining pins of the MPU6050 module are left dangling
NOTE:
lThe AD0 terminal of MPU6050 module is an address line
nConnect the high level or suspended device address is 0x68, ground address is 0x69, this experiment device address is 0x68
lWhen the MPU6050 module is used as a master, I2C uses XDA and XCL terminals
lWhen the MPU6050 module is used as a slave, I2C uses SDA and SCL terminals
nIn this experiment, MPU6050 module is the slave and DEBIX is the master
lThe INT terminal of MPU6050 module is an interrupt port, and no interrupt was used in this experiment
Modify the device tree imx8mp-evk.dts
1. Add MPU6050 node under I2c4 node:
&i2c4 {
status = "okay";
MPU6050: MPU6050@68 {
compatible = "MPU6050";
reg = <0x68>;
};
2. Compile and replace the device tree file
l Compile the device tree in the source directory: make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- dtbs
l Copy the compiled new device tree file arch/arm64/boot/dts/freescale/imx8mp-evk.dtb to the /boot directory of DEBIX and restart DEBIX.
3. Check whether dts contains the modified device tree node.
l Run the command ls /sys/bus/i2c/devices
Write and Load Drivers
The driver code is as follows:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/delay.h>
#define MPU_GYRO_REG (0x1B)
#define MPU_ADCC_REG (0x1C)
#define MPU_CFG_REG (0X1A)
#define MPU_SAR_REG (0X19)
#define MPU_IRQ_REG (0x38)
#define MPU_DEVID_REG (0x75)
#define MPU_PWRM1_REG (0X6B)
#define MPU_PWRM2_REG (0X6C)
#define MPU_ACCXH_REG (0x3B)
#define MPU_ACCXL_REG (0x3C)
#define MPU_ACCYH_REG (0x3D)
#define MPU_ACCYL_REG (0x3E)
#define MPU_ACCZH_REG (0x3F)
#define MPU_ACCZL_REG (0x40)
#define MPU_TEMPH_REG (0x41)
#define MPU_TEMPL_REG (0x42)
#define MPU_GYROXH_REG (0x43)
#define MPU_GYROXL_REG (0x44)
#define MPU_GYROYH_REG (0x45)
#define MPU_GYROYL_REG (0x46)
#define MPU_GYROZH_REG (0x47)
#define MPU_GYROZL_REG (0x48)
static struct i2c_client* fxs_i2c_client;
typedef struct {
u16 nACCx;
u16 nACCy;
u16 nACCz;
u16 nGYROx;
u16 nGYROy;
u16 nGYROz;
u16 nTemp;
}sMPU6050Output_t;
static sMPU6050Output_t sMPU6050Output;
/**< basic i2c driver >**/
static s32 i2c_write_reg(u8 reg_addr, u8 data, u8 len){
u8 buff[256];
struct i2c_msg msgs[] = {
[0] = {
.addr = fxs_i2c_client->addr,
.flags = 0,
.len = len + 1,
.buf = buff,
},
};
buff[0] = reg_addr;
memcpy(&buff[1], &data, len);
return i2c_transfer(fxs_i2c_client->adapter, msgs, 1);
}
static s32 i2c_read_reg(u8 reg,void* data, int len){
int ret;
struct i2c_msg msgs[] = {
[0]={
.addr = fxs_i2c_client->addr,
.flags = 0,
.buf = ®,
.len = sizeof(reg),
},
[1]={
.addr = fxs_i2c_client->addr,
.flags = I2C_M_RD,
.buf = data,
.len = len,
},
};
ret = i2c_transfer(fxs_i2c_client->adapter,msgs,2);
if(ret == 2) {
return ret;
}else{
printk("read:i2c_transfer err..\n");
return -1;
}
}
/**< basic read write func >**/
// modify one register
static s32 MPU6050_ReadReg(u8 reg,u8* data){
return i2c_read_reg(reg,data,1);
}
static s32 MPU6050_WriteReg(u8 reg,u8 data){
return i2c_write_reg(reg,data,1);
}
// modify bits of a register
static s32 MPU6050_SetReg(u8 reg,u8 bitMask,u8 value){
int ret;
u8 writeValue,readValue;
ret = MPU6050_ReadReg(reg,&readValue);
if(ret < 0 ){
printk("MPU6050_ReadReg err1..\n");
return ret;
}
writeValue = readValue &(~bitMask);
writeValue |= value ;
ret = MPU6050_WriteReg(reg,writeValue);
if(ret < 0 ){
printk("MPU6050_WriteReg err2..\n");
return ret;
}
ret = MPU6050_ReadReg(reg,&readValue);
if(ret < 0 ){
printk("MPU6050_ReadReg err3..\n");
return ret;
}
if((readValue&bitMask) == value){
return 1;
}
return -1;
}
static s32 MPU6050_GetReg(u8 reg,u8 bitMask,u8 *value){
int ret;
u8 readValue;
ret = MPU6050_ReadReg(reg,&readValue);
if(ret < 0 ){
printk("MPU6050_ReadReg err..\n");
return ret;
}
*value = readValue & bitMask;
return 1;
}
/**MPU6050 GYRO range
* @param val range[ 0:250 , 1:500 , 2:1000 , 3:2000]
*/
static s32 MPU6050_SetGYRORange(u8 val){
if((val >=0) && (val <= 3))
return MPU6050_SetReg(MPU_GYRO_REG,0x18,val);
else
return -1;
}
static s32 MPU6050_SetADCCRange(u8 val){
if((val >=0) && (val <= 3))
return MPU6050_SetReg(MPU_ADCC_REG,0x18,val);
else
return -1;
}
static s32 MPU6050_SetDefaultCfg(void){
return MPU6050_SetReg(MPU_CFG_REG,0x07,4);
}
static s32 MPU6050_SetSampleFreq(u8 val){
u16 data;
data = 1000 / (val + 1);
return MPU6050_SetReg(MPU_SAR_REG,0xff,data);
}
// MPU6050 default init func
static u8 MPU6050_Init(void){
u8 ID;
MPU6050_SetReg(MPU_PWRM1_REG,0xff,0x80); // reset
mdelay(100);
MPU6050_SetReg(MPU_PWRM1_REG,0xff,0x00); // start
MPU6050_SetGYRORange(3); // GYRO range 2000
MPU6050_SetADCCRange(0); // ADCC range 2g
MPU6050_SetDefaultCfg(); // GYRO Freq : 1kHz
MPU6050_SetSampleFreq(19);// SampleFreq = 1kHz / (1+19) = 50Hz
MPU6050_SetReg(MPU_IRQ_REG,0xff,0x00); // turn off irq
MPU6050_GetReg(MPU_DEVID_REG,0xff,&ID); // read device ID
if(ID == 0x68){
MPU6050_SetReg(MPU_PWRM1_REG,0xff,0x01);
MPU6050_SetReg(MPU_PWRM2_REG,0xff,0x00);
return 0;
}
return -1;
}
static void MPU6050_GetOutPutData(sMPU6050Output_t* sMPU6050Output){
u8 data[14];
u16 result[7];
u8 i;
for(i=0;i<14;i++){
MPU6050_GetReg(MPU_ACCXH_REG + i,0xff,&data[i]);
}
for(i=0;i<7;i++){
result[i] = ((data[i*2] & 0xff) << 8) | data[(i*2)+1];
}
sMPU6050Output->nACCx = result[0];
sMPU6050Output->nACCy = result[1];
sMPU6050Output->nACCz = result[2];
sMPU6050Output->nTemp = 36 + result[3]/340;
sMPU6050Output->nGYROx = result[4];
sMPU6050Output->nGYROy = result[5];
sMPU6050Output->nGYROz = result[6];
}
int misc_open (struct inode *inode, struct file *file){
int ret;
printk("misc_open..\n");
ret = MPU6050_Init();
if(ret < 0){
printk("MPU6050_Init err..\n");
return -1;
}
return 0;
}
int misc_release (struct inode *inode, struct file *file){
printk("misc_release..\n");
return 0;
}
ssize_t misc_read (struct file *file, char __user *ubuf, size_t size , loff_t * off){
int ret;
sMPU6050Output_t* spMPU6050Output = &sMPU6050Output;
MPU6050_GetOutPutData(spMPU6050Output);
ret=copy_to_user(ubuf,spMPU6050Output,sizeof(sMPU6050Output_t));
if(ret!=0){
printk("copy_to_user err..\n");
return -1;
}
printk("cdev_read..\n");
return ret;
}
static const struct file_operations misc_fops={
.owner = THIS_MODULE,
.open = misc_open,
.read = misc_read,
.release = misc_release
};
static struct miscdevice misc_test={
.minor = MISC_DYNAMIC_MINOR,
.name = "MPU6050",
.fops = &misc_fops
};
static int MPU6050_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id){
int ret;
fxs_i2c_client = client;
ret = misc_register(&misc_test);
if(ret<0){
printk("misc_register err..\n");
return -1;
}
printk("i2c probe success..\n");
return 0;
}
static int MPU6050_i2c_remove(struct i2c_client *client){
misc_deregister(&misc_test);
return 0;
}
static const struct i2c_device_id id_MPU6050_i2c_driver[] = {
{"MPU6050",},
{}
};
static const struct of_device_id of_MPU6050_i2c_driver[] = {
{.compatible = "MPU6050",},
{},
};
static struct i2c_driver MPU6050_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "MPU6050",
.of_match_table = of_MPU6050_i2c_driver,
},
.probe = MPU6050_i2c_probe,
.remove = MPU6050_i2c_remove,
.id_table = id_MPU6050_i2c_driver,
};
static int __init i2c_init(void){
int ret ;
ret = i2c_add_driver(&MPU6050_i2c_driver);
if(ret < 0){
printk("i2c_add_driver error..\n");
return ret;
}
printk("i2c_init..\n");
return 0;
}
static void __exit i2c_exit(void){
i2c_del_driver(&MPU6050_i2c_driver);
printk("i2c_exit..\n");
}
module_init(i2c_init);
module_exit(i2c_exit);
MODULE_LICENSE("GPL");
1. Run the above command to compile the driver make will get the .ko file
2. Copy the .ko file to DEBIX, run the command sudo insmod mpu6050.ko
3. Run the command sudo dmesg | tail -2 , check the driver print information:
![](/Uploads/Temp/image/20240415/95.png)
4. You can see that the probe function matches the device tree successfully.
Write Application Test Code
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
typedef struct {
unsigned short nACCx;
unsigned short nACCy;
unsigned short nACCz;
unsigned short nGYROx;
unsigned short nGYROy;
unsigned short nGYROz;
unsigned short nTemp;
}sMPU6050Output_t;
int main(int argc,char *argv[]){
int fd,ret;
int value;
sMPU6050Output_t sMPU6050Output;
fd = open("/dev/MPU6050", O_RDWR);
if (fd < 0){
perror("open error \n");
return fd;
}
while (1){
read(fd,&sMPU6050Output,sizeof(sMPU6050Output_t));
printf("accx is %d..\n",sMPU6050Output.nACCx);
printf("accy is %d..\n",sMPU6050Output.nACCy);
printf("accz is %d..\n",sMPU6050Output.nACCz);
printf("temp is %d.%-d..\n",sMPU6050Output.nTemp/10,
sMPU6050Output.nTemp%10);
printf("gyrox is %d..\n",sMPU6050Output.nGYROx);
printf("gyroy is %d..\n",sMPU6050Output.nGYROy);
printf("gyroz is %d..\n",sMPU6050Output.nGYROz);
printf("-----------------------\n");
sleep(1);
}
close(fd);
return 0;
}
Compile the application file, run the command aarch64-none-linux-gnu-gcc app.c -o app, copy the compiled executable file to DEBIX.
Test result
lChange the angle and direction of the MPU6050 module and the output information will change accordingly.
![](/Uploads/Temp/image/20240415/96.png)
In the above printed information:
laccx, accy, accz are the acceleration values.
ltemp is the current temperature value.
lgyrox, gyrox, gyrox are gyroscope values.