在嵌入式系統中,即時回應外部訊號至關重要,工業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_event和adxl345_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;
}
內容解密:
iio_push_event函式:此函式負責將特定事件推播到使用者空間的佇列中,以便應用程式能夠讀取並回應這些事件。ev_code引數:透過IIO_MOD_EVENT_CODE宏構建,包含有關事件的詳細資訊,如通道型別、修飾符、方向和事件型別。timestamp引數:記錄事件發生的精確時間,有助於應用程式進行時序分析或排序處理。- 在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_pins和spi0_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>;
};
// ...
}
內容解密:
#size-cells和#address-cells屬性:這兩個屬性定義了子節點中reg屬性的地址和大小欄位的表示方式。在本例中,#address-cells設為1,表示每個子節點的地址需要一個單元格(u32)來表示;#size-cells設為0,表示子節點的大小欄位不需要任何單元格。compatible屬性:該屬性必須與驅動程式中的of_device_id結構中的相容字串一致,以確保驅動程式能夠正確匹配裝置。spi-max-frequency屬性:指定了裝置的最大SPI時脈頻率。- 子節點屬性:
reg:設定MAX11300裝置的埠號。port-mode:設定所選埠的組態模式。AVR:選擇ADC電壓參考源。adc-range和dac-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裝置進行互動,主要程式碼段包括:
包含必要的標頭檔案:
#include <linux/spi/spi.h>建立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);註冊到SPI匯流排作為驅動程式:
module_spi_driver(max11300_spi_driver);新增"maxim,max11300"到驅動程式支援的裝置列表:
static const struct of_device_id max11300_of_match[] = { { .compatible = "maxim,max11300", }, {}, }; MODULE_DEVICE_TABLE(of, max11300_of_match);定義spi_device_id結構陣列:
static const struct spi_device_id max11300_spi_ids[] = { { .name = "max11300", }, {} }; MODULE_DEVICE_TABLE(spi, max11300_spi_ids);初始化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子系統中,主要程式碼段包括:
包含必要的標頭檔案:
#include <linux/iio/iio.h>定義全域性私有資料結構
max11300_state:struct max11300_state { struct device *dev; const struct max11300_rw_ops *ops; struct gpio_chip gpiochip; // 其他成員變數... };在
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() 函式負責初始化和註冊裝置。關鍵步驟包括:
- 裝置資料結構初始化:建立並初始化
struct max11300_state,儲存裝置的狀態和組態資訊。 - IIO 裝置註冊:使用
iio_device_alloc()分配 IIO 裝置,並將其與 SPI 裝置繫結。 - 通道組態:呼叫
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 的埠。主要的步驟包括:
- 遍歷 DT 子節點:使用
device_for_each_child_node()遍歷 MAX11300 節點下的每個子節點。 - 讀取埠模式:使用
fwnode_property_read_u32()讀取每個子節點的port-mode屬性,決定埠的操作模式。 - 組態埠引數:根據不同的埠模式,讀取並儲存相關的組態引數,如 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", ®);
// ...
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():讀取子節點中的屬性值,如reg和port-mode。switch (st->port_modes[reg]):根據埠模式進行不同的組態處理,例如設定 ADC 或 DAC 相關引數。