千家信息网

FS4412开发板怎么使用Linux IIO驱动框架实现ADC驱动

发表于:2024-12-04 作者:千家信息网编辑
千家信息网最后更新 2024年12月04日,这篇文章给大家介绍FS4412开发板怎么使用Linux IIO驱动框架实现ADC驱动,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。1.概述FS4412开发板有一个4通道(0/1/
千家信息网最后更新 2024年12月04日FS4412开发板怎么使用Linux IIO驱动框架实现ADC驱动

这篇文章给大家介绍FS4412开发板怎么使用Linux IIO驱动框架实现ADC驱动,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

1.概述

FS4412开发板有一个4通道(0/1/2)、10/12比特精度的 ADC ,其中:

  • 1)ADCIN0: 在核心板中引出

  • 2)ADCIN1: 在核心板中引出

  • 3)ADCIN2: 在核心板中引出

  • 4)ADCIN3: 连接开发板的VR1电位器

主要介绍基于IIO驱动框架的ADC的简单实现方法。

配置DTS节点

FS4412 ADC 的 DTS 节点在 kernel/arch/arm/boot/dts/exynos4412-fs4412.dts 文件中添加如下定义:

adc: adc@12C60000 {                                         compatible = "samsung,exynos-adc-fs4412";           reg = <0x126C0000 0x100>, <0x10020718 0x4>;         clocks = <&clock 303>;                              clock-names = "adc";                                #io-channel-cells = <1>;                            io-channel-ranges;                                  status = "okay";                            };

编写驱动

ADC 的驱动源码为 fs4412_adc.c

定义 ADC 通道

可用的通道列表在 fs4412_adc.c 中定义:

#define ADC_CHANNEL(_index, _id) {                      \    .type = IIO_VOLTAGE,                                \    .indexed = 1,                                       \    .channel = _index,                          \    .address = _index,                          \    .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),       \    .datasheet_name = _id,                              \}/* 通道信息 */static const struct iio_chan_spec fs4412_adc_iio_channels[] = {    ADC_CHANNEL(0, "adc0"),    ADC_CHANNEL(1, "adc1"),    ADC_CHANNEL(2, "adc2"),    ADC_CHANNEL(3, "adc3"),};

ADC采集原始数据原理

根据Exynos4412处理器的官方使用手册提供的资料,可以总结ADC的基本使用方法如下:

  • 初始化过程

    1)初始化ADC_CFG(0x0x10010118) [16] = 0 设置ADC为普通模式 2)初始化ADCCON(0x126C0000) [16] = 1 使用12位ADC [14] = 1 允许分频 [13:6]=0xFF 分频系数 使ADC的工作频率控制在5MHz以内 3)选择输入引脚ADCMUX(0x126C001C) [3:0] = 0x03 选择AIN3作为输入引脚

  • 执行采集转换过程

    1)开始转换ADCCON(0x126C0000) [0] = 1 ADC开始转换 2)判断是否转换完成ADCCON(0x126C0000) 读取[15]位状态=1表示转换完成 3)读取转换结果ADCDAT(0x126C000C) 读ADC的转换结果

  • 计算采集到的电压

    使用标准电压将 AD 转换的值转换为用户所需要的电压值。其计算公式如下: Vref / (2^n-1) = Vresult / raw 注:



Vref 为标准电压 n 为 AD 转换的位数 Vresult 为用户所需要的采集电压 raw 为 AD 采集的原始数据

例如,标准电压为 1.8V,AD 采集位数为 10 位,AD 采集到的原始数据为 568,则:    Vresult = (1800mv * 568) / 1023;### 驱动测试例程 以下为完整的读取 ADC 的驱动例程:

#include #include #include #include

MODULE_AUTHOR("LvXin lvx_sy@farsight.com.cn"); MODULE_DESCRIPTION("FS4412 ADC driver"); MODULE_LICENSE("GPL v2");

#define CON(x) ((x) + 0x00) #define DLY(x) ((x) + 0x08) #define DATX(x) ((x) + 0x0C) #define INTCLR(x) ((x) + 0x18) #define MUX(x) ((x) + 0x1c)

#define CON_RES (1u << 16) #define CON_PRSCEN (1u << 14) #define CON_PRSCLV(x) (((x) & 0xFF) << 6) #define CON_STANDBY (1u << 2)

#define MAX_CHANNELS 4

#define ADC_CON_EN_START (1u << 0) #define ADC_DATX_MASK 0xFFF

/* adc类 */ struct fs4412_adc { void __iomem *regs; u32 value; };

static const struct of_device_id fs4412_adc_match[] = { { .compatible = "samsung,exynos-adc-fs4412"}, {}, }; MODULE_DEVICE_TABLE(of, fs4412_adc_match);

/* 读取数据 */ static int exynos_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct fs4412_adc *info = iio_priv(indio_dev); u32 con1;

if (mask != IIO_CHAN_INFO_RAW)    return -EINVAL;mutex_lock(&indio_dev->mlock);/* 选择通道 */writel(chan->address, MUX(info->regs));/* 启动转换 */con1 = readl(CON(info->regs));writel(con1 | ADC_CON_EN_START,        CON(info->regs));/* 等待转换完成 */while((readl(CON(info->regs)) & (1<<15))==0){};/* 读取转换数据*/info->value = readl(DATX(info->regs)) & ADC_DATX_MASK;*val = info->value;mutex_unlock(&indio_dev->mlock);return IIO_VAL_INT;

}

static int fs4412_adc_reg_access(struct iio_dev *indio_dev, unsigned reg, unsigned writeval, unsigned *readval) { struct fs4412_adc *info = iio_priv(indio_dev);

if (readval == NULL)    return -EINVAL;*readval = readl(info->regs + reg);return 0;

}

/* IIO信息对象 */ static const struct iio_info fs4412_adc_iio_info = { .read_raw = &exynos_read_raw, .debugfs_reg_access = &fs4412_adc_reg_access, .driver_module = THIS_MODULE, };

#define ADC_CHANNEL(_index, _id) {
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = _index,
.address = _index,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.datasheet_name = _id,
}

/* 通道信息 */ static const struct iio_chan_spec fs4412_adc_iio_channels[] = { ADC_CHANNEL(0, "adc0"), ADC_CHANNEL(1, "adc1"), ADC_CHANNEL(2, "adc2"), ADC_CHANNEL(3, "adc3"), };

/* ADC硬件初始化 */ static void fs4412_adc_hw_init(struct fs4412_adc *info) { u32 con;

/* 设置预分频值 */con =  CON_PRSCLV(49) | CON_PRSCEN;/* 12位AD转换 */con |= CON_RES;writel(con, CON(info->regs));

}

/* 设备匹配函数 */ static int fs4412_adc_probe(struct platform_device *pdev) { struct fs4412_adc *info = NULL; struct device_node *np = pdev->dev.of_node; struct iio_dev *indio_dev = NULL; struct resource *mem; int ret = -ENODEV;

if (!np)    return ret;/* 动态申请iio设备 */indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct fs4412_adc));if (!indio_dev) {    dev_err(&pdev->dev, "failed allocating iio device\n");    return -ENOMEM;}info = iio_priv(indio_dev);/* 获得ADC寄存器地址 */mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);info->regs = devm_ioremap_resource(&pdev->dev, mem);if (IS_ERR(info->regs))    return PTR_ERR(info->regs);/* 设置私有数据 */platform_set_drvdata(pdev, indio_dev);indio_dev->name = dev_name(&pdev->dev);indio_dev->dev.parent = &pdev->dev;indio_dev->dev.of_node = pdev->dev.of_node;indio_dev->info = &fs4412_adc_iio_info;indio_dev->modes = INDIO_DIRECT_MODE;indio_dev->channels = fs4412_adc_iio_channels;  /* 通道数据 */indio_dev->num_channels = MAX_CHANNELS;/* 注册iio设备 */ret = iio_device_register(indio_dev);if (ret)    return ret;/* ADC硬件初始化 */fs4412_adc_hw_init(info);return 0;

}

/* 设备移除 */ static int fs4412_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev);

/* 注销iio设备 */iio_device_unregister(indio_dev);return 0;

}

/* 平台设备对象 */ static struct platform_driver fs4412_adc_driver = { .probe = fs4412_adc_probe, .remove = fs4412_adc_remove, .driver = { .name = "exynos-adc", .owner = THIS_MODULE, .of_match_table = fs4412_adc_match, }, };

/* 平台设备模块 */ module_platform_driver(fs4412_adc_driver);

将以上源码保存为 drivers/iio/adc/fs4412_adc.c ,并在 drivers/iio/adc/Makefile 后加入:

obj-$(CONFIG_FS4412_ADC) += fs4412_adc.o

编译并烧写内核,启动后即可在终端下运行以下命令来读取 ADC3 的值:

while true;

do cat /sys/devices/126c0000.adc/iio:device0/in_voltage3_raw; sleep 1; done

数据采集的过程中,旋转电位器的旋钮,改变电位器的电阻分压,就会改变转换后的结果。

关于FS4412开发板怎么使用Linux IIO驱动框架实现ADC驱动就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

0