ADC,analog-to-digital conversions,模数转换器,需要使用它的场合非常多,但是能让它发挥完美性能的电路设计却不多。电源本身自带的噪声、PCB板布线引入的干扰、信号放大电路糟糕的设计……太多的因素会影响ADC的表现。几乎所有的主流单片机都集成了ADC模块,ATmega16是10位,而MSP430F2616和STM32F103RBT6是12位,同样,它的使用也有很多需要注意的地方。
最简单的ADC使用方法是,单次转换一个通道,不产生中断,即Single-channel, single-conversion (CONSEQ=0)。这里时钟源设置为8MHz的MCLK,相应的采集周期和时钟分频可以拉长一些,使用内部的2.5V参考源,所以外部的正负参考引脚eREF+/REF-可以不接或者接地。
/************************************************/
void wait_xt2clock(void)
{
BCSCTL1 &= ~XT2OFF;
BCSCTL3 |= XT2S_2;
do {
IFG1 &= ~OFIFG;
Delay100Us_DcoDef(1);
} while (IFG1 & OFIFG);
BCSCTL2 |= SELM_2;
}
int main (void)
{
WDTCTL = WDTPW + WDTHOLD;
wait_xt2clock();
UART0_Init();
// 设置IO的外设功能以及方向
P6DIR &= ~0x01;
P6SEL |= 0x01;
// 打开12位的ADC
// 为ADC12MEM0~ADC12MEM7选择32 ADC12CLK cycles,
// 为ADC12MEM8~ADC12MEM15选择32 ADC12CLK cycles,
// 打开内部参考源,并且选择2.5V
ADC12CTL0 = ADC12ON + SHT0_3 + SHT1_3 + REFON + REF2_5V;
// SAMPCON signal is sourced from the sampling timer.
// 使用MCLK时钟源
// 使用Single-channel, single-conversion模式
// ADC时钟2分频
ADC12CTL1 = SHP + ADC12SSEL_2 + CONSEQ_0 + ADC12DIV1;
// 第一个转换通道,选择VR+ = VREF+ and VR- = AVSS,以及P6.0的输入
ADC12MCTL0 = SREF_1 | INCH_0;
// 等待17ms以上直到内部参考源REF稳定
DelayMs_xt2_8MHz(18);
ADC12CTL0 |= ENC;
while (1) {
ADC12CTL0 |= ADC12SC;
while (!(ADC12IFG & BIT0));
UART0_printf("ADC result = 0x%4x.\r\n", ADC12MEM0);
}
return 0;
}
/************************************************/
不加中断的单次转换,最容易实现,唯一比较坑爹的是,在msp430-lib提供的msp430f2616.h头文件里面,关于ADC的寄存器宏定义含义不是那么明确,比如INCH0和INCH_0、ADC12SSEL0和ADC12SSEL_0等等,这些宏定义的含义差别很大,一个不小心就会填错。不加中断的单次转换其实非常容易实现,如果调试了半天结果都不对,就检查一下这里。
/*****************************************************************************************/
当CONSEQ=1时,即Sequence-of-channels,如果需要采集的ADC通道有很多,用它就很合适。等待序列中的通道转换完毕(即EOS标记的通道转换完成),再操作结果。
/************************************************/
void wait_xt2clock(void)
{
BCSCTL1 &= ~XT2OFF;
BCSCTL3 |= XT2S_2;
do {
IFG1 &= ~OFIFG;
Delay100Us_DcoDef(1);
} while (IFG1 & OFIFG);
BCSCTL2 |= SELM_2;
}
int main (void)
{
WDTCTL = WDTPW + WDTHOLD;
wait_xt2clock();
UART0_Init();
// 设置IO的外设功能以及方向
P6DIR &= ~0x07;
P6SEL |= 0x07;
// 打开12位的ADC
// 为ADC12MEM0~ADC12MEM7选择16 ADC12CLK cycles,
// 为ADC12MEM8~ADC12MEM15选择16 ADC12CLK cycles,
// 打开内部参考源,并且选择2.5V
// 选择Multiple sample and conversion
ADC12CTL0 = ADC12ON + SHT0_2 + SHT1_2 + REFON + REF2_5V + MSC;
// SAMPCON signal is sourced from the sampling timer.
// 使用MCLK时钟源
// 使用Sequence-of-channels模式
// ADC时钟2分频
ADC12CTL1 = SHP + ADC12SSEL_2 + CONSEQ_1 + ADC12DIV1;
// 设置各个通道的参考源和输入IO,以及结束通道标志
ADC12MCTL0 = SREF_1 + INCH_0;
ADC12MCTL1 = SREF_1 + INCH_1;
ADC12MCTL2 = SREF_1 + INCH_2 + EOS;
// 等待17ms以上直到内部参考源REF稳定
DelayMs_xt2_8MHz(18);
ADC12CTL0 |= ENC;
while (1) {
ADC12CTL0 |= ADC12SC;
while (!(ADC12IFG & BIT2));
UART0_printf("ADC result = 0x%4x 0x%4x 0x%4x.\r\n",
ADC12MEM0, ADC12MEM1, ADC12MEM2);
}
return 0;
}
/************************************************/
ADC12MCTLx控制Sequence-of-channels转换,在Sequence中可以以任何次序转换任意数量的通道,比如5、1、0,直到被设置为EOS的通道转换完成则整个序列完成。等待ADC12IFGx置位就可以读取ADC的结果,对ADC12MEMx的访问会使ADC12IFGx自动清零。