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

