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, ¶m)) { 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