美文网首页BB-black开发板[Linux arm-v8]
MPU6500六轴陀螺仪linux驱动(spi)--Apple的

MPU6500六轴陀螺仪linux驱动(spi)--Apple的

作者: applecai | 来源:发表于2020-12-13 22:12 被阅读0次

    一,前言

    MPU6500六轴陀螺仪linux驱动(i2c)--Apple的学习笔记后,我将i2c接口连线改成了spi接口。目的就是为了学习spi驱动,所以我才买了MPU6500,否则买个MPU6050就够了,哈哈~linux驱动代码依然上传到我的gitee工程16中的apple6500dev4.c

    二,MPU6500和bb black开发板连线

    am335x的spi的数据先d0和d1是可以自己配置方向的。我连接如下。为什么不用spi1呢?其实我一开始用spi1,后面听我将故事吧,其实都可以的。


    image.png

    am335x spi dts配置

        spi0_pins: pinmux_spi0_pins {
            pinctrl-single,pins = <
                AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK,PIN_INPUT, MUX_MODE0)       /* SPI0_SCLK */
                AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT_PULLUP, MUX_MODE0)     /* SPI0_D0 */
                AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_OUTPUT, MUX_MODE0)   /* SPI0_D1 */
                AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_OUTPUT, MUX_MODE0)  /* SPI0_CS0 */
            >;
        };
    
    
    &spi0 {
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&spi0_pins>;
    
        apple_MPU6500spi@0 {
            compatible ="applecai,apple6500spi";//"rohm,dh2228fv";
            reg = <0>;
            spi-cpol;
            spi-cpha;
            spi-max-frequency = <100000>;
            spi-tx-bus-width=<1>;
            spi-rx-bus-width=<1>;
        };
    };
    

    三,遇到的问题

    1. 发id命令后,返回值为0。
    首先spi的初始化和i2c不同,需要延时100ms,及配置116寄存器,这个我注意到并且添加了,但是为什么id读取不正确呢?
    步骤1:我先猜测是否我的POL,PAH相位不符,果然查看spec,都应该设置为1,是第二个沿的上升沿读值。对应dts应该如何修改呢?查看源码找到了模式设置的关键字。

        /* Mode (clock phase/polarity/etc.) */
        if (of_property_read_bool(nc, "spi-cpha"))
            spi->mode |= SPI_CPHA;
        if (of_property_read_bool(nc, "spi-cpol"))
            spi->mode |= SPI_CPOL;
        if (of_property_read_bool(nc, "spi-3wire"))
            spi->mode |= SPI_3WIRE;
        if (of_property_read_bool(nc, "spi-lsb-first"))
            spi->mode |= SPI_LSB_FIRST;
        if (of_property_read_bool(nc, "spi-cs-high"))
            spi->mode |= SPI_CS_HIGH;
    

    改完读取的id值还是0。
    步骤2:我检查完连线正确。
    步骤3:还是用示波器看吧,最直观。虽然我家的示波器使用2路,也不影响。
    于是发现我一开始设置d0为am335的发送,d1为接收。但是其实d1为发送。后来源码搜索了下,发现了猫腻,d0和d1的发送或接收可以通过DPE0,DPE1,IS寄存器来修改的。于是我还是修改了下设备树。
    另外一个问题,示波器中发送的命令应该是0x75+0x80=0xF5,但是示波器传的命令是181不是0xF5。原来是我代码中发命令mask不是0x7F,而是0x3F导致的问题。修改后,能看到第二个clock时候读取的值是0x70

    image.png
    由此说明MPU6500已经能够正确响应117的who am i命令请求。id无法读取说明是am335x芯片这边的问题了。
    2. MISO输入波形正确,为什么代码中读取不到呢?
    步骤1:分析源码,但是读取是哪个中断进入的,我暂时还不清楚。spi源码我只看到spi_sync层级,这个函数之后会调用具体芯片比如omap的spi处理函数。关于接收我还不清楚。那么先网上搜索下,发现了spidev_test.c在tools文件夹下,可以用来测试芯片的spi驱动,读写一致即可。
                ev2-114   [000] ....    61.424361: spi_transfer_one_message <-__spi_pump_messages
                 ev2-114   [000] ....    61.424379: <stack trace>
     => __spi_pump_messages
     => __spi_sync
     => spi_sync
     => spi_write_then_read
     => apple6500_read
     => apple6500_open
    

    步骤2:开始研究下spidev_test的使用及kernel的配置,需要添加usr spi dev,它会调用spidev.c来处理spi的ioctrl命令进行读写。于是我运行了下将rx和tx短接。./spidev_test -s 100000 -v,rx都是0。
    天呢,这个问题讨厌了,难道我真的要把代码全部看完吗?于是看了个博客它把如何使用spidev_test写的很详细,包括配置设备树,我发现它的4个pin脚都设置为了INPUT,于是我抄它的配置,结果rx和tx一样了。于是这样的设备树,我改成了自己的MPU6500依然读取id不成功,后来我又自己改了设备树引脚的配置也不成功。
    步骤3:我开始查看他们的代码,但是我了发现差不多呢?于是想到一个办法,spide_test.c的tx一开始是0xff 0xff,我改成了0x75 0x75结果奇迹出现了,我发现rx为0x00 0x70说明spidev_test的代码可以通过MPU6500读取id为0x70了。这是我期望的值。说明此事的设备树配置正确,于是我改成我自己的MPU6500 spi驱动能正确读取id值了。后来尝试了多种引脚配置,发现clock脚要配置为input,暂时原因不知道,之后我会继续熟悉源码找答案。

    image.png

    3. input上报值就一次,然后停止上报,并且值不正确
    查看我的驱动代码,发现open后polling很快发生,但是open里面需要延时200ms,所以我添加了锁。依然存在问题,后来发现源码的read blocks的cmd我之前参考了内核源码中的adxl34spi.c写的,里面的reg cmd offset和我的MPU6500不同,我没有修改导致的bug。修改后测试正确。

    四,测试正确

    Welcome to Buildroot
    buildroot login: root
    # cd /usr/study/
    # insmod apple6500_dev4.ko 
    [   13.955164] apple6500_dev4: loading out-of-tree module taints kernel.
    [   13.963313] input: apple6500sensor as /devices/platform/ocp/48000000.interconnect/48000000.interconnect:segment@0/48030000.target-module/48030000.spi/spi_master/spi0/spi0.0/input/input0
    # ./ev2
    ACC x=0.13 y=0.51 z=0.85 
    ACC x=0.12 y=0.51 z=0.85 
    Gyro x=-0.01 z=0.01 
    temp=17.71 
    ACC x=0.12 y=0.51 z=0.85 
    Gyro x=-0.01 z=0.01 
    temp=17.72 
    

    五,MPU6500 spi linux驱动源码

    我是在昨天的i2c MPU6500 linux驱动上简单修改为spi MPU6500 linxu驱动的,之后会参考adxl34x的设计思路将2种总线中共有的部分进行重新提取。

    /**********************************************************************************
    Copyright (C), by AppleCai
    Project                      : Study Kernel
    Description                  : BSP level for MPU6500
    CPU and Compiler             : AM335x,ARM-LINIX-GCC
    |----------------------------------------------------------------------------------
    |               R E V I S I O N   H I S T O R Y
    |----------------------------------------------------------------------------------
    | Date        Version  Author   Description
    | --------    -------  ------   ---------------------------------------------------
    | 2020-12-08  1.0.0    AppleCai MPU6500_001: Initial release version just add frame
    | 2020-12-09  1.0.1    AppleCai MPU6500_002: add WMI reg and I2C DTB
    | 2020-12-10  1.0.2    AppleCai MPU6500_003: add Initial reg and report functions
    | 2020-12-11  1.0.3    AppleCai MPU6500_004: Optimize code structure
    | 2020-12-12  1.0.3    AppleCai MPU6500_005: change i2c to spi
    | 2020-12-13  1.0.3    AppleCai MPU6500_006: fix bug:spi send cmd then read incorrect
    **********************************************************************************/
    #if 0 // DTS
    spi0_pins: pinmux_spi0_pins {
        pinctrl-single,pins = <
            AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK,PIN_INPUT, MUX_MODE0)       /* SPI0_SCLK */
            AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT_PULLUP, MUX_MODE0)     /* SPI0_D0 */
            AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_OUTPUT, MUX_MODE0)   /* SPI0_D1 */
            AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_OUTPUT, MUX_MODE0)  /* SPI0_CS0 */
        >;
    };
    
    
    &spi0 {
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&spi0_pins>;
    
        apple_MPU6500spi@0 {
            compatible ="applecai,apple6500spi";//"rohm,dh2228fv";
            reg = <0>;
            spi-cpol;
            spi-cpha;
            spi-max-frequency = <100000>;
            spi-tx-bus-width=<1>;
            spi-rx-bus-width=<1>;
        };
    };
    #endif
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/delay.h>
    //#include <linux/i2c.h>
    #include <linux/spi/spi.h>
    #include <linux/input-polldev.h>
    #include <linux/of_device.h>
    
    #define APPLE6500_DRV_NAME  "apple6500sensor"
    
    #define POLL_INTERVAL           200
    #define POLL_INTERVAL_MAX       500
    
    /* bit op */
    #define VAL_SIT_BIT(n,addr)        *addr |= (1<<n)
    #define VAL_CLEAR_BIT(n,addr)      *addr &= ~(1<<n)
    #define VAL_CHANGE_BIT(n,addr)     *addr ^= (1<<n)
    #define VAL_TEST_BIT(n,addr)       *addr &= (1<<n)
    
    /* spi */
    #define APPLE6500_CMD_MULTB (1 << 6)
    #define APPLE6500_CMD_READ  (1 << 7)
    #define APPLE6500_WRITECMD(reg) (reg & 0x7F)
    #define APPLE6500_READCMD(reg)  (APPLE6500_CMD_READ | (reg & 0x7F))
    
    
    struct apple6500_chip_reg {
        /* regs */
        char who_am_i;
        char pwr_mgmt_1;
        char pwr_mgmt_2;
        char smplrt_div;
        char sensor_config;
        char accel_config;
        char accel_config2;
        char accel_xout_h;
        char temp_out_h;
        char gyro_xout_h;
        char gyro_config;
        char signal_path_reset;
        char user_config;
    };
    static const struct apple6500_chip_reg chipregs={
        /* reg addr */
        .who_am_i= 117,
        .pwr_mgmt_1= 107,
        .pwr_mgmt_2= 108,
        .smplrt_div= 25,
        .sensor_config= 26,
        .gyro_config= 27,
        .accel_config= 28,
        .accel_config2= 29,
        .accel_xout_h= 59,
        .temp_out_h= 65,
        .gyro_xout_h= 67,
        .signal_path_reset= 104,
        .user_config= 106,
    };
    
    struct apple6500_chip_config{
        char chip_id;
        char chip_enable_sensor;
        char chip_frequency_acc;
        char chip_frequency_gyro;
        char chip_frequency_DIV;
        char chip_reset;
        char chip_sensor_reset;
        char chip_sensitivity_acc;
        char chip_sensitivity_gyro; 
        char chip_selectedclock;
        char chip_usrconfig;
    };
    struct apple6500;
    struct apple6500_transfer_func {
        int (*read) (struct apple6500 *m, unsigned off);
        int (*write) (struct apple6500 *m, unsigned off, unsigned char v);
        int (*readblock)(struct apple6500 *m, unsigned off,unsigned char *buf, size_t size);
    };
    
    /* apple6500 status */
    struct apple6500 {
        struct device   *dev;
        struct input_polled_dev *idev;
        const struct apple6500_chip_reg *regs;
        struct apple6500_chip_config cfgs;
        const struct apple6500_transfer_func *tf;
        struct mutex mulock;
    };
    
    static int apple6500_read(struct apple6500 *m, unsigned off)
    {
        //struct i2c_client *c = m->client;
        struct spi_device *spi = to_spi_device(m->dev);//to_i2c_client(m->dev);
        unsigned char cmd;
        int ret;
        cmd = APPLE6500_READCMD(off);
        //printk("cmd=%d\n",cmd);
        ret=spi_w8r8(spi, cmd);
        //printk("id=0x%x",ret);
        return ret;
    }
    
    static int apple6500_write(struct apple6500 *m, unsigned off, unsigned char v)
    {
        //struct i2c_client *c = m->client;
        struct spi_device *spi = to_spi_device(m->dev);//to_i2c_client(m->dev);
    
        unsigned char buf[2];
    
        buf[0] = APPLE6500_WRITECMD(off);
        buf[1] = v;
    
        return spi_write(spi, buf, sizeof(buf));
    }
    
    static int apple6500_read_block(struct apple6500 *m, unsigned off,unsigned char *buf, size_t size)
    {
        //struct i2c_client *c = m->client;
        struct spi_device *spi = to_spi_device(m->dev);//to_i2c_client(m->dev);
        ssize_t status;
    
        off = APPLE6500_READCMD(off);
        status = spi_write_then_read(spi, &off, 1, buf, size);
    
        return (status < 0) ? status : 0;
    }
    static const struct apple6500_transfer_func apple6500_tf_spi = {
        .write = apple6500_write,
        .read = apple6500_read,
        .readblock = apple6500_read_block
    };
    static void apple6500_poll(struct input_polled_dev *dev)
    {
        struct apple6500 *m = dev->private;
    
        int ax, ay, az,gx,gy,gz,temp;
        int ret;
        u8 buf[14];
        mutex_lock(&m->mulock);
        ret = m->tf->readblock(m, m->regs->accel_xout_h, buf, sizeof(buf));
        if (ret < 0)
        {
            dev_err(m->dev, "polling error\n");
            return;
        }
        mutex_unlock(&m->mulock);
        ax = ( buf[0] << 8 ) | buf[1];
        ay = ( buf[2] << 8 ) | buf[3];
        az = ( buf[4] << 8 ) | buf[5];
        temp = ( buf[6] << 8 ) | buf[7];
        gx = ( buf[8] << 8 ) | buf[9];
        gy = ( buf[10] << 8 ) | buf[11];
        gz = ( buf[12] << 8 ) | buf[13];
    
        input_report_abs(dev->input, ABS_X, ax);
        input_report_abs(dev->input, ABS_Y, ay);
        input_report_abs(dev->input, ABS_Z, az);
        input_report_abs(dev->input, ABS_RX, gx);
        input_report_abs(dev->input, ABS_RY, gy);
        input_report_abs(dev->input, ABS_RZ, gz);
        input_report_abs(dev->input, ABS_MISC, temp);//Temperature = 36.53 + regval/340
        input_sync(dev->input);
    
    }
    static void apple6500_Init_reg(struct apple6500_chip_config * cfgs)
    {
        cfgs->chip_id = 0x70; // id for MPU6500
        cfgs->chip_enable_sensor = 0; // enable all
        cfgs->chip_frequency_acc = 4; // 50Hz/2=25Hz then select 20Hz
        cfgs->chip_frequency_DIV = 19; // 1000/(19+1) = 50Hz clock for acc
        cfgs->chip_frequency_gyro = 4; // 1K 20Hz,due to FCHOICE_B=0
        cfgs->chip_reset = 1<<7; // true 
        cfgs->chip_sensor_reset = 7; // all reset
        cfgs->chip_usrconfig = 0x11; // spi enable and reset all path  
        cfgs->chip_sensitivity_acc = 0; // 65536/4 = 16384 LBS/s
        cfgs->chip_sensitivity_gyro = 3<<3; // 65536/4000 = 16.384 LBS/s
        cfgs->chip_selectedclock = 1; // PLL with axis 
    }
    static int apple6500_CheckId(struct apple6500 *m)
    {
        int IdNum = 0;
        IdNum = m->tf->read(m, m->regs->who_am_i);
        if(m->cfgs.chip_id != IdNum)
        {
            dev_err(m->dev, "Not found MPU6500,id is %d\n",IdNum);
            return -ENXIO;
        }   
        return 0;
    }
    
    static int apple6500_ResetChipAndSelectClock(struct apple6500 *m)
    {
        int err = 0;
        int val = 0;
        val = m->cfgs.chip_reset+m->cfgs.chip_selectedclock;
        err = m->tf->write(m, m->regs->pwr_mgmt_1,val);
        if(err)
        {
            dev_err(m->dev, "reset command error\n");
        }
        /* if use spi shall add delay timer */
        msleep(100);
        err = m->tf->write(m, m->regs->signal_path_reset,m->cfgs.chip_sensor_reset);
        if(err)
        {
            dev_err(m->dev, "reset command2 error\n");
        }
        msleep(100);
        err = m->tf->write(m, m->regs->user_config,m->cfgs.chip_usrconfig);
        if(err)
        {
            dev_err(m->dev, "usr config error\n");
        }
        msleep(100);
        return err;
    }
    
    static int apple6500_EnableSensor(struct apple6500 *m)
    {
        int err = 0;
        err = m->tf->write(m, m->regs->pwr_mgmt_2,m->cfgs.chip_enable_sensor);
        if(err)
        {
            dev_err(m->dev, "enable command error\n");
        }   
        return err;
    }
    
    static int apple6500_SetACCSamplerate(struct apple6500 *m)
    {
        int err = 0;
        err = m->tf->write(m, m->regs->accel_config2,m->cfgs.chip_frequency_acc);
        if(err)
        {
            dev_err(m->dev, "set acc bandwidths error\n");
        }   
        err = m->tf->write(m, m->regs->smplrt_div,m->cfgs.chip_frequency_DIV);
        if(err)
        {
            dev_err(m->dev, "set acc clock division error\n");
        }   
        return err;
    }
    
    static int apple6500_SetGyroSamplerate(struct apple6500 *m)
    {
        int err = 0;
        err = m->tf->write(m, m->regs->sensor_config,m->cfgs.chip_frequency_gyro);
        if(err)
        {
            dev_err(m->dev, "set gyro bandwidths error\n");
        }   
        return err;
    }
    
    static int apple6500_SetAccSensitivity(struct apple6500 *m)
    {
        int err = 0;
        err = m->tf->write(m, m->regs->accel_config,m->cfgs.chip_sensitivity_acc);
        if(err)
        {
            dev_err(m->dev, "set acc sensitivity error\n");
        }   
        return err;
    }
    
    static int apple6500_SetGyroSensitivity(struct apple6500 *m)
    {
        int err = 0;
        err = m->tf->write(m, m->regs->gyro_config,m->cfgs.chip_sensitivity_gyro);
        if(err)
        {
            dev_err(m->dev, "set gyro sensitivity error\n");
        }   
        return err;
    }
    
    static int apple6500_Init(struct apple6500 *m)
    {
        
        int ret = 0;
    
        /* load default configuration value */
        apple6500_Init_reg(&m->cfgs);
        /* set reset chip and select clock */
        ret = apple6500_ResetChipAndSelectClock(m);
        /* check id */
        ret = apple6500_CheckId(m);
        /* enable acc and gyro */
        ret = apple6500_EnableSensor(m);
        /* set sample rate and bandwith for acc*/
        ret = apple6500_SetACCSamplerate(m);
        /* set sample rate and bandwith for gyro*/
        ret = apple6500_SetGyroSamplerate(m);   
        /* set sensitivity for acc*/
        ret = apple6500_SetAccSensitivity(m);   
        /* set sensitivity for gyro*/
        ret = apple6500_SetGyroSensitivity(m);
        if (ret < 0)
        {
            dev_err(m->dev,"init error\n");
            return -1;
        }
        
        return ret;
    }
    
    /* Initialize the APPLE6500 chip */
    static void apple6500_open(struct input_polled_dev *dev)
    {
        int ret = 0;
        struct apple6500 *m = dev->private;
        mutex_init(&m->mulock);
        mutex_lock(&m->mulock);
        ret = apple6500_Init(m);
        mutex_unlock(&m->mulock);
        if (ret < 0)
        {
            dev_err(m->dev, "apple6500 initialization failed\n");
            return;
        }
    }
    
    static void apple6500_close(struct input_polled_dev *dev)
    {
    
        //struct apple6500 *m = dev->private;
        //int ret = 0;
        /* sleep */
        //ret = m->tf->write(m, APPLE6500_PWR_REG107,APPLE6500_PWR_SLEEP);
        //if (ret < 0)
        //  return;
    }
    static const struct of_device_id apple6500_dt_ids[] = {
        { .compatible = "applecai,apple6500spi", .data = &chipregs},
        { /* sentinel */ }
    };
    /*
     * I2C init/probing/exit functions
     */
    static int apple6500_spi_probe(struct spi_device *spi)
    {
        struct input_polled_dev *idev;
        struct apple6500 *m;
        int err;
        const struct of_device_id *match;
        
        match = of_match_node(apple6500_dt_ids,spi->dev.of_node);
        if (!match)
        {
            dev_err(&spi->dev, "No such device or address.\n");
            return -ENXIO;
        }
        m = devm_kzalloc(&spi->dev, sizeof(*m), GFP_KERNEL);
        if (!m)
            return -ENOMEM;
    
        idev = devm_input_allocate_polled_device(&spi->dev);
        if (!idev)
            return -ENOMEM;
    
        //m->client = c;
        m->dev = &spi->dev;
        m->idev = idev;
        m->regs = match->data;
        m->tf = &apple6500_tf_spi;
        spi_set_drvdata(spi, m);//i2c_set_clientdata(c, m);
        idev->private       = m;
        idev->input->name   = APPLE6500_DRV_NAME;
        idev->input->id.bustype = BUS_SPI;//BUS_I2C;
        idev->input->id.vendor = 0xABCD;
        idev->input->id.product = 0x0001;
        idev->input->id.version = 0x0100;
        idev->poll      = apple6500_poll;
        idev->poll_interval = POLL_INTERVAL;
        idev->poll_interval_max = POLL_INTERVAL_MAX;
        idev->open      = apple6500_open;
        idev->close     = apple6500_close;
    
        __set_bit(EV_ABS, idev->input->evbit);
        input_set_abs_params(idev->input, ABS_X, -32768, 32767, 0, 0);
        input_set_abs_params(idev->input, ABS_Y, -32768, 32767, 0, 0);
        input_set_abs_params(idev->input, ABS_Z, -32768, 32767, 0, 0);
        input_set_abs_params(idev->input, ABS_RX, -32768, 32767, 0, 0);
        input_set_abs_params(idev->input, ABS_RY, -32768, 32767, 0, 0);
        input_set_abs_params(idev->input, ABS_RZ, -32768, 32767, 0, 0);
        input_set_abs_params(idev->input, ABS_MISC, -32768, 32767, 0, 0);
    
        err = input_register_polled_device(idev);
        if (err) {
            input_unregister_polled_device(idev);
            dev_err(&spi->dev, "failed to register polled input device\n");
            goto out;
        }
        return 0;
    out:
        input_unregister_polled_device(idev);
        return err;
    }
    
    static int apple6500_spi_remove(struct spi_device *spi)
    {
        struct apple6500 *m = spi_get_drvdata(spi);//i2c_get_clientdata(c);
        //disable sensor
        input_unregister_polled_device(m->idev);
        input_free_polled_device(m->idev);
        dev_info(m->dev, "%s: removed\n", APPLE6500_DRV_NAME);
        kfree(m);
        return 0;
    }
    
    
    MODULE_DEVICE_TABLE(of, apple6500_dt_ids);
    
    static struct spi_driver apple6500_driver = {
        .driver = {
            .name   = APPLE6500_DRV_NAME,
            .of_match_table = apple6500_dt_ids,
        },
        .probe      = apple6500_spi_probe,
        .remove   = apple6500_spi_remove,
    };
    
    module_spi_driver(apple6500_driver);
    
    MODULE_AUTHOR("AppleCai");
    MODULE_DESCRIPTION("APPLE6500 Acceler and Gyro Driver");
    MODULE_LICENSE("GPL");
    
    

    相关文章

      网友评论

        本文标题:MPU6500六轴陀螺仪linux驱动(spi)--Apple的

        本文链接:https://www.haomeiwen.com/subject/vrzpgktx.html