总线驱动--SPI驱动(下)

简介: 总线驱动--SPI驱动

ICM20608实例

修改设备树

1、添加 ICM20608 所使用的 IO

首先在 imx6ull-alientek-emmc.dts 文件中添加 ICM20608 所使用的 IO 信息,在 iomuxc 节点中添加一个新的子节点来描述 ICM20608 所使用的 SPI 引脚,子节点名字为 pinctrl_ecspi3,节点内容如下所示:

pinctrl_ecspi3: icm20608 {
       fsl,pins = <
            MX6UL_PAD_UART2_TX_DATA  GPIO1_IO20     0x10b0 /* CS */
            MX6UL_PAD_UART2_RX_DATA  ECSPI3_SCLK    0x10b1 /* SCLK */
            MX6UL_PAD_UART2_RTS_B  ECSPI3_MISO      0x10b1 /* MISO */           
            MX6UL_PAD_UART2_CTS_B  ECSPI3_MOSI      0x10b1 /、* MOSI */
       >;    
};

UART2_TX_DATA 这个 IO 是 ICM20608 的片选信号,这里我们并没有将其复用为 ECSPI3的 SS0 信号,而是将其复用为了普通的 GPIO。因为我们需要自己控制片选信号,所以将其复用为普通的 GPIO。

2、在 ecspi3 节点追加 icm20608 子节点

在 imx6ull-alientek-emmc.dts 文件中并没有任何向 ecspi3 节点追加内容的代码,这是因为NXP 官方的 6ULL EVK 开发板上没有连接 SPI 设备。在 imx6ull-alientek-emmc.dts 文件最后面加入如下所示内容:

&ecspi3 {
     fsl,spi-num-chipselects = <1>;
     cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;
     pinctrl-names = "default";
     pinctrl-0 = <&pinctrl_ecspi3>;
     status = "okay";
spidev: icm20608@0 {
        compatible = "alientek,icm20608";
        spi-max-frequency = <8000000>;
        reg = <0>;
   };

};

第 2 行,设置当前片选数量为 1,因为就只接了一个 ICM20608。

第 3 行,一定要使用 “cs-gpios”属性来描述片选引脚,SPI 主机驱动就会控制片选引脚。

第 5 行,设置 IO 要使用的 pinctrl 子节点,也就是我们在示例代码 中新建的pinctrl_ecspi3。

第 6 行,imx6ull.dtsi 文件中默认将 ecspi3 节点状态(status)设置为“disable”,这里我们要将其改为“okay”。

第 8~12 行,icm20608 设备子节点,因为icm20608 连接在ECSPI3 的第 0 个通道上,因此@后面为 0。第 9 行设置节点属性兼容值为“alientek,icm20608”,第 10 行设置 SPI 最大时钟频率为 8MHz,这是 ICM20608 的SPI 接口所能支持的最大的时钟频率。第 11 行,icm20608 连接

在通道 0 上,因此 reg 为 0。

imx6ull-alientek-emmc.dts 文件修改完成以后重新编译一下,得到新的 dtb 文件,并使用新的 dtb 启动 Linux 系统

编写 ICM20608 驱动

icm20608reg.h

#ifndef ICM20608_H
#define ICM20608_H
#define ICM20608G_ID            0XAF    /* ID值 */
#define ICM20608D_ID            0XAE    /* ID值 */
/* ICM20608寄存器 
 *复位后所有寄存器地址都为0,除了
 *Register 107(0X6B) Power Management 1     = 0x40
 *Register 117(0X75) WHO_AM_I               = 0xAF或0xAE
 */
/* 陀螺仪和加速度自测(出产时设置,用于与用户的自检输出值比较) */
#define ICM20_SELF_TEST_X_GYRO      0x00
#define ICM20_SELF_TEST_Y_GYRO      0x01
#define ICM20_SELF_TEST_Z_GYRO      0x02
#define ICM20_SELF_TEST_X_ACCEL     0x0D
#define ICM20_SELF_TEST_Y_ACCEL     0x0E
#define ICM20_SELF_TEST_Z_ACCEL     0x0F
/* 陀螺仪静态偏移 */
#define ICM20_XG_OFFS_USRH          0x13
#define ICM20_XG_OFFS_USRL          0x14
#define ICM20_YG_OFFS_USRH          0x15
#define ICM20_YG_OFFS_USRL          0x16
#define ICM20_ZG_OFFS_USRH          0x17
#define ICM20_ZG_OFFS_USRL          0x18
#define ICM20_SMPLRT_DIV            0x19
#define ICM20_CONFIG                0x1A
#define ICM20_GYRO_CONFIG           0x1B
#define ICM20_ACCEL_CONFIG          0x1C
#define ICM20_ACCEL_CONFIG2         0x1D
#define ICM20_LP_MODE_CFG           0x1E
#define ICM20_ACCEL_WOM_THR         0x1F
#define ICM20_FIFO_EN               0x23
#define ICM20_FSYNC_INT             0x36
#define ICM20_INT_PIN_CFG           0x37
#define ICM20_INT_ENABLE            0x38
#define ICM20_INT_STATUS            0x3A
/* 加速度输出 */
#define ICM20_ACCEL_XOUT_H          0x3B
#define ICM20_ACCEL_XOUT_L          0x3C
#define ICM20_ACCEL_YOUT_H          0x3D
#define ICM20_ACCEL_YOUT_L          0x3E
#define ICM20_ACCEL_ZOUT_H          0x3F
#define ICM20_ACCEL_ZOUT_L          0x40
/* 温度输出 */
#define ICM20_TEMP_OUT_H            0x41
#define ICM20_TEMP_OUT_L            0x42
/* 陀螺仪输出 */
#define ICM20_GYRO_XOUT_H           0x43
#define ICM20_GYRO_XOUT_L           0x44
#define ICM20_GYRO_YOUT_H           0x45
#define ICM20_GYRO_YOUT_L           0x46
#define ICM20_GYRO_ZOUT_H           0x47
#define ICM20_GYRO_ZOUT_L           0x48
#define ICM20_SIGNAL_PATH_RESET     0x68
#define ICM20_ACCEL_INTEL_CTRL      0x69
#define ICM20_USER_CTRL             0x6A
#define ICM20_PWR_MGMT_1            0x6B
#define ICM20_PWR_MGMT_2            0x6C
#define ICM20_FIFO_COUNTH           0x72
#define ICM20_FIFO_COUNTL           0x73
#define ICM20_FIFO_R_W              0x74
#define ICM20_WHO_AM_I              0x75
/* 加速度静态偏移 */
#define ICM20_XA_OFFSET_H           0x77
#define ICM20_XA_OFFSET_L           0x78
#define ICM20_YA_OFFSET_H           0x7A
#define ICM20_YA_OFFSET_L           0x7B
#define ICM20_ZA_OFFSET_H           0x7D
#define ICM20_ZA_OFFSET_L           0x7E
#endif

icm20608.c

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "icm20608reg.h"
#define ICM20608_CNT    1
#define ICM20608_NAME   "icm20608"
struct icm20608_dev {
    dev_t devid;                /* 设备号   */
    struct cdev cdev;           /* cdev     */
    struct class *class;        /* 类        */
    struct device *device;      /* 设备    */
    struct device_node  *nd;    /* 设备节点 */
    int major;                  /* 主设备号 */
    void *private_data;         /* 私有数据         */
    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;        /* 温度原始值            */
};
static struct icm20608_dev icm20608dev;
/*
 * @description : 从icm20608读取多个寄存器数据
 * @param - dev:  icm20608设备
 * @param - reg:  要读取的寄存器首地址
 * @param - val:  读取到的数据
 * @param - len:  要读取的数据长度
 * @return      : 操作结果
 */
static int icm20608_read_regs(struct icm20608_dev *dev, u8 reg, void *buf, int len)
{
    int ret = -1;
    unsigned char txdata[1];
    unsigned char * rxdata;
    struct spi_message m;
    struct spi_transfer *t;
    struct spi_device *spi = (struct spi_device *)dev->private_data;
    t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);   /* 申请内存 */
    if(!t) {
        return -ENOMEM;
    }
    rxdata = kzalloc(sizeof(char) * len, GFP_KERNEL);   /* 申请内存 */
    if(!rxdata) {
        goto out1;
    }
    /* 一共发送len+1个字节的数据,第一个字节为
    寄存器首地址,一共要读取len个字节长度的数据,*/
    txdata[0] = reg | 0x80;     /* 写数据的时候首寄存器地址bit8要置1 */           
    t->tx_buf = txdata;         /* 要发送的数据 */
    t->rx_buf = rxdata;         /* 要读取的数据 */
    t->len = len+1;             /* t->len=发送的长度+读取的长度 */
    spi_message_init(&m);       /* 初始化spi_message */
    spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
    ret = spi_sync(spi, &m);    /* 同步发送 */
    if(ret) {
        goto out2;
    }
    memcpy(buf , rxdata+1, len);  /* 只需要读取的数据 */
out2:
    kfree(rxdata);                  /* 释放内存 */
out1:   
    kfree(t);                       /* 释放内存 */
    return ret;
}
/*
 * @description : 向icm20608多个寄存器写入数据
 * @param - dev:  icm20608设备
 * @param - reg:  要写入的寄存器首地址
 * @param - val:  要写入的数据缓冲区
 * @param - len:  要写入的数据长度
 * @return    :   操作结果
 */
static s32 icm20608_write_regs(struct icm20608_dev *dev, u8 reg, u8 *buf, u8 len)
{
    int ret = -1;
    unsigned char *txdata;
    struct spi_message m;
    struct spi_transfer *t;
    struct spi_device *spi = (struct spi_device *)dev->private_data;
    t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);   /* 申请内存 */
    if(!t) {
        return -ENOMEM;
    }
    txdata = kzalloc(sizeof(char)+len, GFP_KERNEL);
    if(!txdata) {
        goto out1;
    }
    /* 一共发送len+1个字节的数据,第一个字节为
    寄存器首地址,len为要写入的寄存器的集合,*/
    *txdata = reg & ~0x80;  /* 写数据的时候首寄存器地址bit8要清零 */
    memcpy(txdata+1, buf, len); /* 把len个寄存器拷贝到txdata里,等待发送 */
    t->tx_buf = txdata;         /* 要发送的数据 */
    t->len = len+1;             /* t->len=发送的长度+读取的长度 */
    spi_message_init(&m);       /* 初始化spi_message */
    spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
    ret = spi_sync(spi, &m);    /* 同步发送 */
    if(ret) {
        goto out2;
    }
out2:
    kfree(txdata);              /* 释放内存 */
out1:
    kfree(t);                   /* 释放内存 */
    return ret;
}
/*
 * @description : 读取icm20608指定寄存器值,读取一个寄存器
 * @param - dev:  icm20608设备
 * @param - reg:  要读取的寄存器
 * @return    :   读取到的寄存器值
 */
static unsigned char icm20608_read_onereg(struct icm20608_dev *dev, u8 reg)
{
    u8 data = 0;
    icm20608_read_regs(dev, reg, &data, 1);
    return data;
}
/*
 * @description : 向icm20608指定寄存器写入指定的值,写一个寄存器
 * @param - dev:  icm20608设备
 * @param - reg:  要写的寄存器
 * @param - data: 要写入的值
 * @return   :    无
 */ 
static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg, u8 value)
{
    u8 buf = value;
    icm20608_write_regs(dev, reg, &buf, 1);
}
/*
 * @description : 读取ICM20608的数据,读取原始数据,包括三轴陀螺仪、
 *              : 三轴加速度计和内部温度。
 * @param - dev : ICM20608设备
 * @return      : 无。
 */
void icm20608_readdata(struct icm20608_dev *dev)
{
    unsigned char data[14] = { 0 };
    icm20608_read_regs(dev, ICM20_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]);
}
/*
 * @description     : 打开设备
 * @param - inode   : 传递给驱动的inode
 * @param - filp    : 设备文件,file结构体有个叫做pr似有ate_data的成员变量
 *                    一般在open的时候将private_data似有向设备结构体。
 * @return          : 0 成功;其他 失败
 */
static int icm20608_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &icm20608dev; /* 设置私有数据 */
    return 0;
}
/*
 * @description     : 从设备读取数据 
 * @param - filp    : 要打开的设备文件(文件描述符)
 * @param - buf     : 返回给用户空间的数据缓冲区
 * @param - cnt     : 要读取的数据长度
 * @param - offt    : 相对于文件首地址的偏移
 * @return          : 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t icm20608_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
    signed int data[7];
    long err = 0;
    struct icm20608_dev *dev = (struct icm20608_dev *)filp->private_data;
    icm20608_readdata(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;
}
/*
 * @description     : 关闭/释放设备
 * @param - filp    : 要关闭的设备文件(文件描述符)
 * @return          : 0 成功;其他 失败
 */
static int icm20608_release(struct inode *inode, struct file *filp)
{
    return 0;
}
/* icm20608操作函数 */
static const struct file_operations icm20608_ops = {
    .owner = THIS_MODULE,
    .open = icm20608_open,
    .read = icm20608_read,
    .release = icm20608_release,
};
/*
 * ICM20608内部寄存器初始化函数 
 * @param   : 无
 * @return  : 无
 */
void icm20608_reginit(void)
{
    u8 value = 0;
    icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_1, 0x80);
    mdelay(50);
    icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_1, 0x01);
    mdelay(50);
    value = icm20608_read_onereg(&icm20608dev, ICM20_WHO_AM_I);
    printk("ICM20608 ID = %#X\r\n", value); 
    icm20608_write_onereg(&icm20608dev, ICM20_SMPLRT_DIV, 0x00);    /* 输出速率是内部采样率                   */
    icm20608_write_onereg(&icm20608dev, ICM20_GYRO_CONFIG, 0x18);   /* 陀螺仪±2000dps量程                */
    icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG, 0x18);  /* 加速度计±16G量程                   */
    icm20608_write_onereg(&icm20608dev, ICM20_CONFIG, 0x04);        /* 陀螺仪低通滤波BW=20Hz               */
    icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz            */
    icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_2, 0x00);    /* 打开加速度计和陀螺仪所有轴                */
    icm20608_write_onereg(&icm20608dev, ICM20_LP_MODE_CFG, 0x00);   /* 关闭低功耗                        */
    icm20608_write_onereg(&icm20608dev, ICM20_FIFO_EN, 0x00);       /* 关闭FIFO                       */
}
 /*
  * @description     : spi驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * 
  */    
static int icm20608_probe(struct spi_device *spi)
{
    /* 1、构建设备号 */
    if (icm20608dev.major) {
        icm20608dev.devid = MKDEV(icm20608dev.major, 0);
        register_chrdev_region(icm20608dev.devid, ICM20608_CNT, ICM20608_NAME);
    } else {
        alloc_chrdev_region(&icm20608dev.devid, 0, ICM20608_CNT, ICM20608_NAME);
        icm20608dev.major = MAJOR(icm20608dev.devid);
    }
    /* 2、注册设备 */
    cdev_init(&icm20608dev.cdev, &icm20608_ops);
    cdev_add(&icm20608dev.cdev, icm20608dev.devid, ICM20608_CNT);
    /* 3、创建类 */
    icm20608dev.class = class_create(THIS_MODULE, ICM20608_NAME);
    if (IS_ERR(icm20608dev.class)) {
        return PTR_ERR(icm20608dev.class);
    }
    /* 4、创建设备 */
    icm20608dev.device = device_create(icm20608dev.class, NULL, icm20608dev.devid, NULL, ICM20608_NAME);
    if (IS_ERR(icm20608dev.device)) {
        return PTR_ERR(icm20608dev.device);
    }
    /*初始化spi_device */
    spi->mode = SPI_MODE_0; /*MODE0,CPOL=0,CPHA=0*/
    spi_setup(spi);
    icm20608dev.private_data = spi; /* 设置私有数据 */
    /* 初始化ICM20608内部寄存器 */
    icm20608_reginit();     
    return 0;
}
/*
 * @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
 * @param - client  : i2c设备
 * @return          : 0,成功;其他负值,失败
 */
static int icm20608_remove(struct spi_device *spi)
{
    /* 删除设备 */
    cdev_del(&icm20608dev.cdev);
    unregister_chrdev_region(icm20608dev.devid, ICM20608_CNT);
    /* 注销掉类和设备 */
    device_destroy(icm20608dev.class, icm20608dev.devid);
    class_destroy(icm20608dev.class);
    return 0;
}
/* 传统匹配方式ID列表 */
static const struct spi_device_id icm20608_id[] = {
    {"alientek,icm20608", 0},  
    {}
};
/* 设备树匹配列表 */
static const struct of_device_id icm20608_of_match[] = {
    { .compatible = "alientek,icm20608" },
    { /* Sentinel */ }
};
/* SPI驱动结构体 */  
static struct spi_driver icm20608_driver = {
    .probe = icm20608_probe,
    .remove = icm20608_remove,
    .driver = {
            .owner = THIS_MODULE,
            .name = "icm20608",
            .of_match_table = icm20608_of_match, 
           },
    .id_table = icm20608_id,
};
/*
 * @description : 驱动入口函数
 * @param       : 无
 * @return      : 无
 */
static int __init icm20608_init(void)
{
    return spi_register_driver(&icm20608_driver);
}
/*
 * @description : 驱动出口函数
 * @param       : 无
 * @return      : 无
 */
static void __exit icm20608_exit(void)
{
    spi_unregister_driver(&icm20608_driver);
}
module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_LICENSE("GPL");

icm20608App.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>
/***************************************************************
使用方法     :./icm20608App /dev/icm20608
***************************************************************/
/*
 * @description     : main主程序
 * @param - argc    : argv数组元素个数
 * @param - argv    : 具体参数
 * @return          : 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
    int fd;
    char *filename;
    signed int databuf[7];
    unsigned char data[14];
    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;
            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;
            accel_y_act = (float)(accel_y_adc) / 2048;
            accel_z_act = (float)(accel_z_adc) / 2048;
            temp_act = ((float)(temp_adc) - 25 ) / 326.8 + 25;
            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("实际值:");
            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;
}

编译测试 APP

在 icm20608App.c 这个测试 APP 中我们用到了浮点计算,而 I.MX6U 是支持硬件浮点的,因此我们在编译 icm20608App.c 的时候就可以使能硬件浮点,这样可以加速浮点计算。使能硬件浮点很简单,在编译的时候加入如下参数即可:

-march-armv7-a -mfpu-neon -mfloat=hard

输入如下命令使能硬件浮点编译 icm20608App.c 这个测试程序:

arm-linux-gnueabihf-gcc   -march=armv7-a   -mfpu=neon   -mfloat-abi=hard   icm20608App.c   -o
icm20608App

编译成功以后就会生成 icm20608App 这个应用程序,那么究竟有没有使用硬件浮点呢?使

用 arm-linux-gnueabihf-readelf 查看一下编译出来的 icm20608App 就知道了,输入如下命令:

arm-linux-gnueabihf-readelf -A icm20608App

可以看出 FPU 架构为 VFPv3, SIMD 使用了 NEON,并且使用了 SP 和 DP,说明 icm20608App 这个应用程序使用了硬件浮点。

struct spi_device {
    /* 设备模型表示 */
    struct device        dev;
    /* SPI控制器 */
    struct spi_master    *master;
    /* 最大时钟频率 */
    u32            max_speed_hz;
    /* 芯片选择 */
    u8            chip_select;
    /* 每个字的位数 */
    u8            bits_per_word;
    /* SPI模式 */
    u16            mode;
#define    SPI_CPHA    0x01            /* 时钟相位 */
#define    SPI_CPOL    0x02            /* 时钟极性 */
#define    SPI_MODE_0    (0|0)            /* (原始MicroWire) */
#define    SPI_MODE_1    (0|SPI_CPHA)
#define    SPI_MODE_2    (SPI_CPOL|0)
#define    SPI_MODE_3    (SPI_CPOL|SPI_CPHA)
#define    SPI_CS_HIGH    0x04            /* 片选高电平有效 */
#define    SPI_LSB_FIRST    0x08            /* 每个字的位在传输线上的顺序 */
#define    SPI_3WIRE    0x10            /* SI/SO信号共享 */
#define    SPI_LOOP    0x20            /* 回环模式 */
#define    SPI_NO_CS    0x40            /* 1个设备/总线,没有片选 */
#define    SPI_READY    0x80            /* 从机拉低以暂停 */
#define    SPI_TX_DUAL    0x100            /* 用2根线传输 */
#define    SPI_TX_QUAD    0x200            /* 用4根线传输 */
#define    SPI_RX_DUAL    0x400            /* 用2根线接收 */
#define    SPI_RX_QUAD    0x800            /* 用4根线接收 */
    /* 中断号 */
    int            irq;
    /* 控制器的运行时状态 */
    void            *controller_state;
    /* 控制器的板级定义,例如FIFO初始化参数;来自board_info.controller_data */
    void            *controller_data;
    /* 驱动程序名称或别名 */
    char            modalias[SPI_NAME_SIZE];
    /* 片选GPIO号 */
    int            cs_gpio;    
    /*
     * 可能需要更多的钩子来处理更多的协议选项,影响控制器与每个芯片的通信方式,例如:
     *  - 内存打包(12位样本打包到低位,其他位置为零)
     *  - 优先级
     *  - 每个字后放弃片选
     *  - 片选延迟
     *  - ...
     */
};
目录
相关文章
|
1月前
嵌入式开发板串口驱动框架
嵌入式开发板串口驱动框架
14 0
|
12天前
|
Perl
【ZYNQ】SPI 简介及 EMIO 模拟 SPI 驱动示例
【ZYNQ】SPI 简介及 EMIO 模拟 SPI 驱动示例
|
26天前
【STM32】通过RTThread驱动W25QXXX
【STM32】通过RTThread驱动W25QXXX
|
1月前
驱动常用技巧
。。。未完,待续。。。
17 0
|
9月前
|
Linux 程序员 容器
总线,设备,驱动与class(二)
总线,设备,驱动与class
60 1
|
9月前
|
Linux API SoC
总线驱动--SPI驱动(上)
总线驱动--SPI驱动
195 0
|
9月前
|
传感器 算法 Linux
总线驱动---IIC驱动(上)
总线驱动---IIC驱动
73 0
|
9月前
|
传感器 Linux
总线驱动---IIC驱动(下)
总线驱动---IIC驱动
61 0
|
9月前
|
Linux 容器
总线,设备,驱动与class(三)
总线,设备,驱动与class(三)
61 1
|
9月前
|
Linux 程序员 Shell
总线,设备,驱动与class(一)
总线,设备,驱动与class
70 0