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

箱包网站建设策划报告展示型网站多少钱

箱包网站建设策划报告,展示型网站多少钱,广东长海建设工程有限公司网站,wordpress轮播图调用目录一、IIO 子系统简介二、IIO子系统使用的一些相关的结构体、函数等1、iio_dev 结构体  ①modes#xff1a;是选择iio驱动设备支持的工作模式#xff0c;模式分别有如下#xff1a;  ②dev#xff1a;其是一个设备结构体。  ②channels#xff1a;为 IIO 设备通道… 目录一、IIO 子系统简介二、IIO子系统使用的一些相关的结构体、函数等1、iio_dev 结构体  ①modes是选择iio驱动设备支持的工作模式模式分别有如下  ②dev其是一个设备结构体。  ②channels为 IIO 设备通道规格结构表。三、IIO驱动框架搭建1、SPI基础框架搭建2、基于SPI基础驱动框架上搭建IIO基础驱动框架  ①测试IIO基础驱动框架程序3、配置IIO设备通道规格结构表  ①了解通道文件命名方式  ①编写IIO驱动程序的配置IIO设备通道规格结构表4、添加Regmap API5、完善xxx_read_raw函数真正实现通道文件可读取传感器数据四、完整的驱动程序1、驱动程序2、测试现象3、icm20608APP程序及测试一、IIO 子系统简介 IIO 全称是 Industrial I/O翻译过来就是工业 I/O大家不要看到“工业”两个字就觉得 IIO是只用于工业领域的。大家一般在搜索 IIO 子系统的时候会发现大多数讲的都是 ADC这是因为 IIO 就是为 ADC 类传感器准备的当然了 DAC 也是可以的。大家常用的陀螺仪、加速度计、电压/电流测量芯片、光照传感器、压力传感器等内部都是有个 ADC内部 ADC 将原始的模拟数据转换为数字量然后通过其他的通信接口比如 IIC、 SPI 等传输给 SOC。   因此当你使用的传感器本质是 ADC 或 DAC 器件的时候可以优先考虑使用 IIO 驱动框架。 二、IIO子系统使用的一些相关的结构体、函数等 1、iio_dev 结构体 IIO 子系统使用结构体 iio_dev 来描述一个具体 IIO 设备此设备结构体定义在include/linux/iio/iio.h 文件中结构体内容如下(有省略) struct iio_dev {int modes;struct device dev;struct iio_chan_spec const *channels;int num_channels;const char *name;const struct iio_info *info;/* 注意这个结构体只列出了该实验驱动使用到的结构体属性其他省略了有兴趣的大家可以查看内核源码有注释的 */ };①modes是选择iio驱动设备支持的工作模式模式分别有如下 至于sysfs接口可以简单理解为生成了带有用于用户访问设备文件也就是说实现sysfs接口可通过脚本命令“echo”、“cat”访问驱动。详细介绍可以点击该链接这位博主写的挺详细的 ②dev其是一个设备结构体。 回顾一下以前我们使用的普通的IO字符设备驱动、SPI、IIC等是不是都会使用到device结构体呀什么分配设备号、添加字符设备、创建设备节点等呀。有了这个之后我们只需要把要操作的一系列字符设备的那个device给实例到该iio_dev-dev下那么通过调用 iio_device_register 该接口就可以内部生成在内核生成字符设备文件了。   简单看一下 iio_device_register 函数接口内部是怎么样的如下 ②channels为 IIO 设备通道规格结构表。 通俗一点来说就是我们获取的传感器信息如加速度传感器、陀螺仪传感器、温度传感器获取这三个传感器是通过通道去获取的那我们怎么知道我们获取的通道数据是哪个传感器的数据呢因此我们需要给每个通道打上对应传感器的标签这像一个树状查询表一样可以继续细分如加速度传感器有X、Y、Z轴三组数据我们需要对这三组数据进行细分对应的管道打上对应的标签。   先看看 iio_chan_spec 结构体如下图 这里我讲一下比较重要的成员变量   224行type 为通道类型 iio_chan_type 是一个枚举类型列举出了可以选择的通道类型定义在 include/uapi/linux/iio/types.h 文件里面内容如下 enum iio_chan_type {IIO_VOLTAGE, /* 电压类型 */IIO_CURRENT, /* 电流类型 */IIO_POWER, /* 功率类型 */IIO_ACCEL, /* 加速度类型 */IIO_ANGL_VEL, /* 角度类型(陀螺仪) */IIO_MAGN, /* 电磁类型(磁力计) */IIO_LIGHT, /* 灯光类型 */IIO_INTENSITY, /* 强度类型(光强传感器) */IIO_PROXIMITY, /* 接近类型(接近传感器) */IIO_TEMP, /* 温度类型 */IIO_INCLI, /* 倾角类型(倾角测量传感器) */IIO_ROT, /* 旋转角度类型 */IIO_ANGL, /* 转动角度类型(电机旋转角度测量传感器) */IIO_TIMESTAMP, /* 时间戳类型 */IIO_CAPACITANCE, /* 电容类型 */IIO_ALTVOLTAGE, /* 频率类型 */IIO_CCT, /* 笔者暂时未知的类型 */IIO_PRESSURE, /* 压力类型 */IIO_HUMIDITYRELATIVE, /* 湿度类型 */IIO_ACTIVITY, /* 活动类型(计步传感器) */IIO_STEPS, /* 步数类型 */IIO_ENERGY, /* 能量类型(卡路里) */IIO_DISTANCE, /* 距离类型 */IIO_VELOCITY, /* 速度类型 */ };225、247行当成员变量 indexed 为 1时候 channel 为通道索引。   246行当成员变量 modified 为 1 的时候 channel2 为通道修饰符。 Linux 内核给出了可用的通道修饰符定义在 include/uapi/linux/iio/types.h 文件里面内容如下(有省略) enum iio_modifier {IIO_NO_MOD,IIO_MOD_X, /* X 轴 */IIO_MOD_Y, /* Y 轴 */IIO_MOD_Z, /* Z 轴 */ ...... };比如 ICM20608 的加速度计部分类型设置为 IIO_ACCEL X、 Y、 Z 这三个轴就用 channel2的通道修饰符来区分。   第 228 行当使用触发缓冲区的时候 scan_index 是扫描索引。   第 229~236 scan_type 是一个结构体描述了扫描数据在缓冲区中的存储格式。我们依次来看一下 scan_type 各个成员变量的涵义   scan_type.sign如果为‘u’表示数据为无符号类型为‘s’的话为有符号类型。   scan_type.realbits数据真实的有效位数比如很多传感器说的 10 位 ADC其真实有效数据就是 10 位。   scan_type.storagebits存储位数有效位数填充位。比如有些传感器 ADC 是 12 位的那么我们存储的话肯定要用到 2 个字节也就是 16 位这 16 位就是存储位数。   scan_type.shift:右移位数也就是存储位数和有效位数不一致的时候需要右移的位数这个参数不总是需要一切以实际芯片的数据手册位数。   scan_type.repeat实际或存储位的重复数量。   scan_type.endianness:数据的大小端模式可设置为 IIO_CPU、 IIO_BE(大端)或 IIO_LE(小端)。   第 237 行 info_mask_separate 标记某些属性专属于此通道 include/linux/iio/types.h 文件中 的 iio_chan_info_enum 枚举类型描述了可选的属性值如下所示 enum iio_chan_info_enum {IIO_CHAN_INFO_RAW 0,IIO_CHAN_INFO_PROCESSED,IIO_CHAN_INFO_SCALE,IIO_CHAN_INFO_OFFSET, ......IIO_CHAN_INFO_DEBOUNCE_TIME, };比如 ICM20608 加速度计的 X、 Y、 Z 这三个轴在 sysfs 下这三个轴肯定是对应三个不同的文件我们通过读取这三个文件就能得到每个轴的原始数据。 IIO_CHAN_INFO_RAW 这个属性表示原始数据当我们在配置 X、 Y、 Z 这三个通道的时候在 info_mask_separate 中使能IIO_CHAN_INFO_RAW 这个属性那么就表示在 sysfs 下生成三个不同的文件分别对应 X、 Y、Z 轴这三个轴的 IIO_CHAN_INFO_RAW 属性是相互独立的。   第 238 行 info_mask_shared_by_type 标记导出的信息由相同类型的通道共享。也就是iio_chan_spec.type 成员变量相同的通道。比如 ICM20608 加速度计的 X、 Y、 Z 轴他们的 type 都是 IIO_ACCEL也就是类型相同。而这三个轴的分辨率(量程)是一样的那么在配置这三个通道的时候就可以在 info_mask_shared_by_type 中使能 IIO_CHAN_INFO_SCALE 这个属性表示这三个通道的分辨率是共用的这样在 sysfs 下就会只生成一个描述分辨率的文件这三个通道都可以使用这一个分辨率文件。 三、IIO驱动框架搭建 1、SPI基础框架搭建 /* * 根据linux内核的程序查找所使用函数的对应头文件。 */ #include linux/types.h #include linux/module.h //MODULE_LICENSEMODULE_AUTHOR #include linux/init.h //module_initmodule_exit #include linux/kernel.h //printk #include linux/fs.h //struct file_operations #include linux/slab.h //kmalloc, kfree #include linux/uaccess.h //copy_to_usercopy_from_user #include linux/io.h //ioremapiounmap #include linux/cdev.h //struct cdev,cdev_init,cdev_add,cdev_del #include linux/device.h //class #include linux/of.h //of_find_node_by_path #include linux/of_gpio.h //of_get_named_gpio #include linux/gpio.h //gpio_request,gpio_direction_output,gpio_set_number #include linux/atomic.h //atomic_t #include linux/of_irq.h //irq_of_parse_and_map #include linux/interrupt.h //request_irq #include linux/timer.h //timer_list #include linux/jiffies.h //jiffies #include linux/atomic.h //atomic_set #include linux/input.h //input #include linux/platform_device.h //platform #include linux/delay.h //mdelay #include linux/i2c.h #include linux/spi/spi.h #include linux/iio/iio.h #include linux/iio/sysfs.h #include linux/iio/trigger_consumer.h #include linux/iio/trigger.h #include linux/iio/buffer.h #include linux/iio/triggered_buffer.h #include linux/regmap.h #include icm20608.h/* 1.7 probe函数 */ static int icm20608_probe(struct spi_device *spi) {int ret 0;return ret; }/* 1.8 remove函数 */ static int icm20608_remove(struct spi_device *spi) {int ret 0;return ret; }/* 1.5 传统的匹配表 */ static const struct spi_device_id icm20608_id[] {{alientek,icm20608, 0},{} };/* 1.6 设备树匹配表 */ static const struct of_device_id icm20608_of_match[] {{ .compatible alientek,icm20608 },{} };/* 1.4 spi_driver结构体 */ 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, };// module_spi_driver(icm20608_driver); /* 1.1 驱动模块入口函数 */ static int __init icm20608_init(void) { return spi_register_driver(icm20608_driver); //注册spi驱动设备 } /* 1.2 驱动模块出口函数 */ static void __exit icm20608_exit(void) { spi_unregister_driver(icm20608_driver); //注销spi驱动设备 } /* 1.3 驱动许可和个人信息 */ module_init(icm20608_init); module_exit(icm20608_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(djw); 2、基于SPI基础驱动框架上搭建IIO基础驱动框架 /** 根据linux内核的程序查找所使用函数的对应头文件。*/ /* 省略... *//* 定义设备名称 */ #define DEVICE_NAME ICM20608/* 2.0 设备结构体 */ struct icm20608_dev {struct spi_device *spi;struct regmap *regmap;struct regmap_config regmap_config;struct mutex mutex_lock; // 互斥锁保证一次只有一个应用读取该数据 };/* 2.3.1 icm20608 通道 1 路温度通道 3 路陀螺仪 3 路加速度计 */ static const struct iio_chan_spec icm20608_channels[] {/* 只有配置好每个通道的项那么才会像一个树状分支那样每个数字对应每个通道或通道细分的分支 */ };/* 2.3.2.1 */ /* 读函数当读取 sysfs 中的文件的时候最终此函数会执行此函数里面会从传感器里面读取各种数据然后上传给应用。* indio_dev : iio_dev, chan : 通道, val : 读取的值的整数部分val2 : 读取的值的小数部分 mask : 掩码。*/ static int icm20608_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) {int ret 0;printk(icm20608_read_raw\r\n);return ret; }/* 2.3.2.2 */ static int icm20608_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) {int ret 0;printk(icm20608_write_raw\r\n);return ret; }/* 2.3.2.3 * 用户空间写数据格式比如我们在用户空间操作 sysfs 来设置传感器的分辨率如果分辨率带小数那么这个小数传递到内核空间应该扩大多少倍此函数就是用来设置这个的。* indio_dev : iio_dev* chan : 通道* mask : 掩码* return : 0成功其他值错误*/ static int icm20608_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) {int ret 0;printk(icm20608_write_raw_get_fmt\r\n);return ret; }/* 2.3.2 */ /* iio_info当应用程序读取相应的驱动文件的时候 xxx_read_raw* 函数就会执行我们在此函数中会读取传感器数据然后返回给应用层。当应用层向相应的驱* 动写数据的时候 xxx_write_raw 函数就会执行。因此 xxx_read_raw 和 xxx_write_raw 这两个函* 数是非常重要的需要我们根据具体的传感器来编写这两个函数是编写 IIO 驱动的核心。* */ static const struct iio_info icm20608_info {.read_raw icm20608_read_raw,.write_raw icm20608_write_raw,.write_raw_get_fmt icm20608_write_raw_get_fmt,.driver_module THIS_MODULE, };/* 1.7 probe函数 */ static int icm20608_probe(struct spi_device *spi) {int ret 0;struct icm20608_dev *dev;struct iio_dev *indio_dev;printk(icm20608_probe successful!\r\n);/* 2.1 申请icm20608_dev结构体大小的内存 为私人结构体“icm20608_dev”分配内存空间 */indio_dev devm_iio_device_alloc(spi-dev, sizeof(*dev));if (!indio_dev){ret -ENOMEM;goto fail_iio_dev;}/* 2.2 获取定义的设备结构体首地址和获取spi_device结构体等数据私有化操作 */dev iio_priv(indio_dev); // 使用 iio_priv 函数从 iio_dev 中提取出私有数据也就是 icm2608_dev 这个自定义结构体变量首地址dev-spi spi; // 设备树匹配成功后系统会分配struct spi_device *spi因此将其spi赋值给我们定义的设备结构体上spi_set_drvdata(spi, indio_dev); // 将indio_dev设置为spi-driver_data私有数据mutex_init(dev-mutex_lock); // 初始化互斥锁/* 2.3 初始化iio_dev */indio_dev-dev.parent spi-dev; // 获取spi-dev结构体indio_dev-channels icm20608_channels; // 通道indio_dev-num_channels ARRAY_SIZE(icm20608_channels); // 通道大小indio_dev-name DEVICE_NAME; // 名称indio_dev-modes INDIO_DIRECT_MODE; // 直接模式提供sysfs接口indio_dev-info icm20608_info;/* 2.4 将iio_dev注册到内核 */ret iio_device_register(indio_dev);if (ret 0){dev_err(spi-dev, unable to register iio device\r\n);goto fail_iio_register;}/* 2.5 设置SPI的模式 */spi-mode SPI_MODE_0; // MODE0,CPOL0, CPHA0spi_setup(spi); //设置好 spi_device 以后需要使用 spi_setup 配置一下return 0;fail_iio_register: fail_iio_dev:return ret; }/* 1.8 remove函数 */ static int icm20608_remove(struct spi_device *spi) {int ret 0;/* 获取私有数据 */struct iio_dev *indio_dev spi_get_drvdata(spi);struct icm20608_dev *dev;dev iio_priv(indio_dev);printk(icm20608_remove finish\r\n);/* 注销iio_dev */iio_device_unregister(indio_dev);return ret; }/* 1.5 传统的匹配表 */ static const struct spi_device_id icm20608_id[] {{alientek,icm20608, 0},{}};/* 1.6 设备树匹配表 */ static const struct of_device_id icm20608_of_match[] {{.compatible alientek,icm20608},{}};/* 1.4 spi_driver结构体 */ 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, };// module_spi_driver(icm20608_driver); /* 1.2 驱动模块入口函数 */ static int __init icm20608_init(void) {return spi_register_driver(icm20608_driver); // 注册spi驱动设备 }/* 1.3 驱动模块出口函数 */ static void __exit icm20608_exit(void) {spi_unregister_driver(icm20608_driver); // 注销spi驱动设备 }/* 1.1 驱动许可和个人信息 */ module_init(icm20608_init); module_exit(icm20608_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(djw);①测试IIO基础驱动框架程序 Ⅰ、使能Linux内核IIO子系统功能操作如下   Ⅱ、加载驱动程序查看现象如下   上面现象可以看出加载驱动后已经生成了我们所期望的设备文件“iio:device0”。 3、配置IIO设备通道规格结构表 ①了解通道文件命名方式 这里我以已经配置好IIO设备通道规格结构表参数后加载驱动后查看的现象中的“in_accel_x_raw”这个生成的通道文件为例 配置加速度传感器x轴原始值程序段,如下图所示 通 道 属 性 的 命 名 模 式 为 [direction][type][index][modifier][info_mask]我们依次来看一下这些命名组织模块 ①编写IIO驱动程序的配置IIO设备通道规格结构表 驱动程序 /** 根据linux内核的程序查找所使用函数的对应头文件。*/ /* 省略... *//* 定义设备名称 */ #define DEVICE_NAME ICM20608/* 2.0 设备结构体 */ struct icm20608_dev {struct spi_device *spi;struct regmap *regmap;struct regmap_config regmap_config;struct mutex mutex_lock; // 互斥锁保证一次只有一个应用读取该数据 };/* 2.3.1.1 ICM20608的扫描元素3轴加速计、3轴陀螺仪、1路温度传感器、一路时间戳 */ enum inv_icm20608_scan {INV_ICM20608_SCAN_ACCL_X,INV_ICM20608_SCAN_ACCL_Y,INV_ICM20608_SCAN_ACCL_Z,INV_ICM20608_SCAN_TEMP,INV_ICM20608_SCAN_GYRO_X,INV_ICM20608_SCAN_GYRO_Y,INV_ICM20608_SCAN_GYRO_Z,INV_ICM20608_SCAN_TIMESTAMP, };/* 2.3.1.2 */ /* _type通道类型加速度和陀螺仪, _channel2当modified为1时channel2为通道修饰符 */ /* info_mask_shared_by_type通道共享; IIO_CHAN_INFO_SCALE 这个属性表示这三个通道的分辨率是共用的这样在 sysfs 下就会只生成一个描述分辨率的文件这三个通道都可以使用这一个分辨率文件。*/ /* info_mask_separate通道独立; IIO_CHAN_INFO_RAW为原始值IIO_CHAN_INFO_CALIBBIAS校准值 */ #define ICM20608_CHANNEL(_type, _channel2, _index) \{ \.type _type, \.modified 1, \.channel2 _channel2, \.info_mask_shared_by_type BIT(IIO_CHAN_INFO_SCALE), \.info_mask_separate BIT(IIO_CHAN_INFO_RAW) | \BIT(IIO_CHAN_INFO_CALIBBIAS), \.scan_index _index, \.scan_type { \.sign s, \.realbits 16, \.storagebits 16, \.shift 0, \.endianness IIO_BE, \}, \}/* 2.3.1 icm20608 通道 1 路温度通道 3 路陀螺仪 3 路加速度计 */ static const struct iio_chan_spec icm20608_channels[] {/* 使用最元素的配置温度 */{.type IIO_TEMP,.info_mask_separate BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),.scan_index INV_ICM20608_SCAN_TEMP,.scan_type {.sign s,.realbits 16,.storagebits 16,.shift 0,.endianness IIO_BE,},},/* 2.3.1.3 加速度X、Y、Z三个通道 */ICM20608_CHANNEL(IIO_ACCEL, IIO_MOD_X, INV_ICM20608_SCAN_ACCL_X), // X轴ICM20608_CHANNEL(IIO_ACCEL, IIO_MOD_Y, INV_ICM20608_SCAN_ACCL_Y), // Y轴ICM20608_CHANNEL(IIO_ACCEL, IIO_MOD_Z, INV_ICM20608_SCAN_ACCL_Z), // Z轴/* 2.3.1.4 陀螺仪X、Y、Z三个通道 */ICM20608_CHANNEL(IIO_ANGL_VEL, IIO_MOD_X, INV_ICM20608_SCAN_GYRO_X), // X轴ICM20608_CHANNEL(IIO_ANGL_VEL, IIO_MOD_Y, INV_ICM20608_SCAN_GYRO_Y), // Y轴ICM20608_CHANNEL(IIO_ANGL_VEL, IIO_MOD_Z, INV_ICM20608_SCAN_GYRO_Z), // Z轴 };/* 2.3.2.1 */ /* 读函数当读取 sysfs 中的文件的时候最终此函数会执行此函数里面会从传感器里面读取各种数据然后上传给应用。* indio_dev : iio_dev, chan : 通道, val : 读取的值的整数部分val2 : 读取的值的小数部分 mask : 掩码。*/ static int icm20608_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) {int ret 0;printk(icm20608_read_raw\r\n);return ret; }/* 2.3.2.2 */ static int icm20608_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) {int ret 0;printk(icm20608_write_raw\r\n);return ret; }/* 2.3.2.3 * 用户空间写数据格式比如我们在用户空间操作 sysfs 来设置传感器的分辨率如果分辨率带小数那么这个小数传递到内核空间应该扩大多少倍此函数就是用来设置这个的。* indio_dev : iio_dev* chan : 通道* mask : 掩码* return : 0成功其他值错误*/ static int icm20608_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) {int ret 0;printk(icm20608_write_raw_get_fmt\r\n);return ret; }/* 2.3.2 */ /* iio_info当应用程序读取相应的驱动文件的时候 xxx_read_raw* 函数就会执行我们在此函数中会读取传感器数据然后返回给应用层。当应用层向相应的驱* 动写数据的时候 xxx_write_raw 函数就会执行。因此 xxx_read_raw 和 xxx_write_raw 这两个函* 数是非常重要的需要我们根据具体的传感器来编写这两个函数是编写 IIO 驱动的核心。* */ static const struct iio_info icm20608_info {.read_raw icm20608_read_raw,.write_raw icm20608_write_raw,.write_raw_get_fmt icm20608_write_raw_get_fmt,.driver_module THIS_MODULE, };/* 1.7 probe函数 */ static int icm20608_probe(struct spi_device *spi) {int ret 0;struct icm20608_dev *dev;struct iio_dev *indio_dev;printk(icm20608_probe successful!\r\n);/* 2.1 申请icm20608_dev结构体大小的内存 为私人结构体“icm20608_dev”分配内存空间 */indio_dev devm_iio_device_alloc(spi-dev, sizeof(*dev));if (!indio_dev){ret -ENOMEM;goto fail_iio_dev;}/* 2.2 获取定义的设备结构体首地址和获取spi_device结构体等数据私有化操作 */dev iio_priv(indio_dev); // 使用 iio_priv 函数从 iio_dev 中提取出私有数据也就是 icm2608_dev 这个自定义结构体变量首地址dev-spi spi; // 设备树匹配成功后系统会分配struct spi_device *spi因此将其spi赋值给我们定义的设备结构体上spi_set_drvdata(spi, indio_dev); // 将indio_dev设置为spi-driver_data私有数据mutex_init(dev-mutex_lock); // 初始化互斥锁/* 2.3 初始化iio_dev */indio_dev-dev.parent spi-dev; // 获取spi-dev结构体indio_dev-channels icm20608_channels; // 通道indio_dev-num_channels ARRAY_SIZE(icm20608_channels); // 通道大小indio_dev-name DEVICE_NAME; // 名称indio_dev-modes INDIO_DIRECT_MODE; // 直接模式提供sysfs接口indio_dev-info icm20608_info;/* 2.4 将iio_dev注册到内核 */ret iio_device_register(indio_dev);if (ret 0){dev_err(spi-dev, unable to register iio device\r\n);goto fail_iio_register;}/* 2.5 设置SPI的模式 */spi-mode SPI_MODE_0; // MODE0,CPOL0, CPHA0spi_setup(spi); //设置好 spi_device 以后需要使用 spi_setup 配置一下return 0;fail_iio_register: fail_iio_dev:return ret; }/* 1.8 remove函数 */ static int icm20608_remove(struct spi_device *spi) {int ret 0;/* 获取私有数据 */struct iio_dev *indio_dev spi_get_drvdata(spi);struct icm20608_dev *dev;dev iio_priv(indio_dev);printk(icm20608_remove finish\r\n);/* 注销iio_dev */iio_device_unregister(indio_dev);return ret; }/* 1.5 传统的匹配表 */ static const struct spi_device_id icm20608_id[] {{alientek,icm20608, 0},{}};/* 1.6 设备树匹配表 */ static const struct of_device_id icm20608_of_match[] {{.compatible alientek,icm20608},{}};/* 1.4 spi_driver结构体 */ 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, };// module_spi_driver(icm20608_driver); /* 1.2 驱动模块入口函数 */ static int __init icm20608_init(void) {return spi_register_driver(icm20608_driver); // 注册spi驱动设备 }/* 1.3 驱动模块出口函数 */ static void __exit icm20608_exit(void) {spi_unregister_driver(icm20608_driver); // 注销spi驱动设备 }/* 1.1 驱动许可和个人信息 */ module_init(icm20608_init); module_exit(icm20608_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(djw);现象 4、添加Regmap API 程序如下 /** 根据linux内核的程序查找所使用函数的对应头文件。*/ /* 省略... *//* 定义设备名称 */ #define DEVICE_NAME ICM20608/* 2.0 设备结构体 */ struct icm20608_dev {struct spi_device *spi;struct regmap *regmap;struct regmap_config regmap_config;struct mutex mutex_lock; // 互斥锁保证一次只有一个应用读取该数据 };/* 2.3.1.1 ICM20608的扫描元素3轴加速计、3轴陀螺仪、1路温度传感器、一路时间戳 */ enum inv_icm20608_scan {INV_ICM20608_SCAN_ACCL_X,INV_ICM20608_SCAN_ACCL_Y,INV_ICM20608_SCAN_ACCL_Z,INV_ICM20608_SCAN_TEMP,INV_ICM20608_SCAN_GYRO_X,INV_ICM20608_SCAN_GYRO_Y,INV_ICM20608_SCAN_GYRO_Z,INV_ICM20608_SCAN_TIMESTAMP, };/* 2.3.1.2 */ /* _type通道类型加速度和陀螺仪, _channel2当modified为1时channel2为通道修饰符 */ /* info_mask_shared_by_type通道共享; IIO_CHAN_INFO_SCALE 这个属性表示这三个通道的分辨率是共用的这样在 sysfs 下就会只生成一个描述分辨率的文件这三个通道都可以使用这一个分辨率文件。*/ /* info_mask_separate通道独立; IIO_CHAN_INFO_RAW为原始值IIO_CHAN_INFO_CALIBBIAS校准值 */ #define ICM20608_CHANNEL(_type, _channel2, _index) \{ \.type _type, \.modified 1, \.channel2 _channel2, \.info_mask_shared_by_type BIT(IIO_CHAN_INFO_SCALE), \.info_mask_separate BIT(IIO_CHAN_INFO_RAW) | \BIT(IIO_CHAN_INFO_CALIBBIAS), \.scan_index _index, \.scan_type { \.sign s, \.realbits 16, \.storagebits 16, \.shift 0, \.endianness IIO_BE, \}, \}/* 2.3.1 icm20608 通道 1 路温度通道 3 路陀螺仪 3 路加速度计 */ static const struct iio_chan_spec icm20608_channels[] {/* 使用最元素的配置温度 */{.type IIO_TEMP,.info_mask_separate BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),.scan_index INV_ICM20608_SCAN_TEMP,.scan_type {.sign s,.realbits 16,.storagebits 16,.shift 0,.endianness IIO_BE,},},/* 2.3.1.3 加速度X、Y、Z三个通道 */ICM20608_CHANNEL(IIO_ACCEL, IIO_MOD_X, INV_ICM20608_SCAN_ACCL_X), // X轴ICM20608_CHANNEL(IIO_ACCEL, IIO_MOD_Y, INV_ICM20608_SCAN_ACCL_Y), // Y轴ICM20608_CHANNEL(IIO_ACCEL, IIO_MOD_Z, INV_ICM20608_SCAN_ACCL_Z), // Z轴/* 2.3.1.4 陀螺仪X、Y、Z三个通道 */ICM20608_CHANNEL(IIO_ANGL_VEL, IIO_MOD_X, INV_ICM20608_SCAN_GYRO_X), // X轴ICM20608_CHANNEL(IIO_ANGL_VEL, IIO_MOD_Y, INV_ICM20608_SCAN_GYRO_Y), // Y轴ICM20608_CHANNEL(IIO_ANGL_VEL, IIO_MOD_Z, INV_ICM20608_SCAN_GYRO_Z), // Z轴 };/* 3.2 icm20608读取单个寄存器 */ static u8 icm20608_read_onereg(struct icm20608_dev *dev, u8 reg) {u32 data 0;u8 ret 0;ret regmap_read(dev-regmap, reg, data);return (u8)data; }/* 3.3 icm20608写一个寄存器 */ static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg, u8 value) {u8 ret 0;ret regmap_write(dev-regmap, reg, value); }/* 3.4 icm20608里面的寄存器初始化 */ void icm20608_reg_init(struct icm20608_dev *dev) {u8 value 0;icm20608_write_onereg(dev, ICM20_PWR_MGMT_1, 0X80); //复位复位后为0X40睡眠模式 mdelay(50);icm20608_write_onereg(dev, ICM20_PWR_MGMT_1, 0X01); //关闭睡眠自动选择时钟mdelay(50);value icm20608_read_onereg(dev, ICM20_WHO_AM_I);printk(ICM20608 ID 0X%X\r\n,value);value icm20608_read_onereg(dev, ICM20_PWR_MGMT_1);printk(ICM20_PWR_MGMT_1 0X%X\r\n,value);/* 以下是关于6轴传感器的初始化 */icm20608_write_onereg(dev, ICM20_SMPLRT_DIV, 0x00); icm20608_write_onereg(dev, ICM20_GYRO_CONFIG, 0x18); icm20608_write_onereg(dev, ICM20_ACCEL_CONFIG, 0x18); icm20608_write_onereg(dev, ICM20_CONFIG, 0x04); icm20608_write_onereg(dev, ICM20_ACCEL_CONFIG2, 0x04);icm20608_write_onereg(dev, ICM20_PWR_MGMT_2, 0x00); icm20608_write_onereg(dev, ICM20_LP_MODE_CFG, 0x00); icm20608_write_onereg(dev, ICM20_FIFO_EN, 0x00); }/* 2.3.2.1 */ /* 读函数当读取 sysfs 中的文件的时候最终此函数会执行此函数里面会从传感器里面读取各种数据然后上传给应用。* indio_dev : iio_dev, chan : 通道, val : 读取的值的整数部分val2 : 读取的值的小数部分 mask : 掩码。*/ static int icm20608_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) {int ret 0;printk(icm20608_read_raw\r\n);return ret; }/* 2.3.2.2 */ static int icm20608_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) {int ret 0;printk(icm20608_write_raw\r\n);return ret; }/* 2.3.2.3 * 用户空间写数据格式比如我们在用户空间操作 sysfs 来设置传感器的分辨率如果分辨率带小数那么这个小数传递到内核空间应该扩大多少倍此函数就是用来设置这个的。* indio_dev : iio_dev* chan : 通道* mask : 掩码* return : 0成功其他值错误*/ static int icm20608_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) {int ret 0;printk(icm20608_write_raw_get_fmt\r\n);return ret; }/* 2.3.2 */ /* iio_info当应用程序读取相应的驱动文件的时候 xxx_read_raw* 函数就会执行我们在此函数中会读取传感器数据然后返回给应用层。当应用层向相应的驱* 动写数据的时候 xxx_write_raw 函数就会执行。因此 xxx_read_raw 和 xxx_write_raw 这两个函* 数是非常重要的需要我们根据具体的传感器来编写这两个函数是编写 IIO 驱动的核心。* */ static const struct iio_info icm20608_info {.read_raw icm20608_read_raw,.write_raw icm20608_write_raw,.write_raw_get_fmt icm20608_write_raw_get_fmt,.driver_module THIS_MODULE, };/* 1.7 probe函数 */ static int icm20608_probe(struct spi_device *spi) {int ret 0;struct icm20608_dev *dev;struct iio_dev *indio_dev;printk(icm20608_probe successful!\r\n);/* 2.1 申请icm20608_dev结构体大小的内存 为私人结构体“icm20608_dev”分配内存空间 */indio_dev devm_iio_device_alloc(spi-dev, sizeof(*dev));if (!indio_dev){ret -ENOMEM;goto fail_iio_dev;}/* 2.2 获取定义的设备结构体首地址和获取spi_device结构体等数据私有化操作 */dev iio_priv(indio_dev); // 使用 iio_priv 函数从 iio_dev 中提取出私有数据也就是 icm2608_dev 这个自定义结构体变量首地址dev-spi spi; // 设备树匹配成功后系统会分配struct spi_device *spi因此将其spi赋值给我们定义的设备结构体上spi_set_drvdata(spi, indio_dev); // 将indio_dev设置为spi-driver_data私有数据mutex_init(dev-mutex_lock); // 初始化互斥锁/* 2.3 初始化iio_dev */indio_dev-dev.parent spi-dev; // 获取spi-dev结构体indio_dev-channels icm20608_channels; // 通道indio_dev-num_channels ARRAY_SIZE(icm20608_channels); // 通道大小indio_dev-name DEVICE_NAME; // 名称indio_dev-modes INDIO_DIRECT_MODE; // 直接模式提供sysfs接口indio_dev-info icm20608_info;/* 2.4 将iio_dev注册到内核 */ret iio_device_register(indio_dev);if (ret 0){dev_err(spi-dev, unable to register iio device\r\n);goto fail_iio_register;}/* 2.5 设置SPI的模式 */spi-mode SPI_MODE_0; // MODE0,CPOL0, CPHA0spi_setup(spi); //设置好 spi_device 以后需要使用 spi_setup 配置一下/* 3.1 初始化 regmap_config 设置 和 regmap *//* 补充当设置SPI读ICM20608操作时使用 regmap 的时候就不需要手动将寄存器地址的 bit7 置 1在初始化 regmap_config* 的时候直接将 read_flag_mask 设置为 0X80 即可这样通过 regmap 读取 SPI 内部寄存器的时候* 就会将寄存器地址与 read_flag_mask 进行或运算结果就是将 bit7 置 1但是整个过程不需要* 我们来操作全部由 regmap 框架来完成的 */dev-regmap_config.reg_bits 8; /* 寄存器长度8bit */dev-regmap_config.val_bits 8; /* 值长度8bit */dev-regmap_config.read_flag_mask 0x80; /* 读掩码 */dev-regmap regmap_init_spi(spi,dev-regmap_config);if(IS_ERR(dev-regmap)) {ret PTR_ERR(dev-regmap);goto fail_regmap_init;}/* 3.5 调用icm20608初始化函数 */icm20608_reg_init(dev);return 0;fail_regmap_init:iio_device_unregister(indio_dev); fail_iio_register: fail_iio_dev:return ret; }/* 1.8 remove函数 */ static int icm20608_remove(struct spi_device *spi) {int ret 0;/* 获取私有数据 */struct iio_dev *indio_dev spi_get_drvdata(spi);struct icm20608_dev *dev;dev iio_priv(indio_dev);printk(icm20608_remove finish\r\n);/* 2.6 注销iio_dev */iio_device_unregister(indio_dev);/* 3.6 删除regmap */regmap_exit(dev-regmap);return ret; }/* 1.5 传统的匹配表 */ static const struct spi_device_id icm20608_id[] {{alientek,icm20608, 0},{}};/* 1.6 设备树匹配表 */ static const struct of_device_id icm20608_of_match[] {{.compatible alientek,icm20608},{}};/* 1.4 spi_driver结构体 */ 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, };// module_spi_driver(icm20608_driver); /* 1.2 驱动模块入口函数 */ static int __init icm20608_init(void) {return spi_register_driver(icm20608_driver); // 注册spi驱动设备 }/* 1.3 驱动模块出口函数 */ static void __exit icm20608_exit(void) {spi_unregister_driver(icm20608_driver); // 注销spi驱动设备 }/* 1.1 驱动许可和个人信息 */ module_init(icm20608_init); module_exit(icm20608_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(djw);现象   从以上程序可以看出Regmap它就是Linux内核给我们封装好的寄存器读写接口我们只需要配置初始化Regmap后就可以直接使用了。   如果要读取连续的多个寄存器值可以使用 regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_count) 如果要写值进连续的多个寄存器内可以使用 regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count) 5、完善xxx_read_raw函数真正实现通道文件可读取传感器数据 因为应 用 程 序 所 有 的 读 取 操 作 最 终 都 会 汇 总 到 iio_info 的 read 函 数 这 里 就是 icm20608_read_raw 函数。   对此我以读取加速度传感器X轴的原始值为例也就是通道规则结构树下的 IIO_CHAN_INFO_RAW-         IIO_ACCEL- 驱动程序如下 /** 根据linux内核的程序查找所使用函数的对应头文件。*/ /* 省略... *//* 定义设备名称 */ #define DEVICE_NAME ICM20608/* 2.0 设备结构体 */ struct icm20608_dev {struct spi_device *spi;struct regmap *regmap;struct regmap_config regmap_config;struct mutex mutex_lock; // 互斥锁保证一次只有一个应用读取该数据 };/* 2.3.1.1 ICM20608的扫描元素3轴加速计、3轴陀螺仪、1路温度传感器、一路时间戳 */ enum inv_icm20608_scan {INV_ICM20608_SCAN_ACCL_X,INV_ICM20608_SCAN_ACCL_Y,INV_ICM20608_SCAN_ACCL_Z,INV_ICM20608_SCAN_TEMP,INV_ICM20608_SCAN_GYRO_X,INV_ICM20608_SCAN_GYRO_Y,INV_ICM20608_SCAN_GYRO_Z,INV_ICM20608_SCAN_TIMESTAMP, };/* 2.3.1.2 */ /* _type通道类型加速度和陀螺仪, _channel2当modified为1时channel2为通道修饰符 */ /* info_mask_shared_by_type通道共享; IIO_CHAN_INFO_SCALE 这个属性表示这三个通道的分辨率是共用的这样在 sysfs 下就会只生成一个描述分辨率的文件这三个通道都可以使用这一个分辨率文件。*/ /* info_mask_separate通道独立; IIO_CHAN_INFO_RAW为原始值IIO_CHAN_INFO_CALIBBIAS校准值 */ #define ICM20608_CHANNEL(_type, _channel2, _index) \{ \.type _type, \.modified 1, \.channel2 _channel2, \.info_mask_shared_by_type BIT(IIO_CHAN_INFO_SCALE), \.info_mask_separate BIT(IIO_CHAN_INFO_RAW) | \BIT(IIO_CHAN_INFO_CALIBBIAS), \.scan_index _index, \.scan_type { \.sign s, \.realbits 16, \.storagebits 16, \.shift 0, \.endianness IIO_BE, \}, \}/* 2.3.1 icm20608 通道 1 路温度通道 3 路陀螺仪 3 路加速度计 */ static const struct iio_chan_spec icm20608_channels[] {/* 使用最元素的配置温度 */{.type IIO_TEMP,.info_mask_separate BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),.scan_index INV_ICM20608_SCAN_TEMP,.scan_type {.sign s,.realbits 16,.storagebits 16,.shift 0,.endianness IIO_BE,},},/* 2.3.1.3 加速度X、Y、Z三个通道 */ICM20608_CHANNEL(IIO_ACCEL, IIO_MOD_X, INV_ICM20608_SCAN_ACCL_X), // X轴ICM20608_CHANNEL(IIO_ACCEL, IIO_MOD_Y, INV_ICM20608_SCAN_ACCL_Y), // Y轴ICM20608_CHANNEL(IIO_ACCEL, IIO_MOD_Z, INV_ICM20608_SCAN_ACCL_Z), // Z轴/* 2.3.1.4 陀螺仪X、Y、Z三个通道 */ICM20608_CHANNEL(IIO_ANGL_VEL, IIO_MOD_X, INV_ICM20608_SCAN_GYRO_X), // X轴ICM20608_CHANNEL(IIO_ANGL_VEL, IIO_MOD_Y, INV_ICM20608_SCAN_GYRO_Y), // Y轴ICM20608_CHANNEL(IIO_ANGL_VEL, IIO_MOD_Z, INV_ICM20608_SCAN_GYRO_Z), // Z轴 };/* 3.2 icm20608读取单个寄存器 */ static u8 icm20608_read_onereg(struct icm20608_dev *dev, u8 reg) {u32 data 0;u8 ret 0;ret regmap_read(dev-regmap, reg, data);return (u8)data; }/* 3.3 icm20608写一个寄存器 */ static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg, u8 value) {u8 ret 0;ret regmap_write(dev-regmap, reg, value); }/* 3.4 icm20608里面的寄存器初始化 */ void icm20608_reg_init(struct icm20608_dev *dev) {u8 value 0;icm20608_write_onereg(dev, ICM20_PWR_MGMT_1, 0X80); //复位复位后为0X40睡眠模式 mdelay(50);icm20608_write_onereg(dev, ICM20_PWR_MGMT_1, 0X01); //关闭睡眠自动选择时钟mdelay(50);value icm20608_read_onereg(dev, ICM20_WHO_AM_I);printk(ICM20608 ID 0X%X\r\n,value);value icm20608_read_onereg(dev, ICM20_PWR_MGMT_1);printk(ICM20_PWR_MGMT_1 0X%X\r\n,value);/* 以下是关于6轴传感器的初始化 */icm20608_write_onereg(dev, ICM20_SMPLRT_DIV, 0x00); icm20608_write_onereg(dev, ICM20_GYRO_CONFIG, 0x18); icm20608_write_onereg(dev, ICM20_ACCEL_CONFIG, 0x18); icm20608_write_onereg(dev, ICM20_CONFIG, 0x04); icm20608_write_onereg(dev, ICM20_ACCEL_CONFIG2, 0x04);icm20608_write_onereg(dev, ICM20_PWR_MGMT_2, 0x00); icm20608_write_onereg(dev, ICM20_LP_MODE_CFG, 0x00); icm20608_write_onereg(dev, ICM20_FIFO_EN, 0x00); }/* 4.1.1.1* 读取ICM20608传感器数据可以用于陀螺仪、加速度计、温度的读取读取2个寄存器值* dev: icm20608设备, reg: 要读取的通道寄存器首地址, anix: 需要读取的通道比如XYZ, val: 保存读取到的值。*/ static int icm20608_sensor_show(struct icm20608_dev *dev, int reg, int axis, int *val) {int ind, result;__be16 d; //定义大端规则的16位整形变量ind (axis - IIO_MOD_X) * 2; //计算读取的陀螺仪和加速度传感器的X、Y、Z轴的偏移寄存器地址result regmap_bulk_read(dev-regmap, reg ind, (u8 *)d, 2);if (result)return -EINVAL;*val (short)be16_to_cpup(d); //将16位整形数据以大端规则转换存储return IIO_VAL_INT; //表示读取的数据类型是整数值没有小数 }/* 4.1.1* 读取 ICM20608 陀螺仪、加速度计、温度通道值-----读取原始数据使用的* indio_dev : iio 设备, chan : 通道, val : 保存读取到的通道值。*/ static int icm20608_read_channel_data(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) {int ret 0;struct icm20608_dev *dev iio_priv(indio_dev);/* 区分通道类型是温度传感器、陀螺仪传感器、加速度传感器 */switch (chan-type) {case IIO_ACCEL: //加速度printk(read channel type is IIO_ACCEL\r\n);ret icm20608_sensor_show(dev, ICM20_ACCEL_XOUT_H, chan-channel2, val); /* channel2为X、Y、Z轴 */return ret;case IIO_ANGL_VEL: //陀螺仪printk(read channel type is IIO_ANGL_VEL\r\n);ret icm20608_sensor_show(dev, ICM20_GYRO_XOUT_H, chan-channel2, val); /* channel2为X、Y、Z轴 */return ret;case IIO_TEMP: //温度printk(read channel type is IIO_TEMP\r\n);ret icm20608_sensor_show(dev, ICM20_TEMP_OUT_H, IIO_MOD_X, val);return ret; default:return -EINVAL; //没有一项符合那么返回参数不符合}return ret; }/* 2.3.2.1 */ /* 读函数当读取 sysfs 中的文件的时候最终此函数会执行此函数里面会从传感器里面读取各种数据然后上传给应用。* indio_dev : iio_dev, chan : 通道, val : 读取的值的整数部分val2 : 读取的值的小数部分 mask : 掩码。*/ static int icm20608_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) {int ret 0;/********** 4.1 根据设置的通道文件参数作为条件依据读取数据类型 **********/struct icm20608_dev *dev iio_priv(indio_dev);// printk(icm20608_read_raw\r\n);/* 区分读取的数据类型如raw、scale、offset等掩码 */switch (mask){case IIO_CHAN_INFO_RAW: //原始数据类型printk(read type is IIO_CHAN_INFO_RAW. \r\n);mutex_lock(dev-mutex_lock); //上锁保证读取的数据正确性ret icm20608_read_channel_data(indio_dev, chan, val); //再进行细分筛选判断读取的是原始数据类型下的哪个通道的数据温度、加速度、陀螺仪而这里val参数不用做处理因为元素数据是ADC数据是整形的mutex_unlock(dev-mutex_lock); //释放锁return ret;case IIO_CHAN_INFO_SCALE: //分辨率数据类型printk(read type is IIO_CHAN_INFO_SCALE. \r\n);return ret;case IIO_CHAN_INFO_OFFSET: //补偿、偏置数据类型printk(read type is IIO_CHAN_INFO_OFFSET. \r\n);return ret;case IIO_CHAN_INFO_CALIBBIAS: //校准数据类型printk(read type is IIO_CHAN_INFO_CALIBBIAS. \r\n);return ret;default:return -EINVAL; //没有一项符合那么返回参数不符合}/********** 4.1 根据设置的通道文件参数作为条件依据读取数据类型 **********/return ret; }/* 2.3.2.2 */ static int icm20608_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) {int ret 0;printk(icm20608_write_raw\r\n);return ret; }/* 2.3.2.3 * 用户空间写数据格式比如我们在用户空间操作 sysfs 来设置传感器的分辨率如果分辨率带小数那么这个小数传递到内核空间应该扩大多少倍此函数就是用来设置这个的。* indio_dev : iio_dev* chan : 通道* mask : 掩码* return : 0成功其他值错误*/ static int icm20608_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) {int ret 0;printk(icm20608_write_raw_get_fmt\r\n);return ret; }/* 2.3.2 */ /* iio_info当应用程序读取相应的驱动文件的时候 xxx_read_raw* 函数就会执行我们在此函数中会读取传感器数据然后返回给应用层。当应用层向相应的驱* 动写数据的时候 xxx_write_raw 函数就会执行。因此 xxx_read_raw 和 xxx_write_raw 这两个函* 数是非常重要的需要我们根据具体的传感器来编写这两个函数是编写 IIO 驱动的核心。* */ static const struct iio_info icm20608_info {.read_raw icm20608_read_raw,.write_raw icm20608_write_raw,.write_raw_get_fmt icm20608_write_raw_get_fmt,.driver_module THIS_MODULE, };/* 1.7 probe函数 */ static int icm20608_probe(struct spi_device *spi) {int ret 0;struct icm20608_dev *dev;struct iio_dev *indio_dev;printk(icm20608_probe successful!\r\n);/* 2.1 申请icm20608_dev结构体大小的内存 为私人结构体“icm20608_dev”分配内存空间 */indio_dev devm_iio_device_alloc(spi-dev, sizeof(*dev));if (!indio_dev){ret -ENOMEM;goto fail_iio_dev;}/* 2.2 获取定义的设备结构体首地址和获取spi_device结构体等数据私有化操作 */dev iio_priv(indio_dev); // 使用 iio_priv 函数从 iio_dev 中提取出私有数据也就是 icm2608_dev 这个自定义结构体变量首地址dev-spi spi; // 设备树匹配成功后系统会分配struct spi_device *spi因此将其spi赋值给我们定义的设备结构体上spi_set_drvdata(spi, indio_dev); // 将indio_dev设置为spi-driver_data私有数据mutex_init(dev-mutex_lock); // 初始化互斥锁/* 2.3 初始化iio_dev */indio_dev-dev.parent spi-dev; // 获取spi-dev结构体indio_dev-channels icm20608_channels; // 通道indio_dev-num_channels ARRAY_SIZE(icm20608_channels); // 通道大小indio_dev-name DEVICE_NAME; // 名称indio_dev-modes INDIO_DIRECT_MODE; // 直接模式提供sysfs接口indio_dev-info icm20608_info;/* 2.4 将iio_dev注册到内核 */ret iio_device_register(indio_dev);if (ret 0){dev_err(spi-dev, unable to register iio device\r\n);goto fail_iio_register;}/* 2.5 设置SPI的模式 */spi-mode SPI_MODE_0; // MODE0,CPOL0, CPHA0spi_setup(spi); //设置好 spi_device 以后需要使用 spi_setup 配置一下/* 3.1 初始化 regmap_config 设置 和 regmap *//* 补充当设置SPI读ICM20608操作时使用 regmap 的时候就不需要手动将寄存器地址的 bit7 置 1在初始化 regmap_config* 的时候直接将 read_flag_mask 设置为 0X80 即可这样通过 regmap 读取 SPI 内部寄存器的时候* 就会将寄存器地址与 read_flag_mask 进行或运算结果就是将 bit7 置 1但是整个过程不需要* 我们来操作全部由 regmap 框架来完成的 */dev-regmap_config.reg_bits 8; /* 寄存器长度8bit */dev-regmap_config.val_bits 8; /* 值长度8bit */dev-regmap_config.read_flag_mask 0x80; /* 读掩码 */dev-regmap regmap_init_spi(spi,dev-regmap_config);if(IS_ERR(dev-regmap)) {ret PTR_ERR(dev-regmap);goto fail_regmap_init;}/* 3.5 调用icm20608初始化函数 */icm20608_reg_init(dev);return 0;fail_regmap_init:iio_device_unregister(indio_dev); fail_iio_register: fail_iio_dev:return ret; }/* 1.8 remove函数 */ static int icm20608_remove(struct spi_device *spi) {int ret 0;/* 获取私有数据 */struct iio_dev *indio_dev spi_get_drvdata(spi);struct icm20608_dev *dev;dev iio_priv(indio_dev);printk(icm20608_remove finish\r\n);/* 2.6 注销iio_dev */iio_device_unregister(indio_dev);/* 3.6 删除regmap */regmap_exit(dev-regmap);return ret; }/* 1.5 传统的匹配表 */ static const struct spi_device_id icm20608_id[] {{alientek,icm20608, 0},{}};/* 1.6 设备树匹配表 */ static const struct of_device_id icm20608_of_match[] {{.compatible alientek,icm20608},{}};/* 1.4 spi_driver结构体 */ 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, };// module_spi_driver(icm20608_driver); /* 1.2 驱动模块入口函数 */ static int __init icm20608_init(void) {return spi_register_driver(icm20608_driver); // 注册spi驱动设备 }/* 1.3 驱动模块出口函数 */ static void __exit icm20608_exit(void) {spi_unregister_driver(icm20608_driver); // 注销spi驱动设备 }/* 1.1 驱动许可和个人信息 */ module_init(icm20608_init); module_exit(icm20608_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(djw);测试现象 四、完整的驱动程序 1、驱动程序 /** 根据linux内核的程序查找所使用函数的对应头文件。*/ #include linux/types.h #include linux/module.h //MODULE_LICENSEMODULE_AUTHOR #include linux/init.h //module_initmodule_exit #include linux/kernel.h //printk #include linux/fs.h //struct file_operations #include linux/slab.h //kmalloc, kfree #include linux/uaccess.h //copy_to_usercopy_from_user #include linux/io.h //ioremapiounmap #include linux/cdev.h //struct cdev,cdev_init,cdev_add,cdev_del #include linux/device.h //class #include linux/of.h //of_find_node_by_path #include linux/of_gpio.h //of_get_named_gpio #include linux/gpio.h //gpio_request,gpio_direction_output,gpio_set_number #include linux/atomic.h //atomic_t #include linux/of_irq.h //irq_of_parse_and_map #include linux/interrupt.h //request_irq #include linux/timer.h //timer_list #include linux/jiffies.h //jiffies #include linux/atomic.h //atomic_set #include linux/input.h //input #include linux/platform_device.h //platform #include linux/delay.h //mdelay #include linux/i2c.h #include linux/spi/spi.h #include linux/iio/iio.h #include linux/iio/sysfs.h #include linux/iio/trigger_consumer.h #include linux/iio/trigger.h #include linux/iio/buffer.h #include linux/iio/triggered_buffer.h #include linux/regmap.h #include icm20608.h/* 定义设备名称 */ #define DEVICE_NAME ICM20608 #define ICM20608_TEMP_OFFSET 0 #define ICM20608_TEMP_SCALE 326800000 //每度是326.8。扩大1000000所得每度对应打数值/* 2.0 设备结构体 */ struct icm20608_dev {struct spi_device *spi;struct regmap *regmap;struct regmap_config regmap_config;struct mutex mutex_lock; // 互斥锁保证一次只有一个应用读取该数据 };/** icm20608陀螺仪分辨率对应250、500、1000、2000计算方法* 以正负250度量程为例500/2^160.007629扩大1000000倍就是7629*/ static const int gyro_scale_icm20608[] {7629, 15258, 30517, 61035};/* * icm20608加速度计分辨率对应2、4、8、16 计算方法* 以正负2g量程为例4/2^160.000061035扩大1000000000倍就是61035*/ static const int accel_scale_icm20608[] {61035, 122070, 244140, 488281};/* 2.3.1.1 ICM20608的扫描元素3轴加速计、3轴陀螺仪、1路温度传感器、一路时间戳 */ enum inv_icm20608_scan {INV_ICM20608_SCAN_ACCL_X,INV_ICM20608_SCAN_ACCL_Y,INV_ICM20608_SCAN_ACCL_Z,INV_ICM20608_SCAN_TEMP,INV_ICM20608_SCAN_GYRO_X,INV_ICM20608_SCAN_GYRO_Y,INV_ICM20608_SCAN_GYRO_Z,INV_ICM20608_SCAN_TIMESTAMP, };/* 2.3.1.2 */ /* _type通道类型加速度和陀螺仪, _channel2当modified为1时channel2为通道修饰符 */ /* info_mask_shared_by_type通道共享; IIO_CHAN_INFO_SCALE 这个属性表示这三个通道的分辨率是共用的这样在 sysfs 下就会只生成一个描述分辨率的文件这三个通道都可以使用这一个分辨率文件。*/ /* info_mask_separate通道独立; IIO_CHAN_INFO_RAW为原始值IIO_CHAN_INFO_CALIBBIAS校准值 */ #define ICM20608_CHANNEL(_type, _channel2, _index) \{ \.type _type, \.modified 1, \.channel2 _channel2, \.info_mask_shared_by_type BIT(IIO_CHAN_INFO_SCALE), \.info_mask_separate BIT(IIO_CHAN_INFO_RAW) | \BIT(IIO_CHAN_INFO_CALIBBIAS), \.scan_index _index, \.scan_type { \.sign s, \.realbits 16, \.storagebits 16, \.shift 0, \.endianness IIO_BE, \}, \}/* 2.3.1 icm20608 通道 1 路温度通道 3 路陀螺仪 3 路加速度计 */ static const struct iio_chan_spec icm20608_channels[] {/* 使用最元素的配置温度 */{.type IIO_TEMP,.info_mask_separate BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),.scan_index INV_ICM20608_SCAN_TEMP,.scan_type {.sign s,.realbits 16,.storagebits 16,.shift 0,.endianness IIO_BE,},},/* 2.3.1.3 加速度X、Y、Z三个通道 */ICM20608_CHANNEL(IIO_ACCEL, IIO_MOD_X, INV_ICM20608_SCAN_ACCL_X), // X轴ICM20608_CHANNEL(IIO_ACCEL, IIO_MOD_Y, INV_ICM20608_SCAN_ACCL_Y), // Y轴ICM20608_CHANNEL(IIO_ACCEL, IIO_MOD_Z, INV_ICM20608_SCAN_ACCL_Z), // Z轴/* 2.3.1.4 陀螺仪X、Y、Z三个通道 */ICM20608_CHANNEL(IIO_ANGL_VEL, IIO_MOD_X, INV_ICM20608_SCAN_GYRO_X), // X轴ICM20608_CHANNEL(IIO_ANGL_VEL, IIO_MOD_Y, INV_ICM20608_SCAN_GYRO_Y), // Y轴ICM20608_CHANNEL(IIO_ANGL_VEL, IIO_MOD_Z, INV_ICM20608_SCAN_GYRO_Z), // Z轴 };/* 3.2 icm20608读取单个寄存器 */ static u8 icm20608_read_onereg(struct icm20608_dev *dev, u8 reg) {u32 data 0;u8 ret 0;ret regmap_read(dev-regmap, reg, data);return (u8)data; }/* 3.3 icm20608写一个寄存器 */ static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg, u8 value) {u8 ret 0;ret regmap_write(dev-regmap, reg, value); }/* 3.4 icm20608里面的寄存器初始化 */ void icm20608_reg_init(struct icm20608_dev *dev) {u8 value 0;icm20608_write_onereg(dev, ICM20_PWR_MGMT_1, 0X80); //复位复位后为0X40睡眠模式 mdelay(50);icm20608_write_onereg(dev, ICM20_PWR_MGMT_1, 0X01); //关闭睡眠自动选择时钟mdelay(50);value icm20608_read_onereg(dev, ICM20_WHO_AM_I);printk(ICM20608 ID 0X%X\r\n,value);value icm20608_read_onereg(dev, ICM20_PWR_MGMT_1);printk(ICM20_PWR_MGMT_1 0X%X\r\n,value);/* 以下是关于6轴传感器的初始化 */icm20608_write_onereg(dev, ICM20_SMPLRT_DIV, 0x00); icm20608_write_onereg(dev, ICM20_GYRO_CONFIG, 0x18); icm20608_write_onereg(dev, ICM20_ACCEL_CONFIG, 0x18); icm20608_write_onereg(dev, ICM20_CONFIG, 0x04); icm20608_write_onereg(dev, ICM20_ACCEL_CONFIG2, 0x04);icm20608_write_onereg(dev, ICM20_PWR_MGMT_2, 0x00); icm20608_write_onereg(dev, ICM20_LP_MODE_CFG, 0x00); icm20608_write_onereg(dev, ICM20_FIFO_EN, 0x00); }/* 4.1.1.1* 读取ICM20608传感器数据可以用于陀螺仪、加速度计、温度的读取读取2个寄存器值* dev: icm20608设备, reg: 要读取的通道寄存器首地址, anix: 需要读取的通道比如XYZ, val: 保存读取到的值。*/ static int icm20608_sensor_show(struct icm20608_dev *dev, int reg, int axis, int *val) {int ind, result;__be16 d; //定义大端规则的16位整形变量ind (axis - IIO_MOD_X) * 2; //计算读取的陀螺仪和加速度传感器的X、Y、Z轴的偏移寄存器地址result regmap_bulk_read(dev-regmap, reg ind, (u8 *)d, 2);if (result)return -EINVAL;*val (short)be16_to_cpup(d); //将16位整形数据以大端规则转换存储return IIO_VAL_INT; //表示读取的数据类型是整数值没有小数 }/* 4.1.1* 读取 ICM20608 陀螺仪、加速度计、温度通道值-----读取原始数据使用的* indio_dev : iio 设备, chan : 通道, val : 保存读取到的通道值。*/ static int icm20608_read_channel_data(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) {int ret 0;struct icm20608_dev *dev iio_priv(indio_dev);/* 区分通道类型是温度传感器、陀螺仪传感器、加速度传感器 */switch (chan-type) {case IIO_ACCEL: //加速度printk(read channel type is IIO_ACCEL\r\n);ret icm20608_sensor_show(dev, ICM20_ACCEL_XOUT_H, chan-channel2, val); /* channel2为X、Y、Z轴 */return ret;case IIO_ANGL_VEL: //陀螺仪printk(read channel type is IIO_ANGL_VEL\r\n);ret icm20608_sensor_show(dev, ICM20_GYRO_XOUT_H, chan-channel2, val); /* channel2为X、Y、Z轴 */return ret;case IIO_TEMP: //温度printk(read channel type is IIO_TEMP\r\n);ret icm20608_sensor_show(dev, ICM20_TEMP_OUT_H, IIO_MOD_X, val);return ret; default:return -EINVAL; //没有一项符合那么返回参数不符合}return ret; }/* 5.1.1* 设置ICM20608的陀螺仪计量程(分辨率)* dev:icm20608设备, val:量程(分辨率值)。* return:0成功其他值错误*/ static int icm20608_write_gyro_scale(struct icm20608_dev *dev, int val) {int result, i;u8 d;for (i 0; i ARRAY_SIZE(gyro_scale_icm20608); i) {if (gyro_scale_icm20608[i] val) {d (i 3);result regmap_write(dev-regmap, ICM20_GYRO_CONFIG, d);if (result)return result;return 0;}}return -EINVAL; }/* 5.1.2* 设置ICM20608的加速度计量程(分辨率)* dev:icm20608设备, val:量程(分辨率值)。* return:0成功其他值错误*/ static int icm20608_write_accel_scale(struct icm20608_dev *dev, int val) {int result, i;u8 d;for (i 0; i ARRAY_SIZE(accel_scale_icm20608); i) {if (accel_scale_icm20608[i] val) {d (i 3);result regmap_write(dev-regmap, ICM20_ACCEL_CONFIG, d);if (result)return result;return 0;}}return -EINVAL; }/* 5.1.3* 设置ICM20608传感器可以用于陀螺仪、加速度计设置* dev:icm20608设备, reg:要设置的通道寄存器首地址, anix:要设置的通道比如XYZ, val:要设置的值。* return:0成功其他值错误*/ static int icm20608_sensor_set(struct icm20608_dev *dev, int reg,int axis, int val) {int ind, result;__be16 d cpu_to_be16(val);ind (axis - IIO_MOD_X) * 2;result regmap_bulk_write(dev-regmap, reg ind, (u8 *)d, 2);if (result)return -EINVAL;return 0; }/* 2.3.2.1 */ /* 读函数当读取 sysfs 中的文件的时候最终此函数会执行此函数里面会从传感器里面读取各种数据然后上传给应用。* indio_dev : iio_dev, chan : 通道, val : 读取的值的整数部分val2 : 读取的值的小数部分 mask : 掩码。*/ static int icm20608_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) {int ret 0;/********** 4.1 根据设置的通道文件参数作为条件依据读取数据类型 **********/struct icm20608_dev *dev iio_priv(indio_dev);u8 regdata 0; //保存读取寄存器的值// printk(icm20608_read_raw\r\n);/* 区分读取的数据类型如raw、scale、offset等掩码 */switch (mask){case IIO_CHAN_INFO_RAW: //原始数据类型printk(read type is IIO_CHAN_INFO_RAW. \r\n);mutex_lock(dev-mutex_lock); //上锁保证读取的数据正确性ret icm20608_read_channel_data(indio_dev, chan, val); //再进行细分筛选判断读取的是原始数据类型下的哪个通道的数据温度、加速度、陀螺仪而这里val参数不用做处理因为元素数据是ADC数据是整形的mutex_unlock(dev-mutex_lock); //释放锁return ret;case IIO_CHAN_INFO_SCALE: //分辨率数据类型printk(read type is IIO_CHAN_INFO_SCALE. \r\n);switch (chan-type) {case IIO_ACCEL: //加速度printk(read channel type is IIO_ACCEL. \r\n);mutex_lock(dev-mutex_lock); //上锁保证读取的数据正确性regdata (icm20608_read_onereg(dev, ICM20_ACCEL_CONFIG) 0x18) 3; //获取配置寄存器的3、4bit位的分辨率配置值*val 0; //陀加速度的分辨率是小数因此整数部分为0*val2 accel_scale_icm20608[regdata];mutex_unlock(dev-mutex_lock); //释放锁return IIO_VAL_INT_PLUS_NANO; //小数部分放大1000000000倍 值为valval2/1000000000 case IIO_ANGL_VEL: //陀螺仪printk(read channel type is IIO_ANGL_VEL\r\n);mutex_lock(dev-mutex_lock); //上锁保证读取的数据正确性regdata (icm20608_read_onereg(dev, ICM20_GYRO_CONFIG) 0x18) 3; //获取配置寄存器的3、4bit位的分辨率配置值*val 0; //陀螺仪的分辨率是小数因此整数部分为0*val2 gyro_scale_icm20608[regdata]; //获取已经计算出来的陀螺仪分辨率表的分辨率值mutex_unlock(dev-mutex_lock); //释放锁return IIO_VAL_INT_PLUS_MICRO; //小数部分放大1000000倍 值为valval2/1000000case IIO_TEMP: //温度printk(read channel type is IIO_TEMP\r\n);*val ICM20608_TEMP_SCALE/ 1000000;*val2 ICM20608_TEMP_SCALE % 1000000;return IIO_VAL_INT_PLUS_MICRO; /* 值为valval2/1000000 */ default:return -EINVAL; //没有一项符合那么返回参数不符合}return ret;case IIO_CHAN_INFO_OFFSET: //补偿、偏置数据类型printk(read type is IIO_CHAN_INFO_OFFSET. \r\n);switch (chan-type) {case IIO_TEMP:*val ICM20608_TEMP_OFFSET;return IIO_VAL_INT;default:return -EINVAL;}return ret;case IIO_CHAN_INFO_CALIBBIAS: //校准数据类型printk(read type is IIO_CHAN_INFO_CALIBBIAS. \r\n);switch (chan-type) {case IIO_ANGL_VEL: /* 陀螺仪的校准值 */mutex_lock(dev-mutex_lock);ret icm20608_sensor_show(dev, ICM20_XG_OFFS_USRH, chan-channel2, val);mutex_unlock(dev-mutex_lock);return ret;case IIO_ACCEL: /* 加速度计的校准值 */mutex_lock(dev-mutex_lock); ret icm20608_sensor_show(dev, ICM20_XA_OFFSET_H, chan-channel2, val);mutex_unlock(dev-mutex_lock);return ret;default:return -EINVAL;}default:return -EINVAL; //没有一项符合那么返回参数不符合}/********** 4.1 根据设置的通道文件参数作为条件依据读取数据类型 **********/return ret; }/* 2.3.2.2 */ static int icm20608_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) {int ret 0;/********** 5.1 根据设置的通道文件参数作为条件依据读取数据类型 **********/struct icm20608_dev *dev iio_priv(indio_dev);// printk(icm20608_write_raw\r\n);switch (mask) {case IIO_CHAN_INFO_SCALE: /* 设置陀螺仪和加速度计的分辨率 */switch (chan-type) {case IIO_ANGL_VEL: /* 设置陀螺仪 */mutex_lock(dev-mutex_lock);ret icm20608_write_gyro_scale(dev, val2);mutex_unlock(dev-mutex_lock);break;case IIO_ACCEL: /* 设置加速度计 */mutex_lock(dev-mutex_lock);ret icm20608_write_accel_scale(dev, val2);mutex_unlock(dev-mutex_lock);break;default:ret -EINVAL;break;}break;case IIO_CHAN_INFO_CALIBBIAS: /* 设置陀螺仪和加速度计的校准值*/switch (chan-type) {case IIO_ANGL_VEL: /* 设置陀螺仪校准值 */mutex_lock(dev-mutex_lock);ret icm20608_sensor_set(dev, ICM20_XG_OFFS_USRH,chan-channel2, val);mutex_unlock(dev-mutex_lock);break;case IIO_ACCEL: /* 加速度计校准值 */mutex_lock(dev-mutex_lock);ret icm20608_sensor_set(dev, ICM20_XA_OFFSET_H,chan-channel2, val);mutex_unlock(dev-mutex_lock);break;default:ret -EINVAL;break;}break;default:ret -EINVAL;break;} /********** 5.1 根据设置的通道文件参数作为条件依据读取数据类型 **********/return ret; }/* 2.3.2.3 * 用户空间写数据格式比如我们在用户空间操作 sysfs 来设置传感器的分辨率如果分辨率带小数那么这个小数传递到内核空间应该扩大多少倍此函数就是用来设置这个的。* indio_dev : iio_dev* chan : 通道* mask : 掩码* return : 0成功其他值错误*/ static int icm20608_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) {/********** 6.1 根据设置的通道文件参数作为条件依据读取数据类型 **********/ switch (mask) {case IIO_CHAN_INFO_SCALE:switch (chan-type) {case IIO_ANGL_VEL: /* 用户空间写的陀螺仪分辨率数据要乘以1000000 */return IIO_VAL_INT_PLUS_MICRO;default: /* 用户空间写的加速度计分辨率数据要乘以1000000000 */return IIO_VAL_INT_PLUS_NANO;}default:return IIO_VAL_INT_PLUS_MICRO;}return -EINVAL;/********** 6.1 根据设置的通道文件参数作为条件依据读取数据类型 **********/ }/* 2.3.2 */ /* iio_info当应用程序读取相应的驱动文件的时候 xxx_read_raw* 函数就会执行我们在此函数中会读取传感器数据然后返回给应用层。当应用层向相应的驱* 动写数据的时候 xxx_write_raw 函数就会执行。因此 xxx_read_raw 和 xxx_write_raw 这两个函* 数是非常重要的需要我们根据具体的传感器来编写这两个函数是编写 IIO 驱动的核心。* */ static const struct iio_info icm20608_info {.read_raw icm20608_read_raw,.write_raw icm20608_write_raw,.write_raw_get_fmt icm20608_write_raw_get_fmt,.driver_module THIS_MODULE, };/* 1.7 probe函数 */ static int icm20608_probe(struct spi_device *spi) {int ret 0;struct icm20608_dev *dev;struct iio_dev *indio_dev;printk(icm20608_probe successful!\r\n);/* 2.1 申请icm20608_dev结构体大小的内存 为私人结构体“icm20608_dev”分配内存空间 */indio_dev devm_iio_device_alloc(spi-dev, sizeof(*dev));if (!indio_dev){ret -ENOMEM;goto fail_iio_dev;}/* 2.2 获取定义的设备结构体首地址和获取spi_device结构体等数据私有化操作 */dev iio_priv(indio_dev); // 使用 iio_priv 函数从 iio_dev 中提取出私有数据也就是 icm2608_dev 这个自定义结构体变量首地址dev-spi spi; // 设备树匹配成功后系统会分配struct spi_device *spi因此将其spi赋值给我们定义的设备结构体上spi_set_drvdata(spi, indio_dev); // 将indio_dev设置为spi-driver_data私有数据mutex_init(dev-mutex_lock); // 初始化互斥锁/* 2.3 初始化iio_dev */indio_dev-dev.parent spi-dev; // 获取spi-dev结构体indio_dev-channels icm20608_channels; // 通道indio_dev-num_channels ARRAY_SIZE(icm20608_channels); // 通道大小indio_dev-name DEVICE_NAME; // 名称indio_dev-modes INDIO_DIRECT_MODE; // 直接模式提供sysfs接口indio_dev-info icm20608_info;/* 2.4 将iio_dev注册到内核 */ret iio_device_register(indio_dev);if (ret 0){dev_err(spi-dev, unable to register iio device\r\n);goto fail_iio_register;}/* 2.5 设置SPI的模式 */spi-mode SPI_MODE_0; // MODE0,CPOL0, CPHA0spi_setup(spi); //设置好 spi_device 以后需要使用 spi_setup 配置一下/* 3.1 初始化 regmap_config 设置 和 regmap *//* 补充当设置SPI读ICM20608操作时使用 regmap 的时候就不需要手动将寄存器地址的 bit7 置 1在初始化 regmap_config* 的时候直接将 read_flag_mask 设置为 0X80 即可这样通过 regmap 读取 SPI 内部寄存器的时候* 就会将寄存器地址与 read_flag_mask 进行或运算结果就是将 bit7 置 1但是整个过程不需要* 我们来操作全部由 regmap 框架来完成的 */dev-regmap_config.reg_bits 8; /* 寄存器长度8bit */dev-regmap_config.val_bits 8; /* 值长度8bit */dev-regmap_config.read_flag_mask 0x80; /* 读掩码 */dev-regmap regmap_init_spi(spi,dev-regmap_config);if(IS_ERR(dev-regmap)) {ret PTR_ERR(dev-regmap);goto fail_regmap_init;}/* 3.5 调用icm20608初始化函数 */icm20608_reg_init(dev);return 0;fail_regmap_init:iio_device_unregister(indio_dev); fail_iio_register: fail_iio_dev:return ret; }/* 1.8 remove函数 */ static int icm20608_remove(struct spi_device *spi) {int ret 0;/* 获取私有数据 */struct iio_dev *indio_dev spi_get_drvdata(spi);struct icm20608_dev *dev;dev iio_priv(indio_dev);printk(icm20608_remove finish\r\n);/* 2.6 注销iio_dev */iio_device_unregister(indio_dev);/* 3.6 删除regmap */regmap_exit(dev-regmap);return ret; }/* 1.5 传统的匹配表 */ static const struct spi_device_id icm20608_id[] {{alientek,icm20608, 0},{}};/* 1.6 设备树匹配表 */ static const struct of_device_id icm20608_of_match[] {{.compatible alientek,icm20608},{}};/* 1.4 spi_driver结构体 */ 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, };// module_spi_driver(icm20608_driver); /* 1.2 驱动模块入口函数 */ static int __init icm20608_init(void) {return spi_register_driver(icm20608_driver); // 注册spi驱动设备 }/* 1.3 驱动模块出口函数 */ static void __exit icm20608_exit(void) {spi_unregister_driver(icm20608_driver); // 注销spi驱动设备 }/* 1.1 驱动许可和个人信息 */ module_init(icm20608_init); module_exit(icm20608_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(djw);2、测试现象 我们修改了加速度传感器分辨率后查看的重力加速度的原始数据值也发生改变大家可以将分辨率与重力加速度原始数据值相乘约等于1个g的加速度。大家自行测试其他的通道文件了。 3、icm20608APP程序及测试 /*************************************************************** #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 #include errno.h/* 字符串转数字将浮点小数字符串转换为浮点数数值 */ #define SENSOR_FLOAT_DATA_GET(ret, index, str, member)\ret file_data_read(file_path[index], str);\dev-member atof(str);\ /* 字符串转数字将整数字符串转换为整数数值 */ #define SENSOR_INT_DATA_GET(ret, index, str, member)\ret file_data_read(file_path[index], str);\dev-member atoi(str);\/* icm20608 iio框架对应的文件路径 */ static char *file_path[] {/sys/bus/iio/devices/iio:device0/in_accel_scale,/sys/bus/iio/devices/iio:device0/in_accel_x_calibbias,/sys/bus/iio/devices/iio:device0/in_accel_x_raw,/sys/bus/iio/devices/iio:device0/in_accel_y_calibbias,/sys/bus/iio/devices/iio:device0/in_accel_y_raw,/sys/bus/iio/devices/iio:device0/in_accel_z_calibbias,/sys/bus/iio/devices/iio:device0/in_accel_z_raw,/sys/bus/iio/devices/iio:device0/in_anglvel_scale,/sys/bus/iio/devices/iio:device0/in_anglvel_x_calibbias,/sys/bus/iio/devices/iio:device0/in_anglvel_x_raw,/sys/bus/iio/devices/iio:device0/in_anglvel_y_calibbias,/sys/bus/iio/devices/iio:device0/in_anglvel_y_raw,/sys/bus/iio/devices/iio:device0/in_anglvel_z_calibbias,/sys/bus/iio/devices/iio:device0/in_anglvel_z_raw,/sys/bus/iio/devices/iio:device0/in_temp_offset,/sys/bus/iio/devices/iio:device0/in_temp_raw,/sys/bus/iio/devices/iio:device0/in_temp_scale, };/* 文件路径索引要和file_path里面的文件顺序对应 */ enum path_index {IN_ACCEL_SCALE 0,IN_ACCEL_X_CALIBBIAS,IN_ACCEL_X_RAW,IN_ACCEL_Y_CALIBBIAS,IN_ACCEL_Y_RAW,IN_ACCEL_Z_CALIBBIAS,IN_ACCEL_Z_RAW,IN_ANGLVEL_SCALE,IN_ANGLVEL_X_CALIBBIAS,IN_ANGLVEL_X_RAW,IN_ANGLVEL_Y_CALIBBIAS,IN_ANGLVEL_Y_RAW,IN_ANGLVEL_Z_CALIBBIAS,IN_ANGLVEL_Z_RAW,IN_TEMP_OFFSET,IN_TEMP_RAW,IN_TEMP_SCALE, };/** icm20608数据设备结构体*/ struct icm20608_dev{int accel_x_calibbias, accel_y_calibbias, accel_z_calibbias;int accel_x_raw, accel_y_raw, accel_z_raw;int gyro_x_calibbias, gyro_y_calibbias, gyro_z_calibbias;int gyro_x_raw, gyro_y_raw, gyro_z_raw;int temp_offset, temp_raw;float accel_scale, gyro_scale, temp_scale;float gyro_x_act, gyro_y_act, gyro_z_act;float accel_x_act, accel_y_act, accel_z_act;float temp_act; };struct icm20608_dev icm20608;/** description : 读取指定文件内容* param - filename : 要读取的文件路径* param - str : 读取到的文件字符串* return : 0 成功;其他 失败*/ static int file_data_read(char *filename, char *str) {int ret 0;FILE *data_stream;data_stream fopen(filename, r); /* 只读打开 */if(data_stream NULL) {printf(cant open file %s\r\n, filename);return -1;}ret fscanf(data_stream, %s, str);if(!ret) {printf(file read error!\r\n);} else if(ret EOF) {/* 读到文件末尾的话将文件指针重新调整到文件头 */fseek(data_stream, 0, SEEK_SET); }fclose(data_stream); /* 关闭文件 */ return 0; }/** description : 获取ICM20608数据* param - dev : 设备结构体* return : 0 成功;其他 失败*/ static int sensor_read(struct icm20608_dev *dev) {int ret 0;char str[50];/* 1、获取陀螺仪原始数据 */SENSOR_FLOAT_DATA_GET(ret, IN_ANGLVEL_SCALE, str, gyro_scale);SENSOR_INT_DATA_GET(ret, IN_ANGLVEL_X_RAW, str, gyro_x_raw);SENSOR_INT_DATA_GET(ret, IN_ANGLVEL_Y_RAW, str, gyro_y_raw);SENSOR_INT_DATA_GET(ret, IN_ANGLVEL_Z_RAW, str, gyro_z_raw);/* 2、获取加速度计原始数据 */SENSOR_FLOAT_DATA_GET(ret, IN_ACCEL_SCALE, str, accel_scale);SENSOR_INT_DATA_GET(ret, IN_ACCEL_X_RAW, str, accel_x_raw);SENSOR_INT_DATA_GET(ret, IN_ACCEL_Y_RAW, str, accel_y_raw);SENSOR_INT_DATA_GET(ret, IN_ACCEL_Z_RAW, str, accel_z_raw);/* 3、获取温度值 */SENSOR_FLOAT_DATA_GET(ret, IN_TEMP_SCALE, str, temp_scale);SENSOR_INT_DATA_GET(ret, IN_TEMP_OFFSET, str, temp_offset);SENSOR_INT_DATA_GET(ret, IN_TEMP_RAW, str, temp_raw);/* 3、转换为实际数值 */dev-accel_x_act dev-accel_x_raw * dev-accel_scale;dev-accel_y_act dev-accel_y_raw * dev-accel_scale;dev-accel_z_act dev-accel_z_raw * dev-accel_scale;dev-gyro_x_act dev-gyro_x_raw * dev-gyro_scale;dev-gyro_y_act dev-gyro_y_raw * dev-gyro_scale;dev-gyro_z_act dev-gyro_z_raw * dev-gyro_scale;dev-temp_act ((dev-temp_raw - dev-temp_offset) / dev-temp_scale) 25;return ret; }/** description : main主程序* param - argc : argv数组元素个数* param - argv : 具体参数* return : 0 成功;其他 失败*/ int main(int argc, char *argv[]) {int ret 0;if (argc ! 1) {printf(Error Usage!\r\n);return -1;}while (1) {ret sensor_read(icm20608);if(ret 0) { /* 数据读取成功 */printf(\r\n原始值:\r\n);printf(gx %d, gy %d, gz %d\r\n, icm20608.gyro_x_raw, icm20608.gyro_y_raw, icm20608.gyro_z_raw);printf(ax %d, ay %d, az %d\r\n, icm20608.accel_x_raw, icm20608.accel_y_raw, icm20608.accel_z_raw);printf(temp %d\r\n, icm20608.temp_raw);printf(实际值:);printf(act gx %.2f°/S, act gy %.2f°/S, act gz %.2f°/S\r\n, icm20608.gyro_x_act, icm20608.gyro_y_act, icm20608.gyro_z_act);printf(act ax %.2fg, act ay %.2fg, act az %.2fg\r\n, icm20608.accel_x_act, icm20608.accel_y_act, icm20608.accel_z_act);printf(act temp %.2f°C\r\n, icm20608.temp_act);}usleep(100000); /*100ms */}return 0; }实验现象
http://www.hkea.cn/news/14311383/

相关文章:

  • 深圳国外网站制作公司php 网站目录结构
  • 怎样做淘宝的导购网站建立企业网站多少钱
  • 网站导航排版布局百度导航最新版本免费下载
  • 深圳鸿运通网站建设网站seo插件
  • 加强网站硬件建设手机号电子邮箱免费注册
  • 服装电子商务网站设计信丰网站制作
  • 个人网站建设域名html5网站开发书籍
  • 专业的免费建站网站404错误来源
  • 网络科技公司网站源码下载什么网站可以直接做word文档
  • 小程序在哪个网站做邯郸城乡建设部网站首页
  • 山西网站建设网站男女做受视频网站
  • 怎么用自己的电脑做网站空间wordpress 怎么上传头像不显示
  • 只有域名怎么做网站网站建设维护公司排名
  • 注册网站英语网站建设实训报告收获
  • 网站开发答辩设计预期目标四川门户网站建设
  • 免费加速器永久免费版不用登录网站做SEO优化多少钱
  • 网站备案管理苏州知名网站建设
  • 深圳网站建设领先网站的域名解析怎么做
  • 黑龙江省城乡和建设厅网站自己做h5制作开发
  • 网站建设主要流程图专业网站建设定制公司哪家好
  • 手机网站带后台源代码网站备案流程图片
  • 做网站 如何注册公司怎么看一个网站是谁做的
  • 怎么申请域名 制作网站网站开发与建设方向
  • 机关网站建设创新软件工程师证书报考网站
  • 网站开发公司怎么接单子河南网站关键词优化
  • 企业网站管理系统论文蚌埠企业做网站
  • 做网站准备的资料关键词优化怎样
  • 制作网站赚钱吗网站建设常见问题及解决办法
  • 培训营销型网站建设电子商务网站创建的4个阶段
  • 建设银行网上交管网站北京工商注册流程