一、Exynos4412 A/D转换器概述
1. 概述
ADC(Analog-to-Digital Converter),就是模数转换器。从字面上看,A我们称为模拟信号(Analog signal),D我们称为数字信号(digital signal)。
模数转换器,在电子技术中即是将模拟信号转换成数字信号,也称为数字量化。当然还有一种叫DAC,就是数模转换,意思相反,即是将数字信号转换成模拟信号。
在我们的日常生活中,会遇到很多的物理量,遇到很多的物理参数,其中,我们经常遇到的物理参数,如电流、电压、温度、压力、速度等电量或非电量都是模拟量。
模拟量的大小是连续分布的,且经常也是时间上的连续函数。要使计算机或数字仪表能识别、处理这些信号,必须首先将这些模拟信号转换成数字信号;而经计算机分析、处理后输出的数字量也往往需要将其转换为相应模拟信号才能为执行机构所接受。这样,就需要一种能在模拟信号与数字信号之间起桥梁作用的电路—模数和数模转换器。
2. 分类
目前常见的A/D转换器中,有:权电阻网络D/A转换器、倒T型电阻网络D/A转换器、权电流型D/A转换器、权电容网络D/A转换器、开关树形D/A转换器。
3. A/D转换主要技术指标
1)分辨率
A/D转换器的分辨率用输出二进制数的位数表示,位数越多,误差越小,转换精度越高。
2)量化误差
在A/D转换中由于整量化产生的固有误差。量化误差在±1/2LSB(最低有效位)之间。
3)转换时间
转换时间是指A/D转换器完成一次转换所需的时间。转换时间是指从接到转换控制信号开始,到输出端得到稳定的数字输出信号所经过的这段时间。
4)偏移误差
输入信号为零时输出信号不为零的值,可外接电位器调至最小。
5)满刻度误差
满刻度输出时对应的输入信号与理想输入信号值之差。
6)线性度
实际转换器的转移函数与理想直线的最大偏移,不包括以上3种误差。
其他指标还有:绝对精度(Absolute Accuracy)、相对精度(Relative Accuracy)、微分非线性、单调性和无错码、总谐波失真(Total Harmonic Distortion,THD)和积分非线性。
二、Exynos 4412 A/D转换器简介
1.简述
10位或12位CMOS再循环式模拟数字转换器,它具有10通道输入,并可将模拟量转换至10位或12位二进制数。5Mhz A/D 转换时钟时,最大1Msps的转换速度。A/D转换具备片上采样保持功能,同时也支持待机工作模式。
2.特性
ADC接口包括如下特性。
1)10bit/12bit输出位可选。
2)微分误差 1.0LSB。
3)积分误差 2.0LSB。
4)最大转换速率5Msps.
5) 功耗少,电压输入1.8V。
6)电压输入范围 0~1.8V。
7)支持偏上样本保持功能。
8)通用转换模式。
3. A/D功能模块图
图1 ADC Functional Block Diagram
2. Exynos4412 A/D 控制器寄存器
1)A/D控制寄存器ADCCON
2)A/D数据寄存器
3)A/D通道Mux寄存器
三、A/D转换器实例
1. 电路连接如下:
利用一个电位计输出电压到4412的AIN3管脚。输入的电压范围为0~1.8V。
1. 代码如下:
#include <asm/ioctl.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#define ADC_MAGIC 'A'
#define CMD_ONE 1
#define ADC_CMD_GET _IOR(ADC_MAGIC, CMD_ONE, int)
//寄存器定义
#define ADC_CFG (0x10010118)
#define ADCCON (0x126C0000)
#define ADCDAT (0x126C000C)
#define ADCMUX (0x126C001C)
dev_t dev;
int minor_base = 0;
int adc_count = 1;
char adc_name[] = "adc";
struct cdev *pcdev = NULL;
struct class *pcls = NULL;
static unsigned int *adccfg;
static unsigned int *adcmux;
static unsigned int *adccon;
static unsigned int *adcdat;
int AdcInit(void)
{
int ret = 0;
adccfg = ioremap(ADC_CFG, 4);
if (adccfg == NULL) {
printk("ioremap adccfg\n");
ret = -ENOMEM;
return ret;
}
adcmux = ioremap(ADCMUX, 4);
if (adcmux == NULL) {
printk("ioremap adcmux\n");
ret = -ENOMEM;
goto ERR1;
}
adccon = ioremap(ADCCON, 4);
if (adccon == NULL) {
printk("ioremap adccon\n");
ret = -ENOMEM;
goto ERR2;
}
adcdat = ioremap(ADCDAT, 4);
if (adcdat == NULL) {
printk("ioremap adcdat\n");
ret = -ENOMEM;
goto ERR3;
}
//ADC_CFG &= ~(0x1 << 16);
writel(readl(adccfg) & ~(0x1<<16), adccfg); //Bit_16:Select ADC Mux 0:General ADC, 1:MTCADC
//ADCMUX = 0x3;
writel(3, adcmux); //0x3: 0011 = AIN3 //原理图管脚查到XadcAIN3
//12bit A/D conversion; enable A/D converter prescaler; prescaler value:20; A/D conversion start by read
//ADCCON = (0x1<<16) | (0x1<<14) | (19<<6) | 0x1<<1;
//ADCDAT & 0xfff;
writel((0x1<<16) | (0x1<<14) | (19<<6) | 0x1<<1, adccon);
return 0;
ERR3:
iounmap(adccon);
ERR2:
iounmap(adcmux);
ERR1:
iounmap(adccfg);
return ret;
}
unsigned int adc_get(void)
{
unsigned int adc_value;
unsigned int temp_value;
readl(adcdat);
while(!(readl(adccon) & (0x1<<15)));
adc_value = readl(adcdat) & 0xfff;
temp_value = 9999 * adc_value / 4095;
return temp_value;
}
long adc_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
//printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
switch (cmd) {
case ADC_CMD_GET:
*(unsigned int *)arg = adc_get();
break;
default:
return -EINVAL;
}
return 0;
}
struct file_operations fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = adc_ioctl,
};
int adc_init(void)
{
int ret;
int major;
struct device *pdevice = NULL;
//printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
ret = alloc_chrdev_region(&dev, minor_base, adc_count, adc_name);
if (0 > ret) {
printk("Can't get device number\n");
return ret;
}
pcdev = cdev_alloc();
if (NULL == pcdev) {
printk("cdev alloc failed.\n");
ret = -ENOMEM;
goto UNREG_DEV_NUM;
}
cdev_init(pcdev, &fops);
pcdev->owner = THIS_MODULE;
ret = cdev_add(pcdev, dev, adc_count);
if (0 > ret) {
printk("add cdev failed.\n");
goto DEL_CDEV;
}
pcls = class_create(pcdev->owner, adc_name);
if (IS_ERR(pcls)) {
printk("class create failed.\n");
ret = PTR_ERR(pcls);
goto DEL_CDEV;
}
major = MAJOR(dev);
pdevice = device_create(pcls, NULL, MKDEV(major, minor_base), NULL, "%s%d", adc_name, minor_base);
if (IS_ERR(pdevice)) {
printk("device %s%d create failed.\n", adc_name, minor_base);
ret = PTR_ERR(pdevice);
goto DESTROY_CLASS;
}
//printk("Body temperature module init done!\n");
if (0 != AdcInit())
{
goto DESTROY_CLASS;
}
printk("adc init\n");
return 0;
DESTROY_CLASS:
class_destroy(pcls);
DEL_CDEV:
cdev_del(pcdev);
UNREG_DEV_NUM:
unregister_chrdev_region(dev, adc_count);
return ret;
}
void adc_exit(void)
{
device_destroy (pcls, MKDEV(MAJOR(dev), minor_base));
class_destroy(pcls);
cdev_del(pcdev);
unregister_chrdev_region(dev, adc_count);
printk("Good bye, adc module\n");
//printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
}
module_init(adc_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ruth Wei");
MODULE_DESCRIPTION("adc for demo");
MODULE_VERSION("1.0.1");
2. 编译测试
经过交叉编译后,加载驱动,写一个应用程序可以读取ADC转换后的值。
四、总结
本文从模数转换器的技术指标和电路连接等方面进行分析,最终写出驱动程序,可以在4412板子上进行测试。
嵌入式物联网需要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去!
无偿分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!某鱼上买估计至少要好几十。(点击找小助理领取)