STM32MP157驱动开发——Linux IIO驱动(下)

article/2025/9/24 11:56:54

STM32MP157驱动开发——Linux IIO驱动(下)

  • 0.前言
  • 一、IIO 触发缓冲区
    • 1.IIO 触发器
    • 2.申请触发器
    • 3.释放触发器
    • 4.注册触发器
    • 5.注销触发器
    • 6. IIO 缓冲区
    • 7.向驱动程序添加触发缓冲功能
    • 8.驱动编写
    • 9.触发缓冲测试
    • 10.缓冲区读取
  • 二、测试App
  • 三、测试结果


0.前言

  上一节完成了 IIO 子系统下的 icm20608 设备驱动开发,但是传感器的数据采集速度太快,这一节就介绍一下驱动中与 IIO 缓冲区有关的内容。
  碍于篇幅,在上一节的驱动开发时,已经将缓冲区相关的代码添加进驱动,所以这一节仅对其进行了解。

一、IIO 触发缓冲区

触发缓冲区就是基于某种信号来触发数据采集,这些信号就是触发器,比如:

① 传感器数据就绪中断
② 周期性中断
③ 用户空间下读取 sysfs 下的指定文件

触发器肯定是触发数据的采集,数据采集到以后会填充到缓冲区里面,最终会以字符设备形式提供给用户空间,用户空间直接读取缓冲区文件即可

1.IIO 触发器

linux 内核使用 iio_trigger 结构体表示触发器,定义在 include/linux/iio/trigger.h 文件中:
在这里插入图片描述
iio_trigger_ops 为触发器的操作函数结构体:
在这里插入图片描述
①set_trigger_state 函数用于设置触发器状态,也就是打开/关闭触发器。如果用中断作为触发器,此函数一般设置传感器的中断使能状态。
②try_reenable 函数当用户计数为 0 的时候尝试重新使能触发器。
③ validate_device 函数用于当触发器改变时,使设备生效。

如果自行创建触发器,需要驱动开发人员编写 iio_trigger_ops。

2.申请触发器

使用 iio_trigger_alloc 函数创建触发器。
原型

struct iio_trigger *iio_trigger_alloc(const char *fmt, ...)

参数
iio_trigger_alloc 是个可变长度参数函数,主要目的就是拼凑触发器名字
返回值
申请到的 iio_trigger。

用法与printf函数相似,也可以使用devm_iio_trigger_alloc 函数申请触发器,这样在卸载驱动时就不用手动释放触发器。
例:iio_trigger_alloc("%s-dev%d", indio_dev->name, indio_dev->id),假设 indio_dev->name 为“icm20608”,indio_dev->id 为 0,那么触发器的名字就是“icm20608-dev0”

3.释放触发器

原型

void iio_trigger_free(struct iio_trigger *trig)

参数
trig:要释放的触发器。
返回值:无

4.注册触发器

原型

int iio_trigger_register(struct iio_trigger *trig_info)

参数
trig_info:要注册的触发器。
返回值
0:成功
其他值:失败

同理,也可以使用 devm_iio_trigger_register 函数。

5.注销触发器

原型

void iio_trigger_unregister(struct iio_trigger *trig_info)

参数
trig_info:要注销的触发器。
返回值:无

6. IIO 缓冲区

IIO 缓冲区就是保存采集到的数据,用户空间可以直接通过访问字符设备 /dev/iio:deviceX(X=0,1,2……)来读取缓冲区中的数据。
创建缓冲区:
原型

int iio_triggered_buffer_setup( struct iio_dev *indio_dev,irqreturn_t (*h)(int irq, void *p),irqreturn_t (*thread)(int irq, void *p),const struct iio_buffer_setup_ops *setup_ops)

参数
indio_dev:需要创建缓冲的 iio_dev
h:触发器中断上半部,上半部程序一定要简单,执行速度越快越好,一般就是提供捕获时间戳。可以直接使用 IIO 框架提供的 iio_pollfunc_store_time 函数。
thread:触发器中断下半部,重要的处理就在这里,此函数中需要将传感器中的数据和上半部获取到的时间戳一起推送到缓冲区中。
setup_ops:缓冲区操作函数集,iio_buffer_setup_ops 结构体内容如下:

474 struct iio_buffer_setup_ops {
475 	int (*preenable)(struct iio_dev *);
476 	int (*postenable)(struct iio_dev *);
477 	int (*predisable)(struct iio_dev *);
478 	int (*postdisable)(struct iio_dev *);
479 	bool (*validate_scan_mask)(struct iio_dev *indio_dev,
480 	const unsigned long *scan_mask);
481 };

如果设置为 NULL,那么就会使用默认的操作集 iio_triggered_buffer_setup_ops。
返回值:无

7.向驱动程序添加触发缓冲功能

修改设备树:
中断是最常用的触发方式,因为一般的传感器都有中断功能,当数据准备就绪或者指定事件发生以后就会产生中断通知 SOC,此时 SOC 就可以读取传感器内部数据。
修改设备树,向 ICM20608 驱动中添加中断引脚 PA14 的配置,在 stm32mp15-pinctrl.dtsi 文件中,添加 PA14 引脚配置:

icm20608_pins_b: icm20608-0 {pins {pinmux = <STM32_PINMUX('A', 14, ANALOG)>;bias-pull-up;};
};

另外,打开 stm32mp157d-atk.dts 文件,修改 icm20608 节点,添加中断信息:

spidev: icm20608@0 {compatible = "alientek,icm20608";reg = <0>; /* CS #0 */pinctrl-names = "default";pinctrl-0 = <&icm20608_pins_b>;interrupt-parent = <&gpioa>;interrupts = <14 IRQ_TYPE_LEVEL_HIGH>;spi-max-frequency = <80000000>;
};

设置中断父节点为 gpioa,设置 PA14 为高电平触发。
然后编译出新的设备树,启动开发板。

8.驱动编写

在上一节中,已经将缓冲区有关的代码段添加到驱动中,所以这里就不在赘述。这里建议再进行了解,iio框架使用情况确实比较多。

9.触发缓冲测试

缓冲区接口目录路径为:/sys/bus/iio/devices/iio:device0/buffer
在这里插入图片描述
进入buffer目录,会有一些属性文件:
在这里插入图片描述
data_available:指示数据是否有效,为 1 时有效,为 0 时无效。
enable:使能缓冲区,写入 1 使能缓冲区,写 0 关闭缓冲区
length:缓冲区大小,也就是可以存储数据的数量。
watermark:阻塞读取的时候只有数据量大于 watermark 的时候才能读取,非阻塞读取时不受 watermark 影响。

上一节提到的 /sys/bus/iio/devices/triggerX(X=0,1,2……)即为触发器目录。

10.缓冲区读取

读取数据之前需要配置缓冲区和触发器,首先使能各个扫描元素,也就是通道,使用以下命令:

echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_accel_x_en
echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_accel_y_en
echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_accel_z_en
echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_anglvel_x_en
echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_anglvel_y_en
echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_anglvel_z_en
echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_temp_en

然后设置ICM20608 所使用的触发器:

echo icm20608-dev0 > /sys/bus/iio/devices/iio:device0/trigger/current_trigger

最后就是设置缓冲区长度并开启:

echo 14 > /sys/bus/iio/devices/iio:device0/buffer/length
echo 1 > /sys/bus/iio/devices/iio:device0/buffer/enable

配置完成以后就可以读取/dev/iio:device0 文件,得到缓冲区的数据。

二、测试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>
#include <errno.h>/** icm20608数据设备结构体*/
struct icm20608_dev {unsigned char data[14];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		: 对icm20608相关触发进行设置 * @param   		: 无* @return 			: 无*/
void icm20608_trigger_set(void)
{system("echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_accel_x_en");system("echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_accel_y_en");	system("echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_accel_z_en");system("echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_anglvel_x_en");system("echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_anglvel_y_en");system("echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_anglvel_z_en");system("echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_temp_en");system("echo icm20608-dev0 > /sys/bus/iio/devices/iio:device0/trigger/current_trigger");system("echo 14 > /sys/bus/iio/devices/iio:device0/buffer/length");system("echo 1 > /sys/bus/iio/devices/iio:device0/buffer/enable");
}/** @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("can't 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		: 读取数据* @param - fd 		: 文件描述符* @return 			: 0 成功;其他 失败*/
int icm20608_read(int fd, struct icm20608_dev *dev)
{int ret = 0;char str[50];int i = 0;file_data_read("/sys/bus/iio/devices/iio:device0/in_accel_scale", str);dev->accel_scale = atof(str);file_data_read("/sys/bus/iio/devices/iio:device0/in_anglvel_scale", str);dev->gyro_scale = atof(str);file_data_read("/sys/bus/iio/devices/iio:device0/in_temp_scale", str);dev->temp_scale = atof(str);ret = read(fd, dev->data, 14);dev->accel_z_raw 	= (signed short)((dev->data[4] << 8) | dev->data[5]);dev->accel_x_raw 	= (signed short)((dev->data[0] << 8) | dev->data[1]); dev->accel_y_raw 	= (signed short)((dev->data[2] << 8) | dev->data[3]); dev->accel_z_raw 	= (signed short)((dev->data[4] << 8) | dev->data[5]); dev->temp_raw   	= (signed short)((dev->data[6] << 8) | dev->data[7]); dev->gyro_x_raw  	= (signed short)((dev->data[8] << 8) | dev->data[9]); dev->gyro_y_raw  	= (signed short)((dev->data[10] << 8) | dev->data[11]);dev->gyro_z_raw  	= (signed short)((dev->data[12] << 8) | dev->data[13]);/* 转换为实际数值 */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 fd;int ret = 0;/* 判断传参个数是否正确 */if(2 != argc) {printf("Usage:\n""\t./icm20608_triggerApp /dev/iio:device0\n");return -1;}icm20608_trigger_set();	/* 配置好触发缓冲区相关设置 *//* 打开设备 */fd = open(argv[1], O_RDONLY);if(0 > fd) {printf("ERROR: %s file open failed!\n", argv[1]);return -1;}/* 循环轮训读取按键数据 */while(1) {icm20608_read(fd, &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 */}
}

①icm20608_trigger_set 函数用于配置触发器和缓冲区,直接使用 system 函数来写入 shell 命令
②file_data_read 函数用于读取文件流,主要用于读取 ICM20608 的加速度计、陀螺仪、温度的分辨率等参数
③icm20608_read 函数用于读取 ICM20608 缓冲区数据,其中使用 read 函数读取/dev/iio:device0 文件内容,然后对读取到的内容进行解析,提取出加速度计、陀螺仪、温度的原始值,经过计算得到具体数值

三、测试结果

在这里插入图片描述


http://chatgpt.dhexx.cn/article/l9icmoBQ.shtml

相关文章

嵌入式linux IIO驱动

IIO子系统简介 我们一般搜索IIO子系统&#xff0c;就会发现大多数讲的都是ADC&#xff0c;这是因为IIO就是为ADC类传感器准备的&#xff0c;当然了DAC也是可以的&#xff0c;我们常用的陀螺仪&#xff0c;加速度计&#xff0c;电压/电流测量芯片等内部都是有个ADC&#xff0c;内…

嵌入式Linux设备驱动程序开发指南17(IIO子系统一)——读书笔记

IIO子系统一 十七、IIO子系统(一)17.1 简介17.2 数模转换——DAC实验17.2.1 IIO缓冲区17.2.2 触发器17.2.3 工业I/O事件17.2.4 iio工具17.2.5 LTC2607——DAC模块介绍17.2.5.1 设备树17.2.5.2 LTC2607驱动模块介绍17.2.5.2.1 用作I2C交互的工业框架17.2.5.2.2 用作IIO设备的工业…

LINUX IIO子系统分析之一 IIO子系统概述

从本章开始&#xff0c;我们进行IIO子系统专栏的分析文档&#xff0c;本次IIO子系统专栏分析文档大概包含如下几章&#xff1a; 一、 IIO子系统概述 二、IIO子系统相关数据结构分析 三、iio trigger 介绍 四、iio event介绍 五、iio buffer介绍 六、iio device的注册与注销介绍…

linux iio子系统

//\\ || 系 统&#xff1a;WindowsXP & Ubuntu14.04 || 工 具&#xff1a; Source Insight 3.5 || 作 者&#xff1a;疯狂的三极管 \\// 最近由于工作的需要&#xff0c;接触了Linux iio子系统&#xff0c;对于这个目录其实以前是很少接触&#xff0c;接…

RK3399平台开发系列讲解(IIO子系统)4.38、什么是IIO(Industrial I/O)

平台内核版本安卓版本RK3399Linux4.4Android7.1🚀返回专栏总目录 文章目录 一、什么是IIO?沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍什么是IIO 一、什么是IIO? 工业I/O (Industrial I/O,IIO)是专用于模数转换器 (ADC)和数模转换器(DAC)的内…

iio驱动

1.简介 由于客户提供的板子有一个ti081c的adc芯片所有在这里简单学习一下&#xff1b;   工业I / O&#xff08;IIO&#xff09;是专用于模数转换器&#xff08;ADC&#xff09;和数模转换器&#xff08;DAC&#xff09;的内核子系统。随着越来越多的具有不同代码实现的传感…

iio子系统框架分析

学习目的 iio子系统主要提供对ADC、DAC相关的设备驱动框架。面向的设备包括&#xff1a; 1.ADC芯片&#xff1b;2.DAC芯片&#xff1b;3.温度传感器&#xff1b;4.光感器&#xff1b;5.陀螺仪&#xff1b;6.加速度传感器&#xff1b;7.CDCs&#xff1b;​8.IMUs&#xff1b;9…

Linux设备驱动之IIO子系统——IIO框架及IIO数据结构

由于需要对ADC进行驱动设计,因此学习了一下Linux驱动的IIO子系统。本文翻译自《Linux Device Drivers Development 》--John Madieu,本人水平有限,若有错误请大家指出。 IIO Framework 工业I / O(IIO)是专用于模数转换器(ADC)和数模转换器(DAC)的内核子系统。随着越来…

Linux IIO 子系统简介

IIO 子系统系统框架 而在IIO子系统内部&#xff0c;则主要包括如下四部分的内容&#xff1a; iio buffer用于处理需要进行连续采集的数据&#xff0c;当一个IIO device的各通道数据支持连续采集时&#xff0c;则调用iio buffer模块提供的接口&#xff0c;创建iio buffer用于存…

LINUX IIO子系统分析之五IIO BUFFER子模块实现分析

上一章我们介绍了iio子系统中的iio event模块&#xff0c;本章我们将介绍iio buffer模块&#xff0c;iio buffer主要用于连续数据采集与缓存功能。IIO buffer模块借助IIO DEVICE字符设备文件与应用程序通信&#xff0c;同时借助iio trigger模块与iio device进行交互&#xff0c…

kudu教程(一)——简介

##kudu教程&#xff08;一&#xff09;——简介 学习kudu先从kudu官网开始&#xff0c;进入主页https://kudu.apache.org/ 我们看到的第一句话就是 A new addition to the open source Apache Hadoop ecosystem, Apache Kudu completes Hadoop’s storage layer to enable fas…

kudu介绍:为什么要使用?

前言 近两年&#xff0c;KUDU 在大数据平台的应用越来越广泛。在阿里、小米、网易等公司的大数据架构中&#xff0c;KUDU 都有着不可替代的地位。本文通过分析 KUDU 的设计&#xff0c; 试图解释为什么 KUDU 会被广泛应用于大数据领域&#xff0c;因为还没有研究过 KUDU 的代码…

Kudu 的架构概述

Kudu 的架构概述 目录 Kudu 的架构概述 体系结构概述 Kudu 的特点 Kudu 的概念和术语 体系结构概述 下图显示了一个Kudu集群&#xff0c;其中有三个主机和多个 tablet servers&#xff0c;每个tablet server 都服务于多个tablet Kudu 的特点 特点一&#xff1a;主从架构…

Outline Of Sodoku

第一步&#xff1a;确定所需要实现的目标 1.数独程序的界面&#xff1a; 1.1初始界面边框 1.1.1边框上的Sodoku标题 1.2初始界面背景图片 1.3初始界面开始键 1.4初始界面难度设置 \\数独界面始终为99的矩阵&#xff0c;可以通过改变初始给定格数来改变难度 1.5响应键盘和鼠标指…

Apache Kudu架构

目录 1. Kudu架构 1. Kudu架构 下图显示了一个具有三个master和多个tablet server的Kudu集群 Kudu采用Raft一致性算法&#xff0c;当写入一条数据&#xff0c;被至少一半replica保存&#xff0c;就可以让客户端访问该条数据。但所有的replica的数据会最终一致 Table&#xff…

kudu的相关介绍

目录 前言 背景 概览 数据模型 核心 API 一致性模型 架构 整体架构 数据分区策略 存储 存储设计目标 存储方式 存储实现 读写过程 应用案例 前言 近两年&#xff0c;KUDU 在大数据平台的应用越来越广泛。在阿里、小米、网易等公司的大数据架构中&#xff0c;KUD…

大数据入门-什么是Kudu

目录 一、概念 二、架构 1.Master Server 2.Tablet Server 3.Table 4.Tablet 三、特性 1.重要性 2.易用性 3.优势 4.与传统关系型数据库比较 5.与其他大数据组件比较 四、常用语句 1.建表 1.建普通表 2.建分区表 2.删除表 3.查询数据 4.添加数据 5.更新数据…

Kudu初入门

目录 介绍&#xff1a; 基础架构&#xff1a; 关于Tablet&#xff1a; Kudu与Impala集成 安装Kudu 配置Impala支持Kudu&#xff1a; 使用案例&#xff1a; 创建表&#xff1a; 查询Impala中现有的Kudu表 使用CREATE TABLE AS SELECT语句查询Impala中的任何其他表或来…

kudu-- 分布式数据库

一、前言 近两年&#xff0c;KUDU 在大数据平台的应用越来越广泛。在阿里、小米、网易等公司的大数据架构中&#xff0c;KUDU 都有着不可替代的地位。本文通过分析 KUDU 的设计&#xff0c; 试图解释为什么 KUDU 会被广泛应用于大数据领域&#xff0c;因为还没有研究过 KUDU 的…

Apache Kudu的介绍

一、Apache Kudu的介绍 1.1、背景介绍 在kudu之前&#xff0c;大数据主要以两种方式存储&#xff1b; &#xff08;1&#xff09;静态数据 &#xff1a; 以HDFS引擎作为存储&#xff0c;适用于高吞吐量的离线大数据分析场景。 这类存储的局限性是数据无法进行随机读写。 &…