当前位置: 首页 > 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表示写入成功,否则表示写入失败。

相关文章:

  • 基于STM32的光照测量报警Proteus仿真设计+程序设计+设计报告+讲解视频
  • 供应链风险管理中,企业如何识别关键风险因素?
  • 【C++】模版(1)
  • 机器学习--特征工程具体案例
  • 2022年下半年信息系统项目管理师——综合知识真题及答案(4)
  • WPF Data Binding 及经典应用示例
  • 《黑马前端ajax+node.js+webpack+git教程》(笔记)——node.js教程+webpack教程(nodejs教程)
  • 【占融数科-注册/登录安全分析报告】
  • Kotlin Compose Button 实现长按监听并实现动画效果
  • Text2SQL在Spark NLP中的实现与应用:将自然语言问题转换为SQL查询的技术解析
  • 深度学习---模型预热(Model Warm-Up)
  • 【实战】GPT-SoVITS+内网穿透:3分钟搭建可公网访问的语音克隆系统
  • 苍穹外卖 - Day03
  • 【AI News | 20250519】每日AI进展
  • 深度强化学习 | 基于SAC算法的移动机器人路径跟踪(附Pytorch实现)
  • Manus AI 突破多语言手写识别技术壁垒:创新架构、算法与应用解析
  • 9万字67道Java集合经典面试题(2025修订版)
  • 安全强化的Linux
  • 云原生时代的系统可观测性:理念变革与实践体系
  • 枪机定焦系统的自动控制装置
  • 国家统计局:4月全国城镇调查失业率为5.1%,比上月下降0.1个百分点
  • 通往国际舞台之路:清政府与万国公会的交往
  • 美将解除对叙利亚制裁,外交部:中方一贯反对非法单边制裁
  • 山东:小伙为救同学耽误考试属实,启用副题安排考试
  • AI含量非常高,2025上海教育博览会将于本周五开幕
  • 西北大学副校长成陕西首富?旗下巨子生物去年净利超20亿,到底持股多少