Linux設備驅動之IIO子系統——IIO框架及IIO數據結構

来源:https://www.cnblogs.com/yongleili717/archive/2019/04/21/10744252.html
-Advertisement-
Play Games

由於需要對ADC進行驅動設計,因此學習了一下Linux驅動的IIO子系統。本文翻譯自《Linux Device Drivers Development 》--John Madieu,本人水平有限,若有錯誤請大家指出。 IIO Framework 工業I / O(IIO)是專用於模數轉換器(ADC)和 ...


  由於需要對ADC進行驅動設計,因此學習了一下Linux驅動的IIO子系統。本文翻譯自《Linux Device Drivers Development 》--John Madieu,本人水平有限,若有錯誤請大家指出。

IIO Framework 

  工業I / O(IIO)是專用於模數轉換器(ADC)和數模轉換器(DAC)的內核子系統。隨著越來越多的具有不同代碼實現的感測器(具有模擬到數字或數字到模擬,功能的測量設備)分散在內核源上,收集它們變得必要。這就是IIO框架以通用的方式所做的事情。自2009年以來,Jonathan Cameron和Linux-IIO社區一直在開發它。

 

  加速度計,陀螺儀,電流/電壓測量晶元,光感測器,壓力感測器等都屬於IIO系列器件。

  IIO模型基於設備和通道架構:

    l 設備代表晶元本身。它是層次結構的頂級。

    l 通道代表設備的單個採集線。設備可以具有一個或多個通道。例如,加速度計是具有  三個通道的裝置,每個通道對應一個軸(X,Y和Z)。

  IIO晶元是物理和硬體感測器/轉換器。它作為字元設備(當支持觸發緩衝時)暴露給用戶空間,以及包含一組文件的sysfs目錄條目,其中一些文件代表通道。單個通道用單個sysfs文件條目表示。

  下麵是從用戶空間與IIO驅動程式交互的兩種方式:

    l /sys/bus/iio/iio:deviceX/:表示感測器及其通道

    l /dev/iio:deviceX: 表示導出設備事件和數據緩衝區的字元設備

 

 IIO框架架構和佈局 

  上圖顯示瞭如何在內核和用戶空間之間組織IIO框架。 驅動程式使用IIO核心公開的一組工具和API來管理硬體並向IIO核心報告處理。 然後,IIO子系統通過sysfs介面和字元設備將整個底層機制抽象到用戶空間,用戶可以在其上執行系統調用。

  IIO API分佈在多個頭文件中,如下所示:

 #include <linux/iio/iio.h>    /* mandatory */
#include <linux/iio/sysfs.h>  /* mandatory since sysfs is used */
#include <linux/iio/events.h> /* For advanced users, to manage iio events */
#include <linux/iio/buffer.h> /* mandatory to use triggered buffers */
#include <linux/iio/trigger.h>/* Only if you implement trigger in your driver (rarely used)*/

  在以下文章中,我們將描述和處理IIO框架的每個概念,例如

    遍歷其數據結構(設備,通道等)

    觸發緩衝支持和連續捕獲,以及其sysfs介面

    探索現有的IIO觸發器

    以單次模式或連續模式捕獲數據

    列出可用於幫助開發人員測試其設備的可用工具

 

(一):IIO data structures:IIO數據結構

  IIO設備在內核中表示為struct iio_dev結構體的一個實例,並由struct iio_info結構體描述。 所有重要的IIO結構都在include/linux/iio/iio.h中定義。

  iio_dev structure(iio_dev結構)

  該結構代表IIO設備,描述設備和驅動程式。 它告訴我們:

  l  設備上有多少個通道?

  l  設備可以在哪些模式下運行:單次,觸發緩衝?

  l  這個驅動程式可以使用哪些hooks鉤子?

struct iio_dev {
   [...]
   int modes;
   int currentmode;
   struct device dev;
   struct iio_buffer *buffer;
   int scan_bytes;
   const unsigned long *available_scan_masks;
   const unsigned long *active_scan_mask;
   bool scan_timestamp;
   struct iio_trigger *trig;
   struct iio_poll_func *pollfunc;
   struct iio_chan_spec const *channels;
   int num_channels;
   const char *name;
   const struct iio_info *info;
   const struct iio_buffer_setup_ops *setup_ops;
   struct cdev chrdev;

};

完整的結構在IIO頭文件中定義。 我們將不感興趣的欄位在此處刪除。 

   modes: 這表示設備支持的不同模式。 支持的模式有:

     INDIO_DIRECT_MODE表示設備提供的sysfs介面。

     INDIO_BUFFER_TRIGGERED表示設備支持硬體觸發器。使用iio_triggered_buffer_setup()函數設置觸發緩衝區時,此模式會自動添加到設備中。

    INDIO_BUFFER_HARDWARE表示設備具有硬體緩衝區。

    INDIO_ALL_BUFFER_MODES是上述兩者的聯合。

  l  currentmode: 這表示設備實際使用的模式。

  l  dev: 這表示IIO設備所依賴的struct設備(根據Linux設備型號)。

  l  buffer: 這是您的數據緩衝區,在使用觸發緩衝區模式時會推送到用戶空間。 使用iio_triggered_buffer_setup函數啟用觸發緩衝區支持時,它會自動分配並與您的設備關聯。

  l  scan_bytes: 這是捕獲並饋送到緩衝區的位元組數。 當從用戶空間使用觸發緩衝區時,緩衝區應至少為indio-> scan_bytes位元組大。

  l  available_scan_masks: 這是允許的位掩碼的可選數組。 使用觸發緩衝器時,可以啟用通道捕獲並將其饋入IIO緩衝區。 如果您不想允許某些通道啟用,則應僅使用允許的通道填充此數組。 以下是為加速度計(帶有X,Y和Z通道)提供掃描掩碼的示例:

/*
 * Bitmasks 0x7 (0b111) and 0 (0b000) are allowed.
 * It means one can enable none or all of them.
 * one can't for example enable only channel X and Y
 */

static const unsigned long my_scan_masks[] = {0x7, 0};
indio_dev->available_scan_masks = my_scan_masks;

l  active_scan_mask: 這是啟用通道的位掩碼。 只有來自這些通道的數據能被推入緩衝區。 例如,對於8通道ADC轉換器,如果只啟用第一個(0),第三個(2)和最後一個(7)通道,則位掩碼將為0b10000101(0x85)。 active_scan_mask將設置為0x85。 然後,驅動程式可以使用for_each_set_bit巨集遍歷每個設置位,根據通道獲取數據,並填充緩衝區。

l  scan_timestamp: 這告訴我們是否將捕獲時間戳推入緩衝區。 如果為true,則將時間戳作為緩衝區的最後一個元素。 時間戳大8位元組(64位)。

l  trig: 這是當前設備觸發器(支持緩衝模式時)。 

l  pollfunc:這是在接收的觸發器上運行的函數。 

l  channels: 這表示通道規範結構,用於描述設備具有的每個通道。

l  num_channels: 這表示通道中指定的通道數。

l  name: 這表示設備名稱。

l  info: 來自驅動程式的回調和持續信息。

l  setup_ops: 啟用/禁用緩衝區之前和之後調用的回調函數集。 這個結構在include / linux / iio / iio.h中定義,如下所示:

struct iio_buffer_setup_ops {
    int (* preenable) (struct iio_dev *);
    int (* postenable) (struct iio_dev *);
    int (* predisable) (struct iio_dev *);
    int (* postdisable) (struct iio_dev *);
    bool (* validate_scan_mask) (struct iio_dev *indio_dev,
                                 const unsigned long *scan_mask);
};

l  setup_ops: 如果未指定,則IIO內核使用drivers / iio / buffer / industrialio-triggered-buffer.c中定義的預設iio_triggered_buffer_setup_ops。 

l  chrdev: 這是由IIO核心創建的關聯字元設備。 

用於為IIO設備分配記憶體的函數是iio_device_alloc():

struct iio_dev * iio_device_alloc(int sizeof_priv) 
///struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv)
/* Resource-managed iio_device_alloc()*/
/*Managed iio_device_alloc. iio_dev allocated with this function is automatically freed on driver detach.
If an iio_dev allocated with this function needs to be freed separately, devm_iio_device_free() must be used. */

  dev是為其分配iio_dev的設備,sizeof_priv是用於為任何私有結構分配的記憶體空間。 這樣,傳遞每個設備(私有)數據結構非常簡單。 如果分配失敗,該函數返回NULL:

 struct iio_dev *indio_dev;
struct my_private_data *data;
indio_dev = iio_device_alloc(sizeof(*data));
if (!indio_dev)
          return -ENOMEM;
/*data is given the address of reserved momory for private data */
data = iio_priv(indio_dev);

 

  在分配IIO設備存儲器之後,下一步是填充不同的欄位。 完成後,必須使用iio_device_register函數向IIO子系統註冊設備:

 

int iio_device_register(struct iio_dev *indio_dev)
       //devm_iio_device_register(dev, indio_dev)
/* Resource-managed iio_device_register() */

 

  在執行此功能後,設備將準備好接受來自用戶空間的請求。 反向操作(通常在釋放函數中完成)是iio_device_unregister():

 

void iio_device_unregister(struct iio_dev *indio_dev)
// void devm_iio_device_unregister(struct device * dev, struct iio_dev * indio_dev)

  一旦取消註冊,iio_device_alloc分配的記憶體可以用iio_device_free釋放:

void iio_device_free(struct iio_dev *iio_dev)
// void devm_iio_device_free(struct device * dev, struct iio_dev * iio_dev)

  給定IIO設備作為參數,可以通過以下方式檢索私有數據:

 

 struct my_private_data *the_data = iio_priv(indio_dev);

 

 

iio_info structure:iio_info結構體

  struct iio_info結構用於聲明IIO內核使用的鉤子,以讀取/寫入通道/屬性值:

struct iio_info {
           struct module *driver_module;
            const struct attribute_group *attrs;
            int (*read_raw)(struct iio_dev *indio_dev,
               struct iio_chan_spec const *chan,
               int *val, int *val2, long mask);

            int (*write_raw)(struct iio_dev *indio_dev,

                struct iio_chan_spec const *chan,

                int val, int val2, long mask);
             [...]

};

l  driver_module: 這是用於確保chrdev正確擁有的模塊結構,通常設置為THIS_MODULE。

l  attrs: 這表示設備屬性。

l  read_raw: 這是用戶讀取設備sysfs文件屬性時的回調運行。 mask參數是一個位掩碼,它允許我們知道請求了哪種類型的值。 channel參數讓我們知道相關的通道。 它可以是採樣頻率,用於將原始值轉換為可用值的比例,或原始值本身。

l  write_raw: 這是用於將值寫入設備的回調。 例如,可以使用它來設置採樣頻率。

  以下代碼顯示瞭如何設置struct iio_info結構:

static const struct iio_info iio_dummy_info = {
    .driver_module = THIS_MODULE,
    .read_raw = &iio_dummy_read_raw,
    .write_raw = &iio_dummy_write_raw,
[...]

/*
 * Provide device type specific interface functions and
 * constant data. 提供設備類型特定的介面功能和常量數據。
 */
indio_dev->info = &iio_dummy_info;

 

IIO channels:IIO通道

通道代表單條採集線。 例如加速度計具有3個通道(X,Y,Z),因為每個軸代表單個採集線。 struct iio_chan_spec是表示和描述內核中單個通道的結構:

      

 struct iio_chan_spec {
        enum iio_chan_type type;
        int channel;
        int channel2;
        unsigned long address;
        int scan_index;
        struct {
            charsign;
            u8 realbits;
            u8 storagebits;
            u8 shift;
            u8 repeat;
            enum iio_endian endianness;
        } scan_type;
        long info_mask_separate;
        long info_mask_shared_by_type;
        long info_mask_shared_by_dir;
        long info_mask_shared_by_all;
        const struct iio_event_spec *event_spec;
        unsigned int num_event_specs;
        const struct iio_chan_spec_ext_info *ext_info;
        const char *extend_name;
        const char *datasheet_name;
        unsigned modified:1;
        unsigned indexed:1;
        unsigned output:1;
        unsigned differential:1;

    };

  各個參數意義:

l  type: 這指定了通道的測量類型。 在電壓測量的情況下,它應該是IIO_VOLTAGE。 對於光感測器,它是IIO_LIGHT。 對於加速度計,使用IIO_ACCEL。 所有可用類型都在include / uapi / linux / iio / types.h中定義,如enum iio_chan_type。 要為給定轉換器編寫驅動程式,請查看該文件以查看每個通道所屬的類型。

l  channel: 這指定.indexed設置為1時的通道索引。

l  channel2: 這指定.modified設置為1時的通道修飾。

l  modified: 這指定是否將修飾符應用於此通道屬性名稱。 在這種情況下,修飾符設置在.channel2中。 (例如,IIO_MOD_X,IIO_MOD_Y,IIO_MOD_Z是關於xyz軸的軸向感測器的修改器)。 可用修飾符列表在內核IIO頭中定義為枚舉iio_modifier。 修飾符只會破壞sysfs中的通道屬性名稱,而不是值。

l  indexed: 這指定通道屬性名稱是否具有索引。 如果是,則在.channel欄位中指定索引。

l  scan_index and scan_type: 當使用緩衝區觸發器時,這些欄位用於標識緩衝區中的元素。 scan_index設置緩衝區內捕獲的通道的位置。 具有較低scan_index的通道將放置在具有較高索引的通道之前。 將.scan_index設置為-1將阻止通道進行緩衝捕獲(scan_elements目錄中沒有條目)。

  暴露給用戶空間的通道sysfs屬性以位掩碼的形式指定。 根據共用信息,可以將屬性設置為以下掩碼之一:

l  info_mask_separate 將屬性標記為特定於此通

l  info_mask_shared_by_type 將該屬性標記為由相同類型的所有通道共用。 導出的信息由相同類型的所有通道共用。

l  info_mask_shared_by_dir 將屬性標記為由同一方向的所有通道共用。 導出的信息由同一方向的所有通道共用。

l  info_mask_shared_by_all 將屬性標記為所有通道共用,無論其類型或方向如何。 導出的信息由所有渠道共用。 用於枚舉這些屬性的位掩碼都在include / linux / iio / iio.h中定義:

 

enum iio_chan_info_enum {
    IIO_CHAN_INFO_RAW = 0,
    IIO_CHAN_INFO_PROCESSED,
    IIO_CHAN_INFO_SCALE,
    IIO_CHAN_INFO_OFFSET,
    IIO_CHAN_INFO_CALIBSCALE,
    [...]
    IIO_CHAN_INFO_SAMP_FREQ,
    IIO_CHAN_INFO_FREQUENCY,
    IIO_CHAN_INFO_PHASE,
    IIO_CHAN_INFO_HARDWAREGAIN,
    IIO_CHAN_INFO_HYSTERESIS,
    [...]
};

位元組序欄位應為以下之一:         

enum iio_endian {
                         IIO_CPU,
                         IIO_BE,
                         IIO_LE,

};

 

Channel attribute naming conventions:通道屬性命名約定 

  屬性的名稱由IIO核心自動生成,具有以下模式:{direction} _ {type} _ {index} _ {modifier} _ {info_mask}:

  l  direction方向對應於屬性方向,根據drivers / iio / industrialio-core.c中的struct iio_direction結構:

static const char * const iio_direction[] = {
   [0] = "in",
   [1] = "out", 
}; 

  l  type對應於通道類型,根據char數組const iio_chan_type_name_spec:

static const char * const iio_chan_type_name_spec[] = {
   [IIO_VOLTAGE] = "voltage",
   [IIO_CURRENT] = "current",
   [IIO_POWER] = "power",
   [IIO_ACCEL] = "accel",
   [...]
   [IIO_UVINDEX] = "uvindex",
   [IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity",
   [IIO_COUNT] = "count",
   [IIO_INDEX] = "index",
   [IIO_GRAVITY]  = "gravity",
};

l  index 索引模式取決於是否設置了通道.indexed欄位。 如果設置,索引將從.channel欄位中獲取,以替換{index}模式。

l  modifier 模式取決於通道所設置的.modified欄位。 如果設置,修飾符將從.channel2欄位中獲取,{modifier}模式將根據char數組struct iio_modifier_names結構替換:

static const char * const iio_modifier_names[] = {
   [IIO_MOD_X] = "x",
   [IIO_MOD_Y] = "y",
   [IIO_MOD_Z] = "z",
   [IIO_MOD_X_AND_Y] = "x&y",
   [IIO_MOD_X_AND_Z] = "x&z",
   [IIO_MOD_Y_AND_Z] = "y&z",
   [...]
   [IIO_MOD_CO2] = "co2",
   [IIO_MOD_VOC] = "voc",
};

l  info_mask取決於char數組iio_chan_info_postfix中的通道信息掩碼,私有或共用索引值:

/* relies on pairs of these shared then separate依賴於這些共用的對,然後分離*/
static const char * const iio_chan_info_postfix[] = {
   [IIO_CHAN_INFO_RAW] = "raw",
   [IIO_CHAN_INFO_PROCESSED] = "input",
   [IIO_CHAN_INFO_SCALE] = "scale",
   [IIO_CHAN_INFO_CALIBBIAS] = "calibbias",
   [...]
   [IIO_CHAN_INFO_SAMP_FREQ] = "sampling_frequency",
   [IIO_CHAN_INFO_FREQUENCY] = "frequency",
   [...]
};

 

Distinguishing channels通道區分

當每種通道類型有多個數據通道時,您可能會遇到麻煩。 困境將是:如何識別它們。 有兩種解決方案:索引和修飾符。

使用索引:給定具有一個通道線的ADC器件,不需要索引。通道定義如下:

static const struct iio_chan_spec adc_channels[] = {
                {
                        .type = IIO_VOLTAGE,
                        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),

               },
}

由前面描述的通道產生的屬性名稱將是in_voltage_raw。

/sys/bus/iio/iio:deviceX/in_voltage_raw

現在讓我們看一下有4個甚至8個通道的轉換器。 我們如何識別它們? 解決方案是使用索引。 將.indexed欄位設置為1將使用.channel值替換{index}模式來替換通道屬性名稱:

 

            

  static const struct iio_chan_spec adc_channels[] = {
        {
                .type = IIO_VOLTAGE,
                .indexed = 1,
                .channel = 0,
                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),

        },

        {
                .type = IIO_VOLTAGE,
                .indexed = 1,
                .channel = 1,
                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
        },
        {
                .type = IIO_VOLTAGE,
                .indexed = 1,
                .channel = 2,
                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
        },
        {
                .type = IIO_VOLTAGE,
                .indexed = 1,
                .channel = 3,
                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
        },
}

生成的通道屬性為:

 /sys/bus/iio/iio:deviceX/in_voltage0_raw
/sys/bus/iio/iio:deviceX/in_voltage1_raw
/sys/bus/iio/iio:deviceX/in_voltage2_raw
/sys/bus/iio/iio:deviceX/in_voltage3_raw

  使用修飾符:給定一個帶有兩個通道的光感測器 - 一個用於紅外光,一個用於紅外和可見光,沒有索引或修改器,屬性名稱將為in_intensity_raw。 在這裡使用索引可能容易出錯,因為使用in_intensity0_ir_raw和in_intensity1_ir_raw是沒有意義的。 使用修飾符將有助於提供有意義的屬性名稱。 通道的定義如下:

       

static const struct iio_chan_spec mylight_channels[] = {

        {
                .type = IIO_INTENSITY,
                .modified = 1,
                .channel2 = IIO_MOD_LIGHT_IR,
                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
                .info_mask_shared = BIT(IIO_CHAN_INFO_SAMP_FREQ),
        },
        {
                .type = IIO_INTENSITY,
                .modified = 1,
                .channel2 = IIO_MOD_LIGHT_BOTH,
                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
                .info_mask_shared = BIT(IIO_CHAN_INFO_SAMP_FREQ),
        },
        {
                .type = IIO_LIGHT,
                .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
                .info_mask_shared =BIT(IIO_CHAN_INFO_SAMP_FREQ),

        },
       }

屬性結果:

l  /sys/bus/iio/iio:deviceX/in_intensity_ir_raw 用於測量IR強度的通道

l  /sys/bus/iio/iio:deviceX/in_intensity_both_raw用於測量紅外和可見光的通道

l  /sys/bus/iio/iio:deviceX/in_illuminance_input用於處理後的數據

l  /sys/bus/iio/iio:deviceX/sampling_frequency 用於採樣頻率,由所有人共用

  這也適用於加速度計,我們將在案例研究中進一步瞭解。 現在,讓我們總結一下我們到目前為止在虛擬IIO驅動程式中討論過的內容。

Putting it all together總結

  讓我們總結一下迄今為止我們在一個簡單的虛擬驅動器中看到的內容,它將暴露出四個電壓通道。 我們將忽略read()或write()函數:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
#include <linux/iio/buffer.h>


#define FAKE_VOLTAGE_CHANNEL(num)                  \
   {                                               \
         .type = IIO_VOLTAGE,                      \
         .indexed = 1,                             \
         .channel = (num),                         \
         .address = (num),                         \
         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
         .info_mask_shared_by_type =BIT(IIO_CHAN_INFO_SCALE) \

   }


struct my_private_data {
    int foo;
    int bar;
    struct mutex lock;
};

static int fake_read_raw(struct iio_dev *indio_dev,
                   struct iio_chan_spec const *channel, int *val,
                   int *val2, long mask)
{
    return 0;
}

static int fake_write_raw(struct iio_dev *indio_dev,
                   struct iio_chan_spec const *chan,
                   int val, int val2, long mask)
{
    return 0;
}

static const struct iio_chan_spec fake_channels[] = {
   FAKE_VOLTAGE_CHANNEL(0),
   FAKE_VOLTAGE_CHANNEL(1), 
   FAKE_VOLTAGE_CHANNEL(2),
   FAKE_VOLTAGE_CHANNEL(3),

};

static const struct of_device_id iio_dummy_ids[] = {
    { .compatible = "packt,iio-dummy-random", },
    { /* sentinel */ }
};

static const struct iio_info fake_iio_info = {
   .read_raw = fake_read_raw,
   .write_raw        = fake_write_raw,
   .driver_module = THIS_MODULE,
};

static int my_pdrv_probe (struct platform_device *pdev)
{
    struct iio_dev *indio_dev;
    struct my_private_data *data;
   indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));

   if (!indio_dev) {
         dev_err(&pdev->dev, "iio allocation failed!\n");
         return -ENOMEM;
   }

   data = iio_priv(indio_dev);
   mutex_init(&data->lock);
   indio_dev->dev.parent = &pdev->dev;
   indio_dev->info = &fake_iio_info;
   indio_dev->name = KBUILD_MODNAME;
   indio_dev->modes = INDIO_DIRECT_MODE;
   indio_dev->channels = fake_channels;
   indio_dev->num_channels = ARRAY_SIZE(fake_channels);
   indio_dev->available_scan_masks = 0xF;
    iio_device_register(indio_dev);
    platform_set_drvdata(pdev, indio_dev);
    return 0;
}
static void my_pdrv_remove(struct platform_device *pdev)
{
    struct iio_dev *indio_dev = platform_get_drvdata(pdev);
    iio_device_unregister(indio_dev);
}
static struct platform_driver mypdrv = {
    .probe      = my_pdrv_probe,
    .remove     = my_pdrv_remove,
    .driver     = {
        .name     = "iio-dummy-random",
        .of_match_table = of_match_ptr(iio_dummy_ids),  
        .owner    = THIS_MODULE,
    },
};
module_platform_driver(mypdrv);
MODULE_AUTHOR("John Madieu <[email protected]>");
MODULE_LICENSE("GPL");

  載入上述模塊後, 我們將有以下輸出, 顯示我們的設備確實對應於我們已註冊的平臺設備:

  ~# ls -l /sys/bus/iio/devices/
lrwxrwxrwx 1 root root 0 Jul 31 20:26 iio:device0 -> ../../../devices/platform/iio-dummy-random.0/iio:device0
lrwxrwxrwx 1 root root 0 Jul 31 20:23 iio_sysfs_trigger -> ../../../devices/iio_sysfs_trigger

  下麵的列表顯示了此設備的通道及其名稱, 這些通道與我們在驅動程式中描述的內容完全對應: 

 ~# ls /sys/bus/iio/devices/iio\:device0/
dev in_voltage2_raw name uevent
in_voltage0_raw in_voltage3_raw power
in_voltage1_raw in_voltage_scale subsystem
~# cat /sys/bus/iio/devices/iio:device0/name
iio_dummy_random

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ps 查看進程,列出執行ps命令的那個時刻的進程快照。如果想要動態顯示,使用top命令 參數格式: UNIX格式:一個"-"開頭 BSD格式:沒有"-"開頭 GNU長格式:兩個"-"開頭 a # 顯示與終端相關的所有進程,包含每個進程的完整路徑 u # 顯示進程的用戶信息 x # 顯示與終端無關的所 ...
  • 註:本文僅針對Cortex-M3/4 系列進行講述。 在傳統的ARM處理器架構中,常使用SWP指令來實現鎖的讀/寫原子操作,但從ARM v6開始,讀/寫訪問在獨立的兩條匯流排上進行,SWP指令已無法在此架構下保證讀/寫訪問的原子操作,因此互斥訪問指令應運而生。本文結合項目中運用的相關方法,總結Cort ...
  • 什麼是雲計算? 雲計算是一種採用按量付費的模式,基於虛擬化技術,將相應計算資源(如網路、存儲等)池化後,提供便捷的、高可用的、高擴展性的、按需的服務(如計算、存儲、應用程式和其他 IT 資源)。 雲計算的基本特征? 自主服務:可按需的獲取雲端的相應資源(主要指公有雲); 網路訪問:可隨時隨地使用任何 ...
  • 一、 概念與由來 LVM:邏輯捲管理(Logical Volume Manager) 普通的磁碟分區管理方式在邏輯分區劃分好之後就無法改變其大小,當一個邏輯分區存放不下某文件時,這個文件因為受上層文件系統的限制,不能跨越多個分區存放,所以也不能放到多個磁碟上。 而當某個分區空間耗盡時,解決的方法通常 ...
  • 胖友,如果你的電腦是windows系統,下麵這十八招windows快捷鍵都不會,還敢說你會用windows? 說到windows的快捷鍵,當然不是只有ctrl+c,ctrl+v這麼簡單,今天我整理了一下一些windows常用的使用快捷鍵技巧,用於提高辦公效率。來吧,一起玩一下。 1,新建文件夾(Ct ...
  • if [ 1 -ne 1 ];then...fi這是指當1不等於1時執行then後的語句-eq:等於-ne:不等於-le:小於等於-ge:大於等於-lt:小於-gt:大於 ...
  • vsftpd丶NFS丶SAMBA nfs基於rpcsamba基於cifs(smb) DRBD:ftp:File Transfer protocol 文件傳輸協議 兩個連接: tcp:命令連接 tcp:數據連接 在被動模式下數據傳輸埠是隨機的除非自己指定 主動模式:伺服器端通過20埠主動連接客戶端 ...
  • PXE介紹 Preboot Excution Environment 預啟動執行環境 Intel公司研發 基於Client/Server的網路模式,支持遠程主機通過網路從遠端伺服器下載映像,並由此支持通過網路啟動操作系統 PXE可以引導和安裝Windows,linux等多種操作系統 所謂的PXE是P ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...