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

Regmap子系统之六轴传感器驱动-编写icm20607.c驱动

(一)在驱动中要操作很多芯片相关的寄存器,所以需要先新建一个icm20607.h的头文件,用来定义相关寄存器值。

#ifndef ICM20607_H

#define ICM20607_H

/***************************************************************

文件名 : icm20607.h

描述 : ICM20607寄存器地址描述头文件

***************************************************************/

#define ICM20608G_ID 0XAF /* ID值 */

#define ICM20608D_ID 0XAE /* ID值 */

#define ICM20607_ID 0X05

/* ICM20607寄存器

 *复位后所有寄存器地址都为0,除了

 *Register 107(0x41) Power Management 1

 *Register 117(0x05) WHO_AM_I

 *Register 26(0x80) CONFIG

 */

/* 陀螺仪和加速度自测(出产时设置,用于与用户的自检输出值比较) */

/* ICM20607 SELF TEST GYRO Modify 0x ->5x */

#define ICM20_SELF_TEST_X_GYRO 0x50

#define ICM20_SELF_TEST_Y_GYRO 0x51

#define ICM20_SELF_TEST_Z_GYRO 0x52

#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

(二)icm20607.c文件编写

(1)头文件引用

#include <linux/module.h>

#include <linux/init.h>

#include <linux/fs.h>           // 包含文件系统相关函数的头文件

#include <linux/uaccess.h>      // 包含用户空间数据访问函数的头文件

#include <linux/cdev.h>         //包含字符设备头文件

#include <linux/device.h>

#include <linux/delay.h>

#include <linux/spi/spi.h>

#include <linux/regmap.h>

#include <linux/of.h>

#include <linux/of_address.h>

#include <linux/of_gpio.h>

#include "icm20607.h"

(2)创建相关宏定义和变量

#define ICM20607_REG_WHOAMI      0x75

#define ICM20607_WHOAMI_VALUE    0xAF

#define DEVICE_NAME "icm20607"  // 设备名称

static dev_t dev_num;   //分配的设备号

int major;  //主设备号

int minor;  //次设备号

struct icm20607_dev {

struct spi_device *spi_dev; /* spi设备 */

dev_t dev_num; /* 设备号  */

struct cdev cdev; /* cdev */

struct class *class; /* 类 */

struct device *device; /* 设备  */

struct device_node *nd; /* 设备节点 */

int cs_gpio; /* 片选所使用的GPIO编号 */

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; /* 温度原始值 */

struct regmap *spi_regmap; /* regmap */

struct regmap_config regmap_config;

};

(3)驱动模块的入口和出口

module_init(icm20607_init);

module_exit(icm20607_exit);

(4)icm20607_init和icm20607_exit实现

static int __init icm20607_init(void)

{

    int ret;

    ret = spi_register_driver(&icm20607_driver);

    if (ret < 0) {

        pr_err("Failed to register ICM20607 driver: %d\n", ret);

        return ret;

    }

    pr_info("ICM20607 SPI device driver loaded\n");

    return 0;

}

static void __exit icm20607_exit(void)

{

    spi_unregister_driver(&icm20607_driver);

    pr_info("ICM20607 SPI device driver unloaded\n");

}

在入口函数中调用了spi_register_driver函数,来注册SPI总线驱动程序。在出口函数中调用了spi_unregister_driver函数,来注销驱动程序。

spi_register_driver函数原型如下:

int spi_register_driver(struct spi_driver *drv);

该函数接受一个指向struct spi_driver结构体的指针作为参数,并返回一个整数值,表示注册是否成功。struct spi_driver结构体定义了SPI总线驱动程序的属性和回调函数。

以下是struct spi_driver结构体的常见成员:

driver:struct device_driver类型的成员,描述了驱动程序的基本信息,如名称、总线类型等。

probe:指向驱动程序的探测函数的指针。探测函数在与设备匹配时被调用,用于初始化设备并注册相关资源。

remove:指向驱动程序的移除函数的指针。移除函数在设备被卸载时被调用,用于清理和释放相关资源。

id_table:指向struct spi_device_id数组的指针,用于匹配驱动程序和设备之间的关联关系。

probe_new:指向新版的探测函数的指针。新版探测函数支持更多功能,并可以替代旧版的probe函数。

remove_new:指向新版的移除函数的指针。新版移除函数支持更多功能,并可以替代旧版的remove函数。

通过调用spi_register_driver函数并传入正确配置的struct spi_driver结构体,可以将SPI总线驱动程序注册到Linux内核,使其能够接收和处理SPI设备的相关操作。

(5)spi_driver类型结构体定义

static struct spi_driver icm20607_driver = {

    .driver = {

        .name = "icm20607",

        .owner = THIS_MODULE,

.of_match_table = icm20607_of__match,

    },

    .probe = icm20607_probe,

    .remove = icm20607_remove,

};

(6)icm20607_of__match实现,用来与设备树中的compatible匹配

static const struct of_device_id icm20608_of_match[] = {

{ .compatible = "icm20607" },

{ /* Sentinel */ }

};

(7)remove函数实现,执行icm20607设备的清理操作

static int icm20607_remove(struct spi_device *spi)

{

struct icm20607_dev *icm20607dev = spi_get_drvdata(spi);

    // 在此处执行 ICM20607 设备的清理操作

//删除cdev

    cdev_del(&icm20607dev->cdev);

//注销设备号

unregister_chrdev_region(icm20607dev->dev_num, 1);

//注销设备

device_destroy(icm20607dev->class, icm20608dev->dev_num);

//注销类

class_destroy(icm20607dev->class);

//删除regmap

regmap_exit(icm20607dev->spi_regmap);

    pr_info("ICM20607 SPI device removed successfully\n");

    return 0;

}

(8)probe函数实现,此处简略描述regmap注册的过程:

static int icm20607_probe(struct spi_device *spi)

{

    int ret;

    unsigned int whoami;

struct icm20607_dev *icm20607dev;

//分配icm20607dev对象的空间

    icm20607dev = devm_kzalloc(&spi->dev, sizeof(*icm20607dev), GFP_KERNEL);

    if(!icm20607dev)

    return -ENOMEM;

// 创建 ICM20607 设备的 regmap

    icm20608dev->spi_regmap = regmap_init_spi(spi, &spi_regmap_config);

    if (IS_ERR(icm20607dev->spi_regmap)) {

        dev_err(&spi->dev, "Failed to initialize regmap: %ld\n", PTR_ERR(icm20607dev->spi_regmap));

        return PTR_ERR(icm20607dev->spi_regmap);

    }

......

/*初始化spi_device */

    icm20607dev->spi_dev = spi;

     spi->mode = SPI_MODE_0;

     spi_setup(spi);

     /* 初始化ICM20607内部寄存器 */

     icm20607_reginit(icm20607dev);

     /* 保存icm20607dev结构体 */

     spi_set_drvdata(spi, icm20607dev);

    pr_info("ICM20607 SPI device probed successfully\n");

    return 0;

}

probe函数中首先使用devm_kzalloc函数分配了icm20607dev的结构体空间,然后使用regmap_init_spi函数创建regmap实例,再进行spi控制器的初始化和配置,最后对ICM20607的内部寄存器进行配置。

其中regmap_init_spi函数中传入了“&spi_regmap_config”参数,前边有提到这个是用来配置regmap对象的,下边我们看这个参数是如何定义的。

(9)spi_regmap_config的定义

static const struct regmap_config spi_regmap_config = {

    .reg_bits = 8,

    .val_bits = 8,

    .read_flag_mask = 0x80,

    .reg_read = icm20607_spi_read,

    .reg_write = icm20607_spi_write,

    .max_register = ICM20607_REG_WHOAMI,

};

可以看到这其中规定了寄存器地址的位数,存储寄存器的位数,读寄存器掩码,读寄存器函数,写寄存器函数,最大寄存器地址。

(10)读写寄存器函数实现

static int icm20607_spi_read(struct icm20608_dev *dev, unsigned int reg, unsigned int *val)

{

    return regmap_read(dev->spi_regmap, reg, val);

}

static int icm20607_spi_write(struct icm20608_dev *dev, unsigned int reg, unsigned int val)

{

    return regmap_write(dev->spi_regmap, reg, val);

}

可以看出读写函数非常简单明了,直接使用regmap_read和regmap_write函数即可。

①regmap_read函数原型如下:

int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);

该函数用于从给定的寄存器地址(reg)读取数据,并将读取的值存储在val指向的变量中。map参数是一个指向struct regmap的指针,表示寄存器映射对象。返回值为0表示读取成功,否则表示读取失败。

②regmap_write函数原型如下:

int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);

该函数用于向给定的寄存器地址(reg)写入数据(val)。map参数是一个指向struct regmap的指针,表示寄存器映射对象。返回值为0表示写入成功,否则表示写入失败。

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

相关文章:

  • 【云实验】Excel文件转存到RDS数据库
  • 【大数据】MapReduce 编程--索引倒排--根据“内容 ➜ 出现在哪些文件里(某个单词出现在了哪些文件中,以及在每个文件中出现了多少次)
  • .NET 函数:检测 SQL 注入风险
  • 关于能管-虚拟电厂的概述
  • Win10 安装单机版ES(elasticsearch),整合IK分词器和安装Kibana
  • 【android bluetooth 协议分析 01】【HCI 层介绍 8】【ReadLocalVersionInformation命令介绍】
  • 【Android构建系统】Soong构建系统,通过.bp + .go定制编译
  • MySQL 故障排查与生产环境优化
  • verify_ssl 与 Token 验证的区别详解
  • Node 服务监控及通过钉钉推送告警提醒
  • 3.安卓逆向2-安卓文件目录
  • WPF点击按钮弹出一个窗口
  • 深入理解 Hadoop 核心组件 Yarn:架构、配置与实战
  • 物联网简介:万物互联的未来图景
  • Eclipse Java 开发调优:如何让 Eclipse 运行更快?
  • Spring Cloud Seata 深度解析:原理与架构设计
  • 甘特图工具怎么选?免费/付费项目管理工具对比测评(2025最新版)
  • java中如何优雅处理多租户系统的查询?
  • Hexo的Next主题的Config文件内方便修改的参数(Chat-Gpt)
  • 多线程进阶
  • java每日精进 5.19【Excel 导入导出】
  • 使用Python将 Excel 中的图表、形状和其他元素导出为图片
  • YouTube视频字幕转成文章算重复内容吗?
  • FD+Mysql的Insert时的字段赋值乱码问题
  • ffmpeg 把一个视频复制3次
  • java配置webSocket、前端使用uniapp连接
  • 【git config --global alias | Git分支操作效率提升实践指南】
  • 开源音视频转文字工具:基于 Vosk 和 Whisper 的多语言语音识别项目
  • 数据分析与应用---数据可视化基础
  • 精益数据分析(70/126):MVP迭代中的数据驱动决策与功能取舍