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.
Part Number: MSP432P401R
Hello folks,
I am looking for a sanity check on my settings of the ADC for the MSP432. If someone could please confirm everything below looks correct (without any blatant bugs that could cause jumpy ADC readings) I would be very grateful. I call initialize_adcs() function at the beginning of the program once, prioritize ADC14_IRQHandler() ISR as shown below.
MAP_Interrupt_setPriority(FAULT_SYSTICK, 0x10); MAP_Interrupt_setPriority(INT_PORT1, 0x20); MAP_Interrupt_setPriority(INT_ADC14, 0x20);
I then call the following once, prior to the main while() loop:
ADC14->CTL0 |= ADC14_CTL0_ENC | /*!< ADC14 enable conversion - same as MAP_ADC14_toggleConversionTrigger(); */ ADC14_CTL0_SC; /*!< ADC14 start conversion */
I am reading ADC 0-13 and pull the internal temperature sensor info which required some unclear modification... (which is mostly why I am concerned regarding the settings below)
void initialize_adcs(void) { // Initialize the shared reference module. By default, REFMSTR=1 => REFCTL is used to configure the internal reference while(REF_A->CTL0 & REF_A_CTL0_GENBUSY);// If ref generator busy, WAIT REF_A->CTL0 |= REF_A_CTL0_VSEL_0 | // Enable internal 1.2V reference REF_A_CTL0_ON; // Turn reference on REF_A->CTL0 |= REF_A_CTL0_ON; // Turn reference on REF_A->CTL0 &= ~REF_A_CTL0_TCOFF; // Enable temperature sensor // Turn on ADC14, extend sampling time to avoid overflow of results ADC14->CTL0 = ADC14_CTL0_ON | /*!< ADC14 on */ ADC14_CTL0_MSC | /*!< ADC14 multiple sample and conversion */ ADC14_CTL0_SHT0__192 | /*!< 192 */ ADC14_CTL0_SHP | /*!< ADC14 sample-and-hold pulse-mode select */ ADC14_CTL0_CONSEQ_3; /*!< Repeat-sequence-of-channels */ ADC14->CTL1 |= ADC14_CTL1_TCMAP; // Enable internal temperature sensor ADC14->MCTL[0] = ADC14_MCTLN_INCH_0; // ref+=AVcc, channel = A0 ADC14->MCTL[1] = ADC14_MCTLN_INCH_1; // ref+=AVcc, channel = A1 ADC14->MCTL[2] = ADC14_MCTLN_INCH_2; // ref+=AVcc, channel = A2 ADC14->MCTL[3] = ADC14_MCTLN_INCH_3; // ref+=AVcc, channel = A3 ADC14->MCTL[4] = ADC14_MCTLN_INCH_4; // ref+=AVcc, channel = A4 ADC14->MCTL[5] = ADC14_MCTLN_VRSEL_1 | ADC14_MCTLN_INCH_22; // ADC input ch A22 => temp sense // ADC14->MCTL[5] = ADC14_MCTLN_INCH_5; // original code without temperature sensor ADC14->MCTL[6] = ADC14_MCTLN_INCH_6; // ref+=AVcc, channel = A6 ADC14->MCTL[7] = ADC14_MCTLN_INCH_7; // ref+=AVcc, channel = A7 ADC14->MCTL[8] = ADC14_MCTLN_INCH_8; // ref+=AVcc, channel = A8 ADC14->MCTL[9] = ADC14_MCTLN_INCH_9; // ref+=AVcc, channel = A9 ADC14->MCTL[10] = ADC14_MCTLN_INCH_10; // ref+=AVcc, channel = A10 ADC14->MCTL[11] = ADC14_MCTLN_INCH_11; // ref+=AVcc, channel = A11 ADC14->MCTL[12] = ADC14_MCTLN_INCH_12; // ref+=AVcc, channel = A12 ADC14->MCTL[13] = ADC14_MCTLN_INCH_13| // ref+=AVcc, channel = A13 ADC14_MCTLN_EOS; ADC14->IER0 = ADC14_IER0_IE13; // Enable ADC14IFG.7 while(!(REF_A->CTL0 & REF_A_CTL0_GENRDY)); // Wait for reference generator to settle __enable_interrupt(); NVIC->ISER[0] = 1 << ((ADC14_IRQn) & 31); // Enable ADC interrupt in NVIC module } /* ADC14 interrupt service routine */ void ADC14_IRQHandler(void) { if (ADC14->IFGR0 & ADC14_IFGR0_IFG13) { A0results[0] = ADC14->MEM[0]; y1_sim = A0results[0]; A0results[1] = ADC14->MEM[1]; A0results[2] = ADC14->MEM[2]; A0results[3] = ADC14->MEM[3]; y2_sim = A0results[3]; A0results[4] = ADC14->MEM[4]; A0results[5] = ADC14->MEM[5]; temp = A0results[5]; lift_off1[0] = ADC14->MEM[6]; lift_off1[1] = ADC14->MEM[7]; lift_off1[2] = ADC14->MEM[8]; lift_off2[0] = ADC14->MEM[9]; lift_off2[1] = ADC14->MEM[10]; lift_off2[2] = ADC14->MEM[11]; A0results[12] = ADC14->MEM[12]; A0results[13] = ADC14->MEM[13]; } }
I am most concerned about ADC values jumping for no reason which could trigger unwanted effects in our system.
Thank you,
Bob
Thanks Amit,
Unfortunately, we are too close too production launch and have done considerable reliability testing to make any major changes. The project started with the MSP432 as an experimental micro and we know all too well the risk associated with updating to API commands from registers on the fly.. don't want any unknown repercussions. Not looking for a super detailed review, just any blatant bugs or issues with regards to ADC ISR prioritization, ISR, etc. I am not saying there is any issue with code posted above. Just a sanity check.
Best,
Bob
Hi Amit,
Currently the IO's pertaining to ADC are set as follows:
/* Set ADC readings as input to read with ADC */ MAP_GPIO_setAsInputPin(GPIO_PORT_P4, GPIO_PIN2); MAP_GPIO_setAsInputPin(GPIO_PORT_P4, GPIO_PIN3); MAP_GPIO_setAsInputPin(GPIO_PORT_P4, GPIO_PIN4); MAP_GPIO_setAsInputPin(GPIO_PORT_P4, GPIO_PIN5); MAP_GPIO_setAsInputPin(GPIO_PORT_P4, GPIO_PIN6); MAP_GPIO_setAsInputPin(GPIO_PORT_P4, GPIO_PIN7); MAP_GPIO_setAsInputPin(GPIO_PORT_P5, GPIO_PIN2); MAP_GPIO_setAsInputPin(GPIO_PORT_P5, GPIO_PIN5); /* SET ADC PINS TO LOW SO RESIDUAL VOLTAGE DOES NOT AFFECT ADC READINGS */ MAP_GPIO_setAsOutputPin(GPIO_PORT_P4, GPIO_PIN0); MAP_GPIO_setAsOutputPin(GPIO_PORT_P4, GPIO_PIN1); MAP_GPIO_setAsOutputPin(GPIO_PORT_P5, GPIO_PIN0); MAP_GPIO_setAsOutputPin(GPIO_PORT_P5, GPIO_PIN1); MAP_GPIO_setAsOutputPin(GPIO_PORT_P5, GPIO_PIN3); MAP_GPIO_setAsOutputPin(GPIO_PORT_P5, GPIO_PIN4); MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P4, GPIO_PIN0); MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P4, GPIO_PIN1); MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P5, GPIO_PIN0); MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P5, GPIO_PIN1); MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P5, GPIO_PIN3); MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P5, GPIO_PIN4);
As you can see, not all ADC channels 0-13 are used for actual readings. There was intentional space left between certain ADC pins which we found were causing a sort of "residual capacitance" noise on the ADC pins right next to each other.
Schematic below shows nodes which are reading ADC signals.
Prior to all of these settings, we initialized the IOs as follow:
// Terminate all remaining pins on the device // P1DIR |= 0xFF; P1OUT = 0; P2DIR |= 0xFF; P2OUT = 0; P3DIR |= 0xFF; P3OUT = 0; P4DIR |= 0xFF; P4OUT = 0; P5DIR |= 0xFF; P5OUT = 0; P6DIR |= 0xFF; P6OUT = 0; P7DIR |= 0xFF; P7OUT = 0; P8DIR |= 0xFF; P8OUT = 0; P9DIR |= 0xFF; P9OUT = 0; P10DIR |= 0xFF; P10OUT = 0;
Thank you!
Hello Amit,
Many thanks for your review. Can you please elaborate on this problem? The design intent was to update the ADC variables (lift_off1, A0results[X], etc) within the ADC ISR as fast as possible for the main loop to process. So as long as it updates the variables with the new ADC value then I am okay. Technically if it did something like:
(uint16_t) old_ADC_value -> (uint16_t) 0 (for a short period of time) -> (uint16_t) new_ADC value
then that would be of concern. But I believe it should just be replacing the all of the value directly and then jumping back to the main loop.
old_ADC_value -> new_ADC value
Am I understanding this correctly or is there something else happening? Perhaps I should only be calling this ADC ISR once per main loop.
Best,
Robert
After reviewing the implementation, I guess it's not necessarily required to run it as fast as I can. So I tested a modification to initialize_adc()
Changed from
ADC14_CTL0_CONSEQ_3 ((uint32_t)0x00060000) /*!< Repeat-sequence-of-channels */
to
ADC14_CTL0_CONSEQ_1 ((uint32_t)0x00020000) /*!< Sequence-of-channels */
and then I called:
ADC14->CTL0 |= ADC14_CTL0_ENC | /*!< ADC14 enable conversion - same as MAP_ADC14_toggleConversionTrigger(); */ ADC14_CTL0_SC; /*!< ADC14 start conversion */
at the beginning of my main loop to take the measurement prior to each calculation.
Is this what you recommend? The other implementation isn't necessarily wrong though is it? The reason it was implemented like that was because I did not want to call the enable ADC conversion command above every time an updated set of ADC value was needed within the main loop and within a timer ISR routine.
I guess I don't understand what you mean by: i.e. the interrupt source is not being cleared in the ISR causing it to become a re-entrant function and the memory to be overwritten. How do I clear the ADC ISR?
Best,
Robert
Hi Amit,
Referencing MSP432 example "msp432p401x_adc14_06.c" under the MSP432P4xx Code Examples.
The ADC ISR routine below:
// ADC14 interrupt service routine void ADC14_IRQHandler(void) { if (ADC14->IFGR0 & ADC14_IFGR0_IFG3) { A0results[index] = ADC14->MEM[0]; // Move A0 results, IFG is cleared A1results[index] = ADC14->MEM[1]; // Move A1 results, IFG is cleared A2results[index] = ADC14->MEM[2]; // Move A2 results, IFG is cleared A3results[index] = ADC14->MEM[3]; // Move A3 results, IFG is cleared index = (index + 1) & 0x7; // Increment results index, modulo __no_operation(); // Set Breakpoint1 here } }
does not clear the ISR as you mentioned which is what I based my code off of.
Is this incorrect? I always assumed you had to clear the ISR..
Is it sufficient to do the following:
/* ADC14 interrupt service routine */ void ADC14_IRQHandler(void) { uint64_t status; status = MAP_ADC14_getEnabledInterruptStatus(); MAP_ADC14_clearInterruptFlag(status); if (status & ADC14->IFGR0 & ADC14_IFGR0_IFG13) { A0results[0] = ADC14->MEM[0]; y1_sim = A0results[0]; A0results[1] = ADC14->MEM[1]; A0results[2] = ADC14->MEM[2]; A0results[3] = ADC14->MEM[3]; y2_sim = A0results[3]; A0results[4] = ADC14->MEM[4]; A0results[5] = ADC14->MEM[5]; temp = A0results[5]; lift_off1[0] = ADC14->MEM[6]; lift_off1[1] = ADC14->MEM[7]; lift_off1[2] = ADC14->MEM[8]; lift_off2[0] = ADC14->MEM[9]; lift_off2[1] = ADC14->MEM[10]; lift_off2[2] = ADC14->MEM[11]; A0results[12] = ADC14->MEM[12]; A0results[13] = ADC14->MEM[13]; } }
Best,
Robert
No the first example is from MSP432Ware. Does that mean that it is correct since we are using MSP432Ware? I will go ahead and clear it via the simple second example.
Amit thank you for all of your help throughout the process. Our team here very much appreciates it.
Wishing you the best in 2018!
Regards,
Robert
Hi Amit,
I have attempted to clear the ADC ISR and came across an issue and now I am unsure as to what is correct.
The following code works (notice if loop)
/* ADC14 interrupt service routine */ void ADC14_IRQHandler(void) { uint64_t status; status = MAP_ADC14_getEnabledInterruptStatus(); MAP_ADC14_clearInterruptFlag(status); if (status & ADC14_IFGR0_IFG13) { A0results[0] = ADC14->MEM[0]; y1_sim = A0results[0]; A0results[1] = ADC14->MEM[1]; A0results[2] = ADC14->MEM[2]; A0results[3] = ADC14->MEM[3]; y2_sim = A0results[3]; A0results[4] = ADC14->MEM[4]; A0results[5] = ADC14->MEM[5]; temp = A0results[5]; lift_off1[0] = ADC14->MEM[6]; lift_off1[1] = ADC14->MEM[7]; lift_off1[2] = ADC14->MEM[8]; lift_off2[0] = ADC14->MEM[9]; lift_off2[1] = ADC14->MEM[10]; lift_off2[2] = ADC14->MEM[11]; A0results[12] = ADC14->MEM[12]; A0results[13] = ADC14->MEM[13]; } }
However the following does not:
/* ADC14 interrupt service routine */ void ADC14_IRQHandler(void) { uint64_t status; status = MAP_ADC14_getEnabledInterruptStatus(); MAP_ADC14_clearInterruptFlag(status); if (status & ADC14->IFGR0 & ADC14_IFGR0_IFG13) { A0results[0] = ADC14->MEM[0]; y1_sim = A0results[0]; A0results[1] = ADC14->MEM[1]; A0results[2] = ADC14->MEM[2]; A0results[3] = ADC14->MEM[3]; y2_sim = A0results[3]; A0results[4] = ADC14->MEM[4]; A0results[5] = ADC14->MEM[5]; temp = A0results[5]; lift_off1[0] = ADC14->MEM[6]; lift_off1[1] = ADC14->MEM[7]; lift_off1[2] = ADC14->MEM[8]; lift_off2[0] = ADC14->MEM[9]; lift_off2[1] = ADC14->MEM[10]; lift_off2[2] = ADC14->MEM[11]; A0results[12] = ADC14->MEM[12]; A0results[13] = ADC14->MEM[13]; } }
The difference in the if statement within the ISR seems to be causing the issue. It only works if I drop:
ADC14->IFGR0
Regarding logic, is it okay to remove this ? Apparently the above IFGR0 represents /*!< Interrupt Flag 0 Register */
I tried to mix the MSP432Ware sample code shown previously with your advice to clear the ADC ISR flag without success unless I do this.
Please advise.
Regards,
Robert
Thanks Amit,
That is quite confusing, why would the example from MSP432Ware be written like that in the first place?
if (ADC14->IFGR0 & ADC14_IFGR0_IFG13)
It seems like the interrupt flag was not supposed to be cleared... Could it have something to do with the fact that the ADC was set to repeat a sequence-of-channels?
ADC14_CTL0_CONSEQ_3 ((uint32_t)0x00060000) /*!< Repeat-sequence-of-channels */
It seems like you believe this is the correct way to implement?
/* ADC14 interrupt service routine */ void ADC14_IRQHandler(void) { uint64_t status; status = MAP_ADC14_getEnabledInterruptStatus(); MAP_ADC14_clearInterruptFlag(status); if (status & ADC14_IFGR0_IFG13) { A0results[0] = ADC14->MEM[0]; y1_sim = A0results[0]; ... ...
Best,
Robert
Okay understood, thank you.
One final question regarding the SDK:
In an example in MSP432 SDK. "adc14_single_conversion_repeat.c", the following is implemented as the ISR.
//![Single Sample Result] /* ADC Interrupt Handler. This handler is called whenever there is a conversion * that is finished for ADC_MEM0. */ void ADC14_IRQHandler(void) { uint64_t status = MAP_ADC14_getEnabledInterruptStatus(); MAP_ADC14_clearInterruptFlag(status); if (ADC_INT0 & status) { curADCResult = MAP_ADC14_getResult(ADC_MEM0); normalizedADCRes = (curADCResult * 3.3) / 16384; MAP_ADC14_toggleConversionTrigger(); } }
Within this ISR, the toggleConverstionTrigger() function is called near the end of the routine. This is obviously done because they are setting the sample timer to ADC_MANUAL_ITERATION, (single sample mode configuration) and a call to ADC14_toggleConversionTrigger() is required at the end of the if loop for the repea to occur. However this seems to me a very roundabout way of performing a repeat of a single channel. Why not just set it to ADC_AUTOMATIC_ITERATION method? Seems odd that the manual method would be used...
Best,
Robert
**Attention** This is a public forum