aam 0 Posted September 12, 2016 Share Posted September 12, 2016 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 Quote Link to post Share on other sites
energia 485 Posted September 12, 2016 Share Posted September 12, 2016 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 aam 1 Quote Link to post Share on other sites
aam 0 Posted September 13, 2016 Author Share Posted September 13, 2016 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)#endifuint16_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 PUSH1short stateb2; // initialize Variables to know the state of PUSH2short previous_state1; // previous state of PUSH1short previous_state2; // previous state of PUSH2boolean 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 Quote Link to post Share on other sites
energia 485 Posted September 13, 2016 Share Posted September 13, 2016 When running the Sketch I posted you should see in the serial monitor "Time: 60" Which is ms/10000 ADS reads. Which is about 166ksps. I only display 1 value after 10000 ADC reads just to make sure that the the last value I am reading is indeed correct. Robert Quote Link to post Share on other sites
aam 0 Posted September 13, 2016 Author Share Posted September 13, 2016 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_analogInPin = readADC(); } 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 Quote Link to post Share on other sites
Fmilburn 446 Posted September 14, 2016 Share Posted September 14, 2016 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); } aam 1 Quote Link to post Share on other sites
aam 0 Posted September 14, 2016 Author Share Posted September 14, 2016 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 Quote Link to post Share on other sites
Fmilburn 446 Posted September 15, 2016 Share Posted September 15, 2016 Glad it works.... If you want to use the MSP432 then you will need to do the same kind of thing as outlined above for the MSP430, i.e. set up the ADC module, write an ISR for the module, and initiate a conversion. I haven't done much with the MSP432 so can't comment much more than that. Quote Link to post Share on other sites
Fmilburn 446 Posted September 15, 2016 Share Posted September 15, 2016 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 #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,¶m); 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; } } energia and veryalive 2 Quote Link to post Share on other sites
aam 0 Posted September 15, 2016 Author Share Posted September 15, 2016 Thank you for your help. I will try to do the same method than the MSP430 but for the MSP432 with your tips. And thanks sharing Robert's code in CCS, it will help me to use CCS Quote Link to post Share on other sites
poliakos 0 Posted November 13, 2016 Share Posted November 13, 2016 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 Quote Link to post Share on other sites
Fmilburn 446 Posted November 14, 2016 Share Posted November 14, 2016 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... energia 1 Quote Link to post Share on other sites
Lukman 1 Posted February 27, 2018 Share Posted February 27, 2018 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 Quote Link to post Share on other sites
Fmilburn 446 Posted February 27, 2018 Share Posted February 27, 2018 Hi @Lukman what processor/LaunchPad are you using and what version of Energia? The above is for the F5529. Quote Link to post Share on other sites
Lukman 1 Posted February 28, 2018 Share Posted February 28, 2018 (edited) 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 March 2, 2018 by Lukman Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.