Linux IIO 驱动

  1. 工业场合里面也有大量的模拟量和数字量之间的转换,也就是 ADC 和 DAC。

手机或者手环里面的加速度计、光传感器、陀螺仪、气压计、磁力计等,这些传感器本质上都是ADC,大家注意查看这些传感器的手册,会发现他们内部都会有个 ADC,传感器对外提供 IIC或者 SPI 接口, SOC 可以通过 IIC 或者 SPI 接口来获取到传感器内部的 ADC 数值,从而得到想要测量的结果。

Linux 内核为了管理这些日益增多的 ADC 类传感器,特地推出了 IIO 子系统,使用 IIO 子系统来编写 ADC 类传感器驱动。

2.IIO 全称是 Industrial I/O(工业 I/O),大家不要看到“工业”两个字就觉得 IIO是只用于工业领域的。在搜索 IIO 子系统的时候,会发现大多数讲的都是 ADC,这是因为 IIO 就是为 ADC 类传感器准备的,当然了 DAC 也是可以的。大家常用的陀螺仪、加速度计、电压/电流测量芯片、光照传感器、压力传感器等内部都是有个 ADC,内部 ADC 将原始的模拟数据转换为数字量,然后通过其他的通信接口,比如IIC、 SPI 等传输给 SOC。

因此,当你使用的传感器本质是 ADC 或 DAC 器件的时候,可以优先考虑使用 IIO 驱动框架。

3.基础驱动框架建立

3.1 SPI 驱动框架

              SPI 驱动框架
/*
* @description : spi 驱动的 probe 函数,当驱动与设备匹配以后此函数就会执行
* @param - spi : spi 设备
* @return : 0,成功;其他值,失败
*/
static int xxx_probe(struct spi_device *spi)
{
    return 0;
}

/*
* @description : spi 驱动的 remove 函数,移除 spi 驱动的时候此函数会执行
* @param - spi : spi 设备
* @return : 0,成功;其他负值,失败
*/
static int xxx_remove(struct spi_device *spi)
{
    return 0;
}

/* 传统匹配方式 ID 列表 */
static const struct spi_device_id xxx_id[] = {
    {"alientek,xxx", 0},
    {}
};

/* 设备树匹配列表 */
static const struct of_device_id xxx_of_match[] = {
    { .compatible = "alientek,xxx" },
    { /* Sentinel */ }
};

/* SPI 驱动结构体 */
static struct spi_driver xxx_driver = {
    .probe = xxx_probe,
    .remove = xxx_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "xxx",
        .of_match_table = xxx_of_match,
    },
    .id_table = xxx_id,
};

/* 驱动入口函数 */
static int __init xxx_init(void)
{
    return spi_register_driver(&xxx_driver);
}

/* 驱动出口函数 */
static void __exit xxx_exit(void)
{
    spi_unregister_driver(&xxx_driver);
}

module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("w");

3.2 IIO 设备申请与初始化

IIO 设备的申请、初始化以及注册在 probe 函数中完成,在注销驱动的时候还需要在 remove函数中注销掉 IIO 设备、释放掉申请的一些内存。

添加 IIO 框架
/* 自定义设备结构体 */
struct xxx_dev {
    struct spi_device *spi; /* spi 设备 */
    struct regmap *regmap;  /* regmap */
    struct regmap_config regmap_config;
    struct mutex lock;
};

/* 通道数组 */
static const struct iio_chan_spec xxx_channels[] = {

};

/*
* @description : 读函数,当读取 sysfs 中的文件的时候最终此函数会执行,
* :此函数里面会从传感器里面读取各种数据,然后上传给应用。
* @param - indio_dev : IIO 设备
* @param - chan : 通道
* @param - val : 读取的值,如果是小数值的话, val 是整数部分。
* @param - val2 : 读取的值,如果是小数值的话, val2 是小数部分。
* @param - mask : 掩码。
* @return : 0,成功;其他值,错误
*/
static int xxx_read_raw(struct iio_dev *indio_dev,
                        struct iio_chan_spec const *chan,
                        int *val, int *val2, long mask)
{
    return 0;
}

/*
* @description : 写函数,当向 sysfs 中的文件写数据的时候最终此函数
* :会执行,一般在此函数里面设置传感器,比如量程等。
* @param - indio_dev : IIO 设备
* @param - chan : 通道
* @param - val : 应用程序写入值,如果是小数的话, val 是整数部分。
* @param - val2 : 应用程序写入值,如果是小数的话, val2 是小数部分。
* @return : 0,成功;其他值,错误
*/
static int xxx_write_raw(struct iio_dev *indio_dev,
                         struct iio_chan_spec const *chan,
                         int val, int val2, long mask)
{
    return 0;
}

/*
* @description : 用户空间写数据格式,比如我们在用户空间操作 sysfs 来设
* :置传感器的分辨率,如果分辨率带小数,那么这个小数传递到
* : 内核空间应该扩大多少倍,此函数就是用来设置这个的。
* @param - indio_dev : iio_dev
* @param - chan : 通道
* @param - mask : 掩码
* @return : 0,成功;其他值,错误
*/
static int xxx_write_raw_get_fmt(struct iio_dev *indio_dev,
                                 struct iio_chan_spec const *chan, long mask)
{
    return 0;
}

/* iio_info 结构体变量 */
static const struct iio_info xxx_info = {
    .read_raw = xxx_read_raw, //当应用程序读取相应的驱动文件的时候, xxx_read_raw函数就会执行,在此函数中会读取传感器数据,然后返回给应用层
    .write_raw = xxx_write_raw, //当应用层向相应的驱动写数据的时候, xxx_write_raw 函数就会执行
    .write_raw_get_fmt = &xxx_write_raw_get_fmt,
};

/*
* @description : spi 驱动的 probe 函数,当驱动与
* 设备匹配以后此函数就会执行
* @param - spi : spi 设备
*
*/
static int xxx_probe(struct spi_device *spi)
{
    int ret;
    struct xxx_dev *data;
    struct iio_dev *indio_dev;

    /* 1、申请 iio_dev 内存 */
    indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
    if (!indio_dev)
    return -ENOMEM;

    /* 2、获取 xxx_dev 结构体地址 */
    data = iio_priv(indio_dev);
    data->spi = spi;
    spi_set_drvdata(spi, indio_dev);
    mutex_init(&data->lock);

    /* 3、初始化 iio_dev 成员变量 */
    indio_dev->dev.parent = &spi->dev;
    indio_dev->info = &xxx_info;
    indio_dev->name = "xxx";
    indio_dev->modes = INDIO_DIRECT_MODE; /* 直接模式 /
    indio_dev->channels = xxx_channels;
    indio_dev->num_channels = ARRAY_SIZE(xxx_channels);

    iio_device_register(indio_dev);

    /* 4、 regmap 相关设置 */

    /* 5、 SPI 相关设置*/

    /* 6、芯片初始化 */

    return 0;

}

/*
* @description : spi 驱动的 remove 函数,移除 spi 驱动的时候此函数会执行
* @param - spi : spi 设备
* @return : 0,成功;其他负值,失败
*/
static int xxx_remove(struct spi_device *spi)
{
    struct iio_dev *indio_dev = spi_get_drvdata(spi);
    struct xxx_dev *data;

    data = iio_priv(indio_dev); ;

    /* 1、其他资源的注销以及释放 */

    /* 2、注销 IIO */
    iio_device_unregister(indio_dev);

    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值