7
\$\begingroup\$

I am trying to retrieve the values from the ADC on my MSP430F5529 and send them to my computer over USB, but I am starting small. All I have right now I something that retrieves the ADC value and stores it in ADCResults, if the read value is over half of Vcc then an LED turns on.

I have pin 6.0 hooked up to a force sensor so I can see it turn off and on when I put my finger down on it or release it.

The program works perfectly when I run it in debug mode but when I try to run it outside debug mode (just powering the board from the computer after the code is downloaded to it), nothing happens when I put my finger on the force sensor.

Whats extremely odd is if i hold down reset while putting my finger on the force sensor (putting my finger down causes the LED to turn on), and release the reset button the LED stays on until I hit reset again with my finger off it, so it seems reset is causing an issue but I'm not sure how.

At first I thought reset was being pulled constantly high (or low, whatever resets the device), but that can't be true because then the program should work if I held reset down, but it doesn't!

Here is my code:

#include "driverlib.h"

volatile uint16_t ADCResults = 0;

void main(void)
{
    //Stop Watchdog Timer
    WDT_A_hold(WDT_A_BASE);

    //P6.0 ADC option select
    GPIO_setAsPeripheralModuleFunctionOutputPin(
        GPIO_PORT_P6,
        GPIO_PIN0
        );

    GPIO_setAsOutputPin(
        GPIO_PORT_P1,
        GPIO_PIN0
        );

    //Initialize the ADC12_A_A Module
    /*
     * Base address of ADC12_A_A Module
     * Use internal ADC12_A_A bit as sample/hold signal to start conversion
     * USE MODOSC 5MHZ Digital Oscillator as clock source
     * Use default clock divider of 1
     */
    ADC12_A_init(ADC12_A_BASE,
                 ADC12_A_SAMPLEHOLDSOURCE_SC,
                 ADC12_A_CLOCKSOURCE_ADC12OSC,
                 ADC12_A_CLOCKDIVIDER_1);

    ADC12_A_enable(ADC12_A_BASE);

    /*
     * Base address of ADC12_A_A Module
     * For memory buffers 0-7 sample/hold for 64 clock cycles
     * For memory buffers 8-15 sample/hold for 4 clock cycles (default)
     * Disable Multiple Sampling
     */
    ADC12_A_setupSamplingTimer(ADC12_A_BASE,
                               ADC12_A_CYCLEHOLD_64_CYCLES,
                               ADC12_A_CYCLEHOLD_4_CYCLES,
                               ADC12_A_MULTIPLESAMPLESDISABLE);

    //Configure Memory Buffer
    /*
     * Base address of the ADC12_A_A Module
     * Configure memory buffer 0
     * Map input A0 to memory buffer 0
     * Vref+ = AVcc
     * Vr- = AVss
     * Memory buffer 0 is not the end of a sequence
     */
    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);

    //Enable memory buffer 0 interrupt
    ADC12_A_clearInterrupt(ADC12_A_BASE,
                           ADC12IFG0);
    ADC12_A_enableInterrupt(ADC12_A_BASE,
                            ADC12IE0);

    while(1)
    {
        //Enable/Start sampling and conversion
        /*
         * Base address of ADC12_A_A Module
         * Start the conversion into memory buffer 0
         * Use the single-channel, single-conversion mode
         */
        ADC12_A_startConversion(ADC12_A_BASE,
                                ADC12_A_MEMORY_0,
                                ADC12_A_SINGLECHANNEL);

        //LPM0, ADC12_A_ISR will force exit
        __bis_SR_register(LPM0_bits + GIE);
        //for Debugger
        __no_operation();
    }
}

#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
        //Is Memory Buffer 0 = A0 > 0.5AVcc?

          ADCResults = ADC12_A_getResults(ADC12_A_BASE,
                                        ADC12_A_MEMORY_0);
        if(ADCResults
           >= 0x7ff)
        {
            //set P1.0
            GPIO_setOutputHighOnPin(
                GPIO_PORT_P1,
                GPIO_PIN0
                );
        }
        else
        {
            //Clear P1.0 LED off
            GPIO_setOutputLowOnPin(
                GPIO_PORT_P1,
                GPIO_PIN0
                );
        }

        //Exit active CPU
        __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;
    }
}

UPDATE

I have tried do make the same functionality not using the peripheral driver library and it seems to work perfectly outside the debugger. This leads me to believe something is wrong with Texas Instruments Peripheral Driver Library.

Here is the code that seemed to work fine outside the debugger and doens't use the Peripheral Driver Library.

#include <msp430.h>

int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  ADC12CTL0 = ADC12SHT02 + ADC12ON;         // Sampling time, ADC12 on
  ADC12CTL1 = ADC12SHP;                     // Use sampling timer
  ADC12IE = 0x01;                           // Enable interrupt
  ADC12CTL0 |= ADC12ENC;
  P6SEL |= 0x01;                            // P6.0 ADC option select
  P1DIR |= 0x01;                            // P1.0 output

  while (1)
  {
    ADC12CTL0 |= ADC12SC;                   // Start sampling/conversion

    __bis_SR_register(LPM0_bits + GIE);     // LPM0, ADC12_ISR will force exit
    __no_operation();                       // For debugger
  }
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = ADC12_VECTOR
__interrupt void ADC12_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(ADC12_VECTOR))) ADC12_ISR (void)
#else
#error Compiler not supported!
#endif
{
  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
    if (ADC12MEM0 >= 0x7ff)                 // ADC12MEM = A0 > 0.5AVcc?
      P1OUT |= BIT0;                        // P1.0 = 1
    else
      P1OUT &= ~BIT0;                       // P1.0 = 0

    __bic_SR_register_on_exit(LPM0_bits);   // Exit active CPU
  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; 
  }
}
\$\endgroup\$
  • \$\begingroup\$ Are you doing this on a LaunchPad or some other TI dev board? Or is this something you've designed? \$\endgroup\$ – DigitalNinja Aug 21 '15 at 0:29
  • 1
    \$\begingroup\$ I'm not familiar enough with MSP430s to be sure, but: Could it be the WDT is force-enabled by a configure bit? Some uC allow you to force the WDT, which may be overridden by the DBGU module. Same goes for any other (protective) interrupt, if I were more familiar I would check your code, but interrupts are a very common source for this kind of behaviour. (Just like the division overflow interrupt: Doesn't get triggered in debug mode on many devices, but does get force-triggered when not in debug mode). \$\endgroup\$ – Asmyldof Aug 21 '15 at 0:36
  • \$\begingroup\$ @DigitalNinja Yes i am on a Launchpad the MSP430F5529 Launchpad \$\endgroup\$ – Aaron Aug 21 '15 at 0:55
  • \$\begingroup\$ @Asmyldof I am not sure if the WDT is doing it, the code clearly disables the WDT but i am still a novice so i have no idea. \$\endgroup\$ – Aaron Aug 21 '15 at 0:55
  • 1
    \$\begingroup\$ @Aaron The way you have described the operation with the reset button makes it sound like it's just simply being held in reset. You bring it out of reset, it see's your input then it goes back to being in reset when you remove the button press. Pressing it down again brings it out and re-initalizes the LED. Normally the debugger takes care of reset for you because it uses the reset line for programming. Have you double checked your jumper configuration? There is a jumper for reset labeled (RST). \$\endgroup\$ – DigitalNinja Aug 21 '15 at 1:14
3
\$\begingroup\$

Sometimes the reason for such behavior is that optimization settings are different in debug mode, and some variable that the compiler thinks isn't needed is then optimized right out.

The fixes for this are to add "volatile" qualifiers to such variables, or to turn optimization off (or at least turn it down).

I don't know if this is your answer (the thread becaume TL;DR), but this tidbit should certainly appear as a possible solution for the search engines.

\$\endgroup\$
  • \$\begingroup\$ volatile qualifier was there in the first code, so even though often an issue, in this case rather not \$\endgroup\$ – Arsenal Aug 24 '15 at 20:27
  • \$\begingroup\$ @Arsenal -- that's the variable he can see. Maybe there's something buried in a library he doesn't know about. First pass for me would be to turn optimization off. It takes ten minutes of reading to figure out how, you do it, and then you know. \$\endgroup\$ – Scott Seidman Aug 24 '15 at 20:30
  • \$\begingroup\$ @ScottSeidman Thanks for the suggestion, optimizations were off from the beginning, i pretty much always work with them off to start out with \$\endgroup\$ – Aaron Aug 24 '15 at 20:46
  • \$\begingroup\$ The library casts every access to the hardware to a volatile pointer which then gets dereferenced, I have not seen them use any notable variable which could be optimized away. \$\endgroup\$ – Arsenal Aug 24 '15 at 21:53
2
+50
\$\begingroup\$

Disclaimer: I am not an expert on MSP430.

I suggest the use of

ADC12_A_disableConversions()

after

ADC12_A_setupSamplingTimer() 

Excerpt from MSP430 DriverLib for MSP430F5xx_6xx Devices


void ADC12_A_startConversion (uint16_t baseAddress, uint16_t tartingMemoryBufferIndex, uint8_t conversionSequenceModeSelect)

This function enables/starts the conversion process of the ADC. If the sample/hold signal source chosen during initialization was ADC12OSC, then the conversion is started immediately, otherwise the chosen sample/hold signal source starts the conversion by a rising edge of the signal. Keep in mind when selecting conversion modes, that for sequenced and/or repeated modes, to keep the sample/hold-and-convert process continuing without a trigger from the sample/hold signal source, the multiple samples must be enabled using the ADC12_A_setupSamplingTimer() function. Note that after this function is called, the ADC12_A_disableConversions() has to be called to re-initialize the ADC, reconfigure a memory buffer control, enable/disable the sampling timer, or to change the internal reference voltage.

Note: Also there are a few good free online course to learn embedded systems design. One of them uses MSP430. I have list few of them below.


References:

\$\endgroup\$
1
\$\begingroup\$

I wonder why it works in debug mode, it's been a while since I worked with MSP430 and I'm not familiar with the driverlib. But:

GPIO_setAsPeripheralModuleFunctionOutputPin(
        GPIO_PORT_P6,
        GPIO_PIN0
        );

Surely isn't the function you want to use to switch this pin to an analog input or is it? I'd try:

GPIO_setAsPeripheralModuleFunctionIntputPin(
        GPIO_PORT_P6,
        GPIO_PIN0
        );

But as seen in the description of the pin functions (thanks @CL.) it becomes clear that setting the pin to the peripheral function will indeed be enough and the direction is ignored. So it's just misleading but not a deal breaker.

Then there is a small thing param.endOfSequence = ADC12_A_NOTENDOFSEQUENCE; which should probably be param.endOfSequence = ADC12_A_ENDOFSEQUENCE; but as you only do a single channel conversion, it shouldn't matter (it's just a bit more clear). And probably change ADC12IFG0 and ADC12IE0 to ADC12_A_IFG0 and ADC12_A_IE0 (but they are just defines to the other values, so no functional issue)

And you are missing a break; after your case in the interrupt vector table, but that won't affect the program much either, just a source for future bugs.

So from a firmware perspective I only have minor nitpicks.

Based on the comments, and reading through the errata sheet, makes me wonder if a single __no_operation(); after the __bic_SR_register_on_exit(LPM0_bits); in the ISR would solve the problem. The errata doesn't explicitly mention the case present here, but there are problems involving setting low power modes, exiting low power modes and corrupting the program counter. So maybe it's another case. Those effects might not be present during debug as the emulation module interferes with normal execution of the core.

But you also mentioned, that if you clear the global interrupt enable your program works also. Which leads me to thinking that your program gets stuck in an interrupt but not the ADC one. You don't have to clear the interrupt flag of the ADC as that is automatically done when reading the memory of the ADC.

Just another note on programming, I'd get the analysis of the ADC value out of the ISR. Keep them as small as possible, just read the value into your global variable and leave the low power mode on exit. Do all the other stuff in your main application. There are cases where you will need a short as possible interrupt latency and if you do stuff inside the ISR you will block other interrupts (except you enable nested interrupts, but those are evil as well).

\$\endgroup\$
  • \$\begingroup\$ As the table on the next page shows, configuring the pin for module function (P6SEL.0 = 1) is enough; the direction does not matter. \$\endgroup\$ – CL. Aug 21 '15 at 7:21
  • \$\begingroup\$ Thanks for the tips, @Arsenal i think i have narrowed down the symptom, but not sure whats the cause. It seems it's only reading the ADC once directly after reset \$\endgroup\$ – Aaron Aug 21 '15 at 15:45
  • \$\begingroup\$ @Arsenal i now know the problem has to do with delays and intrinsic functions, but i do not know enough about intrinsic functions to fix it \$\endgroup\$ – Aaron Aug 21 '15 at 18:14
  • \$\begingroup\$ @Aaron I have changed my answer, maybe it helps a bit, for fun I added some general guidelines for ISRs as well. \$\endgroup\$ – Arsenal Aug 22 '15 at 0:32
  • \$\begingroup\$ @Arsenal thank you, i have looked into it. I am still trying new things and thing ihave a breakthrough if you want to see my updated OP \$\endgroup\$ – Aaron Aug 24 '15 at 16:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.