当前位置:首页 > 公众号精选 > 嵌入式客栈
[导读]关注、星标嵌入式客栈,精彩及时送达[导读]朋友们,大家好,我是逸珺。今天分享一下如何在用户空间操作IIO设备。IIO设备能实现很多有价值的应用,有兴趣的一起来看看~什么是IIO设备IIO是IndustrialI/O的缩写,是Linux下为工业输入输出所设计的子系统。其主要目的是为...

关注、星标嵌入式客栈,精彩及时送达[导读] 朋友们,大家好,我是逸珺。

今天分享一下如何在用户空间操作IIO设备。IIO设备能实现很多有价值的应用,有兴趣的一起来看看~

什么是IIO设备

IIO是 Industrial I/O 的缩写,是Linux下为工业输入输出所设计的子系统。其主要目的是为模数转换 (ADC) 或数模转换 (DAC) 或两者兼而有之的设备提供设备驱动支持。Linux下原来有Hwmon以及输入子系统,但这两个子系统不能很好的涵盖上面需求。而IIO子系统就是为了填补这一空白而设计的。

什么是Hwmon?针对用于监测和控制系统本身的低采样率传感器,如风扇速度控制或温度测量。

而输入子系统(Input subsystem), 顾名思义,主要抽象人机交互输入设备(键盘、鼠标、触摸屏)。当然,从这些描述来看,这两个需求与 IIO 之间存在相当大的重叠。

而IIO子系统则主要管理抽象这些类别的设备:

  • 模数转换器(ADC)
  • 加速度计(Accelerometers)
  • 陀螺仪(Gyros)
  • 惯性测量单元 IMUs)
  • 电容数字转换器 (CDC)
  • 压力传感器
  • 颜色、光线和接近传感器
  • 温度传感器
  • 磁力计(Magnetometers)
  • 数模转换器(DAC)
  • 直接数字合成器(DDS)
  • 锁相环(PLL)
  • 可变/可编程增益放大器(VGA、PGA)
  • ....
此类设备,一般会通过I2C/SPI总线连接到处理器,SPI/I2C通常用来传输控制信息,而这些设备的输入输出应用数据则是通过其他的接口与处理器进行通信,比如采用LVDS。以AD9467为例:

三线制SPI则是控制信息,用于配置ADC的寄存器,而采样数据则是通过LVDS上传给处理器的。

IIO设备长什么样?

IIO设备一般也会在/dev下创建一个设备文件,比如:

然后在sys下也会创建文件,比如在/sys/bus下就会创建一条名为iio的总线:

进入到iio之后:

在devices下,则有:

如果有多个iio设备,则会这样编号:

iio:device0  iio:device1  iio:device2  ......
进入到iio:device0后

则可以看到有mode,scale,of_node,frequeceny等属性。进到scan_elements下:

scan_elements用于描述扫描样本的相关属性:

  • in_voltage0_en:使能控制,设置为1为使能,为0则关闭
  • in_voltage0_index :索引
  • in_voltage0_type:采样类型,本设备该值为le:S16/16>>0,表示是小端有符号整型
buffer中的属性为:

  • data_available :有多少数量的数据准备好了
  • enable :激活缓冲区捕获,设置为1则开始缓冲区捕获
  • length:缓冲区可以存储的数据样本总数
  • length_align_bytes: 对齐要求,用于设置DMA的对齐要求。
  • watermark:此属性从内核版本 4.2 开始可用。它是一个正数,指定阻塞读取应等待多少个扫描元素。如果使用轮询,它将阻塞直到达到watermark设定值。只有当watermark大于请求的读取量时才有意义。它不会影响非阻塞读取。
在用户空间读取iio设备的数据,有扫描方法或者触发方法,这里主要分享一下连续扫描读取。

  • 首先将scan_elements中in_voltage0_en设置为1
root@idaq:/sys/bus/iio/devices/iio:device0/scan_elements# echo 1 > in_voltage0_en
root@idaq:/sys/bus/iio/devices/iio:device0/scan_elements# cat in_voltage0_en
1
  • 然后将buffer中的enable,也设置为1。驱动就开始工作了。marked complete 表示一次DMA传输结束。
root@idaq:/sys/bus/iio/devices/iio:device0/buffer# echo 1 > enable
iio iio:device0: iio_dma_buffer_enable
dma-axi-dmac 43c20000.axi_dmac: vchan (ptrval): txd (ptrval)[2]: submitted
dma-axi-dmac 43c20000.axi_dmac: txd (ptrval)[2]: marked complete
  • 然后就可以标准文件操作直接读取/dev/iio:device0了

代码实现

有了前面的介绍,就有具体实现的思路了。这里以QT为例,设计一个类先进行文件操作,然后不停读取文件即可。

下面是头文件:

#ifndef _IIO_DEVICE_H_
#define _IIO_DEVICE_H_
#include 
#include 
#include 
//继承自QThread
class IIODevice : public QThread
{
    Q_OBJECT
public:
    explicit IIODevice(QObject *parent = 0,
                       QString fName="iio:device0",
                       QByteArray *pBuffer=nullptr,
                       QMutex *pMutex=nullptr)
;
    ~IIODevice();

    int  openDevice(void);
    int  closeDevice(void);
    void startSample();
    void stopSample(void);
    int  readDevice(unsigned char * buffer,int size);
    bool isStarted(void);

public slots:

protected:
    void run();

private:
    int fd;
    QString fileName;
    bool m_abort;
    //用于缓存数据
    QByteArray *buffer;
    QMutex *mutex;
    bool m_started;
};

#endif
实现文件如下:

IIODevice::IIODevice(QObject *parent,QString fName,QByteArray *pBuffer,QMutex *pMutex) :
    QThread(parent),
    fileName(fName),
    buffer(pBuffer),
    mutex(pMutex)
{  
    m_abort = false;
    m_started = false;  
}

int IIODevice::openDevice(void)
{
    //使能in_voltage0_en
    QString cmdStr = "echo 1 > /sys/bus/iio/devices/" fileName "/scan_elements/in_voltage0_en";
    QByteArray cmdBuffer = cmdStr.toLocal8Bit();
    char * cmd = cmdBuffer.data();
    system(cmd);

    //使能采集
    cmdStr = "echo 1 > /sys/bus/iio/devices/" fileName "/buffer/enable";
    cmdBuffer = cmdStr.toLocal8Bit();
    cmd = cmdBuffer.data();
    system(cmd);

    QString path = "/dev/" fileName;
    QFile * file = new QFile();
    file->setFileName(path);
    if( !file->exists() ){
        qDebug() << "file does not exist";
        return -1;
    }

    //O_NONBLOCK方式打开文件
    fd = open(path.toUtf8().data(), O_RDONLY|O_NONBLOCK);
    if( fd==-1 ){
        qDebug() << "can not open file";
        return -2;
    }

    return 0;
}

int IIODevice::closeDevice(void)
{
    QString cmdStr;
    QByteArray cmdBuffer;
    //1.禁止采样
    cmdStr = "echo 0 > /sys/bus/iio/devices/" fileName "/buffer/enable";
    cmdBuffer = cmdStr.toLocal8Bit();
    char * cmd = cmdBuffer.data();
    system(cmd);

    //2.禁止in_voltage0_en
    cmdStr = "echo 0 > /sys/bus/iio/devices/" fileName "/scan_elements/in_voltage0_en";
    cmdBuffer = cmdStr.toLocal8Bit();
    cmd = cmdBuffer.data();
    
    system(cmd);
    //3.关闭文件
    if(fd) {
     close(fd);
     fd = -1;    
    }   
  
    return 0;
}

int IIODevice::readDevice(unsigned char * buffer,int size)
{
    int length = 0;
    length = read(fd,buffer,size);
    return length;
}

IIODevice::~IIODevice(void)
{
  if(fd)
    close(fd);
}

void IIODevice::startSample()
{
  mutex->lock();
  m_abort = false;
  m_started = true;
  if(!buffer->isEmpty())
     buffer->remove(0,buffer->size());
  mutex->unlock();
  openDevice();
  start();
}

bool IIODevice::isStarted()
{
  return m_started;
}

void IIODevice::stopSample()
{
  mutex->lock();
  m_abort = true;
  m_started = false;
  closeDevice();
  mutex->unlock();
    
  //完全关闭线程的写法
  wait();
}

#define TEMP_BUFFER_SIZE 4096
//开辟线程用于读取
void IIODevice::run()
{
  int bytes = 0;
  unsigned char devBuf[TEMP_BUFFER_SIZE];
  while(1) {
      bytes = 4096;//假定内部buffer为4096
      while(bytes==4096)
      {
        bytes = readDevice(devBuf,TEMP_BUFFER_SIZE);
        mutex->lock();
        buffer->append((const char *)devBuf,bytes);
        mutex->unlock();
      }

      if (m_abort)
        return;
      //周期性扫描
      usleep(600);
  }
}
QMutex用于线程间数据保护,由于该类是一个线程类,实际使用的时候可能是另一个线程读取IIO设备的采集数据,用于传输或者后续应用处理,两个线程操作同一个QByteArray对象,存在并发竞态,所以需要做互斥访问保护。

如此一来,外界的物理信号就经由ADC芯片,进入Linux内核设备iio:device0,再由用户空间的IIODevice类所例化的对象,传递到用户空间,从而可以在Linux下实现采样应用了,整个信号传递可以用下面这个图来描述。

总结一下

IIO设备在很多工业仪器类非常有用,如前所说,不仅仅能实现AD采样,还可以实现DA,DDS等很多非常有应用价值的设备。要用好IIO设备,可能涉及到设备驱动、信号链设计、用户空间应用程序编写等等技术。本文以一个AD采样IIO设备为例,分享一下如何从用户空间访问控制IIO设备,希望对有兴趣的朋友们有所帮助。

—— The End —


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭