目录
一、ADC介绍
ADC是模拟到数字转换器(Analog-to-Digital Converter)的缩写。它是一种电子设备或模块,2440内部拥有一个ADC外设。用于将连续变化的模拟信号转换为离散的数字信号,以便数字系统(如微处理器、微控制器等)能够对其进行处理和分析。

1.模拟信号和数字信号:
模拟信号:一般是指连续变化的电压信号,其数值在一定范围内变化。
数字信号:由一系列离散的数字表示,只能取有限的值,通常以二进制形式表示。
2.ADC工作原理:
ADC的工作原理是将模拟信号分割成一系列离散的取样,并将每个取样值转换为相应的数字表示。这个过程涉及到四个主要步骤:采样、保持、量化、编码。
(1)采样:
ADC将连续变化的模拟信号在一定时间间隔内进行取样。取样频率决定了每秒采集的样本数,通常以赫兹(Hz)表示。采样过程通过保持并测量模拟信号在每个采样时间点的电压值来实现。采样频率必须满足奈奎斯特采样定理(采样频率大于等于信号最高频率的两倍),否则会失真
(2)保持:
采样后需要将电压值稳定一段时间,让后续电路(比较器、逐次逼近寄存器等)有足够时间处理。采样保持电路通常用一个电容 + 开关实现
(3)量化(ADC的核心):
采样得到的连续模拟信号经过量化转换为数字形式。量化是将每个采样值映射到一个离散的数字值的过程。这通常通过比较采样值与参考电压之间的差异,并将其转换为数字表示。
量化原理:
ADC量化的过程是相对于一个基准值的,这个基准值称之为基准电压。一般采用逐次逼近法的ADC会先拿采用电压Vadc跟基准电压Vref的1/2进行比较,如果Vadc>Vref,则结果为1,否则结果为0。之后继续拿Vadc和Vref的1/4或Vref的3/4继续比较。这个过程有点像二分法,每次比较都会使量化的结果逼近真实值。很明显,比较的次数决定了测量的精度,这个精度被称之为ADC的分辨率。比如一个比较了8次的ADC外设,它就称为8位ADC,其结果是0~255之间的一个数值,设该数值为n,那么实际电压就是Vref* (n/255)。如果把比较次数增加到10次,结果就是0~1023之间的一个数。常见的分辨率包括8位,10位,12位和16位。
量化过程具体怎么比:
-
ADC 内部有一个比较器和电阻分压网络
-
逐次比较输入电压与内置电阻产生的电压,从 MSB 到 LSB 依次确定每一位。
-
每次比较就是一次"量化决策":输入电压落在哪个电压区间,就输出对应的二进制码。
(4)编码:
将量化后的台阶序号(十进制 0~4095)转换为二进制或十六进制数字,供单片机或 DSP 读取。
例如 1024 → 0x400(二进制 0100 0000 0000)。
二、IMX6ULL的ADC配置
I.MX6ULL内部具有两个ADC控制器。都是采用逐次逼近法(量化那里有介绍) 设计的。每路ADC的分辨率可选,分别为8/10/12位。I.MX6ULL内部ADC具有自动校准功能,因此能够保证更高的测量精度;最大转换速率为1MHz,即完成一次转换所需的时间最快只需要1us。I.MX6ULL的ADC还具有硬件平均功能和比较功能,大大方便了软件设计。我们实验所使用的开发板将基准电压设置为3.3V,参考《IMX6ULL_CORE_V2.0(核心板原理图).pdf 》;
1.ADC通道:
每个ADC拥有10个通道,这里的通道就是指输入的电压信号是从哪个引脚输入进来的意思。10个引脚其实就是GPIO1_IO00~GPIO1_IO09这10个引脚 。参考《IMX6ULL参考手册.pdf 》P394页。我们在配置引脚功能时只需将引脚配置为GPIO就行,电器配置时可以保持默认配置下使能Keeper即可,这里我们使用通道1 。
2.ADC时钟源:
I.MX6ULL的时钟源可以是igpclk、igpclk/2和ADACK,其中ADACK是I.MX6ULL内部提供的时钟源,只能提供给ADC外设使用,这样做的目的是保证系统处于低功耗状态时,ADC依旧能够运行。ADACK的时钟默认为20MHz。我们接下来的操作就把时钟配置为ADACK。
3.ADC配置流程:
I.MX6ULL的ADC使用还是很简单的,首先在初始化的时候配置好引脚 ,例如GPIO1_IO01这个引脚;之后配置ADCx_CFG和ADCx_GC寄存器(有关这两个寄存器的详细说明请参考手册)。然后启动一次自动校准,系统校准完成后每次向ADCx_HC0寄存器中写入一次数据(其实就是切换到别的通道之后再切换回来)就会启动一次ADC转换。转换结束之后的数据就保存再ADCx_R0寄存器中。
手册第412页有示例配置ADC的流程图:

(1)ADC初始化:
根据示例流程图配置即可

(2)读取ADC数值:
选择ADC通道,每次切换通道时会引发一次ADC转换。所以在采集数据函数中切换到别的通道再切换回来相当于启动一次ADC转换,转换结束之后的数据就保存在ADCx_R0寄存器中。
代码为:

(3)整体代码:
#include "MCIMX6Y2.h"
#include "epit.h"
#include "fsl_iomuxc.h"
int adc_init(ADC_Type* base)
{
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO01_GPIO1_IO01, 0);
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO01_GPIO1_IO01, 0x10b0);
//配置时钟和模式
base->CFG |= (0x3 << 0);
base->CFG |= (0x2 << 2);
//打开ADACK时钟
base->GC |= (1 << 0);
base->GS |= (1 << 1); //写1先清零校准标记位
//校准
base->GC |= (1 << 7);//开始校准
while (base->GC & (1 << 7))
; //校准结束
//判断校准结果
if (base->GS & (1 << 1)) //校准失败,返回
return -2;
//校准成功继续
return 0;
}
unsigned short adc_read(ADC_Type* base)
{
unsigned short adc_value = 0;
base->HC[0] |= (0x1f << 0); //关闭通道
//切换通道启动转换
base->HC[0] = 0x1; //设置为通道1
//等待转换
int time = 50;
while (!(base->HS & (1 << 0)) && time--)
{
delay_us(1);
}; //为0则未完成
if (time <= 0) //超时保护
{
return -1;
}
//转换完成,读取结果
adc_value = (base->R[0] & 0xfff);
return adc_value;
}
4.电压计算:
写好adc后,读出来的数值是一个二进制的,我们需要把他转换成电压:
这里用3.3V和12位ADC做例子,假如adc的读数为0x400

那么实际电压为:
主函数代码为:
#include "MCIMX6Y2.h"
#include "adc.h"
#include "beep.h"
#include "clk.h"
#include "core_ca7.h"
#include "epit.h"
#include "fsl_iomuxc.h"
#include "gpt.h"
#include "i2c.h"
#include "irq.h"
#include "key.h"
#include "led.h"
#include "stdio.h"
#include "uart.h"
void gpio1_io18_handler(void)
{
GPIO1->DR ^= (1 << 3);
GPIO5->DR ^= (1 << 1);
}
void epit1_irq_handler()
{
GPIO1->DR ^= (1 << 3);
GPIO5->DR ^= (1 << 1);
}
int main(void)
{
unsigned short data = 0;
system_irq_init();
clk_init();
// led_init();
beep_init();
key_irq_init(gpio1_io18_handler);
// epit1_init(epit1_irq_handler);
gpt1_init();
uart_init(UART1);
// printf("i2c 1 start init\r\n");
// i2c_init(I2C1);
// printf("i2c 1 init end\r\n");
while (1)
{
int ret = adc_init(ADC1);
if (ret < 0)
{
printf("adc init failed\r\n");
delay_ms(3000);
continue;
}
data = adc_read(ADC1);
unsigned int adc_value = data * 300 / 4096 ;
printf("data = 0x%x value = %d.%02d\r\n", data, adc_value / 100, adc_value % 100);
delay_ms(3000);
}
return 0;
}
最后上板验证一下(用一根线接这个IO引脚一根接电压或接地,看数据是否正确)
三、总结
要搞清楚ADC工作原理,特别是量化的过程。