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.
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; }
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