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.

MSP430FR2676: Call to Captivate function CAPT_updateSensorRawCount not returning

Part Number: MSP430FR2676

I'm trying to use the Captivate functionality of the MSP430FR2676 to read one electrode. When I do this in a simple prototyping project, I can make repeated calls to 'CAPT_updateSensorRawCount' and the function returns as expected continually. When I integrate that same code into my production project, the function call may return correctly for one or more invocations, but eventually it will not return. Interrupts continue to assert and be serviced elsewhere.

My setup code is as follows:

  // Power on the captivate peripheral
  MAP_CAPT_powerOn();

  // Reset the captivate peripheral
  MAP_CAPT_reset();

  // Initialize the captivate peripheral
  MAP_CAPT_init();

  // Set input impedance to maximum
  CAPT_selectInputImpedanceBiasCurrent(eZeroIbias);

  // Set voltage source to internal voltage
  CAPT_selectElectrodeChargeVoltageSource(eVRegSupply);

  // Initialize the sensor IO
  CAPT_initSensorIO(&_sensor);

  // Enable the peripheral (?) sensor
  CAPT_enableSensorIO(&_sensor);

  // Initialize the sensor
  CAPT_initSensor(&_sensor);

  CAPT_enableISR(CAPT_END_OF_CONVERSION_INTERRUPT);

  _last_event_ms = tick_get_uptime_ms();

  _last_capsense_counts = 0;

  DEBUG_HIGH();
  CAPT_updateSensorRawCount(&_sensor, eStandard, eNoOversampling, LPM0_bits);
  DEBUG_LOW();

And my periodic code is as follows:

void captivate_interface_periodic(void)
{
  uint16_t current_uptime_ms;

  if (g_bEndOfConversionFlag)
  {
    g_bEndOfConversionFlag = false;

    DEBUG_HIGH();
    // *** THIS CALL WILL NOT RETURN
    CAPT_updateSensorRawCount(&_sensor, eStandard, eNoOversampling, LPM0_bits);
    DEBUG_LOW();

    _last_capsense_counts = _element.ui16CompositeRawCount;

    _last_event_ms = _tick_get_uptime_ms();
  }
  else
  {
    current_uptime_ms = _tick_get_uptime_ms();

    if (CAPSENSE_TIMEOUT_MS < (current_uptime_ms - _last_event_ms))
    {
      self_testing_failsafe(EVENT_CODE_CAPSENSE_TIMEOUT);
    }
  }
}

From what I can tell by looking at the disassembly during debugging, my call in the initialization code to CAPT_enableISR(CAPT_END_OF_CONVERSION_INTERRUPT) sets a bit at a symbol named 'CAPTIVATE_CAPIE', which is at address 0x0B20. When the call to CAPT_updateSensorRawCount(...) does not return, the bit at the 'CAPTIVATE_CAPIE' address is still set, and global interrupts are still enabled. Assuming that CAPT_updateSensorRawCount(...) requires interrupts in order to complete and return, it appears that the interrupts I can control are still enabled.

The application production project is more complex and enables and disables interrupts during critical sections. Because the Captivate library is either in a static library or embedded in ROM, I'm not able to find the source of the issue. However, if I attach a debugger and place a breakpoint at the CAPT_updateSensorRawCount(...) call, and then Resume each time, it appears that the function returns more reliably. That seems to indicate that it is an race condition of some sort, possibly due to interrupts.

If there are any suggestions for finding the cause of this problem, please let me know.

Thanks for any help!

  • Hi JD,

    One of the issues, among several, is I don't see that have performed the initial calibration that is required at power on.  Without the calibration step, the Captivate peripheral won't operate very well, if at all.  So I will make some recommendations here to put you on the right track.

    Here is what I would suggest.  Take a look at the software library chapter in the Captivate technology guide, "adding captivate to an existing project" starting down the page at step #4. I copied a section of it down below for convenience.

    There it shows you the basic steps and 3 simple APIs required to get everything up and running.  They are CAPT_initUI, CAPT_calibrateUI and CAPT_updateUI.  If you are reading only one sensor you can skip CAPT_updateUI, but, I would still strongly recommend using CAPT_updateSensor() rather than just performing a raw measurement.  CAPT_updateSensor does additional work that is important to filtering the measurements, setting the prox and touch flags and more.  You can always read the "raw" result stored in the sensor's data struct from your callback function (read more about callbacks), which is called automatically from within the CAPT_updateSensor() function. It does all the work for you.

    Again, I would highly recommend using it instead and definitely read more about it here.

    Now, the CAPT timer controls how often the sensor is measured by generating an interrupt which basically sets the g_bConvTimerFlag at some periodic rate.  In all the Captivate examples this is typically 33ms.  You are welcome to use it or any other timer to control how often you perform a measurement.

    So remember, the 3 functions you need are CAPT_initUI, CAPT_calibrateUI, and in your case, CAPT_updateSensor.

  • Hi Dennis,

    Thanks for the prompt response, I appreciate your time and insight. I'll provide some more detail as to the application. I apologies for not doing so at the outset.

    I don't see that have performed the initial calibration that is required at power on

    This was intentional, as the object to be detected may be present on startup. The sensor cannot be calibrated during normal operation for this reason. The nature of the system is such that the object is either present to some degree, or not; it does not become present periodically as would a finger.

    The end-product is to be calibrated once in manufacturing and the calibration values are to be used throughout the product lifetime - in this case the values of the tCaptiveElementTuning structure are retained from calibration. In the field, raw counts are compared to a threshold (with margins of error included to prevent false positives). Also in the application code are other methods of inferring the object presence, but those are outside of the scope of this question. Think of the capacitive sense as a first line of defense.

    In preliminary testing with the MSP430 (as well as other vendor platforms), this method has proven to be sufficient. It has been shown that changes in temperature do impact readings. A method of temperature correction that has worked in the past is forthcoming, with much testing to be performed before release.

    The documentation mentions a number of times that raw counts are available. This section titled "Update a Sensor’s Raw Data Only" highlights my use case:

    For certain custom applications it may be desirable to only update a sensor’s raw data after a conversion, bypassing all of the high-level processing. For applications that require this, the CAPT_updateSensorRawCount() may be used directly. This function only updates raw count values for each element in the sensor. No processing is performed on the data, and the sensor callback function is not called upon completion of the update. The raw data update function takes two additional parameters that specify details about type of conversion. [...]

    High level processing of the data is something I'd prefer to avoid, so this seems like the ideal method for getting readings. Filtering, proximity and touch flags are not needed and would likely serve only to skew other time critical operations. I would think filtering would have the greatest impact on execution times. I would prefer to trigger a reading and receive a result with minimal impact on CPU cycles.

    All that being said, I'm having trouble understanding what drives a conversion. It appears that calling CAPT_updateSensorRawCount() triggers a conversion. Is that correct?

    Related to my original question: Are there other initialization items that I'm missing that would account for the CAPT_updateSensorRawCount() call not returning?

    In the meantime, I'll try utilize the CAPT libraries as you suggested and see if the issue goes away.

    Thanks again for your time, and I hope that my questions now have more context.

  • Another thing I'd like to point out is that the code I had provided invokes all the same function calls as your suggestion, aside from three things: calling calibrate, using a timer to drive conversions, and invoking CAPT_updateSensor rather than CAPT_updateSensorRawCount. The calls I make are direct to the CAPT library/ROM, bypassing the CAPT_App code. The timer interrupt appears only to set a flag to indicate to CAPT_appHandler to invoke the CAPT_updateSensor call for each sensor (in my case one sensor). As far as I can tell, that is the only substantial difference. My implementation invokes CAPT_updateSensorRawCount every 5 milliseconds using a SW timer outside of the scope of the code provided rather than using the HW timer.

    From what I can tell the calibration call updates the tCaptivateElementTuning structure for the sensors used, but I admit there could be many other operations that are performed that we as users are not made aware. Those tuning values are what I intend to store for the application use. If CAPT_calibrateSensor performs other work within the hidden Captivate registers, I suppose I could invoke it then restore my saved tuning values. Can you confirm that the call to CAPT_calibrateSensor does indeed perform some required operation?

    The Captivate library and ROM code being obfuscated makes these answers very difficult to answer on my own. Is the source for the Captivate library available anywhere?

    Thanks again for your time.

  • Hi JD,

    Ok, you are fine with how you are doing it.

    Yes, the calibration is required, at least once (either at every power cycle or once in the lifetime and then store the coarse, fine and offset calibration values as described here).

    From the Captivate technology guide:

    Regarding the source code for the Captivate library, unfortunately no.  I will send you a friend request so we can discuss the library details off-line.

  • Hi JD,

    I want to point you to this simple example, in case you haven't run across it.  It is located in your Captivate design center installation path.

    C:\ti\msp\CapTIvateDesignCenter_1.83.00.13\CapTIvateDesignCenter\example_projects\CaptivateDesignCenterWorkspace\TI_Examples\FR2633_CodeSizeOptimized_OneButton

    Also, this other posting might be helpful.

    Here is the minimal code you would need to get measurements from Captivate.

    #1 CAPT_initUI()
    #2 CAPT_calibrateUI();
    #3 configure captivate timer for 5ms timeout
    #4 g_bConvTimerFlag = 0;
     
    while()
    {
     
      if (g_bConvTimerFlag)
      {
        #5 g_bConvTimerFlag = 0;
        
        // The measurement is performed in CAPT_updateSensorRawCount
        #6 CAPT_updateSensorRawCount(&_capsense_sensor, eStandard, eNoOversampling, LPM0_bits);

        // ...get the raw counts
        #6 _last_capsense_counts = _capsense_element.ui16CompositeRawCount;
      }
       
       //OPTIONAL - enter low power mode
       __bis_SR(LPM3_bits | GIE);
       
    }

    Take a look at the example code in your Captivate design center installation and the other E2E posting above.

    All that you need to get your code working is there.  Just keep it simple to start with.

  • Thank you Dennis! I've applied the timer driven conversion code from the link you provided and so far it appears timing has improved considerably. I'll be testing the implementation for a while to confirm that conversion are timely and consistent.

    Thank you again!

**Attention** This is a public forum