赞
踩
模数转换器,ADC(Analog to Digital Converter),是一个将模拟信号转换为数字信号的器件(电路),例如将温度、湿度、压力、位置等信息转换为数字信号。但由于数字信号本身不具有实际意义,仅仅表示一个相对大小。所以ADC都需要一个参考模拟量(REF)作为转换的标准。
ADC按工作原理可以分成直接ADC和间接ADC。主要有以下几种:
并联比较型ADC;
逐次逼近型ADC;
双积分型ADC。
其中逐次逼近型ADC是一种直接ADC。由于其采样速率中等,分辨率中等,且位数较多时使用元器件较少等原因(成本较低),所以被广泛应用于集成ADC中。
A/D转换的作用是将时间、幅值连续的模拟信号转换为时间、幅值离散的数字信号。所以,A/D转换一般要经过采样、保持、量化及编码四个过程。
采样是指在时间上将模拟信号离散化,即是将时间上连续的信号转为一系列等时间间隔的信号离散序列。其中离散信号脉冲的幅度取决于输入模拟量。下图列举了一个模拟信号从采样到保持的过程。
采样需要满足采样原理: 采样频率大于模拟信号中最高频率成分的两倍时,采样值才能不失真的反映原来模拟信号。
量化是用有限个幅度值近似原来连续变化的幅度值,把模拟信号的连续幅度变为有限数量的有一定间隔的离散值。而编码则是按照一定的规律,把量化后的值用二进制数字表示。下图列举了12bits ADCFSR为3V时的量化到编码的过程
n:分辨率,用于对输入进行量化的位数
FSR: Full-Scale Range,满量程
LSB: Least Significant Bit,最低有效位
MSB: Most Significant Bit,最高有效位
理论上,n位输出的ADC能区分2^n 个不同等级的模拟输入电压。如上图所示,能分辨的最小输入电压步长LSB = FSR / 2^n = 732uV。
量化误差是由于量化过程引入的误差,通常是以输出误差的最大值形式标出。表示ADC实际输出的数字量和理论输出数字量之间的误差。使用“四舍五入法”时,ADC 转换器的量化误差是 ±½ LSB。
转换时间是指ADC从转换控制信号触发开始,到输出端得到稳定的数字信号所经过的时间。该时间受ADC类型、ADC时钟和外部输入阻抗等因素影响。
STM32中的ADC是逐次逼近型ADC(Successive Approximation ADC),是逐个产生比较电压VREF,并逐次与输入电压分别比较,以逐渐逼近的方式进行A/D转换的。
SAR ADC的转换原理是把输入的模拟信号按规定的时间间隔采样(采样),并与一系列标准的数字信号相比较,数字信号逐次收敛,直至两种信号相等为止(量化),最后输出代表此信号的二进制数(编码)。
结构上主要包括采样保持电路(S/H),比较器(COMPARATOR,COMP),SAR逻辑控制电路、时钟(CLOCK)和时序(TIMING)控制电路及DAC电路。
被采样的脉冲宽度一般是很短的,在下一个采样脉冲到来之前,要暂时保持所采得的样值脉冲幅度,以便进行后续转换。所以,在采样电路之后要加保持电路。下图是一个简单的采样保持电路配置框图。
大多SAR ADC的DAC都使用电容式DAC来提供内在的跟踪/保持功能。电容式DAC是采用电荷再分配原理来产生模拟输出电压的。电容式DAC由N个具有二进制权重值的电容器阵列再加上一个“虚拟LSB”电容器组成。
电容器阵列容量总量要等于2C。
转换步骤数等于 ADC的分辨率,比如10bits ADC就有10个转换步骤,每个 ADC 时钟产生一个数据位。以下步骤以10bits ADC为例。
该状态下,电容充电至电压VIN。Sa切换至VIN,采样期间Sb开关闭合。
该状态下,输入断开,电容保持输入电压。Sb开关打开,然后S1-S11切换至接地,且Sa切换至VREF。
该状态下,每个 ADCCLK 执行一个步骤,每一步完成后 ADC 输出一位数。采用二分法进行逐次逼近到 ADC 的精度(位数)。整个转换过程如下图所示。
整个逐次逼近的步骤如下面的二叉树所示。
比如2.5V输入到以3.3V为参考电压的SAR ADC中,则转换过程如下所示。
第一个逼近步骤时,MSB先设置为1。DAC以1/2 REF去和VIN比较,若VIN > 1/2 REF,则保持MSB = 1(反之则MSB = 0)。等待下一个ADCCLK,执行下一步。
第二个逼近步骤时,MSB往后移动1个bit,再以3/4 REF去和VIN进行比较,若VIN > 3/4 REF,则保持MSB = 1(反之则MSB = 0)。等待下一个ADCCLK,执行下一步,一直到所有bit确定,然后输出编码值。
STM32中的ADC转换时间 = 采样周期 + 转换周期。
由采样周期设置决定,要注意的是,该值需要和外部电路的输入阻抗匹配。从而保证在采用阶段,采样保持电容有足够的时间充电。
如前述采样保持电路所示,其中有一个采样保持电容C。
该值取决于ADC的转换精度,STM32F4xx的SAR ADC默认为12bits,可配置为10、8、6bits。
NO | ADC精度 | 转换时间 |
---|---|---|
1 | 12 bits | 12 x ADCCLK |
2 | 10 bits | 10 x ADCCLK |
3 | 8 bits | 8 x ADCCLK |
4 | 6 bits | 6 x ADCCLK |
ADC 转换的数值 = (VIN x 2^n) / VREF,n为ADC的分辨率。以上述10bits的ADC为例,则ADC 转换的数值 = (VIN x 1024) / VREF。
/** 定义ADC GPIO*/ #define ADC_IN0_GPIO_PORT GPIOA #define ADC_IN0_GPIO_PIN GPIO_Pin_0 #define ADC_IN0_GPIO_CLK RCC_AHB1Periph_GPIOA /** 定义ADC 相关信息*/ #define DEBUG_ADC ADC1 #define DEBUG_ADC_BASE ADC1_BASE #define DEBUG_ADC_CLK RCC_APB2Periph_ADC1 #define DEBUG_ADC_CHANNEL ADC_Channel_0 #define DEBUG_ADC_DR_ADDR ((uint32_t)DEBUG_ADC_BASE + 0x4C) /** 定义ADC DMA相关信息*/ #define DEBUG_ADC_DMA_CLK RCC_AHB1Periph_DMA2 #define DEBUG_ADC_DMA_CHANNEL DMA_Channel_0 #define DEBUG_ADC_DMA_STREAM DMA2_Stream0 /** 定义数据存储变量*/ uint16_t adcConvertedValue;
这里默认APB2的CLK PCLK2为60MHz。那么当设置ADC分辨率为12bits,ADCCLK = PCLK2 / 2 = 30MHz时。结合前述“转换时间”章节中讲到的计算方式,可以得知单次
ADC转换时间 = 采样周期 + 转换周期
= 3 x ADCCLK + 12 x ADCCLK
= 15 ADC CLK
= 15 / 30MHz
= 0.5 us
每次ADC转换间隔为20 x ADCCLK。
/** 初始化ADC IO*/ void ADC_GPIOConfig(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(ADC_IN0_GPIO_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin = ADC_IN0_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_Init(ADC_IN0_GPIO_PORT, &GPIO_InitStructure); } /** 初始化ADC相应的DMA*/ void ADC_DMAConfig(void) { DMA_InitTypeDef DMA_InitStructure; RCC_AHB1PeriphClockCmd(DEBUG_ADC_DMA_CLK, ENABLE); /** ADC 数据寄存器地址*/ DMA_InitStructure.DMA_PeripheralBaseAddr = DEBUG_ADC_DR_ADDR; DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&adcConvertedValue; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = 1; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; /** ADC DR数据大小为半字,即两个字节*/ DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; /** 存储器数据大小为半字*/ DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_InitStructure.DMA_Channel = DEBUG_ADC_DMA_CHANNEL; DMA_Init(DEBUG_ADC_DMA_STREAM, &DMA_InitStructure); DMA_Cmd(DEBUG_ADC_DMA_STREAM, ENABLE); } /** 初始化ADC*/ void ADC_Config(void) { ADC_InitTypeDef ADC_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; RCC_APB2PeriphClockCmd(DEBUG_ADC_CLK , ENABLE); ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; /** 设置ADCCLK为PCLK2的2分频*/ ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2; ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; /** 采样时间 间隔*/ ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; ADC_CommonInit(&ADC_CommonInitStructure); ADC_StructInit(&ADC_InitStructure); /** ADC 分辨率*/ ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; /** 数据右对齐*/ ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; /** 转换通道 1个*/ ADC_InitStructure.ADC_NbrOfConversion = 1; ADC_Init(DEBUG_ADC, &ADC_InitStructure); /** 配置 ADC 通道转换顺序为1,采样时间为3个时钟周期*/ ADC_RegularChannelConfig(DEBUG_ADC, DEBUG_ADC_CHANNEL, 1, ADC_SampleTime_3Cycles); /** 初始化ADC的DMA*/ ADC_DMAConfig(); /** 使能DMA请求*/ ADC_DMARequestAfterLastTransferCmd(DEBUG_ADC, ENABLE); /** 使能ADC DMA*/ ADC_DMACmd(DEBUG_ADC, ENABLE); /** 使能ADC*/ ADC_Cmd(DEBUG_ADC, ENABLE); /** 开始软件触发 ADC转换*/ ADC_SoftwareStartConv(DEBUG_ADC); }
相对于单通道,GPIO和ADC数据存储器相应的增加了定义。
/** 定义ADC GPIO*/ #define ADC_IN0_GPIO_PORT GPIOA #define ADC_IN0_GPIO_PIN GPIO_Pin_0 #define ADC_IN0_GPIO_CLK RCC_AHB1Periph_GPIOA #define ADC_IN1_GPIO_PORT GPIOA #define ADC_IN1_GPIO_PIN GPIO_Pin_1 #define ADC_IN1_GPIO_CLK RCC_AHB1Periph_GPIOA #define ADC_IN2_GPIO_PORT GPIOA #define ADC_IN2_GPIO_PIN GPIO_Pin_2 #define ADC_IN2_GPIO_CLK RCC_AHB1Periph_GPIOA /** 定义ADC 相关信息*/ #define DEBUG_ADC ADC1 #define DEBUG_ADC_BASE ADC1_BASE #define DEBUG_ADC_CLK RCC_APB2Periph_ADC1 #define DEBUG_ADC_CHANNEL0 ADC_Channel_0 #define DEBUG_ADC_CHANNEL1 ADC_Channel_1 #define DEBUG_ADC_CHANNEL2 ADC_Channel_2 #define DEBUG_ADC_DR_ADDR ((uint32_t)DEBUG_ADC_BASE + 0x4C) /** 定义ADC DMA相关信息*/ #define DEBUG_ADC_DMA_CLK RCC_AHB1Periph_DMA2 #define DEBUG_ADC_DMA_CHANNEL DMA_Channel_0 #define DEBUG_ADC_DMA_STREAM DMA2_Stream0 /** 定义数据存储数组*/ #define ADC_CONV_CH_SIZE 3 uint16_t adcConvertedValue[ADC_CONV_CH_SIZE];
相对于单通道,ADC需要开启扫描模式,DMA配置为存储器增量,ADC Channel数增加到3个。
/** 初始化ADC IO*/ void ADC_GPIOConfig(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(ADC_IN0_GPIO_CLK, ENABLE); RCC_AHB1PeriphClockCmd(ADC_IN1_GPIO_CLK, ENABLE); RCC_AHB1PeriphClockCmd(ADC_IN2_GPIO_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin = ADC_IN0_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_Init(ADC_IN0_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = ADC_IN1_GPIO_PIN; GPIO_Init(ADC_IN1_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = ADC_IN2_GPIO_PIN; GPIO_Init(ADC_IN2_GPIO_PORT, &GPIO_InitStructure); } /** 初始化ADC相应的DMA*/ void ADC_DMAConfig(void) { DMA_InitTypeDef DMA_InitStructure; RCC_AHB1PeriphClockCmd(DEBUG_ADC_DMA_CLK, ENABLE); /** ADC 数据寄存器地址*/ DMA_InitStructure.DMA_PeripheralBaseAddr = DEBUG_ADC_DR_ADDR; DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&adcConvertedValue; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; /** buffer size 和需扫描的ADC通道数一致*/ DMA_InitStructure.DMA_BufferSize = ADC_CONV_CH_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /** 存储器地址递增*/ DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; /** ADC DR数据大小为半字,即两个字节*/ DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; /** 存储器数据大小为半字*/ DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_InitStructure.DMA_Channel = DEBUG_ADC_DMA_CHANNEL; DMA_Init(DEBUG_ADC_DMA_STREAM, &DMA_InitStructure); DMA_Cmd(DEBUG_ADC_DMA_STREAM, ENABLE); } /** 初始化ADC*/ void ADC_Config(void) { ADC_InitTypeDef ADC_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; RCC_APB2PeriphClockCmd(DEBUG_ADC_CLK , ENABLE); ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; /** 设置ADCCLK为PCLK2的2分频*/ ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2; ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; /** 采样时间 间隔*/ ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; ADC_CommonInit(&ADC_CommonInitStructure); ADC_StructInit(&ADC_InitStructure); /** ADC 分辨率*/ ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; /** 开启扫描模式*/ ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; /** 数据右对齐*/ ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; /** 转换通道和需扫描的ADC通道数一致*/ ADC_InitStructure.ADC_NbrOfConversion = ADC_CONV_CH_SIZE; ADC_Init(DEBUG_ADC, &ADC_InitStructure); /** 配置 各ADC通道转换顺序,采样时间都为3个时钟周期*/ ADC_RegularChannelConfig(DEBUG_ADC, DEBUG_ADC_CHANNEL0, 1, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(DEBUG_ADC, DEBUG_ADC_CHANNEL1, 2, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(DEBUG_ADC, DEBUG_ADC_CHANNEL2, 3, ADC_SampleTime_3Cycles); /** 初始化ADC的DMA*/ ADC_DMAConfig(); /** 使能DMA请求*/ ADC_DMARequestAfterLastTransferCmd(DEBUG_ADC, ENABLE); /** 使能ADC DMA*/ ADC_DMACmd(DEBUG_ADC, ENABLE); /** 使能ADC*/ ADC_Cmd(DEBUG_ADC, ENABLE); /** 开始软件触发 ADC转换*/ ADC_SoftwareStartConv(DEBUG_ADC); }
在硬件抗干扰能力不足的情况下,我们可以牺牲采用速率,采用软件方法进行滤波,从而得到更加稳定的采样值。一般应用的情况下,“抗脉冲干扰均值滤波”算法就能够满足需求。也可以根据情况使用更复杂更合适的滤波算法。
/** Anti-interference Average Filtering*/ uint16_t Anti_AverageFilter(uint16_t *buffer, uint16_t length) { uint16_t temp; uint16_t i,j; /**Bubbling Sort*/ for(j = 0; j < length - 1; j++) { for(i = 0; i < length - j - 1; i++) { if(buffer[i] > buffer[i + 1]) { temp = buffer[i]; buffer[i] = buffer[i + 1]; buffer[i + 1] = temp; } } } temp = 0; for(i = 1; i < length - 1; i++) { temp += buffer[i]; } temp /= (length - 2); return temp; }
可通过数字低通、高通等滤波器进行软件滤波。比如知道被测信号中的噪声来自50 Hz供电线,通过适当的数字滤波,可以只抑制50 Hz频率并传输无此噪声的数据信号。
如果采样值较为固定,但离目标值相差大,还可以利用线性拟合(线性校准曲线)的方式让采样值更接近目标值。
首先基于标准源表采样足够多的点(点数越多,拟合越准确)。
NO | 采样值(mv) | 目标值(mv) |
---|---|---|
1 | 96.7 | 100 |
2 | 194.5 | 200 |
3 | 498.8 | 500 |
4 | 796 | 800 |
5 | 1495 | 1500 |
在数学工具(Matlab或Excel等)中做线性拟合(线性、多项式、指数皆可),得到校准公式和相关系数R值。
用拟合步骤中得到的校准公式对采样值进行校准。
NO | 采样值(mv) | 目标值(mv) | 校准值(mv) |
---|---|---|---|
1 | 96.7 | 100 | 100.1 |
2 | 194.5 | 200 | 197.9 |
3 | 498.8 | 500 | 502.5 |
4 | 796 | 800 | 799.9 |
5 | 1495 | 1500 | 1499.4 |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。