Other Parts Discussed in Thread: TMS320F28335, SYSBIOS
With a TMS320F28335 McBSP port configured as a SPI master with transmit and receive interrupts enabled, occasionally a receive interrupt is missing. That is, one fewer receive interrupt is generated than the number of bytes sent. The IFR and PIEIFR registers show that no interrupt is pending, and the McBSP SPCR1 register shows that RRDY is 0. There are no transmit or receive sync errors or receive overflows.
Transmit and receive interrupts are enabled when the system has bytes to send. The transmit ISR disables the transmit interrupt when the required number of bytes have been sent; the receive ISR disables the receive interrupt when the same number of bytes have been received. Sometimes thousands of bytes can be received before missing one, sometimes just a few. The problem seems to be made worse by enabling other interrupts, such as the timer and SCI, and occurs more frequently the faster the clock (greater than 2 MHz).
The McBSP SPI configuration is:
regs->SPCR2.all = 0x0000;
regs->SPCR1.all = 0x0000;
/* Step 3. Program the registers that affect SPI operation. */
/* Table 6-3 Bit Values Required to Configure the McBSP as an SPI Master */
regs->SPCR1.bit.DLB = 0; /* Digital Loopback mode disabled */
regs->SPCR1.bit.RJUST = 0; /* Right justify the data and zero fill */
regs->SPCR1.bit.CLKSTP = 2;
regs->SPCR1.bit.DXENA = 0; /* DX delay enabler off */
regs->SPCR1.bit.RINTM = 0; /* interrupt when RRDY bit changes from 0 to 1 */
regs->SPCR2.bit.FREE = 1; /* Free run bit */
regs->SPCR2.bit.SOFT = 0; /* Ignored when FREE=1 */
regs->SPCR2.bit.FRST = 0; /* reset frame-synchronization logic */
regs->SPCR2.bit.GRST = 0; /* reset sample rate generator */
regs->SPCR2.bit.XINTM = 0; /* interrupt when XRDY bit changes from 0 to 1 */
regs->RCR1.bit.RFRLEN1 = 0; /* Receive frame length of 1 word */
regs->RCR1.bit.RWDLEN1 = 0; /* Receive word length = 8 bits */
regs->RCR2.bit.RPHASE = 0; /* Single-phase frame */
regs->RCR2.bit.RFRLEN2 = 0; /* Ignored when RPHASE=0 */
regs->RCR2.bit.RWDLEN2 = 0; /* Ignored when RPHASE=0 */
regs->RCR2.bit.RCOMPAND = 0; /* No companding, any size data, MSB received first */
regs->RCR2.bit.RFIG = 0; /* Frame-synchronization detect */
regs->RCR2.bit.RDATDLY = 1; /* 1-bit data delay */
regs->XCR1.bit.XFRLEN1 = 0; /* Transmit frame length of 1 word */
regs->XCR1.bit.XWDLEN1 = 0; /* Transmit word length = 8 bits */
regs->XCR2.bit.XPHASE = 0; /* Single-phase frame */
regs->XCR2.bit.XFRLEN2 = 0; /* Ignored when XPHASE=0 */
regs->XCR2.bit.XWDLEN2 = 0; /* Ignored when XPHASE=0 */
regs->XCR2.bit.XCOMPAND = 0; /* No companding, any size data, MSB received first */
regs->XCR2.bit.XFIG = 0; /* Frame-synchronization detect */
regs->XCR2.bit.XDATDLY = 1; /* 1-bit data delay */
regs->SRGR1.bit.FWID = 0; /* Frame-synchronization pulse width bits for FSG */
regs->SRGR1.bit.CLKGDV = 0x07;
regs->SRGR2.bit.GSYNC = 0; /* No clock synchronization */
regs->SRGR2.bit.CLKSM = 1; /* With SCLKME, use LSPCLK */
regs->SRGR2.bit.FSGM = 0; /* Generate transmit frame-synchronization pulse when the content of XDR is copied to XSR */
regs->SRGR2.bit.FPER = 0; /* Frame-synchronization period bits */
regs->PCR.bit.FSXM = 1; /* Transmit frame synchronization is generated internally by the sample rate generator */
regs->PCR.bit.FSRM = 1; /* Receive frame synchronization is generated internally by the sample rate generator */
regs->PCR.bit.CLKXM = 1; /* The McBSP is a master in the SPI protocol */
regs->PCR.bit.CLKRM = 1; /* Internal MCLKR is driven by the sample rate generator of the McBSP. */
regs->PCR.bit.SCLKME = 0; /* With CLKSM, use LSPCLK */
regs->PCR.bit.FSXP = 1; /* Transmit frame-synchronization pulses are active low */
regs->PCR.bit.FSRP = 1; /* Receive frame-synchronization pulses are active low */
regs->PCR.bit.CLKXP = 1;
regs->PCR.bit.CLKRP = 0;
regs->MFFINT.bit.RINT = 0; /* Receive interrupt on RRDY is disabled */
regs->MFFINT.bit.XINT = 0; /* Transmit interrupt on XRDY is disabled */
/* Step 4. Enable the sample rate generator. */
regs->SPCR2.bit.GRST = 1;
wait_two_sample_rate_generator_clock_periods(regs->SRGR1.bit.CLKGDV);
/* Step 5. Enable the transmitter and receiver. */
regs->SPCR2.bit.XRST = 1;
regs->SPCR1.bit.RRST = 1;
wait_two_sample_rate_generator_clock_periods(regs->SRGR1.bit.CLKGDV);
/* Step 6. If necessary, enable the frame-synchronization logic of the
* sample rate generator. */
regs->SPCR2.bit.FRST = 1;
Transmit ISR:
static __interrupt void halo_spi_transmit_isr_mcbsp_a(void)
{
halo_spi_t* spi = &halo_spis[halo_spi_mcbsp_a];
uint16_t word_out;
assert(spi->mcbsp_regs->MFFINT.bit.XINT);
assert(spi->buf.tx_begin != spi->buf.end);
assert(spi->mcbsp_regs->SPCR2.bit.XRDY);
word_out = *spi->buf.tx_begin;
spi->buf.tx_begin++;
spi->mcbsp_regs->DXR1.all = word_out;
/* If the transmit buffer is empty, then disable the transmit interrupts and
* rely solely on receive interrupts to complete the asynchronous
* transaction. */
if (spi->buf.tx_begin == spi->buf.end)
{
spi->mcbsp_regs->MFFINT.bit.XINT = 0;
}
PieCtrlRegs.PIEACK.all = M_INT6;
}
Receive ISR:
static __interrupt void halo_spi_receive_isr_mcbsp_a(void)
{
halo_spi_t* spi = &halo_spis[halo_spi_mcbsp_a];
const uint16_t input_mask = (1 << halo_spi_character_size) - 1;
assert(spi->mcbsp_regs->MFFINT.bit.RINT);
assert(spi->mcbsp_regs->SPCR1.bit.RRDY);
assert(spi->buf.rx_begin != spi->buf.tx_begin);
/* TODO: Handle RFULL and RSYNCERR separately */
assert(!spi->mcbsp_regs->SPCR1.bit.RFULL);
assert(!spi->mcbsp_regs->SPCR1.bit.RSYNCERR);
const uint16_t byte_in = spi->mcbsp_regs->DRR1.all & input_mask;
/* The receive pointer is always less than the transmit pointer
* (transmits always occur first) */
assert(spi->buf.rx_begin < spi->buf.tx_begin);
/* The receive pointer can never reach the end of the buffer except
* through reading from the receive register. */
assert(spi->buf.rx_begin < spi->buf.end);
/* Only write into the buffer if write permissions are enabled, either
* by async_read or async_transfer. Otherwise, just increment
* the receive pointer. The receive pointer is the basis for determining
* when the transfer is complete, so it must be updated and tracked even
* if it is not used for writing into the buffer. */
*spi->buf.rx_begin++ = byte_in;
/* The transfer is complete when there are no more pending bytes in the
* receive buffer. */
if (spi->buf.rx_begin == spi->buf.end)
{
/* Disable interrupts for the SPI receive. There are no more characters
* in the input buffer or the tx FIFO. One of the async operations will
* re-enable the interrupts. */
spi->mcbsp_regs->MFFINT.bit.RINT = 0;
io_service_post(&spi->buf.context); /* Add a node to a ready queue */
}
PieCtrlRegs.PIEACK.all = M_INT6;
}
