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.

CCS/MSP432P401R: Sampling for a pulse

Part Number: MSP432P401R

Tool/software: Code Composer Studio

Hello everyone, 

I'm currently trying to learn how to code micro controllers, more specifically, the MSP432P401R, and I've taken a DIY summer project for fun. The project involves reading a very short pulse, about 3ms with a max of around 2.5 V, and turning on an LED when it detects said pulse. The problem is that I don't know when the pulse will be generated so I want to code the MCU to continuously read from an analog source until the pulse is generated and detected. I've looked at some of the examples on the MSP432 SDK.TI Driver folder but can't find a good starting point. I'm new to coding in CCS so any references and/or tips are greatly appreciated. 

  • Use the GPIO interrupt example* and connect your pulse to one of the gpio's that support interrupts.

    Once your interrupt happens, measure the time and if it matches your 3ms threshold, light the LED.

    * In the driverlib section of the examples, it is called "gpio_input_interrupt"

  • Thank you!! 

    Could this solution be extended to when the interrupt occurs, the GPIO reads the value and uses the ADC to obtain an approximate voltage? I've been reading the datasheets and it's quite a bit to digest as a beginner, sorry if I'm asking obvious questions!   

  • Sure, as a beginner, just take it in steps.

    The examples are your friends.

  • Thanks for you help!! 

    One last question before I dive into this, will setting up the GPIO trigger off a certain reference voltage be fast enough to read the voltage within the 3ms frame? Like if I set the trigger to go off at relatively low voltage from the expected peak voltage be fast enough for the adc to pick up samples from the pulse?

  • [It's been said that if you ask two engineers a question, you'll get three different answers.]

    A GPIO interrupt triggers when the pin goes (in your case) high, i.e. voltage level Vih. (Vih isn't settable, though it scales with the power supply.)  Looking at data sheet (SLAS826G) Table 5-23, Vih (called here "Vit+") is up to 2.25V at VCC=3.0V, and is presumably higher at VCC=3.3V as seen on the Launchpad. This seems to me uncomfortably close to your 2.5V peak, and you probably want a threshold somewhat below that.

    All that is intended to say: I would suggest you use the ADC, rather than a GPIO interrupt. You could just compare each captured sample with a threshold, but the ADC also has a Window Comparator which can do most of this work for you. When the Window Comparator triggers, you'll also have the ADC sample (voltage level) that triggered it.

    An example checking each sample (with code) is in msp432P401x_adc14_01 (http://dev.ti.com/tirex/explore/node?node=AMQ02id8Xtzmn4ojlL4e3A__z-lQYNj__LATEST)

    An example using the Window Comparator is in msp432p401x_adc14_21 (http://dev.ti.com/tirex/explore/node?node=APMrBo3o-N.zyfhRMJp.gg__z-lQYNj__LATEST)

    The only trick is to decide the sample rate. If all you need to do is detect, then once per 3ms is enough. If you need a pulse width, you'll need to increase the sample rate proportional to the accuracy you want. Here the Window Comparator helps, since the hardware can be "searching" in the background, and can do it fairly fast. (Keith is certainly correct that the effective sample rate of a GPIO interrupt -- as fast as the CPU clock -- is faster than the ADC can go.)

  • [The third answer:]

    Use a GPIO interrupt as Keith suggests, but run your system at e.g. 2.7V.

  • Thank you for your responses! This should give me a good starting point for my project!

  • Actually, just for the practice, you should try *all* the suggestions. 8^)

  • Will do! This may sound like a dumb question but how would I go about changing the reference voltage for the comparator? The example has it set to 2.5V but I would like for it to be at 3.3V. What file would I have to modify?

  • Example adc14_21 has

    >     ADC14->MCTL[0] |= ADC14_MCTLN_INCH_1 | ADC14_MCTLN_VRSEL_1 | ADC14_MCTLN_WINC;

    This sets ADC14VRSEL=1, which requests a high side according to the internal reference (max 2.5V). To request a high side of Vcc (presumed 3.3V) use VRSEL=0 [See also Technical Reference (SLAU356H) Table 22-11]:

    >     ADC14->MCTL[0] |= ADC14_MCTLN_INCH_1 | ADC14_MCTLN_VRSEL_0 | ADC14_MCTLN_WINC;

  • Thank you!!

    I was about to test this when I got the message below, any suggestions on how to fix it? I've tried deleting and re opening the example but no luck. I'm running CCSv9.

  • CCS internal errors are somewhat out of my league. [Anyone?]

    Just guessing from the message: Do you have EnergyTrace set to automatically start up? [Window->Preferences->Code Composer->Advanced->EnergyTrace->"Enable Auto-Launch"] If so, maybe try un-checking that box.

  • Hello Jerry,

    Which version of the SDK are you using?

    I imported the example "./examples/nortos/MSP_EXP432P401R/registerLevel/msp432p401x_adc14_21/ccs" from the SimpleLink MSP432P4 SDK v3.10.00.08 into CCS v9.0.1 and the import went fine.

    Thanks,

    Sai

  • I got it solved! I had to erase a file from the system then reboot CCS, it's working fine now!

  • Thanks for all the help Bruce McKenney47378 and Keith Barkley!! I've been playing with the ADC interrupt and it seems to work perfectly for what I want to do. I've been using the 3.3V supply and a potentiometer to trigger the flags at different values. How would I go about storing a couple of samples after the flag has been triggered using the ADC? Is there an example I can reference that could help me out with this?

  • I haven't seen an example of this, but where I'd start is manipulating the ADC Done IFG along with a counter.

    1) When you get a ADC14HIIFG, save the triggering sample, turn on the ADC14IE0 and set a counter. 

    2) Each new ADC14IFG0 trigger, save the sample and decrement the counter

    3) When the counter hits 0, turn off the ADC14IE0

    I suppose that your current design already gets new samples (triggers) for as long as the signal is "high". This would give you a fixed number of samples, whatever the signal did.

  • Thanks! Would I have to set ADC14_CTL0_CONSEQ_0 to ADC14_CTL0_CONSEQ_2 in order to repeatedly convert a single channel? 

  • Example adc14_21 uses software triggering (ADCSC), so I suppose that's what you're doing. 

    If you set CONSEQ=2 and MSC=1, the ADC will sample repeatedly, as fast as it can. One ADCSC trigger is all you ever need.

    If you use CONSEQ=2 and MSC=0, you will get one sample per ADCSC trigger, which is what you have now.

    What is it that you want to do?

    [Edit: Minor clarification.]

  • Thanks for the reply!!

    I'm trying to take a small sample over 3ms after the ADC is triggered and save it to the chip memory. I already have the interrupt working and I'm currently looking at the adc14_single_conversion_repeat_timer_source example on how to save the ADC readings. I don't need it to trigger off the timer, only when the voltage read goes above 1V. The question I'm trying to answer by looking at the example is how to access the voltage readings from the ADC and save them as an array if possible.

  • Each conversion stores the resulting value in the ADC14MEMx register you've chosen (typically ADC14MEM0). This is true even if you're only enabling HIIFG. So when you get the HIIFG, you can just fetch the value and store it in an array with an incrementing index (adc_reading[i++] = ADC14MEM0)..

    If you only want the samples when the signal is "high", this will give you every such sample. My suggestion above only applies if you want to continue reading samples beyond the high period.

  • I was reading the data sheet and figured that's where it was saved but didn't know how often it did, thanks for the clarification!! So I'm expecting a pulse signal with a 3ms duration, with a peak voltage of around 2.5V. I want to start taking a sample right before the peak voltage so around when the pulse is reaching 1V. A flag at HIIF = 1V to start taking samples should be adequate for what I'm trying to do correct? I currently have the reference voltage at 3.3V so there should be enough margin between the peak and the max voltage.

  • At this point I'd say you should try it and tune from there.

    You may find it useful to instrument your ISR by toggling a GPIO pin, so you can watch it on a scope alongside the signal.

  • I've managed to detect the signal by toggling the blue LED on the board so I've made some good progress! This forum has been really helpful! However, when I tried to set voltageRead=ADC14MEM0 it says "ADC14MEM0 unidentified." Is there a certain setup I have to do first before enabling the ADC value fetching and storing it into the variable? 

  • In the CMSIS style ADC14MEM0 would be "ADC14->MEM[0]". I'm supposing you've set up ADC14->MCTL[0] appropriately.

  • That fixed it thank you!!  Yes the MCTL was set to 0 when i changed the reference voltage. The ADC doesn't read values between 0-16384, when I read in from the memory, it offsets between 1400-14000, could this be because of a timing issue? I have the board connected to it's own power supply with a potentiometer to output between 0 and 3.3V but the most it goes is about 2.2V.

  • You can try adjusting the sample/hold time (SHT), though 16 ADC clocks is usually plenty for the kind of low-impedance source you allude to.

    What do you get if you connect the ADC input directly to GND or VCC?

  • I have pin 6.0(A15) as my ADC and when I connect it directly to GND and VCC I get 6000 min and 11032 max respectively, when connected to the potentiometer I read 4971 min and 14895 respectively. I noticed when I connect the pin by hand it won't be as responsive as the potentiometer. Could this be a error in the conversion setup? The ADC is still configured to single channel single conversion.

  • Not getting 0 for GND seems rather odd. Can you post your setup and sampling code?

  • I found my mistake! It was a very simple error I made, but it's working properly now!

    So to clarify, I'm using the msp432p401x_adc14_21_MSP_EXP432P401R example, which I had to modify to fit my project, but from what I understand, the ADC will continuously read for a voltage (every 20ms on the example) and throw the appropriate flag. Does the ADC go back to reading right after it throws the flag, or does it reset after the flag executes? Also you mentioned the ADC reads as fast as it could, how would I go about determining that speed?

  • That example uses CONSEQ=0 and a software trigger (SHS=0), so there's nothing continuous about it. You probably need to describe what you've changed.

    If you're using CONSEQ=2 and MSC=1 (and SHS=0), it will convert continuously (after the initial SC), setting the IFG at the end of each conversion. Reading the MEM register clears the IFG. For this case, you can estimate the sample rate by counting ADC clocks while referring to TRM (SLAU356G) Fig 20-8.

  • Ok that's where I'm a little confused, so in the original example:

    ADC14->CTL0 = ADC14_CTL0_SHT0_2 | ADC14_CTL0_SHP | ADC14_CTL0_ON | ADC14_CTL0_SHS_0 | ADC14_CTL0_SSEL_0 | ADC14_CTL0_CONSEQ_0;

    it does a single-conversion, throws the appropriate interrupt, and reiterates in the while(1) loop, but it's only a one time event for every countdown reset. I changed the timer from 20ms to 10us (for (i = 10; i > 0; i--);) to make sure the ADC doesn't delay in reading the voltage values. The ADC would not detect any pulse at all before I made this change. But this creates the problem that the ADC can't read fast enough to capture some values within that pulse that's causing the interrupt. Would continuous allow me to read some of these values quickly? All I would need is about 5-10 samples of this 3ms pulse. 

    Would this be the appropriate setup?

    ADC14->CTL0 = ADC14_CTL0_SHT0_2 | ADC14_CTL0_SHP | ADC14_CTL0_ON | ADC14_CTL0_SHS_0 | ADC14_CTL0_SSEL_0 | ADC14_CTL0_CONSEQ_2 | ADC14_CTL0_MSC;

    I appreciate all the help, the examples are limited to certain setups.

  • That looks like the right setup for "fast as possible" mode. Two consequences you should have in the back of your mind:

    1) The sample rate is determined by the ADC clock. You won't really know what your sample rate is, since MODOSC is specified +/- 8%. If you decide to slow it down by using SYSOSC, that's +/-15%. [Ref data sheet (SLAS826G) Tables 5-17/18]. This may not matter in your application.

    2) The samples will probably [see also Fig 20-8] be coming faster than you can collect them, so you'll lose some. If you can still collect them as fast as you need them, this may not matter in your application.

  • Would adding a counter loop help with this uncertainty in time? I just need a couple points so I'm not worried about skipping some values, as long as it detects some of the values past my min voltage. I set the code how I described it above, but for some reason the variables I had set up to keep and eye on the results do not change like they did with the single conversion setup. Is that because if the speed at which it's being sampled?

  • If you're interrupting on every conversion in fast-as-possible mode, you will (my prediction, but do the arithmetic) spend all your time in the ISR, since the samples are arriving faster than you can pick them up. If you're only interrupting during the pulse, and all your work during the pulse is done by the ISR, that should be workable -- save the data during the pulse, then process it in main between pulses.

    In this case, the amount of work you do in the ISR directly affects how many samples you can collect, so you want to minimize that. Did you implement the GPIO wiggle in the ISR I suggested? If you have a scope, that should tell you directly how long your ISR is taking.

  • I see, I thought that might be a problem at the beginning, but given the chip speed I thought I might've been able to get away with it. So could I have the ADC continously reading values, then when it goes above a certain min voltage have an if statement to save the data then convert in the ISR? Or would that be the same thing?

  • I thought that was the original idea -- trigger only on HIIFG, and capture the MEM register when it triggers. With "fast as possible" mode, these interrupts will be coming very fast when the signal is "high", but not at all when the signal is "low" or "middle". (You'll need some criterion to decide when the pulse has passed.) What is your code doing now?

    This isn't the most graceful way to do this, but it should get you some initial results without going down too many rabbit-holes. Possible future improvements (not mutually exclusive):

    1) DMA. I'm pretty sure there's an Example that uses DMA with the ADC, but I've never had occasion to use DMA with the Window Comparator.

    2) A timer trigger with the timer tuned to maximize the sample rate without overrunning.

    3) Speed up the CPU clock so you can do more capture and analysis. I'm pretty sure there is at least one Example that does this.

  • Well what I intended to do with this project was to read from a sensor that generates pulses about 3ms in duration, the peak is around 2.5V, and take a small sample from it (5 to 10 points). There is little noise from the signal, but just to make sure I have my LOIFG set to 1V and HIIFG to 3V. Once the ADC detects a voltage between those two parameters, I'll take those 5 to 10 points and store them in an array. I'm not sure how often the pulse will be generated but I assumed every 5-8 seconds just to be on the safe side, that's why I thought ISR was a good approach. I apologize if I was vague in my description. 

    What my code does right now is monitor for a pulse with the ADC (single conversion)  and toggle P2.2 when it detects said pulse. I have a red LED connected to the signal generator as well so I know the ADC is picking up at least 1.55V. The only issue is it won't take any samples from the pulse because the ISR takes some time to execute. 

**Attention** This is a public forum