溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

FS4412開發(fā)板怎么使用Linux IIO驅動框架實現(xiàn)ADC驅動

發(fā)布時間:2021-10-22 09:22:16 來源:億速云 閱讀:448 作者:柒染 欄目:互聯(lián)網(wǎng)科技

這篇文章給大家介紹FS4412開發(fā)板怎么使用Linux IIO驅動框架實現(xiàn)ADC驅動,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

1.概述

FS4412開發(fā)板有一個4通道(0/1/2)、10/12比特精度的 ADC ,其中:

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

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

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

  • 4)ADCIN3: 連接開發(fā)板的VR1電位器

主要介紹基于IIO驅動框架的ADC的簡單實現(xiàn)方法。

配置DTS節(jié)點

FS4412 ADC 的 DTS 節(jié)點在 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采集原始數(shù)據(jù)原理

根據(jù)Exynos4412處理器的官方使用手冊提供的資料,可以總結ADC的基本使用方法如下:

  • 初始化過程

    1)初始化ADC_CFG(0x0x10010118) [16] = 0 設置ADC為普通模式 2)初始化ADCCON(0x126C0000) [16] = 1 使用12位ADC [14] = 1 允許分頻 [13:6]=0xFF 分頻系數(shù) 使ADC的工作頻率控制在5MHz以內 3)選擇輸入引腳ADCMUX(0x126C001C) [3:0] = 0x03 選擇AIN3作為輸入引腳

  • 執(zhí)行采集轉換過程

    1)開始轉換ADCCON(0x126C0000) [0] = 1 ADC開始轉換 2)判斷是否轉換完成ADCCON(0x126C0000) 讀取[15]位狀態(tài)=1表示轉換完成 3)讀取轉換結果ADCDAT(0x126C000C) 讀ADC的轉換結果

  • 計算采集到的電壓

    使用標準電壓將 AD 轉換的值轉換為用戶所需要的電壓值。其計算公式如下: Vref / (2^n-1) = Vresult / raw 注:



Vref 為標準電壓 n 為 AD 轉換的位數(shù) Vresult 為用戶所需要的采集電壓 raw 為 AD 采集的原始數(shù)據(jù)

例如,標準電壓為 1.8V,AD 采集位數(shù)為 10 位,AD 采集到的原始數(shù)據(jù)為 568,則:

    Vresult = (1800mv * 568) / 1023;

### 驅動測試例程

 以下為完整的讀取 ADC 的驅動例程:

#include <linux/module.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/iio/iio.h>

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);

/* 讀取數(shù)據(jù) */ 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){};

/* 讀取轉換數(shù)據(jù)*/
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));

}

/* 設備匹配函數(shù) */ 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;

/* 動態(tài)申請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);

/* 設置私有數(shù)據(jù) */
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;  /* 通道數(shù)據(jù) */
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

數(shù)據(jù)采集的過程中,旋轉電位器的旋鈕,改變電位器的電阻分壓,就會改變轉換后的結果。

關于FS4412開發(fā)板怎么使用Linux IIO驅動框架實現(xiàn)ADC驅動就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

AI