Jump to content
43oh

Problem Sampling Rate MSP430F5529 in Energia


Recommended Posts

Hello Everybody,

 

I am using a MSP430F5529 Launchpad with Energia IDE in order to measure the current flowing on the motor. But I have a problem with sampling rate. The current that I want to measure has a 22kHz frequency and I have a 10kHz sampling rate. I would like to have a 200kHz sampling frequency.

 

I found in an other topic that the sampling rate can be reduce because of the serial.print. So, I stored my values in a array before displaying them. i have a better sampling (10kHz) but not enough.

 

Could you tell me how can I improve the sampling rate ? 

Attached the sketch.

 

Thank you.

 

 

Sketch Current Measurement .txt

Link to post
Share on other sites

With the current implementation of analogRead(), 10kHz is about the best you can get. Below is Sketch that advanced but will get you to up about 166kHz.

The main difference between this and the implementation in Energia is the clock source for the ADC. 

#if defined(__MSP430_HAS_ADC12_PLUS__)#define REFV_MAP(x) ((x>>8) & 0x70)#define REF_MAP(x)  (x & 0xB1)#define ADCxMEM0 ADC12MEM0 #define DEFAULT_READ_RESOLUTION 12#endif#if defined(__MSP430_HAS_ADC12_PLUS__)#define DEFAULT      (ADC12SREF_0 << 8)#define INTERNAL1V5 ((ADC12SREF_1 << 8) | REFON | REFMSTR | REFVSEL_0)#define INTERNAL2V0 ((ADC12SREF_1 << 8) | REFON | REFMSTR | REFVSEL_1)#define INTERNAL2V5 ((ADC12SREF_1 << 8) | REFON | REFMSTR | REFVSEL_2)#define EXTERNAL     (ADC12SREF_2 << 8)#endifuint16_t analog_reference = DEFAULT;#define NUM_READS 10000void setup() {  Serial.begin(115200);  Serial.println("Setup done");  setupADC(A0);}uint32_t mark, time_it_took;uint16_t i;void loop() {//  Serial.println(readADC());  mark = millis();  for(i = 0; i < NUM_READS; i++) {    readADC();  }  Serial.print("Time: ");  Serial.println(millis() - mark);  Serial.println(readADC());}uint16_t setupADC(uint8_t pin) {  uint8_t channel;  // Check if pin is a special analog pin (A10 = temp sensor, A11 = Vcc/2, etc.)  if (pin >= 128)    channel = pin - 128;  else    channel = digitalPinToADCIn(pin);  // Check if pin is valid  if (pin == NOT_ON_ADC)    return 0;#if defined(__MSP430_HAS_ADC12_PLUS__)  ADC12CTL0 &= ~ADC12ENC;                 // disable ADC  ADC12CTL1 = ADC12SSEL_2 | ADC12DIV_0;   // ADC12OSC as ADC12CLK (~5MHz) / 5  while (REFCTL0 & REFGENBUSY);           // If ref generator busy, WAIT  if (pin == TEMPSENSOR) {// if Temp Sensor    REFCTL0 = REF_MAP(INTERNAL1V5);               // Set reference to internal 1.5V    ADC12MCTL0 = channel | REFV_MAP(INTERNAL1V5); // set channel and reference  } else {    REFCTL0 = REF_MAP(analog_reference);          // Set reference using masking off the SREF bits. See Energia.h.    ADC12MCTL0 = channel | REFV_MAP(analog_reference); // set channel and reference  }  ADC12CTL0 = ADC12ON | ADC12SHT0_4;      // turn ADC ON; sample + hold @ 64 
Link to post
Share on other sites

Hello,

 

Thank you for response.

However, I have 2 problems with this sketch:

- The first problem is when I use this sketch in my program, I have only one value displayed while I need a lot of values (for example 1000 values) in order to have the signal waveform.(Attached screenshot Capture.JPG). I tried to store my values in a array before displaying them (like I did in my first sketch) but it doesn't work.

How can I display an array resulting of my conversion ?

 

- The second problem is when I upload the sketch directly without changing anything, I measure a 2.4kHz sampling frequency. (Attached the result of my test with a 100Hz sinus signal)

Why the sampling is still not satisfying ?

 

Thank you for your help.

 

My sketch is below: 

 

// Use the Servo header in order to command the ESC
#include <Servo.h>

// Define differents PPM
#define MIN_SIGNAL 1000
#define ONE_SIGNAL 1100
#define TWO_SIGNAL 1200
#define THREE_SIGNAL 1300
#define FOUR_SIGNAL 1400
#define FIVE_SIGNAL 1500
#define SIX_SIGNAL 1600
#define SEVEN_SIGNAL 1700
#define EIGHT_SIGNAL 1800
#define NINE_SIGNAL 1900
#define MAX_SIGNAL 2000

#define MAX_SIGNAL 2000
#define MINPLUS_SIGNAL 1200
#define MID_SIGNAL 1400
#define MIDPLUS_SIGNAL 1600


// Selected the pin for send the signal to the ESC
#define MOTOR_PIN 38

// Define the name for red and green LED
#define LED1 RED_LED
#define LED2 GREEN_LED

#if defined(__MSP430_HAS_ADC12_PLUS__)
#define REFV_MAP(x) ((x>>8) & 0x70)
#define REF_MAP(x)  (x & 0xB1)
#define ADCxMEM0 ADC12MEM0
#define DEFAULT_READ_RESOLUTION 12
#endif

#if defined(__MSP430_HAS_ADC12_PLUS__)
#define DEFAULT      (ADC12SREF_0 << 8)
#define INTERNAL1V5 ((ADC12SREF_1 << 8) | REFON | REFMSTR | REFVSEL_0)
#define INTERNAL2V0 ((ADC12SREF_1 << 8) | REFON | REFMSTR | REFVSEL_1)
#define INTERNAL2V5 ((ADC12SREF_1 << 8) | REFON | REFMSTR | REFVSEL_2)
#define EXTERNAL     (ADC12SREF_2 << 8)
#endif

uint16_t analog_reference = DEFAULT;
#define NUM_READS 10000

/***************************************************************************** Program *******************************************************************************************/

Servo motor;

//const int analogInPin = A2;  // Analog input pin that the potentiometer is attached to
//const int analogOutPin = 9; // Analog output pin that the LED is attached to

/* Related to digital Pin */
short stateb1; // initialize Variables to know the state of PUSH1
short stateb2; // initialize Variables to know the state of PUSH2
short previous_state1; // previous state of PUSH1
short previous_state2; // previous state of PUSH2
boolean toggle1 = false;

///* Related to the analog pin */
//long outputValue = 0;        // value output to the PWM (analog out)
//long Vout = 0;
//long Iout = 0;
short j = 0;
//
///* Sampling window */
const int data_size = 501;
//
///* Array contain sampled data */
//int captured_data_analogInPin[data_size];
int captured_data_analogInPin[data_size];
//int captured_data_analogOutPin[data_size];


void setup()
{
  Serial.begin(115200);
  Serial.println("Setup done");
  setupADC(A0);
  // Initialize the push button pins:PUSH1 and PUSH2 as inputs
  pinMode(PUSH1, INPUT_PULLUP); // (P2.1)
  pinMode(PUSH2, INPUT_PULLUP); // (P1.1)
  // Initialize the digital pin as outputs.
//  pinMode(LED1, OUTPUT);     
//  pinMode(LED2, OUTPUT);
 
  motor.attach(MOTOR_PIN);
//  Serial.println("\nMaximum Signal ON");
  motor.writeMicroseconds(MAX_SIGNAL);
}

uint32_t mark, time_it_took;
uint16_t i;

void loop()
{
  digitalWrite (PUSH1 , HIGH);
  stateb1 = digitalRead (PUSH1);
  digitalWrite (PUSH2 , HIGH);
  stateb2 = digitalRead (PUSH2);
  delay (20);  
 
  if  ((stateb1 != previous_state1) && (j == 0))
  {
    if (!stateb1)
    {
      motor.writeMicroseconds(MIN_SIGNAL);
      
      Serial.read();
      Serial.println("\nMinimum Signal On ");
      j++;
      delay(1500);
      analog();
    }
    // The previous_state1 and 2 take the state of state1 and 2
      previous_state1 = stateb1;
  }

  if  ((stateb1 != previous_state1) && (j == 1))
  {
    if (!stateb1)
    {
      motor.writeMicroseconds(ONE_SIGNAL);
      
      Serial.read();
      Serial.println("\n1100 signal On ");
      j++;
      delay(1500);
      analog();
    }
    // The previous_state1 and 2 take the state of state1 and 2
      previous_state1 = stateb1;
  }
 
}

void analog ()
{
//  for (int x = 0 ; x < data_size ; x++)
//  {
//    captured_data_analogInPin[x] = analogRead(analogInPin);
//  }      
     
//  Serial.println(readADC());
  mark = millis();

  for(i = 0; i < NUM_READS; i++) {
    readADC();
  }

//  Serial.print("Time: ");
//  Serial.println(millis() - mark);
  Serial.println(readADC());   
}


uint16_t setupADC(uint8_t pin) {
  uint8_t channel;

  // Check if pin is a special analog pin (A10 = temp sensor, A11 = Vcc/2, etc.)
  if (pin >= 128)
    channel = pin - 128;
  else
    channel = digitalPinToADCIn(pin);

  // Check if pin is valid
  if (pin == NOT_ON_ADC)
    return 0;

#if defined(__MSP430_HAS_ADC12_PLUS__)
  ADC12CTL0 &= ~ADC12ENC;                 // disable ADC
  ADC12CTL1 = ADC12SSEL_2 | ADC12DIV_0;   // ADC12OSC as ADC12CLK (~5MHz) / 5
  while (REFCTL0 & REFGENBUSY);           // If ref generator busy, WAIT
  if (pin == TEMPSENSOR) {// if Temp Sensor
    REFCTL0 = REF_MAP(INTERNAL1V5);               // Set reference to internal 1.5V
    ADC12MCTL0 = channel | REFV_MAP(INTERNAL1V5); // set channel and reference
  } else {
    REFCTL0 = REF_MAP(analog_reference);          // Set reference using masking off the SREF bits. See Energia.h.
    ADC12MCTL0 = channel | REFV_MAP(analog_reference); // set channel and reference
  }
  ADC12CTL0 = ADC12ON | ADC12SHT0_4;      // turn ADC ON; sample + hold @ 64

post-49371-0-13386800-1473775767_thumb.jpg

post-49371-0-89243800-1473775776_thumb.jpg

Link to post
Share on other sites

You are right I saw in the serial monitor "Time 60", I just removed it in my sketch. I understood how you can got 166ksps thank you. 

Maybe, it is because of the serial.print which is reduced the sampling rate if I obtain a 2.4kHz frequency. I think store this value in an array before displaying in the serial monitor would increase the sampling rate. 

 

To display 500 values I did: 

 

int capture_data_analogInPin[NUM_READS];

 

void analog ()

{     
//  Serial.println(readADC());
  mark = millis();

  for(i = 0; i < NUM_READS; i++) {
    captured_data_analogInPinreadADC();

  }
  Serial.println(captured_data_analogInPin);   

}

 

And I call this function in the loop however, it doesn't work. 

Yet, when I used it in my first sketch (with analogRead) it worked 

 

 for (int x = 0 ; x < data_size ; x++)

{

   captured_data_analogInPin[x] = analogRead(analogInPin);

}      

 

Do you know, how can I display more than 1 value using the sketch that you posted please ? 

 

Thank you for your help

Link to post
Share on other sites

Use an array to store the values, something like this:

  • set NUM_READS to the number of values you wish to record
  • inside loop(), declare an array to contain NUM_READS values
  • inside the for loop that reads values, read the values into the array
  • outside of the loop that reads the values, print the values

If you make NUM_READS too large it will corrupt the program.  Do not place the serial print inside the loop with the analog read and make sure there is sufficient time for the serial print to finish before starting another read cycle. Serial print is very slow.  With Robert's code changing NUM_READS to 2000 and replacing loop() with this will work:

void loop() {

  mark = millis();

  uint16_t analogValue[NUM_READS];
  for(i = 0; i < NUM_READS; i++) {
    analogValue[i] = readADC();
  }

  Serial.print("Time: ");
  Serial.println(millis() - mark);
  for(i = 0; i < NUM_READS; i++) {
    Serial.println(analogValue[i]);
  }
  Serial.print(NUM_READS);
  Serial.println(" values printed");
  while(1);
}
Link to post
Share on other sites

I tried your sketch using an array and it works !! Thank you very much for your sketches !

I did the same test with a 100Hz sinus signal and I got about 1620 sample per period giving about 162ksps. The waveform is given by the picture.

 

I have just one more question. I have a MSP432P401R Launchpad  and I have the same problem than the MSP430F5529.

Do you know how can I improve the sampling rate ?

 

Thanks again

post-49371-0-69976800-1473863200_thumb.jpg

Link to post
Share on other sites

A bit off topic but I have been looking into driverlib and decided to rewrite Robert's code in CCS with driverlib using the TI compiler.  It is not apples to apples but I tried to get close.  It runs slower than Robert's code - it was actually faster when polled for the conversion to complete than with the interrupt as shown.  MCLK is  set to 24 MHz rather than 25 MHz.  Anyway, it was an interesting exercise.

 

EDIT:  Use energia's code - not mine :P


#include "driverlib.h"
#include <stdio.h>      // increase heap size to 320 in linker for printf()

#define MCLK_FREQ_KHZ                       24000
#define FLLREF_KHZ                          32
#define UCS_MCLK_FLLREF_RATIO   	    MCLK_FREQ_KHZ/FLLREF_KHZ
#define NUM_READS                           10000
#define ADC_PIN                             GPIO_PORT_P6,GPIO_PIN0

void setupPMM();
void setupUCS();
void setupWDT();
unsigned long milliSecs();
void setupADC(void);
unsigned long readADC(void);

volatile unsigned long oneThirdMillis = 0;   // counter - 1/3 of a millisecond

//--------------------------------  m a i n  -----------------------------------
int main(void) {

    WDT_A_hold(WDT_A_BASE);

	setupPMM();
	setupUCS();
	setupWDT();
    setupADC();

    __bis_SR_register(GIE);

    int i;
    unsigned long analogValue;
    unsigned long mark = milliSecs();

    for(i = 0; i < NUM_READS; i++){
    	analogValue = readADC();
    }

    unsigned long finished = milliSecs();
    printf("Time = %d ms\n", finished - mark);
    printf("Last value = %d mV\n", (analogValue * 3230) / 4096);   // Vcc = 3.23V

    while(1);
}
//-----------------------------  s e t u p A D C  -------------------------------
void setupADC(void){

    GPIO_setAsPeripheralModuleFunctionOutputPin(ADC_PIN);

    ADC12_A_init(ADC12_A_BASE,
                 ADC12_A_SAMPLEHOLDSOURCE_SC,
                 ADC12_A_CLOCKSOURCE_ADC12OSC,
                 ADC12_A_CLOCKDIVIDER_1);

    ADC12_A_enable(ADC12_A_BASE);

    ADC12_A_setupSamplingTimer(ADC12_A_BASE,
                               ADC12_A_CYCLEHOLD_4_CYCLES,
                               ADC12_A_CYCLEHOLD_4_CYCLES,
                               ADC12_A_MULTIPLESAMPLESDISABLE);

    ADC12_A_configureMemoryParam param = {0};
    param.memoryBufferControlIndex = ADC12_A_MEMORY_0;
    param.inputSourceSelect = ADC12_A_INPUT_A0;
    param.positiveRefVoltageSourceSelect = ADC12_A_VREFPOS_AVCC;
    param.negativeRefVoltageSourceSelect = ADC12_A_VREFNEG_AVSS;
    param.endOfSequence = ADC12_A_NOTENDOFSEQUENCE;

    ADC12_A_configureMemory(ADC12_A_BASE,&param);

    ADC12_A_enableInterrupt(ADC12_A_BASE, ADC12_A_IE0);
}
//-----------------------------  r e a d A D C  --------------------------------
unsigned long readADC(void){

	ADC12_A_startConversion(ADC12_A_BASE,
							ADC12_A_MEMORY_0,
							ADC12_A_SINGLECHANNEL);
        __bis_SR_register(LPM3_bits);                     // sleep until converted
	return ADC12_A_getResults(ADC12_A_BASE, ADC12_A_MEMORY_0);
}
//----------------------------  s e t u p P M M  -------------------------------
void setupPMM(void){
    // set VCore = 0 for 8 MHz and below
    //           = 1 for 12 MHz
    //           = 2 for 20 MHz
    //           = 3 for 25 MHz
    PMM_setVCore(PMM_CORE_LEVEL_3);
}
//----------------------------  s e t u p U C S  -------------------------------
void setupUCS(void){
    UCS_initClockSignal(                     // use REFO for ACLK (32768 Hz)
        UCS_ACLK,
        UCS_REFOCLK_SELECT,
        UCS_CLOCK_DIVIDER_1);

    UCS_initClockSignal(                     // set DCO FLL ref to DFOCLK
        UCS_FLLREF,
        UCS_DCOCLK_SELECT,
        UCS_CLOCK_DIVIDER_1);

    UCS_initClockSignal(                     // use DFOCLK for SMCLK
        UCS_SMCLK,
        UCS_DCOCLK_SELECT,
        UCS_CLOCK_DIVIDER_1);

    UCS_initFLLSettle(                       // set MCLK frequency and ratio
    	MCLK_FREQ_KHZ,
        UCS_MCLK_FLLREF_RATIO);
}
//----------------------------  s e t u p W D T  -------------------------------
void setupWDT(void){
    WDT_A_initIntervalTimer(WDT_A_BASE,
                            WDT_A_CLOCKSOURCE_SMCLK,
                            WDT_A_CLOCKDIVIDER_8192);

    WDT_A_start(WDT_A_BASE);

    //Place WDT in timer interrupt mode
    SFR_clearInterrupt(SFR_WATCHDOG_INTERVAL_TIMER_INTERRUPT);
    SFR_enableInterrupt(SFR_WATCHDOG_INTERVAL_TIMER_INTERRUPT);
}
//---------------------------  m i l l i S e c s  ----------------------------
unsigned long milliSecs(){
	return oneThirdMillis / 3;               // return millis
}
//---------------------------  W D T _ A _ I S R  ----------------------------
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=WDT_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(WDT_VECTOR)))
#endif
void WDT_A_ISR(void)
{
    oneThirdMillis++;                        // count thirds of a millisecond
}
//-------------------------  A D C 1 2 _ A _ I S R  --------------------------
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=ADC12_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(ADC12_VECTOR)))
#endif
void ADC12_A_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
        ADC12_A_clearInterrupt(ADC12_A_BASE, ADC12_A_IE0);
        __bic_SR_register_on_exit(LPM0_bits);
    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;
    }
}
Link to post
Share on other sites
  • 1 month later...

Hello guys,

I tried to use Robert's code and it's working well. However i would like to know what should i modifiy in order to read values from two differents channels?

 

Do i have to make my code like this?

 

setupADC(A0);

setupADC(A1);

 

analogvalue=readADC();

 

=> However what should i do in order to select the appropriate channel?

Because readADC() seems to be a void function :/

 

thanks in advance for your help :)

 

Link to post
Share on other sites

Hi @@poliakos,

 

The ADC converter has multiple channels (pins) that can be multiplexed to it.  The user can choose among various conversion modes:  single channel (what is being used above), repeat single channel, a sequence of channels (autoscan), or a repeated sequence of channels (repeated autoscan).  What I believe you are describing is a sequence of channels.  For the F5529, see slau208O, the Family User's Guide for a complete description.

 

The user specifies what mode they want the ADC to operate in and then reads the appropriate register(s).  I just had a quick look at MSP430 ware for the F5529 in Code Composer Studio and there is an example for a repeated sequence of conversions.   It shouldn't be hard to change that to a single sequence if that is your objective but the ADC module is fairly complicated if you aren't used to working directly with registers.

 

I haven't tried to incorporate this into Energia...

Link to post
Share on other sites
  • 1 year later...

Hi All,
Nice to meet you all, actually I just tried Energia and do not have much experience.
I tried to use Robert's code using Energia, but cannot work. I found that readADC() is a function, any one in here can share the readADC() function?

I appreciate your help.

Thank you.
Best Regards,

 

Lukman

Link to post
Share on other sites

Hello @Fmilburn & @energia

Thanks for your response, Currently I use MSP430FR5969 Launchpad. I check, the ADC also 12 bit, so it should be the same I think.

I have an application for measuring the current from the DC motor and I saw this forum. 
I saw you use "readADC()" function, is it the same with analogRead() on the Energia? Is it?

 

I appreciate your answer.

 

Thank you very much.

Best Regards,

 

Lukman

Edited by Lukman
Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...