ARM ADC转换编程实践

一、ADC的引入

1、什么是 ADC

(1) ADC : analog digital converter,AD 转换,模数转换(也就是模拟转数字)。

(2) CPU 本身是数字的,而外部世界变量(如电压、温度、高度、压力···)都是模拟的,所以需要用 CPU 来处理这些外部的模拟变量的时候,就需要做 AD 转换。


2、为什么需要ADC

(1) 为了用数字技术来处理外部的模拟物理量。


3、关于模拟量和数字量

(1) 模拟的就是连续的,现实生活当中的时间、电压、高度等都是模拟的(连续分布的,划分的话可以无限的更小划分)。模拟量反映在数学里面就是无限的小数位(从 0 到 1 之间有无数个数)。

(2) 数字的就是离散的,离线的就是不连续的。这种离散处理实际上是从数学上对现实中的模拟量的一种有限精度的描述。数字化就是离散化,就是把连续分布的模拟量按照一定精度进行取点(采样)变成有限多个不连续分布的数字值,就叫数字量。

(3) 数字化的意义就在于,可以用(离散)数学来简化描述模拟量,这东西是计算机技术的基础。

(4) 计算机处理参量的时候都是数字化的,计算机需要数字化的值来参与运算。如果系统输入参数中有模拟量,就需要外加 AD 转换器将模拟量转成数字量,再给计算机。


4、有 AD 自然就有 DA

(1) AD 是 analog to digital,DA 自然就是 digital to analog,数字转模拟。

(2) 纯粹用 cpu 是不可能实现数字转模拟,因为 cpu 本身就是数字的。使用一些(具有一些积分或微分效果的)物理器件就可实现数字转模拟。

(3) 数字转模拟的作用。譬如可以用来做波形发生器。


二、ADC 的主要相关概念

1、量程(模拟量输入范围)

(1) AD 转换器是一个电子器件,所它只能输入电压信号。其他种类的模拟信号要先经过传感器(Sensor)的转换变成模拟的电压信号,然后才能给 AD。

(2) AD 输入端的模拟电压要求有一个范围,一般是 0~3.3V 或 0~5V 或者是 0~12V 等等。模拟电压的范围是 AD 芯片本身的一个参数。实际工作时,给 AD 的电压信号不能超过这个电压范围。


2、精度(分辨率 resolution)

(1) AD 转换输出的数字值是有一定的位数的(譬如说 10 位,意思就是输出的数字值是用 10 个二进制位来表示的,这种就叫 10 位 AD)。这个位数就表示了转换精度。

(2) 10 位 AD 就相当于把整个范围分成了 1024 个格子,每个格子之间的间隔,就是电压的表示精度。假如 AD 芯片的量程是 0~3.3V,则每个格子代表的电压值是 3.3V/1024 =0.0032265V。如果此时 AD 转换后得到的数字量是 447,则这个数字量代表的模拟值是:447×0.0032265V=1.44V。

(3) AD 的位数越多,则每个格子表示的电压值越小,将来算出来的模拟电压值就越精确。

(4) AD 的模拟量程一样的情况下,AD 精度位数越多精度越高,测出来的值越准。但是如果 AD 的量程不一样。譬如 2 个 AD,A 的量程是 0~50V,B 的量程是 0~0.5V,A 是 12 位的,B是 10 位的,可能 B 的精度比 A 的还要高。(A的精度:50/1024=0.04883,B的精度:0.5/4096=0.000122)


3、转换速率(MSPS 与 conventor clock 的不同)

(1) 首先要明白:AD 芯片进行 AD 转换是要耗费时间的。这个时间需要多久,不同的芯片是不一样的,同一颗芯片在配置不一样(譬如说,精度配置为 10 位时的时间,比精度配置为 12 位时要小,譬如说有些 AD 可以配转换时钟,时钟频率高,则转换时间短)时,转换时间也不一样。

(2) 详细的需要时间可以参考数据手册。一般数据手册中描述转换速率用的单位是 MSPS(第一个 M 是兆,S是 sample,就是采样;PS 就是 per second,总的意思就是,兆样本每秒,每秒种转出来多少 M 个数字值)

(3) AD 工作都需要一个时钟,这个时钟有一个范围,我们实际给他配置时不要超出这个范围就可以了。AD 转换是在这个时钟下进行的,时钟的频率控制着 AD 转换的速率。注意:时钟频率和 MSPS 不是一回事,只是成正比,并不是完全相等。譬如 S5PV210 中的 AD 转换器,MSPS = 时钟频率 / 5。

在这里插入图片描述


4、通道数

(1) AD 芯片有多少路 analog input 通道,代表了将来可以同时进行多少路模拟信号的输入。


三、S5PV210 的 ADC 控制器

1、ADC 和(电阻式)触摸屏的关系

(1) ADC 在 210 的数据手册的 Section10.7。

在这里插入图片描述


(2) 电阻式触摸屏本身工作时依赖于 AD 转换,所以在 210 的 SoC 中,电阻触摸屏接口本身和 ADC 接口是合二为一的。或者说电阻触摸屏接口使用了(复用了)ADC 的接口。

在这里插入图片描述

《x210cv3.pdf》


2、ADC 的工作时钟框图

(1) ADCCLK 是 ADC 控制器工作的时钟,也就是《3、转换速率(MSPS 与 conventor clock 的不同)》节中讲到的 conventor clock。从时钟框图可以看出,它是 PCLK(当然是 PCLK_PSYS)经过了一次分频后得到的。所以将来初始化 ADC 控制器时,一定有一个步骤是初始化这里的分频器。

在这里插入图片描述


在这里插入图片描述


3、210 的 10 个 ADC 通道(注意 ADC 引脚和 GPIO 的区别)

(1) 210 一共支持 10 个 ADC 通道,分别叫 AIN[0]~AIN[9] 。理论上可以同时做 10 路 AD 转换。

在这里插入图片描述


(2) SoC 的引脚至少分 2 种:digit 数字引脚和 analog 模拟引脚。我们以前接触的 GPIO 都属于数字引脚,ADC channel 通道引脚属于模拟引脚。数字引脚和模拟引脚一般是不能混用的。


4、ADC 控制器的主要寄存器

TSADCCON0
TSDATX0  TSDATY0		转出来的 AD 值存在这里,我们读也是读这里
CLRINTADC0				清中断
ADCMUX					选择当前正在操作的 AD 通道

(1) 等待触摸屏转换完毕的方法有 2 种:一种是检查标志位,第二种是中断。第一种方式下,我们先开启一次转换,然后循环不停检查标志位,直到标志位为 1 ,表明已经转换完可以去读了;第二种方式下就是设置好中断,写好中断 isr 来读取 AD 转换数据。然后开启中断后,CPU 就不用管了,等 AD 转换完成后,会生成一个中断信号给 CPU,就会进入中断处理流程。第一种方法是同步的,第二种方式是异步的。


(2) AD 转换都是需要反复进行的,那么转完一次一般要立即开启下一次转换,所以需要有一种机制能够在一次转完时自动开启下一次。这个机制就叫 start by read,这个机制的工作方法是:当我们读取本次 AD 转换的 AD 值后,硬件自动开启下一次 AD 转换。

在这里插入图片描述


四、AD 转换的编程实践1

1. AD 控制器初始化

2、循环进行 AD 采样

#include "main.h"


#define  TSADCCON0   0xE1700000 
#define  TSDATX0     0xE170000C
#define  TSDATY0     0xE1700010
#define  CLRINTADC0  0xE1700014
#define  ADCMUX      0xE170001C

#define  rTSADCCON0   (*(volatile unsigned int*)TSADCCON0)  
#define  rTSDATX0     (*(volatile unsigned int*)TSDATX0)   
#define  rTSDATY0     (*(volatile unsigned int*)TSDATY0)   
#define  rCLRINTADC0  (*(volatile unsigned int*)CLRINTADC0)
#define  rADCMUX      (*(volatile unsigned int*)ADCMUX)      


//注意: 第一,延时要确实能延时;第二,延时时间必须合适.
static void delay(void)
{
    volatile unsigned int i, j;
    for (i=0; i < 4000; i++)
	for (j=0; j < 1000; j++);
}

//初始化 ADC 控制器的函数
static void adc_init()
{
    rTSADCCON0 |= (0x1 << 16);   // resolution  set to 12bit
    rTSADCCON0 |= (0x1 << 14);   // enable clock prescaler

    rTSADCCON0 &= ~(0xFF << 6);
    rTSADCCON0 |= (65 << 6);     // convertor clock = 66/66 M = 1 MHZ, MSPS = 200KHZ

    rTSADCCON0 &= ~(0x1 << 2);   // normal operation mode

    rTSADCCON0 &= ~(0x1 << 1);   // disable  start by read mode



    rADCMUX    &= ~(0xF << 0);   // MUX 选择 ADCIN0

}


//测试ADC,完成的功能就是循环检测ADC并得到ADC转换数字
//值打印出来
void adc_test(void)
{
    int val = 0;
    adc_init();	

    while(1)
    {

        //第一步,人为手工开启ADC 转换
	rTSADCCON0 |= (0x1 << 0);  
	
	//第二步,等待ADC 转换完毕
	while (!(rTSADCCON0 & (0x1 << 15))) ;


	//第三步,读取ADC 的数字值
	//第四步,处理/显示数字值
	val = rTSDATX0;
	printf("x: bit14 = %d.\r\n", (val & (0x1 << 14)));
	printf("x: adc value = %d.\r\n", (val & (0xFFF << 0)));
	
	val = rTSDATY0;
	printf("y: bit14 = %d.\r\n", (val & (0x1 << 14)));
	printf("y: adc value = %d.\r\n", (val & (0xFFF << 0)));

	
	//第五步,延时一段
	delay();

    }
}

实验现象:

在这里插入图片描述


五、AD 转换的编程实践2

1、start by read模式介绍

(1) 应用方法:开启 start by read 模式,第一次先读一次丢掉,这次读就能开启下一次 AD 转换,然后以后就可以不停的读取 AD 值了。

#include "main.h"


#define  TSADCCON0   0xE1700000 
#define  TSDATX0     0xE170000C
#define  TSDATY0     0xE1700010
#define  CLRINTADC0  0xE1700014
#define  ADCMUX      0xE170001C

#define  rTSADCCON0   (*(volatile unsigned int*)TSADCCON0)  
#define  rTSDATX0     (*(volatile unsigned int*)TSDATX0)   
#define  rTSDATY0     (*(volatile unsigned int*)TSDATY0)   
#define  rCLRINTADC0  (*(volatile unsigned int*)CLRINTADC0)
#define  rADCMUX      (*(volatile unsigned int*)ADCMUX)      


//注意: 第一,延时要确实能延时;第二,延时时间必须合适.
static void delay(void)
{
    volatile unsigned int i, j;
    for (i=0; i < 4000; i++)
	for (j=0; j < 1000; j++);
}

//初始化 ADC 控制器的函数
static void adc_init()
{
    rTSADCCON0 |= (0x1 << 16);   // resolution  set to 12bit
    rTSADCCON0 |= (0x1 << 14);   // enable clock prescaler

    rTSADCCON0 &= ~(0xFF << 6);
    rTSADCCON0 |= (65 << 6);     // convertor clock = 66/66 M = 1 MHZ, MSPS = 200KHZ

    rTSADCCON0 &= ~(0x1 << 2);   // normal operation mode

    rTSADCCON0 |= (0x1 << 1);    // enable  start by read mode

    rADCMUX    &= ~(0xF << 0);   // MUX 选择 ADCIN0
}


//测试ADC,完成的功能就是循环检测ADC并得到ADC转换数字
//值打印出来
void adc_test(void)
{
    int val = 0;
    adc_init();	

    //先启动一次读操作,开启 ADC 转换.注意,此次的数据是无效数据
    val = rTSDATX0;

    while(1)
    {

        //第一步,人为手工开启ADC 转换
	//rTSADCCON0 |= (0x1 << 0);  
	
	//第二步,等待ADC 转换完毕
	while (!(rTSADCCON0 & (0x1 << 15))) ;


	//第三步,读取ADC 的数字值
	//第四步,处理/显示数字值
	val = rTSDATX0;
	printf("x: bit14 = %d.\r\n", (val & (0x1 << 14)));
	printf("x: adc value = %d.\r\n", (val & (0xFFF << 0)));
	
	val = rTSDATY0;
	printf("y: bit14 = %d.\r\n", (val & (0x1 << 14)));
	printf("y: adc value = %d.\r\n", (val & (0xFFF << 0)));

	
	//第五步,延时一段
	delay();

    }
}

实现现象:

在这里插入图片描述


源自朱有鹏老师.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值