4 个版本 (2 个破坏性版本)
0.3.1 | 2022年1月28日 |
---|---|
0.3.0 | 2021年5月26日 |
0.2.0 | 2019年12月29日 |
0.1.0 | 2018年11月22日 |
368 在 嵌入式开发 中排名
每月下载量 2,865
在 2 个 Crates 中使用(通过 industrial-io)
675KB
17K SLoC
Rust Linux 工业级 I/O
Rust 库 Crates,用于使用 Linux 工业级 I/O (IIO) 子系统,主要用于在用户空间中从 Linux 系统输入和输出模拟数据。请参阅 IIO Wiki。
当前版本是用户空间 C 库的包装器,libiio。后续版本可能直接访问内核 ABI 接口。
要在应用程序中使用,请将以下内容添加到 Cargo.toml:
[dependencies]
industrial-io = "0.5"
预发布说明
这是该 Crates 的预发布版本。API 正在稳定,但仍在积极开发中,最终发布前可能会有所变化。
此初始开发工作包装了 特定 版本(v0.21)的 libiio。它假定库已在构建系统和目标系统上预安装。
贡献
我们欢迎对该项目的贡献。请记住以下几点
- 请将所有拉取请求针对存储库的
develop
分支。我们更喜欢在发布之间保持 master 分支相对稳定,并在 develop 分支中进行集成和测试。 - 请将单个拉取请求保持在一个主题内。
- 请不要与其他更新一起重新格式化代码。任何代码格式化都应在单独的提交或 PR 中进行。格式化规范在
.rustfmt.toml
中,目前需要 nightly 发布。
我们特别欢迎任何关于不同 IIO 设备的调整或反馈。如果您测试、工作或遇到特定 IIO 硬件或驱动程序的任何问题,请告诉我们。
也欢迎为不同硬件提供新的示例。
最新消息
总体来说,我们正在进行将此软件包提升至生产质量的努力。这包括:
- 对 libiio API 的全面覆盖——或者尽可能多的覆盖。
- 一套完整的示例。
- 单元测试和持续集成。
要了解此项目的最新公告,请关注:
Twitter: @fmpagliughi
0.5.2 版本新特性
- PR #26 - 添加了 'utilities' 功能,可以关闭二进制应用程序的构建(即仅构建库)。
- #21 - 更新 nix 依赖以避免链接到有漏洞的版本。
- 更新了
clap
和ctrlc
包的依赖。
0.5.1 版本新特性
iio_info_rs
工具现在支持网络和 URI 环境。- PR #19 macOS 构建在搜索 Homebrew 框架(libiio 库)时区分了 Intel 和非 Intel 构建。
- PR #20 修复了一些 clippy 建议。特别是更清洁的原始指针转换等。
基础
C libiio 库为 Linux 工业级 I/O 子系统提供了一个用户空间接口,用于连接(可能是高速)模拟硬件,例如 A/D 转换器、D/A 转换器、加速度计、陀螺仪等。此软件包在 C 库周围提供了一个相对薄的、安全的包装。
要访问任何物理设备,您必须首先创建一个 Context
,它可以使用多个后端之一,其中最常见的是:
- "local" - 用于访问本地机器上的硬件
- "network" - 用于访问运行 IIO 网络守护进程的远程机器上的硬件,
iiod
还有 USB 和串行设备的后端。
默认上下文将使用本地机器,但可以通过 IIOD_REMOTE
环境变量来覆盖,该变量在设置时给出网络上下文的主机名。
创建后,给定上下文可以查询包含的设备及其提供通道的信息。上下文、设备和它们的通道包含 属性,用于读取和设置数据以及收集它的参数。
可以通过 触发 设备开始数据采样和/或输出,这些设备可以是硬件或软件定时器,用于收集定期数据,也可以是基于外部事件(如 GPIO 输入)的触发器。请注意,某些设备也可以自我触发。
该库还可以使用内核中的环形缓冲区以更高的速度收集数据,并减少抖动。
在 examples/ 目录中有许多应用。
硬件和驱动程序的特性
Linux IIO 子系统和 libiio 抽象了大量具有不同功能集的不同类型的硬件。在硬件的不同功能和为它们编写的驱动程序之间,当开始使用新设备时,应用程序通常会遇到奇怪和意外的结果。示例应用程序不保证与所有不同类型的硬件一起工作。但它们为最常见的使用提供了一个相当好的模板。在使用新设备时通常需要进行一些修改和实验。
实现细节
Rust 工业级 I/O 库是 C libiio 的一个相对薄的包装,添加了一些功能,使其更具 Rust 风格。
库包装
要对libiio进行任何操作,应用程序必须首先创建一个Context
来操作本地设备上的硬件(即本地上下文),或者通过网络连接与远程设备上的硬件进行通信。创建本地上下文与其他库操作相比是一项较为耗时的操作,因为它将扫描硬件并在内存中建立本地表示。
因此,上下文是创建时的硬件快照。任何在上下文外部添加的硬件(例如,另一个进程创建新的hrtimer)将不会反映在其中。需要创建新的上下文来重新扫描硬件。
但是,查找硬件非常高效,因为它只需在上下文中的数据结构中进行搜索。像ctx.find_device('adc0')
这样的调用只需在硬件设备列表中查找字符串匹配,而由底层库调用返回的指针只是对现有上下文数据结构的引用。
当声明新的Rust硬件结构(如Device
或Channel
)时,不会创建或销毁任何内容。因此,Rust结构可以很容易地通过复制库结构的指针进行克隆。
Rust的Context
对象只是对InnerContext
的引用计数智能指针。这使得在不同的对象(设备、通道等)之间共享C上下文以及管理其生命周期变得容易。实际上,InnerContext
封装了C上下文。当它超出作用域时,通常是当最后一个引用消失时,C上下文将被销毁。克隆InnerContext
会创建C库上下文的完整副本。
这导致人们对“克隆”Context产生了一些混淆。因为Context
只是对内部上下文的线程安全、引用计数的智能指针,克隆它只是创建对现有内部/C上下文的新共享指针。这使得在多个从它创建的Device
对象之间共享上下文并保证其生命周期变得容易。实际上,克隆Context
并将克隆发送到另一个线程将在这两个线程之间共享InnerContext
(以及C上下文)。
然而,当使用单独的线程来管理每个设备时,为每个线程创建一个完全独立的C上下文可能更有效率。为此,需要创建一个“深度”克隆的Context
。这只是一个InnerContext
的克隆,并在这个内部上下文周围创建新的智能指针。因此,这是一个克隆的InnerContext
,它实际上复制了C库上下文。有关详细信息,请参阅下一节。
线程安全
此库的早期版本(v0.4.x及之前)是在错误地认为底层libiio不是线程安全的情况下编写的。有关该库的一些公共信息有点误导,但根据库维护者的反馈和额外的发布信息,线程限制正逐渐从这个库中解除。
从v0.5版本开始,现在可以做到以下这些
InnerContext
,实际上封装了C库上下文,现在是Sync
和Send
。它可以在线程之间共享。Context
现在通过一个Arc
指向其InnerContext
实现。因此,这些对内部上下文的引用可以被发送到不同的线程,并且这些线程可以共享相同的上下文。- 持有
Context
引用的Device
对象现在可以被Send
移动到创建上下文的不同线程。 - 目前,
Channel
和Buffer
对象仍然是!Send
和!Sync
,需要与Device
同步存在于同一线程中,但这些限制可能随着我们了解哪些特定操作不是线程安全的而放宽。 Buffer::refill()
函数现在接受一个对 self 的可变引用,&mut self
,为放宽缓冲区的线程限制做准备。缓冲区绝对不能同时被两个不同的线程填充。
即使有了这些新的线程功能,当由 IIO 上下文描述的物理设备可以被不同的线程操作时,通常仍然希望为每个线程使用一个单独的 Context
实例。有两种方法可以实现这一点
- 简单地在每个线程中创建一个新的
Context
对象,使用相同的 URI 等等。这些可能并不完全相同,如果在此两个上下文创建之间添加或删除了新的硬件。但这可能是罕见的。 - 创建一个上下文,然后克隆其
InnerContext
对象并将其发送到其他线程。然后每个对象都可以用来在该线程中创建一个新的Context
实例。
第二个选项效率更高,并且可以以多种方式完成。
一种方法是对上下文进行“深度”克隆并将其发送到另一个线程
let ctx = Context::new()?;
let thr_ctx = ctx.try_deep_clone()?;
thread::spawn(move || {
let dev = thr_ctx.find_device("somedevice")?
// ...
});
这会复制一个内部上下文,该上下文克隆了 C 库上下文。然后它将唯一的引用发送到另一个线程,给予它对那个 C 上下文的独占访问。
或者,为了明确克隆内部上下文,可以这样做
let ctx = Context::new()?;
let cti = ctx.try_clone_inner()?;
thread::spawn(move || {
let thr_ctx = Context::from(cti);
let dev = thr_ctx.find_device("somedevice")?
// ...
});
在这里,内部上下文被克隆到 cti
对象中,该对象被移动到线程中并被消耗以创建一个新的上下文对象,thr_ctx
。这个程序在库的早期版本中是必需的,在 v0.5.0 之前。
另一种在线程和进程之间共享设备的方法是在本地机器上运行 IIO 网络守护进程,并允许它控制本地上下文。然后多个客户端应用程序可以使用网络上下文从 localhost 访问它。守护进程将序列化对设备的访问,并让多个客户端共享它。客户端中的每个线程仍然需要一个单独的网络上下文。
需要注意的是,尽管 Rust 可以在单个进程中强制执行线程安全,但整个 IIO 子系统是对其他进程开放的。不同的设备和驱动程序可能会以不同的方式公开访问。确保使用 IIO 的进程之间不相互干扰的责任在于系统设计者。
对 crate 进行测试
用户空间IIO库的一个优点是,如果您在较新的Linux主机上进行开发,您可以开始实验而无需在板上进行开发。您可以在嵌入式板上运行IIO服务器守护进程,然后使用此crate通过网络连接与其通信。当您的应用程序工作正常时,然后您可以为其目标板编译它,进行本地测试,并部署。
或者,您可以在开发主机上使用模拟的“虚拟”环境进行测试。这是一个模拟多个设备的内核模块。它可以从主机机器的本地上下文中使用来进行一些库的初始开发和测试。以下是如何将其加载到内核中的详细信息。
BeagleBone
可以使用多个制造商的板来轻松尝试工业I/O子系统。BeagleBone Black和Green具有板载AM335X A/D转换器,而BeagleBone AI有一个STM触摸屏芯片,可以用于模拟输入。
BeagleBones的IIO库支持单个和缓冲采样,但没有外部触发支持。最近针对板的Debian 9.x IoT发行版已经将IIO编译进内核,可以直接使用 - 尽管用户空间库libiio应该升级(见下文)。
Linux开发主机
一些现代Linux发行版,如Ubuntu 18.04,为内核编译了IIO模块,如dummy上下文。它们可以被加载到内核中,如下所示:
$ sudo modprobe iio_dummy
$ sudo modprobe iio_trig_hrtimer
一旦加载,可以使用configfs创建设备和触发器。此存储库中包含的load_dummy.sh脚本可用于加载模块和配置设备,适用于基本实验或运行单元测试。
$ sudo ./load_dummy.sh
macOS
该crate也与macOS兼容,但仅提供网络上下文。libiio框架可以从源代码构建或从社区homebrew公式安装
brew install tfcollins/homebrew-formulae/libiio
安装C库
在目标板上安装libiio v0.21。如果您在Linux主机上进行开发,请在那里安装相同版本的库,以便您可以在主机上进行一些开发:
检查目标版本
如果您使用BeagleBone Black,旧库可能已与发行版一起发货。安装板的最新发行版。(这已在Debian 9.5 2018-10-07 4GB SD IoT上进行测试)。
登录到板上并检查版本
$ iiod --version
0.21
如果此版本低于0.21,请删除Debian包并从源安装。
首先,删除现有的库和实用程序
$ sudo apt-get purge libiio-utils libiio0
从源代码构建
安装构建的先决条件
$ sudo apt-get install cmake flex bison libxml2-dev libserialport-dev
然后下载库源代码并构建
$ cd /tmp
$ wget https://github.com/analogdevicesinc/libiio/archive/v0.21.tar.gz
$ tar -xf v0.21.tar.gz
$ cd libiio-0.21/
$ mkdir build ; cd build
$ cmake .. && make && sudo make install
然后检查安装的版本是否正确
$ iiod --version
0.21
构建Rust crate
这是一个围绕C库的相当标准的Rust包装项目。它包含一个不安全的"-sys"子crate,用于包装C库API,以及主crate中的高级、安全的Rust库。该crate还包含通过库与IIO设备接口的实用程序。要构建库和实用程序
$ cargo build
库可以以较少的依赖项构建。仅构建库
$ cargo build --lib --no-default-features
还有许多示例应用程序。它们都可以使用以下方式构建
$ cargo build --examples