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.

MSP-EXP430FR5994: DMA ISR will sometimes not trigger

Part Number: MSP-EXP430FR5994
Other Parts Discussed in Thread: BQ79616

I have the MSP-EXP430FR5994 communicating with the BQ79600 EVM through the UART; DMA is used for UART receive. BQ79600 EVM is connected to a custom BQ79616 PCB. The communication is successful majority of the time, BQ chip is able to make cell voltage and temperature measurements. I have confirmed this by starting a debug session in CCS, capturing a 5 second trigger using a logic analyzer. After which, I halt CCS and look at the expressions. Attached is a screenshot of successful IDE expressions (BQUartDMASuccess.PNG).

After approximately 7 such IDE runs, the BQ auto-address sequence stalls after the first DLL throw away read. The BQ chip is responding to this first step as seen in the logic analyzer screenshot attached (AABreak.PNG). The DMA ISR does not trigger. From the attached IDE screenshot (dmaRxAA.PNG), the dma_value buffer is receiving all 7 expected bytes. I want to understand why the DMA ISR is sporadic about triggering.

 

#define BQUART_enableInterrupt        EUSCI_A_UART_enableInterrupt
#define BQUART_RX_INT                 EUSCI_A_UART_RECEIVE_INTERRUPT
#define BQUART_clearInterrupt         EUSCI_A_UART_clearInterrupt
#define BQUART_TXBUF                  UCA1TXBUF
#define BQUART_RXBUF                  UCA1RXBUF
#define BQUART_IFG                    UCA1IFG


#define BQUART_ISR                    USCI_A1_ISR
#define BQUART_VECTOR                 USCI_A1_VECTOR
#define BQUART_IFG                    UCA1IFG
#define BQUART_IV                     UCA1IV


#define BQUART_DMA_RX_TRIGGER         DMA1TSEL__UCA1RXIFG
#define BQUART_DMASA                  DMA1SA
#define BQUART_DMADA                  DMA1DA
#define BQUART_DMAxCTL                DMA1CTL           //DMA Channel 1
#define BQUART_DMACTLx                DMACTL0           //holds the DMA trigger
#define BQUART_DMAIV_IFG              DMAIV_DMA1IFG
#define BQUART_DMASZ                  DMA1SZ

/*
 * uartCommands.c
 *
 *  Created on: Apr 21, 2022
 *      Author: Priya.Nadathur
 */


#include <driverlib.h>
#include <string.h>
#include "bspFuncs.h"
#include "mbbConfig.h"
#include "timer.h"
#include "B0_reg.h"

#ifdef BQ_UART
#include "uartCommands.h"

extern uint16_t bq79600Addr;

extern BYTE autoaddr_response_frame[(1+6)*TOTALBOARDS];
extern uint8_t response_frame[(8)*TOTALBOARDS];

uint8_t uartRxLen;
uint8_t dmaDataReady = 0;
uint16_t dma_value[(8)*TOTALBOARDS] = {0};
uint8_t startMeasure = 0;
uint8_t bqAddr = 0;

// CRC16 TABLE
// ITU_T polynomial: x^16 + x^15 + x^2 + 1
const uint16_t crc16_table[256] = { 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301,
        0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1,
        0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81,
        0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
        0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00,
        0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1,
        0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380,
        0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141,
        0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501,
        0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0,
        0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881,
        0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
        0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401,
        0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1,
        0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180,
        0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740,
        0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01,
        0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1,
        0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80,
        0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
        0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200,
        0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1,
        0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780,
        0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41,
        0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901,
        0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1,
        0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80,
        0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
        0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 };


BYTE bBuf[8];

uint8_t pFrame[64];
int bRes = 0;
BYTE bReturn = 0;

BYTE* currCRC;
int crc_i = 0;
uint16_t wCRC2 = 0xFFFF;
int crc16_i = 0;

/**
 * @brief Setup DMA for BQ UART receive
 *
 * Single transfer; increment destination address; source address unchanged;
 * byte to word transfer source to DMA; word to byte transfer destination to DMA;
 * rising edge DMA trigger; DMA enable
 *
 */

void DMA_Init(){
    __data20_write_long((uintptr_t)&BQUART_DMASA, (uintptr_t)&BQUART_RXBUF);
    __data20_write_long((uintptr_t)&BQUART_DMADA, (uintptr_t) dma_value);

    BQUART_DMAxCTL &= ~DMAEN;
    BQUART_DMACTLx |= BQUART_DMA_RX_TRIGGER;
    DMACTL4 |= DMARMWDIS;

    BQUART_DMAxCTL |= DMADT_0|DMADSTINCR_3|DMASRCINCR_0|DMASRCBYTE__WORD|DMADSTBYTE__WORD|DMA_TRIGGER_RISINGEDGE|DMAEN;

}

/**
 * @brief Configure BQ UART @1000000 baud
 *
 * http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430BaudRateConverter/index.html
 */

void UART_Init(void){

    BQUART_initParam param = {0};

    param.clockPrescalar = 4,
    param.firstModReg = 0,
    param.secondModReg = 0,
    param.selectClockSource = BQUART_CLOCKSOURCE;
    param.parity =            BQUART_PARITY;
    param.msborLsbFirst =     BQUART_BITORDER;
    param.numberofStopBits =  BQUART_STOPBIT;
    param.uartMode =          BQUART_MODE;
    param.overSampling =      BQUART_OVERSAMPLING;

    if(STATUS_FAIL == BQUART_INIT(BQUART, &param))
    {
       return;
    }

    BQUART_enable(BQUART);
}

//FORMAT WRITE DATA, SEND TO
//BE COMBINED WITH REST OF FRAME

int WriteReg(BYTE bID, uint16_t wAddr, uint64_t dwData, BYTE bLen, BYTE bWriteType){
    // device address, register start address, data bytes, data length, write type (single, broadcast, stack)
    bRes = 0;
    memset(bBuf,0,sizeof(bBuf));
    switch (bLen) {
    case 1:
        bBuf[0] = dwData & 0x00000000000000FF;
        bRes = WriteFrame(bID, wAddr, bBuf, 1, bWriteType);
        break;
    case 2:
        bBuf[0] = (dwData & 0x000000000000FF00) >> 8;
        bBuf[1] = dwData & 0x00000000000000FF;
        bRes = WriteFrame(bID, wAddr, bBuf, 2, bWriteType);
        break;
    case 3:
        bBuf[0] = (dwData & 0x0000000000FF0000) >> 16;
        bBuf[1] = (dwData & 0x000000000000FF00) >> 8;
        bBuf[2] = dwData & 0x00000000000000FF;
        bRes = WriteFrame(bID, wAddr, bBuf, 3, bWriteType);
        break;
    case 4:
        bBuf[0] = (dwData & 0x00000000FF000000) >> 24;
        bBuf[1] = (dwData & 0x0000000000FF0000) >> 16;
        bBuf[2] = (dwData & 0x000000000000FF00) >> 8;
        bBuf[3] = dwData & 0x00000000000000FF;
        bRes = WriteFrame(bID, wAddr, bBuf, 4, bWriteType);
        break;
    case 5:
        bBuf[0] = (dwData & 0x000000FF00000000) >> 32;
        bBuf[1] = (dwData & 0x00000000FF000000) >> 24;
        bBuf[2] = (dwData & 0x0000000000FF0000) >> 16;
        bBuf[3] = (dwData & 0x000000000000FF00) >> 8;
        bBuf[4] = dwData & 0x00000000000000FF;
        bRes = WriteFrame(bID, wAddr, bBuf, 5, bWriteType);
        break;
    case 6:
        bBuf[0] = (dwData & 0x0000FF0000000000) >> 40;
        bBuf[1] = (dwData & 0x000000FF00000000) >> 32;
        bBuf[2] = (dwData & 0x00000000FF000000) >> 24;
        bBuf[3] = (dwData & 0x0000000000FF0000) >> 16;
        bBuf[4] = (dwData & 0x000000000000FF00) >> 8;
        bBuf[5] = dwData & 0x00000000000000FF;
        bRes = WriteFrame(bID, wAddr, bBuf, 6, bWriteType);
        break;
    case 7:
        bBuf[0] = (dwData & 0x00FF000000000000) >> 48;
        bBuf[1] = (dwData & 0x0000FF0000000000) >> 40;
        bBuf[2] = (dwData & 0x000000FF00000000) >> 32;
        bBuf[3] = (dwData & 0x00000000FF000000) >> 24;
        bBuf[4] = (dwData & 0x0000000000FF0000) >> 16;
        bBuf[5] = (dwData & 0x000000000000FF00) >> 8;
        bBuf[6] = dwData & 0x00000000000000FF;
        bRes = WriteFrame(bID, wAddr, bBuf, 7, bWriteType);
        break;
    case 8:
        bBuf[0] = (dwData & 0xFF00000000000000) >> 56;
        bBuf[1] = (dwData & 0x00FF000000000000) >> 48;
        bBuf[2] = (dwData & 0x0000FF0000000000) >> 40;
        bBuf[3] = (dwData & 0x000000FF00000000) >> 32;
        bBuf[4] = (dwData & 0x00000000FF000000) >> 24;
        bBuf[5] = (dwData & 0x0000000000FF0000) >> 16;
        bBuf[6] = (dwData & 0x000000000000FF00) >> 8;
        bBuf[7] = dwData & 0x00000000000000FF;
        bRes = WriteFrame(bID, wAddr, bBuf, 8, bWriteType);
        break;
    default:
        break;
    }
    return bRes;
}

//GENERATE COMMAND FRAME
int WriteFrame(BYTE bID, uint16_t wAddr, BYTE * pData, BYTE bLen, BYTE bWriteType) {
    int bPktLen = 0;
    uint8_t * pBuf = pFrame;
    uint16_t wCRC;
    memset(pFrame, 0x7F, sizeof(pFrame));

    *pBuf++ = 0x80 | (bWriteType) | ((bWriteType & 0x10) ? bLen - 0x01 : 0x00); //Only include blen if it is a write; Writes are 0x90, 0xB0, 0xD0
    if (bWriteType == FRMWRT_SGL_R || bWriteType == FRMWRT_SGL_W)
    {
        *pBuf++ = (bID & 0x00FF);
    }
    *pBuf++ = (wAddr & 0xFF00) >> 8;
    *pBuf++ = wAddr & 0x00FF;

    while (bLen--)
        *pBuf++ = *pData++;

    bPktLen = pBuf - pFrame;

    wCRC = CRC16(pFrame, bPktLen);
    *pBuf++ = wCRC & 0x00FF;
    *pBuf++ = (wCRC & 0xFF00) >> 8;
    bPktLen += 2;
    //THIS SEEMS to occasionally drop bytes from the frame. Sometimes is not sending the last frame of the CRC.
    //(Seems to be caused by stack overflow, so take precautions to reduce stack usage in function calls)
    uartSend(bPktLen, pFrame);

    return bPktLen;
}


//GENERATE READ COMMAND FRAME AND THEN WAIT FOR RESPONSE DATA (INTERRUPT MODE FOR SCIRX)
int ReadReg(BYTE bID, uint16_t wAddr, BYTE * pData, BYTE bLen, uint32_t dwTimeOut, BYTE bWriteType) {
    // device address, register start address, byte frame pointer to store data, data length, read type (single, broadcast, stack)
    int crcChk;
    bRes = 0;

    if (bWriteType == FRMWRT_SGL_R) {
        ReadFrameReq(bID, wAddr, bLen, bWriteType, bLen+6);
        memset(pData, 0, sizeof(pData));


    } else if (bWriteType == FRMWRT_STK_R) {
        bRes = ReadFrameReq(bID, wAddr, bLen, bWriteType, (bLen + 6) * (TOTALBOARDS - 1));
        memset(pData, 0, sizeof(pData));

    } else if (bWriteType == FRMWRT_ALL_R) {
        bRes = ReadFrameReq(bID, wAddr, bLen, bWriteType, (bLen + 6) * TOTALBOARDS);
        memset(pData, 0, sizeof(pData));

    } else {
        bRes = 0;
    }

//    //CHECK IF CRC IS CORRECT
    for(crc_i=0; crc_i<bRes; crc_i+=(bLen+6))
    {
        if(CRC16(&pData[crc_i], bLen+6)!=0)
        {
//            printConsole("\n\rBAD CRC=%04X,i=%d,bLen=%d\n\r",(pData[crc_i+bLen+4]<<8|pData[crc_i+bLen+5]),crc_i,bLen);
//            PrintFrame(pData, bLen);
            crcChk = 1;
        }
        else crcChk = 0;
    }
    /*
    crc_i = 0;
    currCRC = pData;
    for(crc_i=0; crc_i<bRes; crc_i+=(bLen+6))
    {
        printConsole("%x",&currCRC);
        if(CRC16(currCRC, bLen+6)!=0)
        {
            printConsole("BAD CRC=%04X,byte=%d\n\r",(currCRC[bLen+4]<<8|currCRC[bLen+5]),crc_i);
            PrintFrame(pData, bLen);
        }
       *currCRC+=(bLen+6);
    }
*/
    return crcChk;
}


int ReadFrameReq(BYTE bID, uint16_t wAddr, BYTE bByteToReturn, BYTE bWriteType, BYTE rxLen) {
    bReturn = bByteToReturn - 1;

    uartRxLen = rxLen;

    //PN setting a flag to identify when BQ79600 address is being read
    if (wAddr == 0x2001)
        bqAddr = 1;

    if (bReturn > 127)
        return 0;

    return WriteFrame(bID, wAddr, &bReturn, 1, bWriteType);
}
uint16_t CRC16(BYTE *pBuf, int nLen) {
    wCRC2 = 0xFFFF;
    //printConsole("CRCOUT = \t");
    for (crc16_i = 0; crc16_i < nLen; crc16_i++) {
        //printConsole("%02x ",*pBuf);
        wCRC2 ^= (*pBuf++) & 0x00FF;
        wCRC2 = crc16_table[wCRC2 & 0x00FF] ^ (wCRC2 >> 8);
    }
    //printConsole("\n\r");

    return wCRC2;
}

/**
 * @brief MSP430 UART transmit data
 *
 * Enable DMA transfer for UART receive and transmit UART data
 *
 * @param[in] transmit packet length
 *            data to transmit
 */
void uartSend(int length, uint8_t * data){
    uint8_t i;

    BQUART_DMASZ = uartRxLen;
    dmaDataReady = 0;
    BQUART_IFG &= ~UCRXIFG;     //clear UART receive buffer

    BQUART_DMAxCTL |= DMAEN;
    BQUART_DMAxCTL |= DMAIE;


    for (i = 0; i < length; i++){
        while (!(BQUART_IFG & UCTXIFG));
        BQUART_TXBUF = data[i];
    }


}

/**
 * @brief Receive MSP430 UART
 *
 * After DMA receive transfer is complete, copy the data over to BQ data processing arrays
 */
void uartReceive(void){
uint8_t i;

   if (startMeasure == 1){
       for (i = 0; i < uartRxLen; i++){
          response_frame[i] = dma_value[i];
       }
       dmaDataReady = 1;
   }

   if (bq79600Addr != Bridge_DEV_CONF1_Response){
       for (i = 0; i < uartRxLen; i++){
          autoaddr_response_frame[i] = dma_value[i];
       }
       dmaDataReady = 1;
       bq79600Addr = autoaddr_response_frame[4];
   }

}

/**
 * @brief DMA ISR: BQUART_DMA_RX_TRIGGER
 */
#pragma vector=DMA_VECTOR
__interrupt void dmaIsrHandler(void)
{
    switch(__even_in_range(DMAIV,  BQUART_DMAIV_IFG))
    {
    case BQUART_DMAIV_IFG:
        BQUART_DMAxCTL &= ~DMAIE;

        uartReceive();

        // Exit low power mode on wake-up
        __bic_SR_register_on_exit(LPM4_bits);
        break;
    case DMAIV_DMA0IFG:
        break;

    case DMAIV_DMA2IFG:
        break;

    case DMAIV_DMA3IFG:
            break;

    case DMAIV_DMA4IFG:
        break;

    case DMAIV_DMA5IFG:
        break;

    default: break;
    }
}

#endif

  • If you still have the debug session open, please post the contents of the DMA1 registers ("Registers" View). DMA1SZ and (often) DMA1CTL usually provide some clue as to what has happened.

  • When the AA sequence does not progress, I am capturing the register contents after the 5 second trigger has completed. When things don't work, the register is saying DMA1SZ=1. Not sure why, the variable uartRxLen is reading the correct number of bytes which is 7. (shown in the screenshots from the first post dmaRxAA). I have also attached DMA register contents when things work correctly.

    Thanks

  • I need pointers on some next steps to try or what can be wrong here. I don't know how the DMA register can have a different value than the variable with the correct value that has been assigned to it. 

  • Knowing that SZ=1 eliminates a number of possible explanations. 

    What I would do next:

    1) Check the UART state (UCA1STATW) for transmission errors, particularly Framing (FE) or Overrun (OE), to see if that's where the missing byte went.

    2) Try clearing the dma_values[] buffer (or at least dma_values[6]) before starting the DMA, to make sure you're not seeing leftovers from the previous operation.

    3) Just before setting DMAEN in uartSend, try clearing DMAEN first. This will reset the channel in case of a race.

    4) If you can, consider speeding up the CPU (MCLK), which will make the DMA run faster relative to your serial port. It is  unusual, but possible, for the DMA to not keep up with a fast-enough source. (This ties in with (1) above.)

  • Thank you for your reply. No UART transmit errors. The MCU is already running at max clock 16MHz. 

    I tried suggestions 2 and 3 and they seem to help with the problem. 10 trials of starting the BQ firmware from CCS led to successful BQ initialization and measurements. I repeated 10 more trials just to make sure. The problem has been solved for now.

  • I'm glad you got it working.

    If step (3) really fixed something, that suggests that there's something about the flow here that's not obvious to the casual reader. Keep the DMAEN-toggling in ("cheap insurance"), but be on the lookout for other oddities. (Do you have multiple wakeup sources, in addition to the DMA ISR?)

  • There is a master timer in the firmware that generates an ISR every 125 ms. That's the only other ISR. I can disable this before initiating a DMA transfer and re-enable it.

  • Just to be clear: By wakeup I mean "bic_SR_register_on_exit(LPMx_bits)", not just any ISR. 

    The hazard (as you've guessed) is that an LPM is waked by source A but the code assumes it was source B, and things get muddled. There may be a way to disambiguate (state variables or something) short of stopping your "heartbeat" timer, but maybe it's worth an experiment (since you're close to the problem at the moment).

  • When does the MSP430 enter LPM? The current draw in LPM is very low, but I'm wondering how much time does the MSP430 find to stay in LPM?

    Can you show an example of what state variable you mean?

    LPM4_bits is currently SCG1+SCG0+OSCOFF+CPUOFF -- will it be to add to this list?

    Will it help this problem to remove   __bic_SR_register_on_exit(LPM4_bits);?

  • LPM is entered using something resembling "_bis_SR_register(LPMx_bits)".

    I haven't seen your main() program, so I don't know what it does. I supposed it was using LPM when I saw the wakeup code in the DMA ISR, but it sounds like maybe it doesn't after all. If it doesn't go to LPM (sleep), the wakeups don't do anything (they don't hurt anything either).

  • I don't have anything in main.c to enter LPM. I will remove the __bic_SR_register_on_exit(LPM4_bits);

    Thanks

**Attention** This is a public forum