IIO (Industrial I/O)子系统通过I2C方式采集数据的实例。这个例子包括驱动程序和用户空间应用程序。
首先,让我们创建一个简单的IIO驱动程序,它通过I2C接口与ADC (模数转换器) 通信,并通过PCI总线连接到系统。
1. 驱动程序 (my_iio_driver.c):
```c
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define DRIVER_NAME "my_iio_driver"
#define I2C_ADC_ADDR 0x48 // 假设ADC的I2C地址是0x48
struct my_iio_data {
struct pci_dev *pdev;
struct i2c_client *i2c;
struct iio_dev *indio_dev;
};
static int my_iio_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct my_iio_data *data = iio_priv(indio_dev);
int ret;
u16 raw_val;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = i2c_smbus_read_word_data(data->i2c, 0);
if (ret < 0)
return ret;
raw_val = ret;
*val = raw_val & 0xFFF; // 假设ADC是12位
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static const struct iio_info my_iio_info = {
.read_raw = my_iio_read_raw,
};
static const struct iio_chan_spec my_iio_channels[] = {
{
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
},
};
static int my_iio_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct my_iio_data *data;
struct i2c_adapter *i2c_adapter;
struct i2c_board_info i2c_info = {
I2C_BOARD_INFO("my_adc", I2C_ADC_ADDR),
};
int ret;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->pdev = pdev;
ret = pcim_enable_device(pdev);
if (ret) {
dev_err(&pdev->dev, "Failed to enable PCI device\n");
return ret;
}
// 假设I2C控制器在BAR 0
i2c_adapter = i2c_get_adapter(0); // 使用适当的I2C适配器号
if (!i2c_adapter) {
dev_err(&pdev->dev, "Failed to get I2C adapter\n");
return -ENODEV;
}
data->i2c = i2c_new_client_device(i2c_adapter, &i2c_info);
if (IS_ERR(data->i2c)) {
dev_err(&pdev->dev, "Failed to create I2C client\n");
return PTR_ERR(data->i2c);
}
data->indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
if (!data->indio_dev) {
ret = -ENOMEM;
goto err_remove_i2c;
}
data->indio_dev->name = DRIVER_NAME;
data->indio_dev->dev.parent = &pdev->dev;
data->indio_dev->info = &my_iio_info;
data->indio_dev->modes = INDIO_DIRECT_MODE;
data->indio_dev->channels = my_iio_channels;
data->indio_dev->num_channels = ARRAY_SIZE(my_iio_channels);
ret = devm_iio_device_register(&pdev->dev, data->indio_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to register IIO device\n");
goto err_remove_i2c;
}
pci_set_drvdata(pdev, data);
return 0;
err_remove_i2c:
i2c_unregister_device(data->i2c);
return ret;
}
static void my_iio_remove(struct pci_dev *pdev)
{
struct my_iio_data *data = pci_get_drvdata(pdev);
i2c_unregister_device(data->i2c);
}
static struct pci_device_id my_iio_ids[] = {
{ PCI_DEVICE(0x1234, 0x5678) }, // 替换为实际的 Vendor ID 和 Device ID
{ 0, }
};
MODULE_DEVICE_TABLE(pci, my_iio_ids);
static struct pci_driver my_iio_driver = {
.name = DRIVER_NAME,
.id_table = my_iio_ids,
.probe = my_iio_probe,
.remove = my_iio_remove,
};
module_pci_driver(my_iio_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("IIO driver for ADC over I2C and PCI");
```
2. 用户空间应用程序 (iio_read_app.c):
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#define IIO_DEVICE "/sys/bus/iio/devices/iio:device0"
int main()
{
char filename[256];
char buf[16];
int fd, ret;
unsigned long raw_value;
// 构建原始数据文件的路径
snprintf(filename, sizeof(filename), "%s/in_voltage_raw", IIO_DEVICE);
// 打开文件
fd = open(filename, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Failed to open %s: %s\n", filename, strerror(errno));
return -1;
}
// 读取原始值
ret = read(fd, buf, sizeof(buf));
if (ret < 0) {
fprintf(stderr, "Failed to read from %s: %s\n", filename, strerror(errno));
close(fd);
return -1;
}
// 关闭文件
close(fd);
// 将读取的字符串转换为整数
raw_value = strtoul(buf, NULL, 10);
// 打印结果
printf("Raw ADC value: %lu\n", raw_value);
return 0;
}
```
使用说明:
1. 编译并加载驱动程序:
```
make -C /lib/modules/$(uname -r)/build M=$PWD modules
sudo insmod my_iio_driver.ko
```
2. 编译用户空间应用程序:
```
gcc -o iio_read_app iio_read_app.c
```
3. 运行应用程序:
```
./iio_read_app
```
这个例子展示了如何创建一个基于IIO的驱动程序,它通过PCI总线连接到系统,并使用I2C与ADC通信。用户空间应用程序通过sysfs接口读取ADC的原始值。
请注意,这个例子是一个简化的演示。在实际应用中,你可能需要:
1. 根据实际的ADC型号调整I2C通信协议。
2. 添加更多的IIO通道和属性。
3. 实现缓冲模式以支持连续采样。
4. 添加触发支持以实现定时或外部触发采样。
5. 根据实际的PCI设备和I2C控制器调整驱动程序。
此外,你可能还需要考虑电压转换、校准等更高级的功能,以将原始ADC值转换为实际的电压值。