返回文章列表

工業I/O子系統中斷事件處理與裝置樹組態

本文探討工業I/O(IIO)子系統中斷與事件處理機制,並以MAX11300驅動程式為例,講解如何在Linux核心實作中斷處理、事件傳遞以及裝置樹組態。文章涵蓋iio_push_event函式的使用、事件程式碼的構建、時間戳的記錄,以及如何在裝置樹中描述MAX11300裝置,包括SPI控制器節點組態、

嵌入式系統 驅動程式開發

在嵌入式系統中,即時回應外部訊號至關重要,工業I/O子系統的中斷和事件處理機制正是為此而生。本文以MAX11300驅動程式開發為例,探討如何在Linux核心實作中斷處理、事件傳遞以及裝置樹的相關組態。驅動程式中使用iio_push_event函式將硬體事件推播到使用者空間,並透過事件程式碼和時間戳提供詳細的事件資訊。裝置樹的組態則確保驅動程式能正確識別和初始化MAX11300裝置,其中包括SPI控制器節點的設定、片選腳位的指定,以及各個埠模式、ADC/DAC範圍等引數的定義。這些組態資訊會被驅動程式解析,用於初始化和組態MAX11300的硬體資源。

工業I/O子系統中斷與事件處理

在工業I/O(IIO)子系統中,中斷和事件處理扮演著至關重要的角色,尤其是在需要即時回應外部訊號的應用中。本章節將探討如何在IIO驅動程式中實作中斷和事件處理,以及相關的使用者空間工具。

IIO驅動程式中的事件處理

在IIO驅動程式中,事件處理涉及設定和回應特定硬體事件,例如模擬數位轉換器(ADC)的閾值觸發或數位輸入的變化。這些事件可以透過sysfs屬性進行組態,使用者空間應用程式可以透過讀寫這些屬性來控制事件的行為。

設定事件處理函式

要實作事件處理,首先需要在驅動程式中定義相關的回撥函式。例如,在ADXL345加速度計驅動程式中,定義了adxl345_read_eventadxl345_write_event函式來處理事件相關的讀寫操作。

static const struct iio_info adxl345_info = {
    .driver_module = THIS_MODULE,
    .read_raw = adxl345_read_raw,
    .write_raw = adxl345_write_raw,
    .read_event_value = adxl345_read_event,
    .write_event_value = adxl345_write_event,
};

事件通知的啟用

事件通知可以透過寫入sysfs屬性來啟用,這些屬性位於/sys/bus/iio/devices/iio:deviceX/events/目錄下。

將IIO事件傳遞到使用者空間

iio_push_event函式用於將事件推播到使用者空間的事件佇列中,通常在執行緒IRQ(中斷請求)處理程式中呼叫。

int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp);
  • indio_dev:指向IIO裝置結構的指標。
  • ev_code:包含通道型別、修飾符、方向和事件型別的事件程式碼。
  • timestamp:事件發生的時間戳。

實作範例

在ADXL345驅動程式的中斷服務例程(ISR)中,當檢測到單次點選事件時,會呼叫iio_push_event將事件推播到使用者空間。

static irqreturn_t adxl345_event_handler(int irq, void *handle)
{
    // ...
    if (int_stat & (SINGLE_TAP)) {
        dev_info(data->dev, "single tap interrupt has occurred\n");
        if (tap_stat & TAP_X_EN) {
            iio_push_event(indio_dev,
                           IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, IIO_EV_TYPE_THRESH, 0),
                           data->timestamp);
        }
        // ...
    }
    return IRQ_HANDLED;
}

內容解密:

  1. iio_push_event函式:此函式負責將特定事件推播到使用者空間的佇列中,以便應用程式能夠讀取並回應這些事件。
  2. ev_code引數:透過IIO_MOD_EVENT_CODE宏構建,包含有關事件的詳細資訊,如通道型別、修飾符、方向和事件型別。
  3. timestamp引數:記錄事件發生的精確時間,有助於應用程式進行時序分析或排序處理。
  4. 在ISR中使用:由於此函式通常在中斷服務例程(ISR)中被呼叫,因此需要確保執行效率,避免在ISR中進行耗時操作。

IIO實用工具

在開發IIO驅動程式時,可以使用一些實用的工具來簡化開發過程。這些工具位於核心原始碼樹的/tools/iio/目錄下,包括:

  • lsiio:列舉IIO觸發器、裝置和可存取通道。
  • iio_event_monitor:監控IIO裝置的ioctl介面上的IIO事件。
  • iio_generic_buffer:監控、處理並列印從IIO裝置緩衝區接收到的資料。
  • libiio:由Analog Devices開發的強大函式庫,用於與IIO裝置互動。

實驗11.1:“IIO混合訊號I/O裝置”模組

本實驗旨在為Maxim MAX11300裝置開發一個IIO驅動程式,並建立幾個使用者應用程式來從使用者空間控制GPIO。MAX11300是一款高度整合的裝置,結合了12位元多通道模擬數位轉換器(ADC)和12位元多通道緩衝數位類別比轉換器(DAC),並提供20個混合訊號高壓雙極性埠,可組態為ADC類別比輸入、DAC類別比輸出、通用輸入埠(GPI)、通用輸出埠(GPO)或類別比開關端子。

硬體設定

實驗中使用Raspberry Pi的SPI引腳連線到PIXI™ CLICK mikroBUS™插座,並進行相應的電源連線。具體步驟包括連線SPI訊號線(CE0、SCLK、MOSI、MISO)和電源引腳(3.3V、5V、GND),以及在HD2聯結器上進行必要的跳線設定。

工業I/O子系統中MAX11300裝置的裝置樹描述

在進行MAX11300裝置的驅動開發時,需要在裝置樹中對該裝置進行描述。以下將詳細介紹如何在bcm2710-rpi-3-b.dts檔案中新增MAX11300節點。

SPI0控制器節點組態

首先,開啟bcm2710-rpi-3-b.dts檔案,找到SPI0控制器主節點。在SPI0節點中,可以看到pinctrl-0屬性指向了組態SPI0控制器引腳的節點(spi0_pinsspi0_cs_pins)。cs-gpios屬性指定了用於片選的GPIO引腳。

&spi0 {
    pinctrl-names = "default";
    pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
    cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
    // ...
}

新增MAX11300節點

在SPI0節點下,新增MAX11300節點。該節點包括二十個子節點,代表MAX11300裝置的不同埠。

max11300@0 {
    #size-cells = <0>;
    #address-cells = <1>;
    compatible = "maxim,max11300";
    reg = <0>;
    spi-max-frequency = <10000000>;

    // 子節點組態
    channel@0 {
        reg = <0>;
        port-mode = <PORT_MODE_7>;
        AVR = <0>;
        adc-range = <ADC_VOLTAGE_RANGE_PLUS10>;
        adc-samples = <ADC_SAMPLES_1>;
    };

    // ...
}

內容解密:

  1. #size-cells#address-cells屬性:這兩個屬性定義了子節點中reg屬性的地址和大小欄位的表示方式。在本例中,#address-cells設為1,表示每個子節點的地址需要一個單元格(u32)來表示;#size-cells設為0,表示子節點的大小欄位不需要任何單元格。
  2. compatible屬性:該屬性必須與驅動程式中的of_device_id結構中的相容字串一致,以確保驅動程式能夠正確匹配裝置。
  3. spi-max-frequency屬性:指定了裝置的最大SPI時脈頻率。
  4. 子節點屬性
    • reg:設定MAX11300裝置的埠號。
    • port-mode:設定所選埠的組態模式。
    • AVR:選擇ADC電壓參考源。
    • adc-rangedac-range:分別設定ADC和DAC相關模式下的電壓範圍。
    • adc-samples:設定ADC相關模式下的取樣次數。
    • negative-input:對於組態為模式8的埠,設定負輸入埠號。

包含必要的標頭檔案

為了使用MAX11300相關的定義,需要在bcm2710-rpi-3-b.dts檔案中包含maxim,max11300.h標頭檔案。

#include <dt-bindings/iio/maxim,max11300.h>

該標頭檔案包含了MAX11300裝置的DT繫結屬性的值,用於子節點的組態。

#ifndef _DT_BINDINGS_MAXIM_MAX11300_H
#define _DT_BINDINGS_MAXIM_MAX11300_H

#define PORT_MODE_0 0
#define PORT_MODE_1 1
// ...

#define ADC_SAMPLES_1 0
#define ADC_SAMPLES_2 1
// ...

#define ADC_VOLTAGE_RANGE_NOT_SELECTED 0
#define ADC_VOLTAGE_RANGE_PLUS10 1
// ...

#endif /* _DT_BINDINGS_MAXIM_MAX11300_H */

內容解密:

  • 該標頭檔案定義了MAX11300裝置的不同工作模式、ADC取樣率以及電壓範圍等宏,用於在裝置樹中組態MAX11300裝置的子節點。

工業I/O子系統中MAX11300驅動程式的實作

MAX11300驅動程式是根據Paul Cercueil的AD5592R驅動程式開發的,主要涉及工業框架作為SPI互動、工業框架作為IIO裝置和GPIO驅動介面三個部分。

工業框架作為SPI互動

驅動程式透過SPI與MAX11300裝置進行互動,主要程式碼段包括:

  1. 包含必要的標頭檔案

    #include <linux/spi/spi.h>
    
  2. 建立spi_driver結構

    static struct spi_driver max11300_spi_driver = {
        .driver = {
            .name = "max11300",
            .of_match_table = of_match_ptr(max11300_of_match),
        },
        .probe = max11300_spi_probe,
        .remove = max11300_spi_remove,
        .id_table = max11300_spi_ids,
    };
    module_spi_driver(max11300_spi_driver);
    
  3. 註冊到SPI匯流排作為驅動程式

    module_spi_driver(max11300_spi_driver);
    
  4. 新增"maxim,max11300"到驅動程式支援的裝置列表

    static const struct of_device_id max11300_of_match[] = {
        { .compatible = "maxim,max11300", },
        {},
    };
    MODULE_DEVICE_TABLE(of, max11300_of_match);
    
  5. 定義spi_device_id結構陣列

    static const struct spi_device_id max11300_spi_ids[] = {
        { .name = "max11300", },
        {}
    };
    MODULE_DEVICE_TABLE(spi, max11300_spi_ids);
    
  6. 初始化max11300_rw_ops結構,包含讀寫MAX11300暫存器的函式

    static const struct max11300_rw_ops max11300_rw_ops = {
        .reg_write = max11300_reg_write,
        .reg_read = max11300_reg_read,
        .reg_read_differential = max11300_reg_read_differential,
    };
    

內容解密:

  • max11300_rw_ops結構定義了與MAX11300裝置互動的回呼函式,包括寫入暫存器、讀取單端模式下的暫存器值和差分模式下的暫存器值。
  • max11300_reg_write函式透過SPI寫入MAX11300暫存器,首先設定命令位元組(最低位為0表示寫入),然後使用spi_sync_transfer傳送命令和資料。
  • max11300_reg_read函式讀取單端模式下的MAX11300暫存器值,設定命令位元組(最低位為1表示讀取),然後透過spi_sync_transfer接收資料,並將接收到的位元組順序調整為正確的位元組順序。
  • max11300_reg_read_differential函式讀取差分模式下的MAX11300暫存器值,過程與單端模式類別似,但對接收到的值進行了符號擴充套件,以正確表示差分模式下的有符號數值。

工業框架作為IIO裝置

驅動程式將MAX11300裝置整合到IIO子系統中,主要程式碼段包括:

  1. 包含必要的標頭檔案

    #include <linux/iio/iio.h>
    
  2. 定義全域性私有資料結構max11300_state

    struct max11300_state {
        struct device *dev;
        const struct max11300_rw_ops *ops;
        struct gpio_chip gpiochip;
        // 其他成員變數...
    };
    
  3. max11300_probe函式中分配IIO裝置和私有資料結構

    struct iio_dev *indio_dev;
    struct max11300_state *st;
    indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
    st = iio_priv(indio_dev);
    st->dev = dev;
    

內容解密:

  • max11300_state結構用於管理MAX11300裝置的狀態和組態,包括指向SPI裝置的指標、GPIO控制器等。
  • max11300_probe函式中,透過devm_iio_device_alloc分配IIO裝置和私有資料結構,並透過iio_priv取得私有資料結構的指標,實作了物理裝置與邏輯裝置之間的指標繫結。

驅動程式透過上述實作,能夠有效地管理MAX11300裝置,並提供與工業I/O子系統的整合。

MAX11300 驅動程式解析:工業 I/O 子系統的應用

MAX11300 是一款高度可組態的混合訊號積體電路,廣泛應用於工業控制系統。本文將探討 MAX11300 驅動程式的實作,特別是在 Linux 工業 I/O(IIO)子系統中的整合。

驅動程式初始化與裝置註冊

在 MAX11300 驅動程式中,max11300_probe() 函式負責初始化和註冊裝置。關鍵步驟包括:

  1. 裝置資料結構初始化:建立並初始化 struct max11300_state,儲存裝置的狀態和組態資訊。
  2. IIO 裝置註冊:使用 iio_device_alloc() 分配 IIO 裝置,並將其與 SPI 裝置繫結。
  3. 通道組態:呼叫 max11300_alloc_ports() 組態 IIO 通道,根據裝置樹(DT)屬性設定埠模式和其他相關引數。
static int max11300_probe(struct spi_device *spi)
{
    struct iio_dev *iio_dev;
    struct max11300_state *st;
    // ...
    iio_dev = iio_device_alloc(&spi->dev, sizeof(*st));
    // ...
    dev_set_drvdata(&spi->dev, iio_dev);
    // ...
    max11300_alloc_ports(st);
    // ...
}

內容解密:

  • iio_device_alloc():分配 IIO 裝置,並將其與 SPI 裝置關聯。
  • dev_set_drvdata():將 IIO 裝置與 SPI 裝置繫結,方便在驅動程式中存取。
  • max11300_alloc_ports():根據 DT 組態 IIO 通道,設定埠模式和其他屬性。

通道組態與 DT 解析

max11300_alloc_ports() 函式負責解析 DT 中的通道子節點屬性,並根據這些屬性組態 MAX11300 的埠。主要的步驟包括:

  1. 遍歷 DT 子節點:使用 device_for_each_child_node() 遍歷 MAX11300 節點下的每個子節點。
  2. 讀取埠模式:使用 fwnode_property_read_u32() 讀取每個子節點的 port-mode 屬性,決定埠的操作模式。
  3. 組態埠引數:根據不同的埠模式,讀取並儲存相關的組態引數,如 ADC 範圍、參考電壓等。
static int max11300_alloc_ports(struct max11300_state *st)
{
    struct fwnode_handle *child;
    u32 reg, tmp;
    // ...
    device_for_each_child_node(st->dev, child) {
        ret = fwnode_property_read_u32(child, "reg", &reg);
        // ...
        ret = fwnode_property_read_u32(child, "port-mode", &tmp);
        if (!ret)
            st->port_modes[reg] = tmp;
        // ...
        switch (st->port_modes[reg]) {
            case PORT_MODE_7:
                // 組態 PORT_MODE_7 相關引數
                break;
            case PORT_MODE_1:
                // 組態 GPI 埠
                st->gpio_offset[offset] = reg;
                st->num_gpios++;
                break;
            // 其他模式處理
        }
    }
    // ...
}

內容解密:

  • device_for_each_child_node():遍歷 DT 中的子節點,處理每個埠的組態。
  • fwnode_property_read_u32():讀取子節點中的屬性值,如 regport-mode
  • switch (st->port_modes[reg]):根據埠模式進行不同的組態處理,例如設定 ADC 或 DAC 相關引數。