This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430F6438的ADC12采样不准,采样结果不随输入信号的改变而变化,怎么办?



程序内容:用6438的ADC12采样0.5+0.5sin(2pi*50t),计算这个信号的真有效值。采样频率fs=256*50=12800Hz,我用到了ADC12、串口usci_a1的UART模式、定时器TA0三个模块。 时钟方面设置了ACLK=XT1=32768Hz,MCLK=SMCLK=(74 + 1) * 32768 = 2457600Hz。定时器TA0采用增计数模式,每隔1/fs产生中断,然后在TA0的中断里打开ADC12的A0通道开始采样,从而保证采样频率为fs,初始值TA0CCR0 =2457600/256/50-1=191。当采样256个点时,在main中计算有效值,通过串口调试助手显示有效值。现在问题是:

1)每次结果都是1.3左右的数,信号幅值变为1、0.7等时,结果还是1.3;把信号去掉,A0通道没有信号时,输出数据还是1.3。这就奇葩了。

2)怀疑A0通道不行,我就使能A1通道,信号加到A1通道上,结果没有数据,在register中看到ADC12MEM1没有数据,ADC12MEM0中竟然有数据。

3)整个程序的功能我转移到449上,发现449上输出结果就对(虽然有误差,但整体趋势是对的啊),所以程序应该没问题。

4)所有模块的初始化,我都是按照TI例程初始化的。

5)怀疑是ADC12通道干扰,我用A0时,就把A1-A7通道接地,但是结果还是和上面的问题一样啊。

6)ROM和RAM都没超出。

所以问题到底出现在哪里?我好急啊,求解答@TI员工。完整的程序如下:

#include <msp430.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define M 256
#define uchar unsigned char
#define uint unsigned int
char buffer[100];
uint adc_flag=0;
uint count=0;
float ad_temp=0;
float ad=0;
int i,j;

void delay(unsigned int n)
{
unsigned int i,j;
for(i=0;i<n;i++)
{
for(j=0;j<1000;j++)
{}
}
}

int main(void)

{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
//时钟初始化
P1DIR |= BIT0; // ACLK set out to pin
P1SEL |= BIT0;
P3DIR |= BIT4; // SMCLK set out to pins

P3SEL |= BIT4;
while(BAKCTL & LOCKBAK) // Unlock XT1 pins for operation
BAKCTL &= ~(LOCKBAK);
UCSCTL6 &= ~(XT1OFF); // XT1 On
UCSCTL6 |= XCAP_3; // Internal load cap
// Loop until XT1 fault flag is cleared
do
{
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG); // Clear XT2,XT1,DCO fault flags
SFRIFG1 &= ~OFIFG; // Clear fault flags
}while (SFRIFG1&OFIFG); // Test oscillator fault flag
// Initialize DCO to 2.45MHz

__bis_SR_register(SCG0); // Disable the FLL control loop

UCSCTL0 = 0x0000; // Set lowest possible DCOx, MODx

UCSCTL1 = DCORSEL_3; // Set RSELx for DCO = 4.9 MHz
UCSCTL2 = FLLD_1 + 74; // Set DCO Multiplier for 2.45MHz
// (N + 1) * FLLRef = Fdco

// (74 + 1) * 32768 = 2457600Hz

// Set FLL Div = fDCOCLK/2
__bic_SR_register(SCG0); // Enable the FLL control loop

__delay_cycles(76563);
// Loop until XT1,XT2 & DCO fault flag is cleared

do

{

UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + XT1HFOFFG + DCOFFG);
// Clear XT2,XT1,DCO fault flags

SFRIFG1 &= ~OFIFG; // Clear fault flags

}while (SFRIFG1&OFIFG); // Test oscillator fault flag


UCSCTL6 &= ~(XT1DRIVE_3); // Xtal is now stable, reduce drive strength
UCSCTL4 |= SELA_0; // ACLK = LFTX1 (by default)

//usci_a1初始化
P8SEL |= (BIT2+BIT3); //p8.2,p8.3=usci_a1TXD,RXD (F6438)
P8DIR |= BIT2;
P8DIR &=~BIT3; // 选择引脚方向
UCA1CTL1 |= UCSWRST; // 状态机复位
UCA1CTL1 |= UCSSEL_1; // 选择时钟源CLK = ACLK


UCA1BR0 = 0x03; //波特率分频 32768/9600=3.41
UCA1BR1 = 0x00; // Modulation UCBRSx=3, UCBRFx=0
UCA1MCTL = UCBRS_3+UCBRF_0;
UCA1CTL1 &=~UCSWRST; // 启动状态机,即启用串口功能
UCA1IE |= UCRXIE; // 允许接收中断

//初始化ADC12
P6SEL |= 0x01; // Enable A/D channel A0
REFCTL0 &= ~REFMSTR; // Reset REFMSTR to hand over control to ADC12_A ref control registers
ADC12CTL0 = ADC12ON+ADC12SHT02+ADC12REFON+ADC12REF2_5V;
// Turn on ADC12, Sampling timeOn Reference Generator and set to2.5V //a single conversion on channel A0.
ADC12CTL1 = ADC12SHP; // Use sampling timer
ADC12MCTL0 = ADC12SREF_1; // Vr+=Vref+ and Vr-=AVss
ADC12IE = 0x01; // Enable interrupt A0
for ( i=0; i<0x30; i++); // Delay for reference start-up
ADC12CTL0 |= ADC12ENC; // Enable conversions

//初始化TA0
TA0CTL = TASSEL_2 + MC_1 + TACLR + TAIE; // SMCLK, upmode, clear TAR
TA0CCR0 = 191; //2457600/256/50=192


_EINT();
while(1)
{
while(adc_flag==1)
{
ad=ad+ad_temp*ad_temp;
count++;
if(count==M)
{
ad=ad/M;
ad=sqrt(ad);
sprintf(buffer,"%.4f\n",ad);
while(!(UCA1IFG&UCTXIFG)); // USCI_A0 TX buffer ready?
for(int k=0;k<sizeof(buffer);k++)
{
UCA1TXBUF=buffer[k];
delay(30);
}
ad=0;
count=0;
ad_temp=0;
}
adc_flag=0;
}
}
}
#pragma vector = ADC12_VECTOR
__interrupt void ADC12_ISR(void)
{
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: ADC12IFG
{
ad_temp=(ADC12MEM0*2.5/4095);
adc_flag=1;
} break;
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;
}
}

#pragma vector=TIMER0_A1_VECTOR
__interrupt void TIMER0_A1_ISR(void)
{
switch(__even_in_range(TA0IV,14))

{
case 0: break; // No interrupt
case 2: break; // CCR1 not used
case 4: break; // CCR2 not used
case 6: break; // reserved
case 8: break; // reserved
case 10: break; // reserved
case 12: break; // reserved
case 14: if(adc_flag==0) // overflow
{
ADC12CTL0 |=ADC12SC;
}break;
default: break;
}
}

  • 1) 随便找一个我们的历程,例如连续单通道采样例程看ADC采样结果是否正常

    2)在你的定时器中断里面和ADC中断里面分别翻一个IO做toggle,用示波器观察这两个引脚波形。是否如你所想的那样,以这样的频率在每次进timer去配置SC,以及随后是否进了ADC中断

  • 这款芯片支持TIMER Trigger ADC的,不一定要在timer中断里开ADC,这样实时性也不是太好

  • HG 说:

    这款芯片支持TIMER Trigger ADC的,不一定要在timer中断里开ADC,这样实时性也不是太好

    写的确实有Up to three timer outputs(see the device-specificdatasheetfor availabletimer sources),但我认真看了F6438的data sheet,并没有这方面的内容啊

  • Jacky Xu 说:

    1) 随便找一个我们的历程,例如连续单通道采样例程看ADC采样结果是否正常

    2)在你的定时器中断里面和ADC中断里面分别翻一个IO做toggle,用示波器观察这两个引脚波形。是否如你所想的那样,以这样的频率在每次进timer去配置SC,以及随后是否进了ADC中断

    TA中断:

    case 14:                                     // overflow
    {
        P4OUT ^=BIT1;
    }break;
    结果很好: 设置的采样频率=250*50=12.8K

    但是程序中一旦写入启动AD的语句:

    结果就乱了:

    至于在AD的中断里toggle一个IO,结果也是无序的,混乱的,并不是一个方波。但是程序逻辑性没有任何错误啊。

    就拿TA中断说,在P4OUT ^=BIT1;下面加一句ADC12CTL0 |=ADC12SC,结果P4.1的输出就是混乱的,不加这一句就是完美的方波。ADC12CTL0 |=ADC12SC这条语句怎么会影响P4OUT ^=BIT1;呢?????

  • 亲,不是看ADC啊,你要看

  • 你为啥要在Overflow的中断里去开ADC呢,就弄一个普通的中断,并且要重新给TIMER赋值

  • 先不说你的timer触发这样用是否合适,单单是单次触发转换这个代码您调通过吗?

    ADC12SHP您配他干嘛?你现在要用的不是由定时器中断里面使能的单次触发方式吗?SHP配成1变成脉冲触发模式,你的ADC需要用SHI信号来触发转换。

    所以我就不细看,你这代码里面还是有错误的。

    一向以来最简单的方式就是拿我们单次触发转换的例程,把SC置位放到TIMER里面去做。就可以了。

    当然楼上说的定时器触发方式是最简单的。

    另外在ADC中断里面最好不要放复杂运算。特别是乘以0.X,除以x。很慢的

  • 建议先运行下官网的例程,在例程的基础上修改和增加其他功能

  • HG 说:

    你为啥要在Overflow的中断里去开ADC呢,就弄一个普通的中断,并且要重新给TIMER赋值

    我的本意是用来控制采样频率:timer定时时间一到(up mode),在TA overflow的中断里来打开ADC开始采样一个点,然后定时时间再到,再采样一个点,依次循环,最后当采样点数=N的时候,对N个采样点进行处理运算。您是说在TA的overflow中断里要重新对TA0CCR0赋初值么??还是这个流程有问题??

  • HG 说:

    亲,不是看ADC啊,你要看

    找到了,我先看下。谢谢!

    能不能就timer触发F6438的ADC12采样,采样的N个点存在一个数组里,您帮我写一下这个小程序呢?谢谢!!我感觉我没有例程的话,已经玩不转430的AD了。。。

  • Jacky Xu 说:

    先不说你的timer触发这样用是否合适,单单是单次触发转换这个代码您调通过吗?

    ADC12SHP您配他干嘛?你现在要用的不是由定时器中断里面使能的单次触发方式吗?SHP配成1变成脉冲触发模式,你的ADC需要用SHI信号来触发转换。

    所以我就不细看,你这代码里面还是有错误的。

    一向以来最简单的方式就是拿我们单次触发转换的例程,把SC置位放到TIMER里面去做。就可以了。

    当然楼上说的定时器触发方式是最简单的。

    另外在ADC中断里面最好不要放复杂运算。特别是乘以0.X,除以x。很慢的

    volatile unsigned int i;
    WDTCTL = WDTPW+WDTHOLD; // Stop watchdog timer
    P6SEL |= 0x01; // Enable A/D channel A0
    REFCTL0 &= ~REFMSTR; // Reset REFMSTR to hand over control to
    // ADC12_A ref control registers
    ADC12CTL0 = ADC12ON+ADC12SHT02+ADC12REFON+ADC12REF2_5V;
    // Turn on ADC12, Sampling time
    // On Reference Generator and set to
    // 2.5V
    ADC12CTL1 = ADC12SHP; // Use sampling timer
    ADC12MCTL0 = ADC12SREF_1; // Vr+=Vref+ and Vr-=AVss

    for ( i=0; i<0x30; i++); // Delay for reference start-up

    ADC12CTL0 |= ADC12ENC; // Enable conversions

    while (1)
    {
    ADC12CTL0 |= ADC12SC; // Start conversion
    while (!(ADC12IFG & BIT0));
    __no_operation(); // SET BREAKPOINT HERE

    }

    这是TI的单通道单次转换使用内部2.5V的例子,它也是设置了SHP为1了啊?您的意思是,让我SHP=0,把SC放到定时器的中断中,然后ADC的中断中不对ADC12MEMx进行相关运算么??

  • Jacky Xu 说:

    先不说你的timer触发这样用是否合适,单单是单次触发转换这个代码您调通过吗?

    ADC12SHP您配他干嘛?你现在要用的不是由定时器中断里面使能的单次触发方式吗?SHP配成1变成脉冲触发模式,你的ADC需要用SHI信号来触发转换。

    所以我就不细看,你这代码里面还是有错误的。

    一向以来最简单的方式就是拿我们单次触发转换的例程,把SC置位放到TIMER里面去做。就可以了。

    当然楼上说的定时器触发方式是最简单的。

    另外在ADC中断里面最好不要放复杂运算。特别是乘以0.X,除以x。很慢的

    晚上刚调了下,问题就是出自在SHP的问题上。我把SHP置零,在timer的overflow中断中操作ADC12SC,就没问题了。谢谢!!!!!!!!!