ADC(模数转换)
(一)ADC原理
1.模数转换:将模拟量转化为数字量
(1)采样:将连续的模拟量转化为时间离散的模拟量
(2)量化:将时间离散的模拟量转化为时间数值都离散的数字量
(3)编码:根据特定规则,将每一级的数字量编码为二进制数
2.ADC转换过程
(1)外部模拟量从输入引脚传入,低通滤波
(2)在采样时间内,采样保持电路进行采样
(3)ADC电路在转换时间内,进行转换(量化 + 编码)
(4)转换后的结果存入缓存中
3.逐次逼近型ADC:现代MCU常用的ADC转换电路
- L S B = V R E F / 2 n LSB = V_{REF}/2^n LSB=VREF/2n
- n 越大,精度越高
(1)将参考电压按ADC的位数 n 分成 2 n 2^n 2n 等份,每一级的电压值为 V R E F / 2 n V_{REF}/2^n VREF/2n
(2)将输入电压与每一级电压逐次比较,大于该级电压就将该为置1,直到小于某级电压置0
(3)如此,输入电压的值就被近似逼近在一个范围内了, V I N = s a m p l e V a l u e ∗ V R E F / 2 n V_{IN} = sampleValue * V_{REF}/2^n VIN=sampleValue∗VREF/2n
(二)编程
1.编程流程
(1)引脚复用为模拟输入
(2)ADC功能配置
- 时钟配置
- 参考电压
- 采样时间
- 转换时间
- 转换通道(采样通道)
- 触发方式(软件/定时器触发)
- 转换模式(单次、连续、扫描(多通道))
- 数据对齐方式(左/右对齐)
(3)ADC模块使能
(4)中断配置(转换完成后可触发中断)
- 中断源
- 中断优先级
- 中断使能
(5)采样数据读取
- 查询模式
- 中断模式
- DMA模式
2.编程实例
(1)寄存器版本
#include "msp.h"
#include "driverlib.h"
void uSDelay( __IO uint32_t us)
{
uint32_t i;
SysTick->LOAD = (uint32_t)(SystemCoreClock/24000000 - 1UL);
SysTick->VAL = 0UL;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
for(i=0;i<us;i++)
{
/* 当计数器的值减小到0的时候,CRTL寄存器的位16会置1*/
while( !((SysTick->CTRL)&(1<<16)) );
}
/* 关闭SysTick定时器*/
SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
}
void initADC()
{
//选用ADC内部的温度传感器采样
//ADC功能配置(采样频率、采样分辨率、采样通道、采样参考电压、采样模式)
//时钟源选择:SMCLK 12MHz,1分频
ADC14->CTL0 |= ADC14_CTL0_SSEL__SMCLK | ADC14_CTL0_PDIV_0 | ADC14_CTL0_DIV__1;
//单通道单次采样(手动采样)
ADC14->CTL0 |= ADC14_CTL0_CONSEQ_0;
//内部温度传感器采样、14位分辨率、内存0
ADC14->CTL1 |= ADC14_CTL1_TCMAP | ADC14_CTL1_RES__14BIT;
ADC14->CTL1 &=~ADC14_CTL1_CSTARTADD_MASK;
//内部参考电压、内部温度传感器在A22
ADC14->MCTL[0] |= ADC14_MCTLN_VRSEL_1 | ADC14_MCTLN_INCH_22;
//ADC内核使能
ADC14->CTL0 |= ADC14_CTL0_ON;
}
uint16_t nSmpVal[5],nI,nMin,nMax,nMinIndex,nMaxIndex,nSampleSum,nValidCnt;
//采样五次,去掉最大值和最小值后取平均值
uint16_t GetSampleValue()
{
//采样使能,开始采样
ADC14->CTL0 |= ADC14_CTL0_ENC | ADC14_CTL0_SC ;
uSDelay(5);
ADC14->CTL0 &=~(ADC14_CTL0_ENC | ADC14_CTL0_SC);
uSDelay(10);
nMin=1024,nMax=0;
for(nI=0;nI<5;nI++)
{
ADC14->CTL0 |= ADC14_CTL0_ENC | ADC14_CTL0_SC ;
uSDelay(5);
nSmpVal[nI]= ADC14->MEM[0];
ADC14->CTL0 &=~(ADC14_CTL0_ENC | ADC14_CTL0_SC);
uSDelay(5);
if(nSmpVal[nI]>nMax)
{
nMaxIndex=nI;
nMax=nSmpVal[nI];
}
if(nSmpVal[nI]<nMin)
{
nMinIndex=nI;
nMin=nSmpVal[nI];
}
}
nSampleSum=0;nValidCnt=0;
for(nI=0;nI<5;nI++)
{
if((nI!=nMinIndex)&&(nI!=nMaxIndex))
{
nSampleSum+=nSmpVal[nI];
nValidCnt++;
}
}
if(nValidCnt>0)
return nSampleSum/nValidCnt;
return 0;
}
float TempSample()
{
float TValue = 0.0f;
uint16_t SampleValue = 0,SampleValue1 = 0;
short SampleValueOff;
//使能内部温度传感器
REF_A->CTL0 &=~REF_A_CTL0_TCOFF;
//设置参考电压(2.5V),使能
REF_A->CTL0 |= REF_A_CTL0_VSEL_3 | REF_A_CTL0_ON;
SampleValue = GetSampleValue();
SampleValue1 = GetSampleValue();
SampleValueOff = SampleValue1 - SampleValue;
if(SampleValueOff >(-10) && SampleValueOff < 10)
{
if(SampleValue == 0)
TValue = 0;
else
TValue = (2.5f*SampleValue)/16383;
}
return TValue;
}
void main()
{
WDTCTL = WDTPW + WDTHOLD;
//解锁时钟寄存器(0x695A)
CS->KEY = CS_KEY;
//DCO 12MHz
CS->CTL0 |= CS_CTL0_DCORSEL_3 | CS_CTL0_DCOEN;
//SMCLK:12MHz
CS->CTL1 |= CS_CTL1_SELS__DCOCLK | CS_CTL1_DIVS__1 | CS_CLKEN_SMCLK_EN;
//锁住时钟寄存器(0xA569)
CS->KEY = CS_KEY_KEY_OFS;
initADC();
float value;
value = TempSample();
while(1);
}
(2)库函数版本
#include "msp.h"
#include "driverlib.h"
void uSDelay( __IO uint32_t us)
{
uint32_t i;
SysTick->LOAD = (uint32_t)(SystemCoreClock/24000000 - 1UL);
SysTick->VAL = 0UL;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
for(i=0;i<us;i++)
{
/* 当计数器的值减小到0的时候,CRTL寄存器的位16会置1*/
while( !((SysTick->CTRL)&(1<<16)) );
}
/* 关闭SysTick定时器*/
SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
}
void initADC()
{
//ADC内核初始化
ADC14_initModule(ADC_CLOCKSOURCE_SMCLK, ADC_PREDIVIDER_1,ADC_DIVIDER_1,ADC_TEMPSENSEMAP);
//ADC采样模式配置
ADC14_configureSingleSampleMode(ADC_MEM0, true);
//ADC采样通道配置
ADC14_configureConversionMemory(ADC_MEM0, ADC_VREFPOS_INTBUF_VREFNEG_VSS,ADC_INPUT_A22, false);
//ADC采样模块使能
ADC14_enableModule();
}
uint16_t nSmpVal[5],nI,nMin,nMax,nMinIndex,nMaxIndex,nSampleSum,nValidCnt;
//采样五次,去掉最大值和最小值后取平均值
uint16_t GetSampleValue()
{
//采样使能,开始采样
ADC14_enableConversion();
ADC14_toggleConversionTrigger();
uSDelay(5);
ADC14_disableConversion();
uSDelay(10);
nMin=1024,nMax=0;
for(nI=0;nI<5;nI++)
{
ADC14_enableConversion();
ADC14_toggleConversionTrigger();
uSDelay(5);
nSmpVal[nI]= ADC14->MEM[0];
ADC14_disableConversion();
uSDelay(5);
if(nSmpVal[nI]>nMax)
{
nMaxIndex=nI;
nMax=nSmpVal[nI];
}
if(nSmpVal[nI]<nMin)
{
nMinIndex=nI;
nMin=nSmpVal[nI];
}
}
nSampleSum=0;nValidCnt=0;
for(nI=0;nI<5;nI++)
{
if((nI!=nMinIndex)&&(nI!=nMaxIndex))
{
nSampleSum+=nSmpVal[nI];
nValidCnt++;
}
}
if(nValidCnt>0)
return nSampleSum/nValidCnt;
return 0;
}
float TempSample()
{
float TValue = 0.0f;
uint16_t SampleValue = 0,SampleValue1 = 0;
short SampleValueOff;
//使能内部温度传感器
REF_A_enableTempSensor();
//设置参考电压(2.5V),使能
REF_A_setReferenceVoltage(REF_A_VREF2_5V);
REF_A_enableReferenceVoltage();
SampleValue = GetSampleValue();
SampleValue1 = GetSampleValue();
SampleValueOff = SampleValue1 - SampleValue;
if(SampleValueOff >(-10) && SampleValueOff < 10)
{
if(SampleValue == 0)
TValue = 0;
else
TValue = (2.5f*SampleValue)/16383;
}
return TValue;
}
void main()
{
/* Halting WDT */
WDT_A_holdTimer();
//设置时钟频率 DCO 12MHz
CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_12);
//时钟源 SMCLK 12MHz
CS_initClockSignal(CS_SMCLK,CS_DCOCLK_SELECT,CS_CLOCK_DIVIDER_1);
initADC();
float value;
value = TempSample();
while(1);
}