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.

TMS320F28062: McBSP as SPI slave with DMA transmits data shifted by one bit

Part Number: TMS320F28062
Other Parts Discussed in Thread: CONTROLSUITE

Hi.  I'm having a problem using the McBSP in SPI mode using DMA.  The receive side works fine, but the output data is transmitting one bit late.  For instance, when I try to transmit test data of 0x0022 0x0044 0x0088..., it appears on the wire as 0x0011 0x0022 0x0044...

Please see below for the relevant code.  I've ensured that the XDATDLY field of XCR2 is set to 0, so there shouldn't be any delay after the chip select (aka frame sync).  I've also played with the polarity of the clock signal (McbspaRegs.PCR.bit.CLKXP) to no avail.  Changing the stop mode (McbspaRegs.SPCR1.bit.CLKSTP) from 2 to 3 breaks the receive side, so I'm pretty sure I have that correct.

Any thoughts on what I'm doing wrong?  Most of this code is copied/modified from the example code available at C:\ti\controlSUITE\device_support\f2806x\v151\F2806x_examples_ccsv5, either mcbsp_spi_loopback or mcbsp_loopback_dma.

Thanks,

Alex

void main(void)
{
    // Unrelated system initialization goes here

    InitMcbsp();
    EnableInterrupts();

    for(;;) {
	    // Not shown in detail, handles high level protocol, 
		// as well as re-enabling DMA channels after interrupts
		// have fired
        HandleSpiCommunications();
    }
}

void InitMcbsp(void)
{
    initDma();
	
	// Start the two DMA channels
    StartSpiRxDmaChannel();
	StartSpiTxDmaChannel();
	
    initMcbspa();
}

void initDma(void)
{
   EALLOW;
   DmaRegs.DMACTRL.bit.HARDRESET = 1;
   __asm(" NOP");                        // Need a nop after performing a hard reset

   DmaRegs.PRIORITYCTRL1.bit.CH1PRIORITY = 0;   // CH 1 is the same priority as other channels (for now)

   // Channel 2, McBSPA transmit
   DmaRegs.CH2.MODE.bit.CHINTE         = 0;     // Disable interrupts while configuring channel
   DmaRegs.CH2.BURST_SIZE.all          = 0;     // 1 16-bit word/burst
   DmaRegs.CH2.SRC_BURST_STEP          = 0;     // no effect when using 1 word/burst
   DmaRegs.CH2.DST_BURST_STEP          = 0;     // Don't change destination address; always writing to SPI TX register
   DmaRegs.CH2.TRANSFER_SIZE           = 7;     // Interrupt every frame (8 bursts/transfer)
   DmaRegs.CH2.SRC_TRANSFER_STEP       = 1;     // Move to next word in buffer after each word in a burst
   DmaRegs.CH2.DST_TRANSFER_STEP       = 0;     // Don't move destination address
   DmaRegs.CH2.SRC_ADDR_SHADOW         = (Uint32) &SpiTxData[0];         // Start address = buffer
   DmaRegs.CH2.SRC_BEG_ADDR_SHADOW     = (Uint32) &SpiTxData[0];      // Not needed unless using wrap function
   DmaRegs.CH2.DST_ADDR_SHADOW         = (Uint32) &McbspaRegs.DXR1.all;      // Start address = McBSPA DXR
   DmaRegs.CH2.DST_BEG_ADDR_SHADOW     = (Uint32) &McbspaRegs.DXR1.all;  // Not needed unless using wrap function
   DmaRegs.CH2.CONTROL.bit.PERINTCLR   = 1;     // Clear peripheral interrupt event flag

   DmaRegs.CH2.CONTROL.bit.ERRCLR      = 1;     // Clear sync error flag
   DmaRegs.CH2.DST_WRAP_SIZE           = 0xFFFF;   // Put to maximum - don't want destination wrap
   DmaRegs.CH2.SRC_WRAP_SIZE           = 0xFFFF;   // Put to maximum - don't want source wrap

   DmaRegs.CH2.CONTROL.bit.PERINTCLR   = 1;     // Clear any spurious interrupt flags
   DmaRegs.CH2.MODE.bit.ONESHOT        = 0;     // Run 1 burst per transfer (register is poorly named)
   DmaRegs.CH2.MODE.bit.CHINTE         = 1;     // Enable channel interrupt
   DmaRegs.CH2.MODE.bit.OVRINTE        = 0;     // Disable overflow interrupt generation
   DmaRegs.CH2.MODE.bit.CONTINUOUS     = 0;     // When TRANSFER_COUNT reaches 0, stop DMA and clear RUNSTS to 0
   DmaRegs.CH2.MODE.bit.CHINTMODE      = 1;     // Interrupt at end of transfer
   DmaRegs.CH2.MODE.bit.PERINTE        = 1;     // Enable peripheral interrupt event
   DmaRegs.CH2.MODE.bit.PERINTSEL      = DMA_MXEVTA; // Peripheral interrupt select = McBSP MXSYNCA
   DmaRegs.CH2.CONTROL.bit.PERINTCLR   = 1;     // Clear any spurious interrupt flags

   // Channel 3, McBSPA Receive
   DmaRegs.CH3.MODE.bit.CHINTE         = 0;     // Disable interrupts while configuring channel
   DmaRegs.CH3.BURST_SIZE.all          = 0;     // 1 16-bit word/burst
   DmaRegs.CH3.SRC_BURST_STEP          = 0;     // no effect when using 1 word/burst
   DmaRegs.CH3.DST_BURST_STEP          = 0;     // no effect when using 1 word/burst
   DmaRegs.CH3.TRANSFER_SIZE           = 7;     // Interrupt every 8 bursts/transfer
   DmaRegs.CH3.SRC_TRANSFER_STEP       = 0;     // Don't move source address
   DmaRegs.CH3.DST_TRANSFER_STEP       = 1;     // Move to next word in buffer after each word in a burst
   DmaRegs.CH3.SRC_ADDR_SHADOW         = (Uint32) &McbspaRegs.DRR1.all; // Start address = McBSPA DRR
   DmaRegs.CH3.SRC_BEG_ADDR_SHADOW     = (Uint32) &McbspaRegs.DRR1.all; // Not needed unless using wrap function
   DmaRegs.CH3.DST_ADDR_SHADOW         = (Uint32) &SpiRxData[0];        // Start address = Receive buffer (for McBSP-A)
   DmaRegs.CH3.DST_BEG_ADDR_SHADOW     = (Uint32) &SpiRxData[0];        // Not needed unless using wrap function
   DmaRegs.CH3.CONTROL.bit.PERINTCLR   = 1;           // Clear peripheral interrupt event flag

   DmaRegs.CH3.CONTROL.bit.ERRCLR      = 1;           // Clear sync error flag
   DmaRegs.CH3.DST_WRAP_SIZE           = 0xFFFF;      // Put to maximum - don't want destination wrap
   DmaRegs.CH3.SRC_WRAP_SIZE           = 0xFFFF;      // Put to maximum - don't want source wrap
   DmaRegs.CH3.CONTROL.bit.PERINTCLR   = 1;           // Clear any spurious interrupt flags
   DmaRegs.CH3.MODE.bit.ONESHOT        = 0;           // Run 1 burst per transfer (register is poorly named)
   DmaRegs.CH3.MODE.bit.CHINTE         = 1;           // Enable channel interrupt
   DmaRegs.CH3.MODE.bit.OVRINTE        = 0;           // Disable overflow interrupt generation
   DmaRegs.CH3.MODE.bit.CONTINUOUS     = 0;           // When TRANSFER_COUNT reaches 0, stop DMA and clear RUNSTS to 0
   DmaRegs.CH3.MODE.bit.CHINTMODE      = 1;           // Interrupt at end of transfer
   DmaRegs.CH3.MODE.bit.PERINTE        = 1;           // Enable peripheral interrupt event
   DmaRegs.CH3.MODE.bit.PERINTSEL      = DMA_MREVTA;  // Peripheral interrupt select = McBSP MRSYNCA
   DmaRegs.CH3.CONTROL.bit.PERINTCLR   = 1;           // Clear any spurious interrupt flags
   EDIS;
}

// These three values are options
#define CLKSTP_OPT_VAL           2
#define TX_CLK_POLARITY_OPT_VAL  1
#define RX_CLK_POLARITY_OPT_VAL  1

// These values are "required" by the reference manual
#define CLKXM_REQ_VAL      0
#define SCLKME_REQ_VAL     0
#define CLKSM_REQ_VAL      1
#define CLKGDV_REQ_VAL     1
#define FSXM_REQ_VAL       0
#define FSXP_REQ_VAL       1
#define XDATDLY_REQ_VAL    0
#define RDATDLY_REQ_VAL    0

void initMcbspa(void)
{

    // McBSP-A register settings
    McbspaRegs.SPCR2.bit.FRST   = 0;               // Frame synchronization is disabled (will enable at end of init)
    McbspaRegs.SPCR2.bit.GRST   = 0;               // Sample rate generator is disabled (will enable at end of init)
    McbspaRegs.SPCR2.bit.XRST   = 0;               // Hold in reset (for now; will be cleared at end of initialization)
    McbspaRegs.SPCR2.bit.FREE   = 0;               // Don't free run when debugging (I think, documentation isn't clear)
    McbspaRegs.SPCR2.bit.SOFT   = 0;               // Stop the clock when hitting a breakpoint (I think, documentation isn't clear)
    McbspaRegs.SPCR2.bit.XINTM  = 0;               // Generate IRQ when XRDY (transmit ready) bit goes high
    McbspaRegs.SPCR2.bit.XSYNCERR = 0;             // Clear frame sync errors

    McbspaRegs.SPCR1.bit.RRST   = 0;               // Hold in reset (for now; will be cleared at end of initialization)
    McbspaRegs.SPCR1.bit.DLB    = 0;               // Digital LoopBack mode disabled
    McbspaRegs.SPCR1.bit.RJUST  = 0;               // Right-justify data, zero-fill MSBs
    McbspaRegs.SPCR1.bit.CLKSTP = CLKSTP_OPT_VAL;  // Clock stop mode, without clock delay
    McbspaRegs.SPCR1.bit.DXENA  = 0;               // Transmit delay enabler off
    McbspaRegs.SPCR1.bit.RINTM  = 0;               // Generate IRQ when RRDY (receive ready) bit goes high.
    McbspaRegs.SPCR1.bit.RSYNCERR = 0;             // Clear frame sync errors

    McbspaRegs.MFFINT.all       = 0;               // Disable all interrupts

    McbspaRegs.RCR2.all         = 0;               // Single-phase frame
    McbspaRegs.RCR2.bit.RFRLEN2 = 0;               // Phase 2 length (ignored, only applies to second phase of frame)
    McbspaRegs.RCR2.bit.RWDLEN2 = 2;               // 16 bit word (ignored, only applies to second phase of frame)
    McbspaRegs.RCR2.bit.RCOMPAND = 0;              // No companding, MSB transmitted first
    McbspaRegs.RCR2.bit.RFIG    = 0;               // Set XSYNCERR in SPCR1 if framing error occurs
    McbspaRegs.RCR2.bit.RDATDLY = RDATDLY_REQ_VAL; // 0-bit data delay
    McbspaRegs.RCR2.bit.RPHASE  = 0;               // Single-phase frame

    McbspaRegs.RCR1.all         = 0;
    McbspaRegs.RCR1.bit.RWDLEN1 = 2;               // 16-bit words
    McbspaRegs.RCR1.bit.RFRLEN1 = 7;               // 8 words per frame

    McbspaRegs.XCR2.all         = 0;
    McbspaRegs.XCR2.bit.XFRLEN2 = 0;               // Phase 2 length (ignored, only applies to second phase of frame)
    McbspaRegs.XCR2.bit.XWDLEN2 = 2;               // 16 bit word (ignored, only applies to second phase of frame)
    McbspaRegs.XCR2.bit.XCOMPAND = 0;              // No companding, MSB transmitted first
    McbspaRegs.XCR2.bit.XFIG    = 0;               // Set XSYNCERR in SPCR1 if framing error occurs
    McbspaRegs.XCR2.bit.XDATDLY = XDATDLY_REQ_VAL; // 0-bit data delay
    McbspaRegs.XCR2.bit.XPHASE  = 0;               // Single-phase frame

    McbspaRegs.XCR1.all         = 0;
    McbspaRegs.XCR1.bit.XWDLEN1 = 2;    // 16-bit word
    McbspaRegs.XCR1.bit.XFRLEN1 = 7;    // 8 words per frame

    McbspaRegs.PCR.all          = 0;
    McbspaRegs.PCR.bit.CLKXM    = CLKXM_REQ_VAL;            // MCBSP is a SPI slave
    McbspaRegs.PCR.bit.CLKRM    = 0;                        // MCLKR pin is input
    McbspaRegs.PCR.bit.FSXM     = FSXM_REQ_VAL;             // Frame sync derived from an external source for TX pin
    McbspaRegs.PCR.bit.FSRM     = 0;                        // Frame sync derived from an external source for RX pin
    McbspaRegs.PCR.bit.FSXP     = FSXP_REQ_VAL;             // Transmit frame sync is active low
    McbspaRegs.PCR.bit.FSRP     = 1;                        // Receive frame sync is active low
    McbspaRegs.PCR.bit.CLKXP    = TX_CLK_POLARITY_OPT_VAL;  // Transmit data is sampled on rising edge of CLK
    McbspaRegs.PCR.bit.CLKRP    = RX_CLK_POLARITY_OPT_VAL;  // Receive data is sampled on falling edge of CLK
    McbspaRegs.PCR.bit.SCLKME   = SCLKME_REQ_VAL;           // Sample clock derived from CPU to sync with external master CLK (used with CLKSM)

    McbspaRegs.SRGR2.bit.GSYNC  = 1;               // CLKG is synced to external clock input
    McbspaRegs.SRGR2.bit.CLKSM  = CLKSM_REQ_VAL;   // Sample clock derived from CPU to sync with external master CLK (used with SCLKME)
    McbspaRegs.SRGR2.bit.FSGM   = 0;               // Ignored because PCR.FSXM = 0
    McbspaRegs.SRGR2.bit.FPER   = (8*16)-1;        // Ignored because GSYNC is 1

    McbspaRegs.SRGR1.bit.FWID   = 0;               // Frame Width = 1 CLKG period
    McbspaRegs.SRGR1.bit.CLKGDV = CLKGDV_REQ_VAL;  // CLKG frequency = 1/2 LSPCLK frequency

    delay_loop();                                  // Wait at least 2 SRG clock cycles

    McbspaRegs.SPCR2.bit.GRST   = 1;               // Enable the sample rate generator
    clkg_delay_loop();                             // Wait at least 2 CLKG cycles

    McbspaRegs.SPCR1.bit.RRST   = 1;               // Release RX from reset
    McbspaRegs.SPCR2.bit.XRST   = 1;               // Release TX from reset
    McbspaRegs.SPCR1.bit.FRST   = 1;               // Release frame sync from reset
}

__interrupt void spiTxDmaIsr(void)
{
    StopSpiTxDmaChannel();                      // Re-enable DMA when the TX buffer is filled and we're clear to send

    __asm(" nop");
    EALLOW;                                     // NEED TO EXECUTE EALLOW INSIDE ISR !!!
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP7;     // To receive more interrupts from this PIE group, acknowledge this interrupt
    EDIS;

    SpiTxBufferFull = 0;

    return;
}

__interrupt void spiRxDmaIsr(void)
{
    StopSpiRxDmaChannel();                  // Need to re-enable after processing the message
    __asm(" nop");
    EALLOW;                                 // NEED TO EXECUTE EALLOW INSIDE ISR !!!
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP7; // To receive more interrupts from this PIE group, acknowledge this interrupt
    EDIS;

    SpiRxBufferFull = 1;

    return;
}

  • Alex,

    I'm sorry to hear that you are having some issues with the McBSP.

    I am interested in your data flow. Can you try to transmit some non-sequential data? it might help to verify that the transmit is not getting the right data from the transmit buffer.

    Additionally, can you pull the DMA off of the McBSP and transmit the data manually? This might help eliminate the dma from the picture.

    -Mark
  • Thanks for the reply, Mark.

    By non-sequential data, I'm assuming you mean use a fixed but "random" test string, correct? If I try to send 0x95A6 0x5643 0x0000 ..., I actually get 0xCAD3 0x2B21 0x8000... So, _most_ of the bits are right-shifted by one, but the first bit is either corrupted or repeating.

    I'll give the non-DMA approach a shot, I have not yet tried that.

    -Alex
  • Mark-

    I disabled DMA as you suggested, and I'm still having the same problem.  I added a line of code at the end of initialization saying "McbspaRegs.DXR1.all = 0x66AA;".  No other writes to DXR1 occur, but the data I see on the output is 0x3355, repeating as often as the master supplies the clock.  If I run the code inside the debugger, I still see 0x66AA in the DXR1 register.

    I'm convinced now that the first bit is repeating, as though the first clock transition isn't being honored.  See the following images, for examples.  The data written to DXR1 is 0x66AA in the first image, 0xC963 in the second one.

    -Alex

  • Alex,
    So you might want to confirm your clock configuration once more. Your current settings are actually not a valid config. Check out Table 15-15. Effects of CLKSTP, CLKXP, and CLKRP on the Clock Scheme in SPRUH18 for the list of all possible configurations.

    -Mark
  • Thanks, Mark. I was going off of table 15-17, which is less restrictive. That was silly of me, but unfortunately not the (only) problem. I just re-confirmed that when I run as CLKSTP = 10b, CLKXP = 1, CLKRP = 0 I'm still having the same issue.

    -Alex
  • Hmm, I was hoping that would be it! Now that you have a proper configuration, have you confirmed that this is the matching config as your master? Judging by the capture you shared, the clock coming from the master is inactive low, and the SIMO is changing on the rising edge. What edge will the master sample the SOMI data on? I think the rising edge.

    I think you want the CLKSTP = 3, CLKXP = 0, CLKRP=1. This matches Figure 15-38. It also matches up with the images you shared.

    Thanks,
    Mark
  • That fixed it. Thanks very much, Mark, I really appreciate it. I could have sworn I tried that before asking for help, but I guess I was mistaken.

    Thanks again,
    Alex
  • I'm glad that we got it solved! Unfortunately the McBSP is a beast of a module. There are a lot of configuration bits that can impact the behavior if set incorrectly.

    Please don't hesitate to create new posts in the future if you have issues!

    Thanks,
    Mark