网站初期建设的成本来源,网站规划与设计案例,英特尔nuc做网站服务器,网站规划的内容四、ADC驱动 ADC是将模拟信号转换为数字信号的转换器#xff0c;在 Exynos4412 上有一个ADC#xff0c;其主要的特性如下。 (1)量程为0~1.8V。 (2)精度有 10bit 和 12bit 可选。 (3)采样时钟最高为5MHz#xff0c;转换速率最高为1MSPS (4)具有四路模拟输入#xff0c;同一时… 四、ADC驱动 ADC是将模拟信号转换为数字信号的转换器在 Exynos4412 上有一个ADC其主要的特性如下。 (1)量程为0~1.8V。 (2)精度有 10bit 和 12bit 可选。 (3)采样时钟最高为5MHz转换速率最高为1MSPS (4)具有四路模拟输入同一时刻只有一路进行转换 (5) 转换完成后可以产生中断。
下面是ADC的控制寄存器
下面是延时和数据寄存器 下面是中断清除和通道多路复用寄存器 根据上面的寄存器描述我们大致可以设计出这个ADC 驱动实现的主要步骤。
(1)初始化 ADC包括选择精度、设置分频值、设置 ADC 为正常工作模式、设置转换启动方式。 (2)注册ADC中断处理函数。 (3)在上层需要ADC数据时选择好ADC 通道启动转换然后等待一个完成量
(4)转换结束后产生中断在中断处理函数中获取转换结果值向 CLRINTADC 寄存器写任意值清除中断然后唤醒等待完成量的进程。 (5)进程被唤醒返回转换结果值给上层。 接下来就是要编写ADC的设备树节点代码如下。
adc126C0000 {compatible fs4412,fsadc;reg 0x126C0000 32;interrupt-parent combiner;interrupts 10 3;
}; reg 属性可以查阅芯片手册得到其寄存器地址。在这里比较麻烦的是中断属性的设置。在 Exynos4412中有的中断是直接接入 GIC 中断控制器的有的中断则是先通过中断组合器(Combiner)将多个中断复合后再接入 GIC 中断控制器的连接图如图所示。 而ADC中断属于INTG10这一组中断手册上的描述如图所示 这一组共用一根中断线中断号如图 所示。 查阅Documentation/devicetree/bindings/arm/samsung/interrupt-combiner.txt内核文档可知:对于这种中断的描述combiner 是其父中断控制器所以interrupt-parent 的值为combiner。interrupts 属性的第一个 cell是中断在组合器中的组号第二个cell是中断在该组中的序号。 在FS4412日标板上有一个电位器的抽头接在了AIN3通道上原理图如图所示 图ADC电路图 接下来就是驱动的实现主要代码如下
#include linux/init.h
#include linux/kernel.h
#include linux/module.h#include linux/fs.h
#include linux/cdev.h#include linux/slab.h
#include linux/ioctl.h
#include linux/uaccess.h#include linux/io.h
#include linux/ioport.h
#include linux/platform_device.h#include linux/of.h
#include linux/interrupt.h#include fsadc.h#define FSADC_MAJOR 256
#define FSADC_MINOR 6
#define FSADC_DEV_NAME fsadcstruct fsadc_dev {unsigned int __iomem *adccon;unsigned int __iomem *adcdat;unsigned int __iomem *clrint;unsigned int __iomem *adcmux;unsigned int adcval;struct completion completion;atomic_t available;unsigned int irq;struct cdev cdev;
};static int fsadc_open(struct inode *inode, struct file *filp)
{struct fsadc_dev *fsadc container_of(inode-i_cdev, struct fsadc_dev, cdev);filp-private_data fsadc;if (atomic_dec_and_test(fsadc-available))return 0;else {atomic_inc(fsadc-available);return -EBUSY;}
}static int fsadc_release(struct inode *inode, struct file *filp)
{struct fsadc_dev *fsadc filp-private_data;atomic_inc(fsadc-available);return 0;
}static long fsadc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct fsadc_dev *fsadc filp-private_data;union chan_val cv;if (_IOC_TYPE(cmd) ! FSADC_MAGIC)return -ENOTTY;switch (cmd) {case FSADC_GET_VAL:if (copy_from_user(cv, (union chan_val __user *)arg, sizeof(union chan_val)))return -EFAULT;if (cv.chan AIN3)return -ENOTTY;writel(cv.chan, fsadc-adcmux);writel(readl(fsadc-adccon) | 1, fsadc-adccon);if (wait_for_completion_interruptible(fsadc-completion))return -ERESTARTSYS;cv.val fsadc-adcval 0xFFF;if (copy_to_user( (union chan_val __user *)arg, cv, sizeof(union chan_val)))return -EFAULT;break;default:return -ENOTTY;}return 0;
}static irqreturn_t fsadc_isr(int irq, void *dev_id)
{struct fsadc_dev *fsadc dev_id;fsadc-adcval readl(fsadc-adcdat);writel(1, fsadc-clrint);complete(fsadc-completion);return IRQ_HANDLED;
}static struct file_operations fsadc_ops {.owner THIS_MODULE,.open fsadc_open,.release fsadc_release,.unlocked_ioctl fsadc_ioctl,
};static int fsadc_probe(struct platform_device *pdev)
{int ret;dev_t dev;struct fsadc_dev *fsadc;struct resource *res;dev MKDEV(FSADC_MAJOR, FSADC_MINOR);ret register_chrdev_region(dev, 1, FSADC_DEV_NAME);if (ret)goto reg_err;fsadc kzalloc(sizeof(struct fsadc_dev), GFP_KERNEL);if (!fsadc) {ret -ENOMEM;goto mem_err;}platform_set_drvdata(pdev, fsadc);cdev_init(fsadc-cdev, fsadc_ops);fsadc-cdev.owner THIS_MODULE;ret cdev_add(fsadc-cdev, dev, 1);if (ret)goto add_err;res platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!res) {ret -ENOENT;goto res_err;}fsadc-adccon ioremap(res-start, resource_size(res));if (!fsadc-adccon) {ret -EBUSY;goto map_err;}fsadc-adcdat fsadc-adccon 3;fsadc-clrint fsadc-adccon 6;fsadc-adcmux fsadc-adccon 7;fsadc-irq platform_get_irq(pdev, 0);if (fsadc-irq 0) {ret fsadc-irq;goto irq_err;}ret request_irq(fsadc-irq, fsadc_isr, 0, adc, fsadc);if (ret)goto irq_err;writel((1 16) | (1 14) | (19 6), fsadc-adccon);init_completion(fsadc-completion);atomic_set(fsadc-available, 1);return 0;
irq_err:iounmap(fsadc-adccon);
map_err:
res_err:cdev_del(fsadc-cdev);
add_err:kfree(fsadc);
mem_err:unregister_chrdev_region(dev, 1);
reg_err:return ret;
}static int fsadc_remove(struct platform_device *pdev)
{dev_t dev;struct fsadc_dev *fsadc platform_get_drvdata(pdev);dev MKDEV(FSADC_MAJOR, FSADC_MINOR);writel((readl(fsadc-adccon) ~(1 16)) | (1 2), fsadc-adccon);free_irq(fsadc-irq, fsadc);iounmap(fsadc-adccon);cdev_del(fsadc-cdev);kfree(fsadc);unregister_chrdev_region(dev, 1);return 0;
}static const struct of_device_id fsadc_of_matches[] {{ .compatible fs4412,fsadc, },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsadc_of_matches);struct platform_driver fsadc_drv { .driver { .name fsadc,.owner THIS_MODULE,.of_match_table of_match_ptr(fsadc_of_matches),}, .probe fsadc_probe,.remove fsadc_remove,
};module_platform_driver(fsadc_drv);MODULE_LICENSE(GPL);
MODULE_AUTHOR(name e-mail);
MODULE_DESCRIPTION(ADC driver);#ifndef _FSADC_H
#define _FSADC_H#define FSADC_MAGIC funion chan_val {unsigned int chan;unsigned int val;
};#define FSADC_GET_VAL _IOWR(FSADC_MAGIC, 0, union chan_val)#define AIN0 0
#define AIN1 1
#define AIN2 2
#define AIN3 3#endif代码第 26 行至第 29 行是各寄存器的指针。代码第 32 行是用于保存采样(转换)结果的变量。代码第32 行是用于同步采结束的完成量。 在fsadc_probe 函数中与字符设备相关的代码和前面基本一样接下来就是资源的获取、IO 内存的映射、中断的获取和中断处理函数的注册。然后就是初始化 ADC将ADCCON 寄存器的第 16 位和第 14 位置 1表示采样精度为 12位使能预分频。并且预分配值设为19即为20分频这是因为在 U-Boot 中我们将 APB 的时钟设为了100MHz。最后没有设置读启动位也就意味着采样的启动是靠 ADCCON 寄存器的比特0来控制的。 fsadc_ioctl 函数是处理采样的关键函数这里用到了自定义的一个联合体 unionchan_val它的定义如下。
union chan_val {unsigned int chan;unsigned int val;
}; 有两个成员分别是 chan 和 val。命令 FSADC_GET_VAL 是读写类型的在应用层往驱动层的传递过程中传递的是要使用的ADC通道chan,在驱动层往应用层传递的过程中传递的是得到的采样值 val。代码第 69 行至第72行先从应用层得到通道号然后代码第73行将通道号写入到ADCMUX 寄存器中接下来将ADCCON 寄存器的比特0置1启动采样最后就调用wait_for_completion_interruptible 来等待完成量。当采样完成时中断处理函数自动被调用代码第 92 行先读取了转换结果值寄存器 ADCDAT然后将 1写入 CLRINT 寄存器中清除中断最后唤醒等待完成量的进程。进程被唤醒后代码第77行和第78 行得到采样值并返回给上层。 测试代码如下。
#include stdio.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include sys/ioctl.h
#include fcntl.h
#include errno.h#include fsadc.hint main(int argc, char *argv[])
{int fd;int ret;union chan_val cv;fd open(/dev/adc, O_RDWR);if (fd -1)goto fail;while (1) {cv.chan 3;ret ioctl(fd, FSADC_GET_VAL, cv);if (ret -1)goto fail;printf(current volatage is: %.2fV\n, 1.8 * cv.val / 4095.0);sleep(1);}
fail:perror(adc test);exit(EXIT_FAILURE);
}将数字值转换为对应的模拟值使用了下面的表达式 1.8 * cv.val / 4095.0 1.8 是满量程的电压4095.0 是满量程时对应的数字值(因为是 12位)cv.val 是采样得到的数字用1.8 乘以采样值和满量程数字值之比就能得到当前的电压编译和测试的命令如下旋转电位器旋钮可以看到电压变化。 新网线就是丝滑之前还会突然断开。