2014-02-08 11:52 AM
I'm currently testing I2S between two F4 Discovery development boards.
One board is master, running DMA, one is slave. Both are configured with the same sample set for verification purposes, so the data sent can be validated at the slave end. The sample set is 29 x 16-bit values that get transferred in one DMA sitting, and then started again.I've not got FIFO configured, and I believe I've got DMA configured first, enabling I2S last ( I've read the F4 is sensitive to the order DMA and I2S/SPI is enabled ).I'm using SPI2 for I2S, DMA 1 Stream 4 Channel 0. No other DMA is being used.On the Master I'm getting some strange DMA FIFO Errors being thrown (DMA_FLAG_FEIF4) when running at higher I2S sample rates:48k - I get one FIFO Error when DMA is enabled and then everything runs fine.96k - 1 or 2 FIFO Errors per second192k - Lots of FIFO Errors.Has anyone come across this before, or had any dealings with FIFO Errors with SPI/I2S DMA on the F4?Thanks,Phil.2014-02-09 03:14 AM
I came across this too. It has been reported here several times, and appears to be considered ''normal''.
https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/DMA+and+Direct+Mode&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B¤tviews=146 JW2014-02-13 12:13 AM
Okay.. I've done a lot of playing around with this, and I think I've solved it.. but for the life of me I do not understand it..
At 192k the FIFO error occurred immediately after enabling DMA inside the ISR. Initially I thought the issue was with timing, perhaps I did not disable the Transfer Complete flag fast enough. I originally did some DMA Error Flag checking first, then checked for Transfer Complete.. So I moved the Error checking to the end of the ISR, made no difference. I then attempted to shorten the Transfer Complete handling, the code that started the next transfer off.. and found something very strange. If I cleared the Transfer complete flag at the top of my code, I got a FIFO error immediately after enabling the DMA again. If I clear the Transfer complete flag at the bottom of my code, immediately after I enabled the DMA again, no FIFO error. I appreciate that this is counter-intuitive and makes no sense to me, but it works. I've done some checking and the sent data is spot on at the destination end. DMA Init Code:
void
I2S_tx_dma_init(
void
) {
printf(
''I2S_tx_dma_init(): Entered
''
);
#if defined(I2S_TX_DMA_IT_TC_EN) || defined(I2S_TX_DMA_IT_HT_EN) || defined(I2S_TX_DMA_IT_TE_EN)
NVIC_InitTypeDef NVIC_InitStructure;
#endif
/* Enable the DMA clock */
RCC_AHB1PeriphClockCmd(I2S_TX_DMA_CLOCK, ENABLE);
/* Configure the DMA Stream */
DMA_Cmd(I2S_TX_DMA_STREAM, DISABLE);
/* Clean up DMA, clear FIFO, interrupt flags etc... */
DMA_DeInit(I2S_TX_DMA_STREAM);
/* Set the parameters to be configured */
DMA_InitStructure.DMA_Channel = I2S_TX_DMA_CHANNEL;
DMA_InitStructure.DMA_PeripheralBaseAddr = I2S_TX_DMA_DREG;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)0;
/* This field will be configured in play function */
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_BufferSize = (uint32_t)0xFFFE;
/* This field will be configured in play function */
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PERIPH_DATA_SIZE;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MEM_DATA_SIZE;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
//DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
//DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
// Default
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(I2S_TX_DMA_STREAM, &DMA_InitStructure);
// Configure interrupts for DMA transfers
#ifdef I2S_TX_DMA_IT_TC_EN
DMA_ITConfig(I2S_TX_DMA_STREAM, DMA_IT_TC, ENABLE);
#endif /* I2S_TX_DMA_IT_TC_EN */
#ifdef I2S_TX_DMA_IT_HT_EN
DMA_ITConfig(I2S_TX_DMA_STREAM, DMA_IT_HT, ENABLE);
#endif /* I2S_TX_DMA_IT_HT_EN */
#ifdef I2S_TX_DMA_IT_TE_EN
DMA_ITConfig(I2S_TX_DMA_STREAM, DMA_IT_TE | DMA_IT_FE | DMA_IT_DME, ENABLE);
#endif /* I2S_TX_DMA_IT_TE_EN */
#if defined(I2S_TX_DMA_IT_TC_EN) || defined(I2S_TX_DMA_IT_HT_EN) || defined(I2S_TX_DMA_IT_TE_EN)
/* I2S DMA IRQ Channel configuration */
NVIC_InitStructure.NVIC_IRQChannel = I2S_TX_DMA_IRQ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = I2S_TX_IRQ_PREPRIO;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = I2S_TX_IRQ_SUBRIO;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
/* Enable the I2S DMA request */
SPI_I2S_DMACmd(I2Sx, SPI_I2S_DMAReq_Tx, ENABLE);
// Patch I2S through to DMA
printf(
''I2S_tx_dma_init(): Exiting.
''
);
return
;
}
DMA ISR:
void
I2S_TX_IRQHandler(
void
)
{
counter++;
//printf(''I2S_TX_IRQHandler(%d): Entered
'', counter);
/*********************
* Transfer Complete *
*********************/
#ifdef I2S_TX_DMA_IT_TC_EN
/* Transfer complete interrupt */
if
(DMA_GetFlagStatus(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_TC) != RESET)
{
/* Clear the Transfer Complete Interrupt flag */
//DMA_ClearFlag(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_TC);
// Calculate statistics
transfer_complete_cnt++;
transfer_complete_interval = systick_counter - transfer_complete_interval_start;
transfer_complete_interval_start = systick_counter;
/* Disable the I2S DMA Stream*/
DMA_Cmd(I2S_TX_DMA_STREAM, DISABLE);
// <-- Something a bit odd going on here
/* Wait the DMA Stream to be effectively disabled */
while
(DMA_GetCmdStatus(I2S_TX_DMA_STREAM) != DISABLE) {}
//printf(''I2S_TX_IRQHandler(%d): Transfer Complete. (%d ms)
'', counter,systick_counter);
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&sample_data[0];
// Address of current location in sample_data[].
DMA_InitStructure.DMA_BufferSize = SAMPLE_DATA_SIZE;
// In 16-bit Sample(s)
//I2S_TX_DMA_STREAM->M0AR = (uint32_t)&sample_data[0]; // Address of current location in sample_data[].
//I2S_TX_DMA_STREAM->NDTR = SAMPLE_DATA_SIZE; // In 16-bit Sample(s)
DMA_Init(I2S_TX_DMA_STREAM, &DMA_InitStructure);
if
( DMA_GetFlagStatus(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_FE ) != RESET )
printf(
''ISR: FE before DMA enabled
.''
);
/*
* If the I2S peripheral is still not enabled, enable it
* NOTE: This does not fire, I2S does not appear to get disabled.
*/
if
((I2Sx->I2SCFGR & I2S_ENABLE_MASK) == 0)
{
printf(
''ISR: I2S Disabled.
''
);
I2S_Cmd(I2Sx, ENABLE);
}
// Kick off another transfer.
DMA_Cmd(I2S_TX_DMA_STREAM, ENABLE);
// Start DMA again. ( NOTE: FIFO Error occurs immediately after DMA enabled ).
if
( DMA_GetFlagStatus(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_FE ) != RESET ) {
//printf(''ISR: FE after DMA enabled
.'');
fifo_inisr_error_cnt++;
}
// Clear any stale DMA interrupts errors.
DMA_ClearFlag(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_TC | I2S_TX_DMA_FLAG_TE | I2S_TX_DMA_FLAG_FE | I2S_TX_DMA_FLAG_DME );
}
// End If: DMA Transfer Complete
#endif
#ifdef I2S_TX_DMA_IT_TE_EN
/************************
* Check for DMA Errors *
************************/
if
( DMA_GetFlagStatus(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_TE ) != RESET ) {
printf(
''I2S_TX_IRQHandler(): Transmit Error.
''
);
DMA_ClearFlag(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_TE);
}
if
( DMA_GetFlagStatus(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_FE ) != RESET ) {
// Calculate statistics
fifo_error_cnt++;
fifo_error_interval = systick_counter - fifo_error_interval_start;
fifo_error_interval_start = systick_counter;
//STM_EVAL_LEDToggle(LED5);
//spi_error_check(I2Sx);
DMA_ClearFlag(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_FE);
}
if
( DMA_GetFlagStatus(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_DME ) != RESET ) {
printf(
''I2S_TX_IRQHandler(): Direct Mode Error.
''
);
DMA_ClearFlag(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_DME);
}
#endif /* I2S_TX_DMA_IT_TE_EN */
}
2014-02-13 12:43 AM
Ignore my statement about it working above. I was wrong. Obviously if I don't clear the Transfer Complete flag first, starting the DMA again fails. The code above only fires once, hence not getting any FIFO errors.
I suspect I'll just have to settle with clearing the Transfer Complete at the top, and clearing the FIFO error at the bottom of the ISR. Sorry for the confusion. Phil.
/*********************
* Transfer Complete *
*********************/
#ifdef I2S_TX_DMA_IT_TC_EN
/* Transfer complete interrupt */
if
(DMA_GetFlagStatus(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_TC) != RESET)
{
/* Clear the Transfer Complete Interrupt flag */
DMA_ClearFlag(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_TC);
// Calculate statistics
transfer_complete_cnt++;
transfer_complete_interval = systick_counter - transfer_complete_interval_start;
transfer_complete_interval_start = systick_counter;
/* Disable the I2S DMA Stream*/
DMA_Cmd(I2S_TX_DMA_STREAM, DISABLE);
/* Wait the DMA Stream to be effectively disabled */
while
(DMA_GetCmdStatus(I2S_TX_DMA_STREAM) != DISABLE) {}
//printf(''I2S_TX_IRQHandler(%d): Transfer Complete. (%d ms)
'', counter,systick_counter);
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&sample_data[0];
// Address of current location in sample_data[].
DMA_InitStructure.DMA_BufferSize = SAMPLE_DATA_SIZE;
// In 16-bit Sample(s)
//I2S_TX_DMA_STREAM->M0AR = (uint32_t)&sample_data[0]; // Address of current location in sample_data[].
//I2S_TX_DMA_STREAM->NDTR = SAMPLE_DATA_SIZE; // In 16-bit Sample(s)
DMA_Init(I2S_TX_DMA_STREAM, &DMA_InitStructure);
if
( DMA_GetFlagStatus(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_FE ) != RESET )
printf(
''ISR: FE before DMA enabled
.''
);
// Kick off another transfer.
DMA_Cmd(I2S_TX_DMA_STREAM, ENABLE);
// Start DMA again. ( NOTE: FIFO Error occurs immediately after DMA enabled ).
if
( DMA_GetFlagStatus(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_FE ) != RESET ) {
//printf(''ISR: FE after DMA enabled
.'');
fifo_inisr_error_cnt++;
}
// Clear any stale DMA interrupts errors.
DMA_ClearFlag(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_TE | I2S_TX_DMA_FLAG_FE | I2S_TX_DMA_FLAG_DME );
}
// End If: DMA Transfer Complete
#endif
#ifdef I2S_TX_DMA_IT_TE_EN
/************************
* Check for DMA Errors *
************************/
if
( DMA_GetFlagStatus(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_TE ) != RESET ) {
printf(
''I2S_TX_IRQHandler(): Transmit Error.
''
);
DMA_ClearFlag(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_TE);
}
if
( DMA_GetFlagStatus(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_FE ) != RESET ) {
// Calculate statistics
fifo_error_cnt++;
fifo_error_interval = systick_counter - fifo_error_interval_start;
fifo_error_interval_start = systick_counter;
//STM_EVAL_LEDToggle(LED5);
//spi_error_check(I2Sx);
DMA_ClearFlag(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_FE);
}
if
( DMA_GetFlagStatus(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_DME ) != RESET ) {
printf(
''I2S_TX_IRQHandler(): Direct Mode Error.
''
);
DMA_ClearFlag(I2S_TX_DMA_STREAM, I2S_TX_DMA_FLAG_DME);
}
#endif /* I2S_TX_DMA_IT_TE_EN */
}
2014-02-13 12:51 AM
> I suspect I'll just have to settle with clearing the Transfer Complete at the top, and clearing the FIFO error at the bottom of the ISR.
Why don't you simply ignore the FIFO error, once you set DMA to Direct mode? JW2014-02-13 11:44 PM
You are right, I will have to just to ignore it.
Thanks.Phil.