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.

MSP430FR5994: DMA/timer capture - broken data after DMA transfer

Part Number: MSP430FR5994

Hi!

I'm trying to sample pulses that goes to device, test setup is MCU with test signal (1uS pulses with 2.4kHz frequency) connected to ports P1.0 and P2.3. My idea was to start timer in compare mode (both edges), and make automatic DMA transfer on TA0CCR2 flag, and to work with collected data later, collecting statistic by pulse widths. In real firmware it is done in FreeRTOS task, but I made simplified project to isolate the problem.

So, I have a buffer with data samples in following format: [1 pulse front timestamp] [1 pulse tail timestamp] [2 pulse start] [2 pulse end] ... ... ... and so on.

So, having 16MHz clock, I expect data like [1 16 8001 8016 16001 16016] or something like this. But sometimes (after minute of working or so) I got strange result - first sample timestamp is wrong, like [1 16 8001 8016 16051 16016].

As I have predictable signal, I can see that it differs from right timestamp by several bits only, i.e 0x2AB0 instead of 0x2AB0, or 0x4370 instead of 0x4340, or 0x08F4 instead of 0x08C0, always wrong bits was set by error, not cleared. 

Here is my code:

#include <stdlib.h>
#include <msp430fr5994.h>

#include "lib/gpio_macros.h"
#include "lib/hardware.h"

volatile unsigned char new_data_available = 0;

#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void) {
    if (P1IV == 0x02) new_data_available = 1;
}

#define DETECTOR_BUFFER_SIZE 32
unsigned short pulse_buffer[DETECTOR_BUFFER_SIZE+1]; // to eliminate out-of-range compiler warning
unsigned short detector_stats[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

int main(void) {
    WDTCTL = WDTPW + WDTHOLD;  // stop watchdog timer to prevent time out reset
    PM5CTL0 &= ~LOCKLPM5;      // disable GPIO power-on default high-impedance mode
    // DMACTL4 |= DMARMWDIS; // have to be commented, slows down

    PIN_CONFIGURATION(LFXT1, MODE_PERIPHERAL_FUNCTION_1);
    PIN_CONFIGURATION(LFXT2, MODE_PERIPHERAL_FUNCTION_1);

    CSCTL0 = CSKEY;                  // unlock CS control register
    CSCTL4 |= LFXTDRIVE_3;           // highest drive setting (for startup only)
    CSCTL4 &= ~(LFXTOFF|LFXTBYPASS); // turn crystal on
    while (CSCTL5 & LFXTOFFG) {      // wait for crystal stabilization (clearing fault flags)
        CSCTL5 &= ~(LFXTOFFG);
        SFRIFG1 &= ~OFIFG;
    }
    CSCTL4 &= ~LFXTDRIVE_3;           // clear LFXTDRIVE (set requested drive mode to lowest current)

    FRCTL0 = FRCTLPW | NWAITS_1;      // see table 8-1 in slau367k.pdf
    CSCTL1 = DCORSEL | DCOFSEL_4;     // set DCO frequency to 16 MHz
    CSCTL2 = SELA__LFXTCLK | SELM__DCOCLK | SELS__DCOCLK; // set clock source to DCO
    CSCTL3 = 0x0000; // all dividers to minimum
    CSCTL0_H = 0x00; // lock CS control register

    PIN_CONFIGURATION(DETECTOR_IN0, MODE_PERIPHERAL_FUNCTION_1); // 39, P2.3, [TA0.0]
    PIN_CONFIGURATION(DETECTOR_IN1, MODE_INPUT_FLOATING);        // 1,  P1.0, [GPIO INT]

    for (unsigned short i=0; i<DETECTOR_BUFFER_SIZE; i++) pulse_buffer[i] = 0;

    DMACTL0 |= DMA0TSEL__TA0CCR2;
    DMA0SA = __DMA_ACCESS_REG__ &TA0CCR2;
    DMA0DA = __DMA_ACCESS_REG__ &pulse_buffer;
    DMA0SZ = DETECTOR_BUFFER_SIZE;
    DMA0CTL = DMADT_4 | DMASRCBYTE__WORD | DMADSTBYTE__WORD | DMADSTINCR_3 | DMAEN; // ring buffer

    TA0CCTL2 = CM_3 | CAP; // both edges
    TA0CTL = TASSEL_2 | MC_2 | TACLR;

    P1IES &= ~(1<<0); // low-to-high
    P1IFG &= ~(1<<0); // clear int flag
    P1IE  |=  (1<<0); // enable int

    __enable_interrupt();

    while (1) {
        unsigned short* buffer_ptr_start = (unsigned short*) &pulse_buffer;
        unsigned short* buffer_ptr_over  = (unsigned short*) &pulse_buffer[DETECTOR_BUFFER_SIZE];
        unsigned short* buffer_ptr_current;
        unsigned short* buffer_ptr;

        buffer_ptr = buffer_ptr_start;
        for (;;) {
            if (!new_data_available) continue;
            new_data_available = 0;

            unsigned short samples_rest = (DMA0SZ+1) >> 1;
            buffer_ptr_current = buffer_ptr_over - samples_rest*2;  // start of two-samples-placeholder
            for (;;) {
                if (buffer_ptr == buffer_ptr_current) break;

                unsigned short start = *buffer_ptr++;
                unsigned short width = *buffer_ptr++;
                width -= start; // we can do this without < check (thanks to 'unsigned short' overflow)
                if (buffer_ptr == buffer_ptr_over) buffer_ptr = buffer_ptr_start;

                if (width > 32768) {
                    break; // <<-- breakpoint here
                }

                width >>= 4;
                if (width > 15) width = 15;

                detector_stats[width]++;
            }
        }
    }
}


What is this?

RAM failure? DMA trick? Unknown errata chapter? :)

Thank you in advance!

  •   TA0CCTL2 = CM_3 | CAP; // both edges

    Do you get the same result if you set SCS as well? Yeah, it would seem that MCLK and SMCLK (=DCO) should be implicitly synchronized, but I've never tried capture without SCS.

  • Wow, this helped! Thank you very much!

    I played with this bit in my main firmware and turning it off helped a bit, so I left SCS bit turned off for test too. But when I turned on it there, it started to work up to 6-7 MCLK ticks between front and tail edges of pulses, like it should.

    So I tried to understand why my test works while main firmware is not, and found that I have __bis_SR_register(LPM4_bits+GIE) in vApplicationIdleHook().

    I have timerA fed from 16MHz SMCLK, so MCU was really in LPM1 mode (what was proved by IAR state log summary).

    I commented out this line and got much better results (not like as in test, but really much better).

    So I changed that line to put CPU in LPM0 mode instead of LPM4 - still works.

    Changed to LPM1 - completely bad. Not only 'spikes' of bad data, but all data is bad, only one edge registered at all (only front or tail edge).

    It works in LPM0 mode, but not works in LPM1.

    I read docs and found no differences between LPM0 and LPM1 modes. I have 16MHz SMCLK for timer, so DCOCLK definitely active at moment of capture, so DMA should start immediately. LPM1 have FRAM disabled, but "(2) Disabling the FRAM through the FRAM controller A allows the application to lower the LPM current consumption but the wake-up time increases when FRAM is accessed (for example, to fetch an interrupt vector). For a wake up that does not access FRAM (for example, a DMA transfer to RAM) the wake-up time is not increased."

    So, after all, I do not understand why DMA is working so strange in LPM1 mode? It has DCOCLK ready and stable, CPU already turned off, everything is ready.. but it not working as expected.

  • I don't know the answer. Recently someone (I can't seem to find it now) was having trouble with DMA in LPM on an FR5994, but that was trying to DMA into FRAM, and it seemed that sloth on the part of the FRAM could be contributing. But that's not your case.

    As I understand it, your test case (above) is now succeeding. What happens if you introduce LPM into that? That would avoid all the FreeRTOS distractions.

  • Same result - it works in LPM0, but fails in LPM1.

    I guess that FRAM start-up locks something inside the chip, so DMA have no chances to write each second pulse and misses transfer.

    Maybe someone from TI will comment on this?..

  • I ran a (hacked-up) copy of your code on a Launchpad, and I don't see your symptom.

    I had to move the devices and pin assignments around, but I also adopted TB0 to generate a waveform similar to what you described (1us pulses at 2.4kHz) and patch-wired that to the capture pin. LPM wakeup was done both by the DMAIFG and a button (standing in for your P1.0 trigger). The mouse-trap was your "negative width" check. I tried LPM0/1/3/4 (yeah, really LPM1 for the last 2).

    Over maybe a half-hour I saw no traps, and a number of spot-checks of the DMA buffer showed everything in order.

    The big difference here is probably that the same MCU was generating and capturing the waveform. This confers a fairly high degree of synchronism. (I even turned off SCS with no ill effects.) This suggests there's something unexpected about your input waveform:

    1) Is it possible it's glitching (pulse < 20ns)? 

    2) How well coordinated is the P1.0 pulse with the TA0.CCI2A waveform?

    /cfs-file/__key/communityserver-discussions-components-files/166/Oleg_2D00_5994.c

  • Pulses are coming from signal generator through 1kOhm resistor (for additional filtering), and controller by oscilloscope, so I sure that signal does not contain glitches.

    Both pins are connected together, so both P1.0 and timer input are getting signal at exactly same time. No additional logic between them.

    In this project we have decided to change working logic, change IC to 64-pin, and make analogue schematic to sort pulses by its duration, so I will use 4 timer CLK inputs just to count them. This will allow to use LPM modes lower than LPM1, what is important for me, because device is using CR2032 battery.

    I suspect that my problem related to IC that I have on my PCB, so I ordered separate launchpad.

    This will require "2-3 weeks" here to get it received, and will try your code (and my code) on 'reference design' launchpad with another IC on it (maybe it will have another revision).

    Anyway, thank you very much for helping me!

**Attention** This is a public forum