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.

MSP430F5438 ADC Setup / sampling rate

Other Parts Discussed in Thread: MSP430F5438

Hello,

I am trying to configure the ADC on the MSP430 experimenters board (MSP430F5438) to continuously sample a signal that is in the 10 kHz range. After each sample, the data is pushed to a radio through UART. I'm testing by sampling a sine wave at various frequencies. The problem is that I cannot seem to sample sine waves above about 50 Hz. From my ADC configuration, I think the ADC should be sampling at about  153 kSps. 12 MHz clock, 12b resolution, 64 cycle SHP (12MHz/(13+65) = ~0.153 MHz).

My best guess is that the actual sampling rate is significantly lower because I call UART_send() in the ADC ISR but I am not sure of another way to do it.

Can anyone give me an idea of what's going on? My code is below, ADC conversions are started by a timer which is not shown in the code.

Thanks, Ben.

 

int ADC_result;

char str[10];

static unsigned char index = 0;

 

int main(void)

{

  WDTCTL = WDTPW + WDTHOLD;             // Stop watchdog timer

 

  clock_init();                         // ACLK = 32 kHz, SMCLK = 12 MHz, MCLK = 12 MHz

 

  UART_init();

  UART_send();

  timer_init();

  ADC_init();

 

  __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0, interrupts enabled  

}

 

void UART_init(void)

{

  P3SEL = 0x30;                             // P3.4,5 = USCI_A0 TXD/RXD

  UCA0CTL1 |= UCSWRST;                      // **Put state machine in reset**

  UCA0CTL1 |= UCSSEL_2;                     // SMCLK

  UCA0BR0 = 104;                            // 12MHz - 115200 (see User's Guide)

  UCA0BR1 = 0;                              // 12MHz - 115200

  UCA0MCTL |= UCBRS_1 + UCBRF_0;            // Modulation UCBRSx=1, UCBRFx=0

  UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**

  UCA0IE |= UCRXIE + UCTXIE;                // Enable USCI_A0 RX interrupt + TX interrupt

}

 

void UART_send(void)

{

  int i;

 

  sprintf(str,"%04d\r\n",ADC_result);

 

  for(i=0;i<6;i++)

  {

    while (!(UCTXIFG));             // USCI_A0 TX buffer ready?

      UCA0TXBUF = str[i];                   // send char

    __delay_cycles(1000);

  }

}

 

void ADC_init(void)

{

  // ADC init

  ADC12CTL0 = ADC12SHT0_5 + ADC12MSC;       // 64 cycles per sample Sampling time, multiple samples

  ADC12CTL0 |= ADC12ON;                      // ADC12 on

  ADC12CTL1 = ADC12SHP + ADC12CONSEQ_2;     // Use sampling timer, repeat single channel, SMCLK - 12 MHz?

  ADC12CTL1 |= 0x0018;                      // SMCLK - 12 MHz?

  ADC12CTL2 = ADC12RES_2;      // 12 bit res

  ADC12IE = 0x0001;                           // Enable interrupt

  ADC12MCTL0 = ADC12INCH_7;                 // map CH 7 to MEM0

  ADC12CTL0 |= ADC12ENC;                    // enable conversion

  P6SEL |= 0x80;                            // P6.7 ADC option select

 

  // ADC start, now in TA CCR1

  //ADC12CTL0 |= ADC12SC;                   // Start sampling/conversion 

}

 

// ADC ISR

#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:  ADC12IFG0

    ADC_result = ADC12MEM0;

    index++;

    UART_send();

    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; 

  }

}

 

  • Hi,

    i believe you are overclocking the ADC.

    ADC12CTL1 = ADC12SHP + ADC12CONSEQ_2;     // Use sampling timer, repeat single channel, SMCLK - 12 MHz?

    ADC12CTL1 |= 0x0018;                      // SMCLK - 12 MHz?

     

    The maximum ADC Clock for the ADC12 is  5.4MHz ( MSP430F5438 Datasheet page 61),  try to use the internal ADC Oscillator ADC12OSC

     

    ADC12CTL1 = ADC12SHP + ADC12CONSEQ_2 + ADC12SSEL_0

  • Basically, your calculation is correct. The ADC could sample at ~154 kHz. This would mean you'll get 154k values per second. These values are in the range of 0..4095. You write them to a string of 6 characters size. This results in a text output of about 600KB per second. You'd need to send them at 6 Megabaud(!!!) to get all of this sent through the UART (which you can't as the UART is 1MHz clock maximum).

    Look at the UART side of the calculation. You have 115200Bd. This means a maximum of 11520 bytes per second transmission rate. 6 Bytes per value, that means you will never get more than 1920 samples per second sent through the UART.

    You can increase this by 1) sending binary values instead of a string and 2) reducing the bitwidth to 8 bit per sample. This way you'd get up to 11520 samples per second, which is still not enough to just detect a 10kHz signal, even less to see something that resembles a waveform. It's not the ADC that is too slow, You simply don't get the data sent in time.

    The only way you can handle this is to use an SPI, which with 12Mhz clock can shuffle up to 1.5MB/s. You can then even set up a DMA channel which will automatically transfer an 8 bit sample value each time the ADC has finished a conversion. Without any program intervention. It could be possible with carefully selected timing to transfer 12bit values with Word-to-Byte transfer (since the TX buffer is empty immediately again after putting the first byte in, provided that there was enough time before to finish the last sending), but I'm not sure whether it works as expected. The docs don't tell anything about this "mixed byte/word transfer" operation mode.

    If you don't need a waveform but just to detect the signal frequency, you can go a different way: just detect whether the sampled value is above or below a threshold and mark a '0' or '1 ' bit in a byte. This way you can send 8 sampling results per byte, which would be enough to measure frequencies up to 40kHz.

  • ox said:

    Hi,

    i believe you are overclocking the ADC.

    ADC12CTL1 = ADC12SHP + ADC12CONSEQ_2;     // Use sampling timer, repeat single channel, SMCLK - 12 MHz?

    ADC12CTL1 |= 0x0018;                      // SMCLK - 12 MHz?

     

    The maximum ADC Clock for the ADC12 is  5.4MHz ( MSP430F5438 Datasheet page 61),  try to use the internal ADC Oscillator ADC12OSC

     

    ADC12CTL1 = ADC12SHP + ADC12CONSEQ_2 + ADC12SSEL_0

    Thanks for pointing this out. While I don't think is my primary problem, I will change the clock source settings. 

     

  • Jens-Michael Gross said:

    Basically, your calculation is correct. The ADC could sample at ~154 kHz. This would mean you'll get 154k values per second. These values are in the range of 0..4095. You write them to a string of 6 characters size. This results in a text output of about 600KB per second. You'd need to send them at 6 Megabaud(!!!) to get all of this sent through the UART (which you can't as the UART is 1MHz clock maximum).

    Look at the UART side of the calculation. You have 115200Bd. This means a maximum of 11520 bytes per second transmission rate. 6 Bytes per value, that means you will never get more than 1920 samples per second sent through the UART.

    You can increase this by 1) sending binary values instead of a string and 2) reducing the bitwidth to 8 bit per sample. This way you'd get up to 11520 samples per second, which is still not enough to just detect a 10kHz signal, even less to see something that resembles a waveform. It's not the ADC that is too slow, You simply don't get the data sent in time.

    The only way you can handle this is to use an SPI, which with 12Mhz clock can shuffle up to 1.5MB/s. You can then even set up a DMA channel which will automatically transfer an 8 bit sample value each time the ADC has finished a conversion. Without any program intervention. It could be possible with carefully selected timing to transfer 12bit values with Word-to-Byte transfer (since the TX buffer is empty immediately again after putting the first byte in, provided that there was enough time before to finish the last sending), but I'm not sure whether it works as expected. The docs don't tell anything about this "mixed byte/word transfer" operation mode.

    If you don't need a waveform but just to detect the signal frequency, you can go a different way: just detect whether the sampled value is above or below a threshold and mark a '0' or '1 ' bit in a byte. This way you can send 8 sampling results per byte, which would be enough to measure frequencies up to 40kHz.

    Jens, thanks for the reply and suggestions. I think I understand the problem now. I think I will be revising my requirements and changing my approach. While I do want to sample continuously and I do need to capture a waveform AND would like to maintain UART comm, I think I can get by with collecting "snap shots." I think I will sample for a set period of time, then stop and send a chunk of data, and repeat. Like you suggested, I will also change the format of the data, sending hex char values should save 1 byte and since I will be sending a number of values, I can change the return and new line into a comma or something saving another byte. I think I will also reduce the sampling rate, maybe to around 50 kSps. With all this, I'm hoping it will perform as I need it to. I still need to implement this, so I can't say yet. I will post my results. Thanks again. 

    -Ben

  • Some more considerations:

    besides increasing the data size to be sent, printf is SLOOOOOOW. I just discovered that a printf with 4 16 bit values (pointers) takes about 1.2ms on 16MHz clock (at least the mspgcc implementation, which has been heavily optimized). This is terribly slow (albeit still faster than the UART) and even slower when you wait for the UART to send the data and THEN calculate the next.

    So you should
    - write your own value-to-hex conversion function and not use (s)printf and
    - use an interrupt-triggered output buffer for the UART (FIFO, ring buffer), so you can calculate the next values while the first ones arestill being sent.

    About overclocking the ADC12, you can use an input divider (/3) for ADC12CLK, so you'll get 4MHz ADC12CLK and ~52kHz sampling frequency. (4MHz/77cycles)