Hello,
I have simple problem but I do not understand why it occurs nor how to fix it. The problem is that TXBUF value in SPI register looks to be overwritten when starting new DMA transfer from BTC interrupt.
I have a kludge in my mind but that is not effective at all, 2 ISRs and wastes 1 N2HET based timer:
- from BTC isr, if more data to send, start N2HET based timer which length is 10bit time in SPI and from N2HET ISR start the DMA
I am using SPI (TX) with DMA to output UART data (10bit transfer), it works ok except when I go around a ring buffer. As I have learn you can't use DMA chaining so if there is more data in the beginning of the buffer I trigger new DMA transfer directly from BTC interrupt.
I noticed that individual chars were missing here and there and by little evaluation (basically shortening the ring buffer made failures occur faster) I managed to solve the failure point and made simple "test program" to illustrate the problem.
Test program has string array "1234567890\r\n" which is formatted as same length uint16 array ( (char <<1) | 0x200U) to contain 10bit UART pattern in each slot. SPI speed is 460800, so it takes ~20us to send one character and SPI is configured to sent 10 bits LSB first (nothing special).
Then I trigger sending that pattern every 1 second so that first the task sends all 12 chars (IRQ sends 0), then 11 ( 1), etc until final round when task sends 1 (11) and then restart from the beginning.
Here is the output (// added by me):
1234567890<CR><LF> // task 12, IRQ 0
1234567890<LF> // task 11, IRQ 1 ( LF)
123456789<CR><LF> // task 10, IRQ 2 (CR LF)
123456780<CR><LF> // task 19, IRQ 3 (0 CR LF)
123456790<CR><LF>
123456890<CR><LF>
123457890<CR><LF>
123467890<CR><LF>
123567890<CR><LF>
124567890<CR><LF>
134567890<CR><LF>
1234567890<CR><LF> // task 1, IRQ11 (now every character came through, because SPI has 2 "buffers", shift + txbuf)!!!!!!!
1234567890<CR><LF> // task 12, IRQ 0
1234567890<LF> // task 11, IRQ 1 ( LF)
123456789<CR><LF> // task 10, IRQ 2 (CR LF)
As you can see, the last character which "task" should output is overwritten by the first character which IRQ gives unless task only sends 1 character (TXBUF is empty when DMA is started because data from task when to SHIFT REG).
If inside the BTC interrupt I make dummy delay to allow 1 character pass though (to ensure that there will be room in SPI buffer the output is ok
uint32 u32Time = HAL_u32TimeGet();
while( (HAL_u32TimeGet()- u32Time) < 24U ){};
1234567890<CR><LF>
1234567890<CR><LF>
1234567890<CR><LF>
1234567890<CR><LF>
1234567890<CR><LF>
1234567890<CR><LF>
1234567890<CR><LF>
1234567890<CR><LF>
1234567890<CR><LF>
Functionality is exactly the same do I use DAT0 or DAT1
tSciTxCTRLPKT.DADD = (uint32)(&(spiREG4->DAT1));
tSciTxCTRLPKT.DADD = (uint32)(&(spiREG4->DAT0));
And it does not matter either if CSHOLD bit is set or not (WDEL is disabled)
spiREG4->GCR1 &= (uint32)~BIT_n( SPI_GCR1_SPI_EN );
spiREG4->DAT1 |= BIT_n( SPI_DAT1_CSHOLD );
spiREG4->GCR1 |= (uint32)BIT_n( SPI_GCR1_SPI_EN );
DAT0/1 selection should not effect and CSHOLD either, so in that way the functionality looks consistent.
DMA is configured to move 16 bit so that upper part of DAT register is not written/modified
tSciTxCTRLPKT.RDSIZE = ACCESS_16_BIT; /* read size */
tSciTxCTRLPKT.WRSIZE = ACCESS_16_BIT; /* write size */
Here is the test function which both TASK and IRQ uses to check/send the data (bStart is set by the TASK before calling this) and 12 magic number is the length of the test string pattern, task needs always send at least 1 item, otherwise IRQ never happens.
void vSendData()
{
uint32 u32Index = 0U;
if( bStart )
{
if( !bIrq )
{
u32Len = 12 - u32NextIdx;
u32IrqLen = 12 - u32Len;
u32Index = 0U;
u32NextIdx++;
if( u32NextIdx > 11U )
{
u32NextIdx = 0U;
}
}
else
{
bStart = FALSE;
bIrq = FALSE;
u32Len = u32IrqLen;
u32Index = 12U-u32Len;
}
}
else
{
bIrq = FALSE;
u32Len = 0U;
}
if( u32Len )
{
u32DmaSendingLen = u32Len;
/* Updated required dma control packet information for TX1 */
dmaRAMREG->PCP[DMA_CH_DBG_TX1].ISADDR = (uint32)&au16DataBuf[ u32Index ]; /* source address */
dmaRAMREG->PCP[DMA_CH_DBG_TX1].ITCOUNT = SET_LOWORD_U32(dmaRAMREG->PCP[DMA_CH_DBG_TX1].ITCOUNT) |
SET_HIWORD_U32( u32Len ); /* frame count */
dmaSetChEnable(DMA_CH_DBG_TX1, DMA_HW); /* Enable DMA channel */
spiREG4->INT0 |= SPI_INT0_DMAREQEN;
}
}
And here is the IRQ function (dummy delay (which makes ok prints) mentioned above commented out)
void DMON_vPacketSent( void )
{
spiREG4->INT0 &= ~SPI_INT0_DMAREQEN;
u32DmaSendingLen = 0U;
uint32 u32Time = HAL_u32TimeGet();
//while( (HAL_u32TimeGet()- u32Time) < 24U ){};
/* if more data in ring buf, sent it */
bIrq = TRUE;
vStartDmaTransfer();
}
There are also proper guards in the TASK side (with IRQ protection) to prevent the task triggering new DMA transfer is one is still ongoing (is this case there is never ongoing transfers)...
if( !u32DmaSendingLen )
{
bStart = TRUE;
vStartDmaTransfer();
}
According to TRM the DMA request should (see DMAREQEN) be generated when the data is moved to SHIFT register so in DMA transfer with used speed (460800) there should be always another data waiting in TXBUF. And based on this, it should be OK to start new HW based DMA transfers even previous transfer residuals are in the SPI buffers.
It also tested the system by setting following checks into ISR function (just after spiREG4->INT0 &= ~SPI_INT0_DMAREQEN; line)
uint32 u32Pend = 0U;
uint32 u32FLG = 0U;
if( dmaREG->PEND & BIT_n( DMA_CH_DBG_TX1 ) )
{
u32Pend = 1U;
}
if( spiREG4->FLG & BIT_n( 9U ) ) // TX empty
{
u32FLG = 1U;
}
And put breakpoint after this and both values are 0 (as they should) when entering into ISR (also tested variable functionality by breaking the execution after start DMA and then both variables were 1 as tey should be because now DMA have had time to sent all data out). How ever the register view is not consistent (don't know the logic how this is updated) when stopping before and after send function. SPI FLG has always TX bit set and DMA has pend bit only if breakpoint is after send function. Why FLG has bit always up even though my flag checker says it is down, is the content in view updated by some delay???
I also tested ISR function by setting the break point before and after vStartDmaTransfer() function, and if break point is before the whole print comes out ok (as expected before that gives time for SPI) and if break point is after then 1 character is again lost (as expected).
PS. I do not use debug mode in DMA so it runs even though the execution in IDE is stopped...
Regards,
Jarkko