1、使用的原因
- Linux下大部分设备的驱动开发都是操作其内部寄存器,比如 I2C/SPI 设备的本质都是一样的,通过I2C/SPI接口读写芯片内部寄存器。
- 在前面学习 I2C 和SPI 驱动的时候,针对 I2C 和 SPI 设备寄存器的操作都是通过相关的 API函数进行操作的。这样 Linux 内核中就会充斥着大量的重复、冗余代码,但是这些本质上都是对寄存器的操作,所以为了方便内核开发人员统一访问 I2C/SPI 设备的时候,Linux下使用i2c_transfer来读写I2C设备中的寄存器。SPI接口的话使用 spi_write/spi_read读写SPI设备中的寄存器。因为驱动属于内核的一部分,导致 Linux 内核里面就会充斥了大量的 i2c_transfer 这类的冗余代码,而且由于iic驱动的和spi各自写各自的驱动文件,导致代码的复用性也会降低。
- 为此引入了 Regmap子系统
2、Regmap简介
-
基于代码复用的原则,Linux内核引入了regmap 模型.
-
regmap将寄存器访问的共同逻辑抽象出来,驱动开发人员不需要再去纠结使用SPI或者 I2C接口 API函数,统一使用 regmap API函数,优点就是上面对应的缺点
-
regmap模型的重点在于:
@通过 regmap 模型提供的统一接口函数来访问器件的寄存器,SOC 内部的寄存器也可以使用regmap接口函数来访问@regmap 是Linux 内核为了减少慢速 I/O 在驱动上的冗余开销,提供了一种通用的接口来操作硬件寄存器。
@regmap在驱动和硬件之间添加了 cache,降低了低速 I/O 的操作次数,提高了访问效率,缺点是实时性会降低
-
使用regmap的情况:
@)硬件寄存器操作,比如选用通过 I2C/SPI 接口来读写设备的内部寄存器,或者需要读写 SOC 内部的硬件寄存器@)提高代码复用性和驱动一致性,简化驱动开发过程。
@)减少底层I/O 操作次数,提高访问效率
3、Regmap驱动框架
/*
@ Linux内核将regmap框架抽象为regmap结构体
@ 定义在文件drivers/base/regmap/internal.h 中
@ regmap 结构体
*/
struct regmap {
union {
struct mutex mutex;
struct {
spinlock_t spinlock;
unsigned long spinlock_flags;
};
};
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg; /* This is passed to lock/unlock functions */
struct device *dev; /* Device we do I/O on */
void *work_buf; /* Scratch buffer used to format I/O */
struct regmap_format format; /* Buffer format */
const struct regmap_bus *bus;
void *bus_context;
const char *name;
bool async;
spinlock_t async_lock;
wait_queue_head_t async_waitq;
struct list_head async_list;
struct list_head async_free;
int async_ret;
...
unsigned int max_register;
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
bool (*precious_reg)(struct device *dev, unsigned int reg);
const struct regmap_access_table *wr_table;
const struct regmap_access_table *rd_table;
const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table;
int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
int (*reg_write)(void *context, unsigned int reg,
unsigned int val);
...
struct rb_root range_tree;
void *selector_work_buf; /* Scratch buffer used for selector */
};
/***************************************************************************************
*上面介绍了结构体,我们要实例化一个结构体也就是实例化regmap框架,实例化就是一系列的初始化
*****************************************************************************************/
/*
@ regmap_config 结构体就是用来初始化 regmap结构体即时初始化regmap框架
@ 定义在include/linux/regmap.h文件中
@ regmap_config 结构体
*/
struct regmap_config {
const char *name;
int reg_bits; /*寄存器地址位数,必填字段*/
int reg_stride; /*寄存器地址步长*/
int pad_bits; /*寄存器和值之间的填充位数*/
int val_bits; /*寄存器值位数,必填字段*/
bool (*writeable_reg)(struct device *dev, unsigned int reg); /*寄存器可写的话此回调函数就会被调用*/
bool (*readable_reg)(struct device *dev, unsigned int reg); /*寄存器可读的话此回调函数就会被调用*/
bool (*volatile_reg)(struct device *dev, unsigned int reg); /*当寄存器值不能缓存的时候此回调函数就会被调用*/
bool (*precious_reg)(struct device *dev, unsigned int reg); /*当寄存器值不能被读出来的时候此回调函数会被调用*/
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg;
int (*reg_read)(void *context, unsigned int reg, unsigned int val); /*读操作回调函数*/
int (*reg_write)(void *context, unsigned int reg, unsigned int val); /*可选的写操作回调函数*/
bool fast_io; /*快速 I/O,使用spinlock替代mutex来提升锁性能*/
unsigned int max_register; /*有效的最大寄存器地址,可选*/
const struct regmap_access_table *wr_table; /*可写的地址范围*/
const struct regmap_access_table *rd_table; /**/
const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table;
const struct reg_default *reg_defaults; /*寄存器模式值,为reg_default结构体类型*/
unsigned int num_reg_defaults; /*默认寄存器表中的元素个数*/
enum regcache_type cache_type;
const void *reg_defaults_raw;
unsigned int num_reg_defaults_raw;
u8 read_flag_mask; /*读标志掩码。*/
u8 write_flag_mask; /*写标志掩码*/
bool use_single_rw;
bool can_multi_write;
enum regmap_endian reg_format_endian;
enum regmap_endian val_format_endian;
const struct regmap_range_cfg *ranges;
unsigned int num_ranges;
};
/*********************************************************************
*上面我们已经说了我们主要就是针对regmapAPI抽象层来下功夫==
*我们一般会在probe 函数中初始化regmap_config,然后申请并初始化 regmap
**********************************************************************/
/*
@ Linux内核提供了针对不同接口的regmap初始化函数,一下举例2个
@ SPI接口初始化函数为regmap_init_spi
@ I2C 接口的regmap初始化函数为regmap_init_i2c
@
@ spi:需要使用regmap的spi_device
@ i2c:需要使用regmap的i2c_client
@ config:regmap_config结构体,需要程序编写人员初始化一个regmap_config实例,然后将其地址赋值给此参数
@ 返回值:申请到的并进过初始化的 regmap
*/
struct regmap * regmap_init_spi(struct spi_device *spi, const struct regmap_config *config)
struct regmap * regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config)
/*
@ 退出驱动的时候需要释放掉申请到的 regmap,不管是什么接口,全部使用 regmap_exit这个函数来释放regmap
@ map:需要释放的 regmap
@ 返回值:无
*/
void regmap_exit(struct regmap *map)
/*******************************************************************
*对于寄存器的操作就两种:读和写。regmap提供了最核心的两个读写操作****
*********************************************************************/
/*
@ 读寄存器操作
@ map:要操作的 regmap。
@ reg:要读的寄存器
@ val:读到的寄存器值
@ 返回值:0,读取成功;其他值,读取失败
*/
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
/*
@ 写寄存器操作
@ map:要操作的 regmap。
@ reg:要写的寄存器
@ val:要写的寄存器值
@ 返回值:0,读取成功;其他值,写失败
*/
int regmap_write(struct regmap *map, unsigned int reg, unsigned int *val)
/*
@ 读写的衍生API=用于读取多个寄存器的值
@ reg:要读取的第一个寄存器。
@ map:要操作的 regmap
@ val:读取到的数据缓冲区。
@ val_count:要读取的寄存器数量。
@ 返回值:0,写成功;其他值,读失败
*/
int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_count)
/*
@ 读写的衍生API=用于写多个寄存器的值
@ reg:要写的第一个寄存器。
@ map:要操作的 regmap
@ val:要写的寄存器数据缓冲区。
@ val_count:要写的寄存器数量。
@ 返回值:0,写成功;其他值,写失败
*/
int regmap_bulk_write(struct regmap *map, unsigned int reg, void *val, size_t val_count)
/*
@ 读写的衍生API==修改寄存器指定的bit
@ map:要操作的 regmap
@ val:需要更新的位值。
@ 返回值:0,写成功;其他值,写失败
*/
int regmap_update_bits (struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val)
4、实战演练1
对象:
imx6ull开发板,传感器ICM20608,总线SPI
- 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/spi/spi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include "icm20608reg.h"
#include <linux/gpio.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/regmap.h>
#define ICM20608_CNT 1
#define ICM20608_NAME "icm20608"
struct icm20608_dev {
struct spi_device *spi; /* spi设备 */
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
struct device_node *nd; /* 设备节点 */
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 *regmap; /* regmap */
struct regmap_config regmap_config;
};
/*
* @description : 读取icm20608指定寄存器值,读取一个寄存器
* @param - dev: icm20608设备
* @param - reg: 要读取的寄存器
* @return : 读取到的寄存器值
*/
static unsigned char icm20608_read_onereg(struct icm20608_dev *dev, u8 reg)
{
u8 ret;
unsigned int data;
ret = regmap_read(dev->regmap, reg, &data);
return (u8)data;
}
/*
* @description : 向icm20608指定寄存器写入指定的值,写一个寄存器
* @param - dev: icm20608设备
* @param - reg: 要写的寄存器
* @param - data: 要写入的值
* @return : 无
*/
static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg, u8 value)
{
regmap_write(dev->regmap, reg, value);
}
/*
* @description : 读取ICM20608的数据,读取原始数据,包括三轴陀螺仪、
* : 三轴加速度计和内部温度。
* @param - dev : ICM20608设备
* @return : 无。
*/
void icm20608_readdata(struct icm20608_dev *dev)
{
u8 ret;
unsigned char data[14];
ret = regmap_bulk_read(dev->regmap, 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)
{
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 cdev *cdev = filp->f_path.dentry->d_inode->i_cdev;
struct icm20608_dev *dev = container_of(cdev, struct icm20608_dev, cdev);
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 - spi : 要操作的设备
* @return : 无
*/
void icm20608_reginit(struct icm20608_dev *dev)
{
u8 value = 0;
icm20608_write_onereg(dev, ICM20_PWR_MGMT_1, 0x80);
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 = %#X\r\n", value);
icm20608_write_onereg(dev, ICM20_SMPLRT_DIV, 0x00); /* 输出速率是内部采样率 */
icm20608_write_onereg(dev, ICM20_GYRO_CONFIG, 0x18); /* 陀螺仪±2000dps量程 */
icm20608_write_onereg(dev, ICM20_ACCEL_CONFIG, 0x18); /* 加速度计±16G量程 */
icm20608_write_onereg(dev, ICM20_CONFIG, 0x04); /* 陀螺仪低通滤波BW=20Hz */
icm20608_write_onereg(dev, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz */
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); /* 关闭FIFO */
}
/*
* @description : spi驱动的probe函数,当驱动与
* 设备匹配以后此函数就会执行
* @param - spi : spi设备
*
*/
static int icm20608_probe(struct spi_device *spi)
{
int ret;
struct icm20608_dev *icm20608dev;
/* 分配icm20608dev对象的空间 */
icm20608dev = devm_kzalloc(&spi->dev, sizeof(*icm20608dev), GFP_KERNEL);
if(!icm20608dev)
return -ENOMEM;
/* 初始化regmap_config设置 */
icm20608dev->regmap_config.reg_bits = 8; /* 寄存器长度8bit */
icm20608dev->regmap_config.val_bits = 8; /* 值长度8bit */
icm20608dev->regmap_config.read_flag_mask = 0x80; /* 读掩码设置为0X80,ICM20608使用SPI接口读的时候寄存器最高位应该为1 */
/* 初始化IIC接口的regmap */
icm20608dev->regmap = regmap_init_spi(spi, &icm20608dev->regmap_config);
if (IS_ERR(icm20608dev->regmap)) {
return PTR_ERR(icm20608dev->regmap);
}
/* 注册字符设备驱动 */
/* 1、创建设备号 */
ret = alloc_chrdev_region(&icm20608dev->devid, 0, ICM20608_CNT, ICM20608_NAME);
if(ret < 0) {
pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", ICM20608_NAME, ret);
goto del_regmap;
}
/* 2、初始化cdev */
icm20608dev->cdev.owner = THIS_MODULE;
cdev_init(&icm20608dev->cdev, &icm20608_ops);
/* 3、添加一个cdev */
ret = cdev_add(&icm20608dev->cdev, icm20608dev->devid, ICM20608_CNT);
if(ret < 0) {
goto del_unregister;
}
/* 4、创建类 */
icm20608dev->class = class_create(THIS_MODULE, ICM20608_NAME);
if (IS_ERR(icm20608dev->class)) {
goto del_cdev;
}
/* 5、创建设备 */
icm20608dev->device = device_create(icm20608dev->class, NULL, icm20608dev->devid, NULL, ICM20608_NAME);
if (IS_ERR(icm20608dev->device)) {
goto destroy_class;
}
icm20608dev->spi = spi;
/*初始化spi_device */
spi->mode = SPI_MODE_0; /*MODE0,CPOL=0,CPHA=0*/
spi_setup(spi);
/* 初始化ICM20608内部寄存器 */
icm20608_reginit(icm20608dev);
/* 保存icm20608dev结构体 */
spi_set_drvdata(spi, icm20608dev);
return 0;
destroy_class:
device_destroy(icm20608dev->class, icm20608dev->devid);
del_cdev:
cdev_del(&icm20608dev->cdev);
del_unregister:
unregister_chrdev_region(icm20608dev->devid, ICM20608_CNT);
del_regmap:
regmap_exit(icm20608dev->regmap);
return -EIO;
}
/*
* @description : spi驱动的remove函数,移除spi驱动的时候此函数会执行
* @param - spi : spi设备
* @return : 0,成功;其他负值,失败
*/
static int icm20608_remove(struct spi_device *spi)
{
struct icm20608_dev *icm20608dev = spi_get_drvdata(spi);
/* 注销字符设备驱动 */
/* 1、删除cdev */
cdev_del(&icm20608dev->cdev);
/* 2、注销设备号 */
unregister_chrdev_region(icm20608dev->devid, ICM20608_CNT);
/* 3、注销设备 */
device_destroy(icm20608dev->class, icm20608dev->devid);
/* 4、注销类 */
class_destroy(icm20608dev->class);
/* 5、删除regmap */
regmap_exit(icm20608dev->regmap);
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");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");
- 测试app
#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>
/*
* @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;
}
5、实战演练2(iic)
对象:
imx6ull开发板,ap3216c传感器、IIC总线
- ap3216creg.h 设备信息
#ifndef AP3216C_H
#define AP3216C_H
#define AP3216C_ADDR 0X1E /* AP3216C器件地址 */
/* AP3316C寄存器 */
#define AP3216C_SYSTEMCONG 0x00 /* 配置寄存器 */
#define AP3216C_INTSTATUS 0X01 /* 中断状态寄存器 */
#define AP3216C_INTCLEAR 0X02 /* 中断清除寄存器 */
#define AP3216C_IRDATALOW 0x0A /* IR数据低字节 */
#define AP3216C_IRDATAHIGH 0x0B /* IR数据高字节 */
#define AP3216C_ALSDATALOW 0x0C /* ALS数据低字节 */
#define AP3216C_ALSDATAHIGH 0X0D /* ALS数据高字节 */
#define AP3216C_PSDATALOW 0X0E /* PS数据低字节 */
#define AP3216C_PSDATAHIGH 0X0F /* PS数据高字节 */
#endif
- ap3216c.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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "ap3216creg.h"
#include <linux/regmap.h>
#define AP3216C_CNT 1
#define AP3216C_NAME "ap3216c"
struct ap3216c_dev {
struct i2c_client *client; /* i2c 设备 */
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
struct device_node *nd; /* 设备节点 */
unsigned short ir, als, ps; /* 三个光传感器数据 */
struct regmap *regmap; /* regmap */
struct regmap_config regmap_config;
};
/*
* @description : 读取ap3216c指定寄存器值,读取一个寄存器
* @param - dev: ap3216c设备
* @param - reg: 要读取的寄存器
* @return : 读取到的寄存器值
*/
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{
u8 ret;
unsigned int data;
ret = regmap_read(dev->regmap, reg, &data);
return (u8)data;
}
/*
* @description : 向ap3216c指定寄存器写入指定的值,写一个寄存器
* @param - dev: ap3216c设备
* @param - reg: 要写的寄存器
* @param - data: 要写入的值
* @return : 无
*/
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
{
regmap_write(dev->regmap, reg, data);
}
/*
* @description : 读取AP3216C的数据,读取原始数据,包括ALS,PS和IR, 注意!
* : 如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms
* @param - ir : ir数据
* @param - ps : ps数据
* @param - ps : als数据
* @return : 无。
*/
void ap3216c_readdata(struct ap3216c_dev *dev)
{
unsigned char i =0;
unsigned char buf[6];
/* 循环读取所有传感器数据 */
for(i = 0; i < 6; i++) {
buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);
}
if(buf[0] & 0X80) /* IR_OF位为1,则数据无效 */
dev->ir = 0;
else /* 读取IR传感器的数据 */
dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);
dev->als = ((unsigned short)buf[3] << 8) | buf[2]; /* 读取ALS传感器的数据 */
if(buf[4] & 0x40) /* IR_OF位为1,则数据无效 */
dev->ps = 0;
else /* 读取PS传感器的数据 */
dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
}
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int ap3216c_open(struct inode *inode, struct file *filp)
{
/* 从file结构体获取cdev的指针,在根据cdev获取ap3216c_dev结构体的首地址 */
struct cdev *cdev = filp->f_path.dentry->d_inode->i_cdev;
struct ap3216c_dev *ap3216cdev = container_of(cdev, struct ap3216c_dev, cdev);
/* 初始化AP3216C */
ap3216c_write_reg(ap3216cdev, AP3216C_SYSTEMCONG, 0x04); /* 复位AP3216C */
mdelay(50); /* AP3216C复位最少10ms */
ap3216c_write_reg(ap3216cdev, AP3216C_SYSTEMCONG, 0X03); /* 开启ALS、PS+IR */
return 0;
}
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
short data[3];
long err = 0;
struct cdev *cdev = filp->f_path.dentry->d_inode->i_cdev;
struct ap3216c_dev *dev = container_of(cdev, struct ap3216c_dev, cdev);
ap3216c_readdata(dev);
data[0] = dev->ir;
data[1] = dev->als;
data[2] = dev->ps;
err = copy_to_user(buf, data, sizeof(data));
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int ap3216c_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* AP3216C操作函数 */
static const struct file_operations ap3216c_ops = {
.owner = THIS_MODULE,
.open = ap3216c_open,
.read = ap3216c_read,
.release = ap3216c_release,
};
/*
* @description : i2c驱动的probe函数,当驱动与
* 设备匹配以后此函数就会执行
* @param - client : i2c设备
* @param - id : i2c设备ID
* @return : 0,成功;其他负值,失败
*/
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret;
struct ap3216c_dev *ap3216cdev;
ap3216cdev = devm_kzalloc(&client->dev, sizeof(*ap3216cdev), GFP_KERNEL);
if(!ap3216cdev)
return -ENOMEM;
/* 初始化regmap_config设置 */
ap3216cdev->regmap_config.reg_bits = 8; /* 寄存器长度8bit */
ap3216cdev->regmap_config.val_bits = 8; /* 值长度8bit */
/* 初始化IIC接口的regmap */
ap3216cdev->regmap = regmap_init_i2c(client, &ap3216cdev->regmap_config);
if (IS_ERR(ap3216cdev->regmap)) {
return PTR_ERR(ap3216cdev->regmap);
}
/* 注册字符设备驱动 */
/* 1、创建设备号 */
ret = alloc_chrdev_region(&ap3216cdev->devid, 0, AP3216C_CNT, AP3216C_NAME);
if(ret < 0) {
pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", AP3216C_NAME, ret);
goto del_regmap;
}
/* 2、初始化cdev */
ap3216cdev->cdev.owner = THIS_MODULE;
cdev_init(&ap3216cdev->cdev, &ap3216c_ops);
/* 3、添加一个cdev */
ret = cdev_add(&ap3216cdev->cdev, ap3216cdev->devid, AP3216C_CNT);
if(ret < 0) {
goto del_unregister;
}
/* 4、创建类 */
ap3216cdev->class = class_create(THIS_MODULE, AP3216C_NAME);
if (IS_ERR(ap3216cdev->class)) {
goto del_cdev;
}
/* 5、创建设备 */
ap3216cdev->device = device_create(ap3216cdev->class, NULL, ap3216cdev->devid, NULL, AP3216C_NAME);
if (IS_ERR(ap3216cdev->device)) {
goto destroy_class;
}
ap3216cdev->client = client;
/* 保存ap3216cdev结构体 */
i2c_set_clientdata(client,ap3216cdev);
return 0;
destroy_class:
device_destroy(ap3216cdev->class, ap3216cdev->devid);
del_cdev:
cdev_del(&ap3216cdev->cdev);
del_unregister:
unregister_chrdev_region(ap3216cdev->devid, AP3216C_CNT);
del_regmap:
regmap_exit(ap3216cdev->regmap);
return -EIO;
}
/*
* @description : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
* @param - client : i2c设备
* @return : 0,成功;其他负值,失败
*/
static int ap3216c_remove(struct i2c_client *client)
{
struct ap3216c_dev *ap3216cdev = i2c_get_clientdata(client);
/* 注销字符设备驱动 */
/* 1、删除cdev */
cdev_del(&ap3216cdev->cdev);
/* 2、注销设备号 */
unregister_chrdev_region(ap3216cdev->devid, AP3216C_CNT);
/* 3、注销设备 */
device_destroy(ap3216cdev->class, ap3216cdev->devid);
/* 4、注销类 */
class_destroy(ap3216cdev->class);
/* 5、释放regmap */
regmap_exit(ap3216cdev->regmap);
return 0;
}
/* 传统匹配方式ID列表 */
static const struct i2c_device_id ap3216c_id[] = {
{"alientek,ap3216c", 0},
{}
};
/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[] = {
{ .compatible = "alientek,ap3216c" },
{ /* Sentinel */ }
};
/* i2c驱动结构体 */
static struct i2c_driver ap3216c_driver = {
.probe = ap3216c_probe,
.remove = ap3216c_remove,
.driver = {
.owner = THIS_MODULE,
.name = "ap3216c",
.of_match_table = ap3216c_of_match,
},
.id_table = ap3216c_id,
};
/*
* @description : 驱动入口函数
* @param : 无
* @return : 无
*/
static int __init ap3216c_init(void)
{
int ret = 0;
ret = i2c_add_driver(&ap3216c_driver);
return ret;
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit ap3216c_exit(void)
{
i2c_del_driver(&ap3216c_driver);
}
/* module_i2c_driver(ap3216c_driver) */
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");
- 测试app
#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>
/*
* @description : main主程序
* @param - argc : argv数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd;
char *filename;
unsigned short databuf[3];
unsigned short ir, als, ps;
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) { /* 数据读取成功 */
ir = databuf[0]; /* ir传感器数据 */
als = databuf[1]; /* als传感器数据 */
ps = databuf[2]; /* ps传感器数据 */
printf("ir = %d, als = %d, ps = %d\r\n", ir, als, ps);
}
usleep(200000); /*100ms */
}
close(fd); /* 关闭文件 */
return 0;
}