《嵌入式硬件(十八):基于IMX6ULL的ADC操作》
一、ADC
1.基本概念
        ADC是模拟到数字转换器(Analog-to-Digital Converter)的缩写。它是一种电子设备或模块,2440内部拥有一个ADC外设。用于将连续变化的模拟信号转换为离散的数字信号,以便数字系统(如微处理器、微控制器等)能够对其进行处理和分析。
模拟信号一般是指连续变化的电压信号,其数值在一定范围内变化。而数字信号是由一系列离散的数字表示,只能取有限的值,通常以二进制形式表示。
2.工作原理
ADC的工作原理是将模拟信号分割成一系列离散的取样,并将每个取样值转换为相应的数字表示。这个过程涉及到两个主要步骤:采样和量化。
采样:ADC将连续变化的模拟信号在一定时间间隔内进行取样。取样频率决定了每秒采集的样本数,通常以赫兹(Hz)表示。采样过程通过保持并测量模拟信号在每个采样时间点的电压值来实现。
量化:采样得到的连续模拟信号经过量化转换为数字形式。量化是将每个采样值映射到一个离散的数字值的过程。这通常通过比较采样值与参考电压之间的差异,并将其转换为数字表示。
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位。
3.imx6ull里的ADC
I.MX6ULL内部具有两个ADC控制器。都是采用逐次逼近法设计的。每路ADC的分辨率可选,分别为8/10/12位。 I.MX6ULL内部ADC具有自动校准功能,因此能够保证更高的测量精度;最大转换速率为1MHz,即完成一次转换所需的时间最快只需要1us。 I.MX6ULL的ADC还具有硬件平均功能和比较功能,大大方便了软件设计。我们实验所使用的开发板将基准电压设置为3.3V。
每个ADC拥有10个通道,这里的通道就是指输入的电压信号是从哪个引脚输入进来的意思。10个引脚其实就是GPIO1_IO00~GPIO1_IO09这10个引脚。我们在配置引脚功能时只需将引脚配置为GPIO就行,电器配置时可以保持默认配置下使能Keeper即可。
I.MX6ULL的时钟源可以是igpclk、igpclk/2和ADACK,其中ADACK是I.MX6ULL内部提供的时钟源,只能提供给ADC外设使用,这样做的目的是保证系统处于低功耗状态时,ADC依旧能够运行。 ADACK的时钟默认为20MHz。我们接下来的操作就把时钟配置为ADACK。
I.MX6ULL的ADC使用还是很简单的,首先在初始化的时候配置好引脚,例如GPIO1_IO01这个引脚;之后配置ADCx_CFG和ADCx_GC寄存器。然后启动一次自动校准,系统校准完成后每次向ADCx_HC0寄存器中写入一次数据(其实就是切换到别的通道之后再切换回来)就会启动一次ADC转换。转换结束之后的数据就保存再ADCx_R0寄存器中。
二、原理


1.HC0

2.HS

3.R0

4.CFG(配置寄存器)



5.GC



6.GS


三、代码
1.adc.c
#include "adc.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"void init_adc1_channle1(void)
{IOMUXC_SetPinMux(IOMUXC_GPIO1_IO01_GPIO1_IO01, 0);IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO01_GPIO1_IO01, IOMUXC_SW_PAD_CTL_PAD_PKE(1));//单独设置一位IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO01_GPIO1_IO01, IOMUXC_SW_PAD_CTL_PAD_PUE(0));ADC1->HC[0] &= ~(1 << 7);//HC是数组ADC1->CFG = 0;ADC1->CFG |= (2 << 2) | (3 << 0);ADC1->GC = 0;ADC1->GC |= (1 << 0);printf(adc1_calibration() ? "Calibration completed normally." : "Calibration failed. ");//校准
}int adc1_calibration(void)
{ADC1->GC |= (1 << 7);while((ADC1->GC & (1 << 7)) != 0);if((ADC1->GS & (1 << 1)) == 0){return 1;}else{return 0;}
}unsigned short adc1_get_origin_value(void)
{ADC1->HC[0] = 0;ADC1->HC[0] = 1;while((ADC1->HS & (1 << 0)) == 0);return ADC1->R[0] & 0xFFF;//0000 0000 1111 1111 1111
}void swap(unsigned short *a, unsigned short *b)
{unsigned short t = *a;*a = *b;*b = t;
}void sort(unsigned short *a, int len)
{int i, j;for(i = 0;i < len - 1;++i){for(j = i + 1;j < len;++j){if(a[i] > a[j])//*(a + i){swap(a + i, a + j);}}}
}unsigned int sum_of_array(unsigned short *a, int len)
{unsigned int sum = 0;int i;for(i = 0;i < len;++i){sum += a[i];}return sum;
}float adc1_get_value(void)
{unsigned short a[100];int i;for(i = 0;i < 100;++i){a[i] = adc1_get_origin_value();}sort(a, sizeof(a) / sizeof(a[0]));return sum_of_array(a + 10, 80) / 80 / 4095.0 * 3.3;
}2.adc.h
#ifndef _ADC_H_
#define _ADC_H_extern void init_adc1_channle1(void);
extern int adc1_calibration(void);
extern unsigned short adc1_get_origin_value(void);
extern float adc1_get_value(void);#endif3.main.c
#include "string.h"
#include "led.h"
#include "beep.h"
#include "MCIMX6Y2.h"
#include "key.h"
#include "interrupt.h"
#include "clock.h"
#include "epit.h"
#include "gpt.h"
#include "delay.h"
#include "uart.h"
#include "stdio.h"
#include "i2c.h"
#include "lm75.h"
#include "adc.h"int main(void)
{init_clock();system_interrupt_init();init_led();init_beep();// init_key();
//    init_epit1();init_gpt1();init_uart1(); init_i2c1();init_adc1_channle1();while(1){float f;f = adc1_get_value();int k = f * 1000;int m = k / 1000;int n = k % 1000;printf("%d.%d\n", m, n);}return 0;
}