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

news/2024/12/2 21:03:20/

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://www.ppmy.cn/news/905124.html

相关文章

STM32MP157驱动开发——USB设备驱动

STM32MP157驱动开发——USB设备驱动 一、简介1.电气属性2.USB OTG3.STM32MP1 USB 接口简介4.Type-C 电气属性 二、USB HOST 驱动开发1.USB HOST 驱动编写2.配置 PHY 控制器3.配置usbh_ehci 三、USB HOST 测试1.鼠标键盘驱动使能2.U盘驱动 四、USB OTG驱动开发1.USB OTG 控制器节…

STM32MP157驱动开发——Linux块设备驱动

STM32MP157驱动开发——Linux块设备驱动 一、简介二、驱动开发1.使用请求队列的方式2.测试①3.不使用请求队列的方式4.测试② 参考文章&#xff1a;【正点原子】I.MX6U嵌入式Linux驱动开发——Linux 块设备驱动 一、简介 之前学习的都是关于字符设备的驱动&#xff0c;包括 pl…

Shiro反序列化漏洞(CVE-2016-4437)+docker靶场+工具利用

一、Shiro反序列化漏洞-CVE-2016-4437原理 将java对象转换为字节序列&#xff08;json/xml&#xff09;的过程叫序列化&#xff0c;将字节序列&#xff08;json/xml&#xff09;恢复为java对象的过程称为反序列化。 Shiro框架提供了“记住我”的功能&#xff0c;用户登陆成功…

习题1.25

对吗?实践出真知,运行看看。代码如下。 (defn square [x] (* x x))(defn fast-expt[b n](println "call iter" n)(cond (= 1 n) b(= 2 n) (square b)(even? n) (square (fast-expt b (/ n 2))):else (* b (fast-expt b (- n 1)))))(defn expmod [base exp m](mod…

python贴吧顶贴_贴吧回复app-贴吧回复(贴吧顶贴神器手机版)v3.2.5-西西软件下载...

贴吧回复(贴吧顶贴神器手机版)&#xff0c;贴吧回复app是一款手机贴吧自动回复软件&#xff0c;可以设置自动回复内容以及间隔时间&#xff0c;可以设置想要回复的贴吧&#xff0c;让你成为贴吧第一回复达人。贴吧回复app推荐使用专用回帖文字诸如顶帖&#xff0c;DD&#xff0…

贴一张标签让你的手机支持NFC

NFC技术很不错&#xff0c;熟悉的人也不少&#xff0c;市场上具备NFC功能的手机却只有寥寥数款。巴克莱银行最近引进了一种名为PayTag的标签&#xff0c;贴着这个标签的任何设备都能实现NFC付款。 在此之前&#xff0c;巴克莱银行推出的借记卡和信用卡已经支持近距离无线支付。…

自制贴纸图案大全图片_贴纸的制作方法

本发明涉及装饰贴纸领域&#xff0c;尤其涉及一种贴纸。 背景技术&#xff1a; 贴纸作为包装用品中的一种&#xff0c;以其方便的印刷方式和粘贴方式受到商家的喜爱。但是在粘贴过程中&#xff0c;需要将贴纸从撕离部上分离出来。为了放置贴纸和底层单独的剥离&#xff0c;现在…

贴海报

题目描述 Bytetown城市要进行市长竞选,所有的选民可以畅所欲言地对竞选市长的候选人发表言论。为了统一管理,城市委员会为选民准备了一个张贴海报的electoral墙。 张贴规则如下: electoral墙是一个长度为N个单位的长方形,每个单位记为一个格子; 所有张贴的海报的高度必…