Hi,
I have to implement an SPI slave interface on MSP430F47186. For this purpose, I setup DMA1 for SPI receiving(UCB0RXIFG triggered) and DMA2 for SPI sending (UCB0TXIFG triggered).
Sending and receiving work well, I don’t have problem with it.
Then, for synchronization , I wanted to implement inter character time out functionality. I mean, the time between each received byte by SPI should not exceed timeout duration which is 10uS.
For this functionality, I setup TIMERB to generate CCR0 interrupt every 10uS. To prevent this interrupt while receiving bytes from SPI, I setup DMA0 to clear TIMERB content and trigger source of this channel also is UCB0RXIFG. In other word, it has same trigger source with DMA1.
DMA0 -> used to clear timerb counter (like a watchdog) -> triggered by UCB0RXIFG
DMA1 -> used to receive character from SPI -> triggered by UCB0RXIFG
DMA2 - > used to send character over SPI -> triggered by UCB0TXIFG
My question is, is there a design issue for my logic? DMA0 has always higher priority and, I was expecting MCU to hang because UCB0RXIFG never get cleared after DMA0 operation. But surprisingly it works in debug mode (while debugger active). But another surprise for me, which I’ve just discovered, DMA stops operating while MCU is not in active debug mode.
static volatile u16 TBCTL_FOR_DMA = TBSSEL_2 | ID_3 | MC_2 | TBCLR;
static void InitializeDMAForSPI(void)
{
DMA0CTL = 0;
DMA1CTL = 0;
DMA2CTL = 0;
TBCTL = 0; // stop
TBCCTL0 = 0;
UCB0CTL1 = UCSWRST;
UCB0CTL0 = UCSYNCSPI | UCCKPLSPI | UCMSBSPI;
// MSP Slave SPI Pin Conf.
P3DIR_bit.P3DIR_0 = 0; // CS Input
P3DIR_bit.P3DIR_1 = 0; // SIMO Input
P3DIR_bit.P3DIR_2 = 1; // SOMI Output
P3DIR_bit.P3DIR_3 = 0; // MCLK Input
// Master is STR
P3SEL_bit.P3SEL_0 = 0; // CS
P3SEL_bit.P3SEL_1 = 1; // SIMO
P3SEL_bit.P3SEL_2 = 1; // SOMI
P3SEL_bit.P3SEL_3 = 1; // MCLK
UCB0CTL1 &= ~UCSWRST; // Initalize USART state machine
IE2_bit.UCB0RXIE = 0;
IE2_bit.UCB0TXIE = 0;
// TimerB Settings for inter-character timeout
TBCCR0 = 10; // ftmr = SMCLK / (8*TBCCR0) -> period = 10us for 8Mhz
TBCTL = 0; // stop
TBCCTL0 = CCIE; // enable timer a CCR0 interrupt
// DMA0 for Intercharacter timeout
DMACTL0_bit.DMA0TSEL0 = 0;
DMACTL0_bit.DMA0TSEL1 = 0;
DMACTL0_bit.DMA0TSEL2 = 1;
DMACTL0_bit.DMA0TSEL3 = 1; // UCB0RXIFG triggered
DMA0CTL = DMADT_4 | DMASRCINCR_0 | DMADSTINCR_0 | DMASWDW; // Edge triger, single repeat, source unchanged, dest unchanged
DMA0SAL = (uint16_t)&TBCTL_FOR_DMA; // Start block address
DMA0DAL = (uint16_t)&TBCTL; // Destination block address
DMA0SZ = 1; // max length
// DMA1 for SPI Receive Buffer
DMACTL0_bit.DMA1TSEL0 = 0;
DMACTL0_bit.DMA1TSEL1 = 0;
DMACTL0_bit.DMA1TSEL2 = 1;
DMACTL0_bit.DMA1TSEL3 = 1; // UCB0RXIFG triggered
DMA1CTL = DMADT_0 | DMASRCINCR_0 | DMADSTINCR_3 | DMASBDB | DMA1IE; // single, source unchanged, dest increment, interrupt enabled
DMA1SAL = (uint16_t)&UCB0RXBUF; // Start block address
DMA1DAL = (uint16_t)&m_SPI_RX_BUF.Stream[0]; // Destination block address
DMA1SZ = sizeof(m_SPI_RX_BUF); // max length
// DMA2 for SPI Transmit Buffer
DMACTL0_bit.DMA2TSEL0 = 1;
DMACTL0_bit.DMA2TSEL1 = 0;
DMACTL0_bit.DMA2TSEL2 = 1;
DMACTL0_bit.DMA2TSEL3 = 1; // UCB0TXIFG triggered
DMA2CTL = DMADT_0 | DMASRCINCR_3 | DMADSTINCR_0 | DMASBDB; // Level triger, single, source increment, dest unchanged
DMA2SAL = (uint16_t)&m_SPI_TX_BUF.Stream[1]; // Start block address
DMA2DAL = (uint16_t)&UCB0TXBUF; // Destination block address
DMA2SZ = sizeof(m_SPI_TX_BUF)-1; // max length
// once we start receiving frame, other side will receive frame start character to be sure synchronization
m_SPI_TX_BUF.Base.Header.FrameStart = SPI_FRAME_START;
UCB0TXBUF = m_SPI_TX_BUF.Stream[0];
DMA2CTL |= DMA2EN; // Enable DMA2
DMA1CTL |= DMA1EN; // Enable DMA1
DMA0CTL |= DMA0EN; // Enable DMA0
}