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.

# MSP432P401R: MSP432P401R ADC in differential mode

Part Number: MSP432P401R

Hi, the idea of the project is to detect the Back Electromagnetic Force (BEMF) of a BLDC and for this I have made this simple program where I wanted to use the ADC of the MSP432P401R in differential mode, since I have used six consecutive channels and the idea was to take the difference between two channels (the phase and the reference voltage) and study the change of sign, which would allow me to detect a zero crossing.
The problem is that I don't know if the idea of defining the channels I am interested in, enable the ADC, start conversion and assign the difference between the channels is correct in each case of the switch-case. The thing is that, despite of having caught six consecutive channels, I would only be interested in dealing with the difference between two channels depending on the state in which I find myself, but I do not know if it is the most indicated way as I have planned it.

1. #include "msp.h"
2. #include <stdint.h>
3. #include <stdio.h>
4.
6.
7. void inicializaciones(void){
8.     // GPIO pin (P5.1) to be set to HIGH when zero-crossing to detect the BEMF
9.     P5SEL0 &= ~BIT1;
10.     P5SEL1 &= ~BIT1;
11.     P5DIR |= BIT1; // output
12. }
13.
18.     P8SEL1 |= (BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7); // Enable A/D input channels
19.     P8SEL0 |= (BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);
21. }
22.
23. void init_interrupciones(void){
25.     __enable_irq(); // Enable global interrupt
26. }
27.
28. void main(void)
29. {
30.     WDT_A->CTL = WDT_A_CTL_PW | WDT_A_CTL_HOLD;     // stop watchdog timer
31.     inicializaciones();
33.     init_interrupciones();
34.
35.     while(1){
36.         static int16_t last_delta = -1; // By using "static" on variables that are inside a function, it allows their value to persist between calls.
37.         static int16_t delta = 0;
38.
40.             case 0: {
41.                 P5OUT &= ~BIT1; // We set the GPIO of the zero-crossing to LOW
46.                 break;
47.             }
48.             case 1: {
49.                 P5OUT &= ~BIT1;
54.                 break;
55.             }
56.             case 2: {
57.                 P5OUT &= ~BIT1;
62.                 break;
63.             }
64.             case 3: {
65.                 P5OUT &= ~BIT1;
70.                 break;
71.             }
72.             case 4: {
73.                 P5OUT &= ~BIT1;
78.                 break;
79.             }
80.             case 5: {
81.                 P5OUT &= ~BIT1;
86.                 break;
87.             }
88.         }
89.
90.         if(last_delta < 0){
91.             if(delta > 0){
93.                 P5OUT |= BIT1; // Set GPIO to HIGH when zero-crossing is detected
94.                 last_delta = delta;
98.                 }
99.             }
100.         }
101.
102.         if(last_delta > 0){
103.             if(delta < 0){
105.                 P5OUT |= BIT1;
106.                 last_delta = delta;
110.                 }
111.             }
112.         }
113.     }
114. }

Once you start (SC=1) a channel with CONSEQ=2, it runs forever. I suspect you want CONSEQ_0 here.

------------

[...]

A conversion using MCTL[0] interrupts on IFG0, not IFG21. This only appears to work since you need parentheses around the "&" subexpression.

Also, you can't change MCTL with ENC=1, you need to set ENC=0 first.

Unsolicited: I suggest you set MCTL[0/1/2] to the channel pairs you want once at the beginning, then change CSTARTADD as needed. Even better: Sample all 3 at once using CONSEQ=1 (and MSC=1).

------------

I'm pretty sure that you don't need to subtract the two MEM registers; if you are using MCTL[0], the (differential) result is in MEM[0]. [Disclaimer: I haven't used differential mode on the MSP432P, but I have on other devices, and I'm pretty sure that's what the book says.]

• Regarding the CONSEQ_2 problem, let's say that my idea was to take ADC measurements consecutively until one of the two conditionals (if last_delta < 0 or if last_delta > 0) was reached, since I would know that at that moment the zero crossing had been detected and the ADC was disabled in order to change the channel to be measured according to the next state.

To solve the second thing you tell me about the interrupts enables in MCTL[0] I have put for example the following for the case where state = 0: ADC14->MCTL[19] |= ADC14_MCTLN_INCH_20 | ADC14_MCTLN_DIF;

And finally regarding that it is unnecessary to make the subtraction of the two channels and assign it to delta I have set for example the following for the case where state = 0:

Even so, one thing I don't understand about the ADC in differential mode is if for example I use channel A20 and A21 if I have to wait until the conversion is completed in channel A20 or channel A21 and so I'm not 100% sure what to put in the while() loops in each state.

• 1) OK. But you should wait for the ADC to finish the current conversion (ADC14BUSY=0) after setting ENC=0. [Ref TRM (SLAU256I) Sec 22.2.8.6]

2) In the ADC registers, the only time you refer to a channel (An) number is when you set the MCTL register. All other references (CSTARTADD, IFGn, IEn) refer to MCTL[] indices. E.g. if you set MCTL[1]=INCH_3, that will trigger (when complete) IFG1, not IFG3.

Setting the DIF bit in  MCTL[19]  with (e.g.) INCH=4 implicitly assigns INCH=(4+1) to that MCTL/MEM entry as well. [Ref TRM table 22-11] So waiting on the MCTL[19] entry waits for both channels to (simultaneously) complete to give you a single result (in MEM[19]).

• Hi, combining the ideas you have suggested in your two answers and after looking at the Resource explorer examples I have something like this, how do you see it?

9.     P8SEL1 |= (BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);
10.     P8SEL0 |= (BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);
12. }

This part of the code would be inside each case statement:

2. while(!ADC14->IFGR0 & ADC14_IFGR0_IFG2); // We wait until the conversion has been completed on the last channel of the sequence
3. delta = ADC14->MEM[1]; /* Waiting on the MCTL[19] entry waits for both channels (A20, A21) to (simultaneously) complete to give you a single result (in MEM[1]).

Likewise, as a substitute for the ADCBUSY as I understand it also works a while loop where you wait until you have set the flag of the ADC channel, since you ensure that the measurement is taken to have it stored in the MEM register of that channel, as I do in my code above.

• It looks about right -- once you figure out you have a zero-crossing you already have the other (simultaneous) data.

I think you need to read MEM[2] in order to clear IFG2. Alternatively, you can just wait (spin) on ADC14BUSY, since each CONSEQ=1 batch stops after one pass.

• Okay, thank you very much! One last question to close the subject... in the users guide it specifies the following about the ADC working in differential mode:
For differential inputs, this means that the result has an offset of 8192 added to it to make the number positive.
As my goal is to be able to have negative ADC read cases for my program idea, is it as simple as when assigning to the delta variable, instead of directly assigning it the value of the MEM[channel] register do the following?