ADC/DAC 详解
Table of Contents
1 介绍
1.1 DAC1
Digital-Analog Converter,数模转换器。将数字量转换成模拟量,使输出的模拟量与输入的数字量成正比的电路。简称D/A转换器或DAC。
1.2 ADC
Analog-Digital Converter,模数转换器。将模拟量转换为数字量。简称A/D转换器或ADC。
2 原理
2.1 D/A
数字量是用代码按数位组合而成的,对于有权码,每位代码都有一定的权值,如能将每一位代码按其权的大小转换成相应的模拟量,然后,将这些模拟量相加,即可得到与数字量成正比的模拟量,从而实现数字量—模拟量的转换。
D/A 转换主要由数码寄存器、n 位模拟开关、解码网络、求和电路等组成。
2.1.1 种类
D/A 转换器的种类很多,例如:T 型电阻网络、倒 T 型电阻网络、权电流、权电流网络、CMOS 开关型等。
Figure 1: 倒T型网络原理图
2.2 A/D
2.2.1 分类
2.2.2 过程
由于输入的模拟信号在时间上是连续量,所以一般的 A/D 转换过程为:采样、保持、量化和编码。
- 采样
采样是将随时间连续变化的模拟量转换为在时间上离散的模拟量。
需遵循采样定理:fs>=2fm 即采用信号的频率需不小于输入模拟信号的最好频率分量的频率的两倍。
- 保持
在取样电路后要求将所采样的模拟信号保持一段时间,以为后续的量化编码的过程提供一个稳定的值。
- 量化和编码
数字信号在数值上是离散的。采样–保持电路的输出电压还需按某种近似方式归化到与 之相应的离散电平上,任何数字量只能是某个最小数量单位的整数倍。量化又称幅值量化,把采样信号经过舍入或截尾的方法变为只有有限个有效数字的数,这一过程称为量化。若取信号可能出现的最大值为A,另其分为D个间隔,则每个间隔长度为R=A/D,R称为量化增长或量化步长。2
量化后的数值最后还需通过编码过程用一个代码表示出来。将离散幅值经量化变为二进制数字的过程中,把采样后的节点变为程序代码可以识别的二进制码,把这些节点以合理的顺序排列出来,实现把一连串的模拟信号用数字信号描述出来的过程即编码。经编码后得到的代码就是A/D 转换器输出的数字量。
两种近似量化方式:只舍不入量化方式、四舍五入量化方式。为减小误差,后者优于前者。
3 单片机中的具体应用
3.1 STC
3.1.1 原理
这里 10 位的 A/D 转换结果位数由不同的 CLK-DIV.5(PCON2.5)/ADRJ值来决定。
STC15 系列单片机 ADC 由多路选择开关、比较器、逐次比较寄存器、10 位 DAC、转换结果寄存器(ADC-RES 和 ADC-RESL)以及 ADC-CONTR 构成。
通过模拟多路开关,将通道 ADC0~7 的模拟量输入并且送给比较器。用数/模转换器(DAC)转换的模拟量与输入的模拟量通过比较器进行比较,将比较结果保存到逐次比较寄存器,并通过逐次比较寄存器输出转换结果。AD 转换结束后,最终的转换结果保存到 ADC 转换结果寄存器 ADC-RES 和 ADC-RESL,同时,置位 ADC 控制寄存器ADC-CONTR 中的 A/D 转换结束标志位 ADC-FLAG,以供程序查询或发出中断申请。其中,模拟通道由 ADC 控制寄存器 ADC-CONTR 中的 CHS2~CHS0 来确定。ADC 的转换速度由 ADC 控制寄存器中的 SPEED1 和 SPEED0 确定。在使用 ADC 之前,应先给 ADC 上电,也就是置位 ADC控制寄存器中的 ADC-POWER 位。
公式如下:
- 当 ADRJ=0 时(结果取 10 位),Vin = [(ADC-RES[7:0],ADC-RESL[1:0])/ 1024 ] * Vcc。
- 当 ADRJ=0 时(结果取 8 位),Vin = [(ADC-RES[7:0])/ 256 ] * VCC
注意:这个逐次比较是从最高位开始的。
- 当 ADRJ=1 时(结果取 10 位),Vin = [(ADC-RES[1:0],ADC-RESL[7:0])/ 1024 ] * Vcc。
3.1.2 代码例程(STC12)
BYTE ch=0; //定义A/D通道
void adc_isr interrupt 5 using 1
{
ADC_CONTR &=!ADC_FLAG;
SendData(ADC_RES);
if(++ch>7)ch=0;
ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | ch;
}
void InitADC( )
{
P1ASF = 0xff; //Set all P1 as analog input port
ADC_RES = 0; //Clear previous result
ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | ch;
Delay(2);//ADC power-on delay and Start A/D conversion
}
3.2 STM323
3.2.1 原理
STM32 拥有 1~3 个 ADC ( STM32F101/102 系列只有 1 个 ADC),这些 ADC 可以独立 使用,也可以使用双重模式(提高采样率)。 STM32 的 ADC 是 12 位逐次逼近型的模拟数 字转换器。它有 18 个通道,可测量 16 个外部和 2 个内部信号源。各通道的 A/D 转换可 以单次、连续、扫描或间断模式执行。 ADC 的结果可以左对齐或右对齐方式存储在 16 位 数据寄存器中。
STM32 的 ADC 最大的转换速率为 1Mhz,也就是转换时间为 1us (在 ADCCLK=14M,采样周期 为 1.5 个 ADC 时钟下得到),不要让 ADC 的时钟超过 14M,否则将导致结果准确度下降。STM32 将 ADC 的转换分为 2 个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换,在注入通道被转换完成之后,规则通道才得以继续转换。STM32 其 ADC 的规则通道组最多包含 16 个转换,而注入通道组最多包含 4 个通道。
STM32 的 ADC 在单次转换模式下,只执行一次转换,该模式可以通过 ADCCR2 寄存器的 ADON 位(只适用于规则通道)启动,也可以通过外部触发启动(适用于规则通道和注入通道),这是 CONT 位为 0。以规则通道为例,一旦所选择的通道转换完成,转换结果将被存在 ADCDR 寄存器中,EOC(转换结束)标志将被置位, 如果设置了 EOCIE,则会产生中断。然后 ADC 将停止,直到下次启动 。
STM32 的 DAC 模块(数字/模拟转换模块)是 12 位数字输入,电压输出型的 DAC。 DAC可以配置为 8 位或 12 位模式,也可以与 DMA 控制器配合使用。 DAC 工作在 12 位模式时,数据可以设置成左对齐或右对齐。 DAC 模块有 2 个输出通道,每个通道都有单独的转换器。在双 DAC 模式下, 2 个通道可以独立地进行转换,也可以同时进行转换并同步地更新 2 个通道的输出。 DAC 可以通过引脚输入参考电压 VREF+以获得更精确的转换结果。
3.2.2 代码例程(标准库函数版)
- A/D
void adc_init() { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO|RCC_APB2Per iph_ADC1,ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6);//12M 最大 14M 设置 ADC 时钟(ADCCLK) GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;//ADC GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; //模拟输入 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); //设置指定 ADC 的规则组通道,设置它们的转化顺序和采样时间 ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_239Cycles5); ADC_Cmd(ADC1,ENABLE); ADC_ResetCalibration(ADC1);//重置指定的 ADC 的校准寄存器 while(ADC_GetResetCalibrationStatus(ADC1));//获取 ADC 重置校准寄存器的状态 ADC_StartCalibration(ADC1);//开始指定 ADC 的校准状态 while(ADC_GetCalibrationStatus(ADC1));//获取指定 ADC 的校准程序 ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能或者失能指定的 ADC 的软件转换启动功能 } adc_init(); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));//转换结束标志位 value=value+ADC_GetConversionValue(ADC1);//返回最近一次 ADCx 规则组的转换结果
- D/A
void dac_init() //DAC 初始化 { GPIO_InitTypeDef GPIO_InitStructure; DAC_InitTypeDef DAC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC,ENABLE); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;//DAC_1 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;//模拟量输入 GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_SetBits(GPIOA,GPIO_Pin_4);//输出高 DAC_InitStructure.DAC_Trigger=DAC_Trigger_None;//不使用触发功能 DAC_InitStructure.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用三角波 //屏蔽 幅值设置 DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0; //关闭缓存 DAC_InitStructure.DAC_OutputBuffer=DAC_OutputBuffer_Disable; DAC_Init(DAC_Channel_1,&DAC_InitStructure);//初始化 DAC 通道 1 DAC_Cmd(DAC_Channel_1,ENABLE);//使能 DAC1 DAC_SetChannel1Data(DAC_Align_12b_R,0);//12 位 右对齐 写 0 数据 }
3.3 MSP430
3.3.1 原理4
MSP430中有ADC10和ADC12两个模块,ADC12A 模块支持快速12位数模转换,具有一个12位的逐次渐进(SAR)内核,转换控制缓冲区允许没有CPU干预的情况下,多达16路独立ADC采样值进行转换和保持。可以模拟多通道,并且有8路外部通道(A0-A7)和4路内部通道。具有高达200ksps的最大转换率。
ADC 内核将一个模拟输入信号转换为 12 位的数字信号,并将其结果存储在转换存储器中。该内核采用两个可编程/选择的电压(VR+和 VR-)作为转换的上限和下限(即我们说的ADC和DAC的基准信号)。当输入信号大于或等于 VR+时,数字输出(NADC )为最大值(0FFFh),而当输入信号小于者等于 VR-时,输出为 0。在转换控制存储器中定义输入通道和参考电压(VR+和 VR-)。ADC转换结果 Nadc 的计算公式为:
Nadc = 4095 × (Vin-VR -)⁄(VR +-VR -)
其中,4095为212-1,12位ADC将VR+和VR-之间分割成212 等份,然后将输入的模拟信号进行转换,输出0~4095的数字。
ADC12A 内核可通过两个寄存器 ADC12CTL0 和 ADC12CTL1 来进行配置,通过 ADC12ON 位使能内核。在不需要转换时可以关闭 ADC12A以节省功耗。除很少例外,只有当 ADC12ENC = 0 时才能修改ADC12A 控制位。在执行转换前 ADC12ENC 必须置 1。
ADC12A 支持 8 位、10 位及 12 位分辨率模式,可以通过 ADC12RES 位选择。模数转换分别需要 9、11、及 13 个 ADC12CLK 周期。SHI 信号源的极性可以通过 ADC12ISSH 位反转。SAMPCON 信号控制采用周期和启动转换。当 SAMPCON 信号为高时,表示正处在采样过程,SAMPCON 由高到低转换将启动模数转换。ADC12SHP 定义了两种不同的采样时序方法,扩展采样时序和脉冲采样时序。
ADC12共有12个转换通道,设置了16个转换存储器(ADC12MEMx)用于暂时存储转换结果,合理设置后,ADC12硬件会自动将转换结果保存到相应的存储器里。
3.3.2 代码例程5
//******************************************************************************
// MSP430F552x Demo - ADC12, Sample A0, Set P1.0 if A0 > 0.5*AVcc
//
// Description: A single sample is made on A0 with reference to AVcc.
// Software sets ADC12SC to start sample and conversion - ADC12SC
// automatically cleared at EOC. ADC12 internal oscillator times sample (16x)
// and conversion. In Mainloop MSP430 waits in LPM0 to save power until ADC12
// conversion complete, ADC12_ISR will force exit from LPM0 in Mainloop on
// reti. If A0 > 0.5*AVcc, P1.0 set, else reset.
//
// MSP430F552x
// -----------------
// /|\| |
// | | |
// --|RST |
// | |
// Vin -->|P6.0/CB0/A0 P1.0|--> LED
//
// Bhargavi Nisarga
// Texas Instruments Inc.
// April 2009
// Built with CCSv4 and IAR Embedded Workbench Version: 4.21
//******************************************************************************
#include <msp430.h>
int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
ADC12CTL0 = ADC12SHT02 + ADC12ON; // Sampling time, ADC12 on
ADC12CTL1 = ADC12SHP; // Use sampling timer
ADC12IE = 0x01; // Enable interrupt
ADC12CTL0 |= ADC12ENC;
P6SEL |= 0x01; // P6.0 ADC option select
P1DIR |= 0x01; // P1.0 output
while (1)
{
ADC12CTL0 |= ADC12SC; // Start sampling/conversion
__bis_SR_register(LPM0_bits + GIE); // LPM0, ADC12_ISR will force exit
__no_operation(); // For debugger
}
}
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = ADC12_VECTOR
__interrupt void ADC12_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(ADC12_VECTOR))) ADC12_ISR (void)
#else
#error Compiler not supported!
#endif
{
switch(__even_in_range(ADC12IV,34))
{
case 0: break; // Vector 0: No interrupt
case 2: break; // Vector 2: ADC overflow
case 4: break; // Vector 4: ADC timing overflow
case 6: // Vector 6: ADC12IFG0
if (ADC12MEM0 >= 0x7ff) // ADC12MEM = A0 > 0.5AVcc?
P1OUT |= BIT0; // P1.0 = 1
else
P1OUT &= ~BIT0; // P1.0 = 0
__bic_SR_register_on_exit(LPM0_bits); // Exit active CPU
case 8: break; // Vector 8: ADC12IFG1
case 10: break; // Vector 10: ADC12IFG2
case 12: break; // Vector 12: ADC12IFG3
case 14: break; // Vector 14: ADC12IFG4
case 16: break; // Vector 16: ADC12IFG5
case 18: break; // Vector 18: ADC12IFG6
case 20: break; // Vector 20: ADC12IFG7
case 22: break; // Vector 22: ADC12IFG8
case 24: break; // Vector 24: ADC12IFG9
case 26: break; // Vector 26: ADC12IFG10
case 28: break; // Vector 28: ADC12IFG11
case 30: break; // Vector 30: ADC12IFG12
case 32: break; // Vector 32: ADC12IFG13
case 34: break; // Vector 34: ADC12IFG14
default: break;
}
}