I2S/SPI DMA RX from audio chip not firing interrupts
Posted: Thu Feb 13, 2014 5:44 pm
I have been struggling to achieve any sort of DMA RX callback success when working with a wolfson audio codec attached to SPI2 on the STM32F4 Discovery board. I have it all set up for I2S manually due to the I2S driver not ready yet. I am unable to find the bug. I have attached a logic analyzer to the Data, Clock, and Frame lines coming from the Wolfson dev kit and indeed I see audio data being transferred over the wires. The Wolfson is set up as slave, and the STM32 is the master. Both are configured for standard I2S mode at 8 KHz. The I2C Configuration to the Wolfson is all working fine. The I2S Configuration calculations are all ripped from the STM32 peripheral library for figuring out the dividers based on the frequency. Any ideas?
This is the DMA RX Callback:
This is the DMA Initialization:
This is the I2S Configuration Setup:
The Pin Setup:
The Caller Thread:
This is the DMA RX Callback:
Code: Select all
uint16_t wolfsonAudioBuffer0[BUFFER_SIZE];
uint16_t wolfsonAudioBuffer1[BUFFER_SIZE];
static void I2SDmaRxInterrupt(SPIDriver* spip, uint32_t flags)
{
if(spip == &SPID2)
{
printConsole("INFO: INSIDE DMA RX ISR\n");
if(flags & STM32_DMA_ISR_TCIF)
{
// TRANSFER COMPLETE
palSetPad( DEV_BLUE_LED_PORT, DEV_BLUE_LED_PIN);
}
else if(flags & STM32_DMA_ISR_FEIF)
{
// FIFO ERROR
palSetPad( DEV_GREEN_LED_PORT, DEV_GREEN_LED_PIN);
}
else if(flags & STM32_DMA_ISR_DMEIF)
{
// DIRECT MODE ERROR
palSetPad( DEV_RED_LED_PORT, DEV_RED_LED_PIN);
}
else if(flags & STM32_DMA_ISR_TEIF)
{
// TRANSFER ERROR
palSetPad( DEV_ORANGE_LED_PORT, DEV_ORANGE_LED_PIN);
}
else if(flags & STM32_DMA_ISR_HTIF)
{
// HALF TRANSFER COMPLETE
palSetPad( DEV_BLUE_LED_PORT, DEV_BLUE_LED_PIN);
}
}
}
This is the DMA Initialization:
Code: Select all
void I2SInit(void)
{
// PROGRAM STREAM MODE SETTINGS
// DEFAULTS IN SPI_LLD.C ARE : PERIPHERAL TO MEMORY, TCIE, DMEIE, TEIE
// DOUBLE BUFFER MODE (STM32_DMA_CR_DBM)
// MEMORY DATA SIZE HALF WORD (16-BIT) (STM32_DMA_CR_MSIZE_HWORD)
// MEMORY INCREMENT MODE (STM32_DMA_CR_MINC)
// PERIPHERAL DATA SIZE HALF WORD (16-bit) (STM32_DMA_CR_PSIZE_HWORD)
// CIRCULAR MODE ENABLE (STM32_DMA_CR_CIRC)
SPID2.rxdmamode |= ( STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD |
STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC |
STM32_DMA_CR_DBM );
// SINGLE FUNCTION ALLOCATES DMA STREAM, ENABLES DMA CLOCK, AND ASSOCIATES IRQ VECTOR
bool streamStatus = dmaStreamAllocate(SPID2.dmarx,
STM32_SPI_SPI2_IRQ_PRIORITY,
(stm32_dmaisr_t)I2SDmaRxInterrupt,
&SPID2);
// FUNCTION ABOVE RETURNS FALSE ON NO ERROR, AND TRUE ON STREAM ALREADY TAKEN ERROR.
if(streamStatus)
{
printConsole("ERROR: SPI2 DMA STREAM ALREADY TAKEN\n");
}
// ENABLE SPI2 PERIPHERAL CLOCK
rccEnableSPI2(FALSE);
// ASSOCIATE THE SPI2 DR REGISTER TO THE DMA STREAM
dmaStreamSetPeripheral(SPID2.dmarx, &SPID2.spi->DR);
// SPI SETUP AND ENABLE
SPID2.spi->CR2 = SPI_CR2_RXDMAEN ;
// PROGRAM STREAM FIFO SETTINGS
// DIRECT MODE DISABLE
// FIFO THRESHOLD 1/2 FULL
dmaStreamSetFIFO(SPID2.dmarx, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_HALF );
dmaStreamSetMemory0(SPID2.dmarx, wolfsonAudioBuffer0);
dmaStreamSetMemory1(SPID2.dmarx, wolfsonAudioBuffer1);
// SET THE NUMBER OF TRANSFERS TO BE PERFORMED
// SEE PAGE 328 OF RM
dmaStreamSetTransactionSize(SPID2.dmarx, BUFFER_SIZE / 2);
dmaStreamSetMode(SPID2.dmarx, SPID2.rxdmamode );
dmaStreamEnable(SPID2.dmarx);
}This is the I2S Configuration Setup:
Code: Select all
void I2SSetup(uint32_t AudioFreq)
{
uint16_t tmpreg = 0, i2sdiv = 2, i2sodd = 0;
uint32_t pllm = 0, plln = 0, pllr = 0;
uint32_t tmp = 0, i2sclk = 0;
// SPI2 I2S CONFIGURATION
SPID2.spi->I2SCFGR &= I2SCFGR_CLEAR_MASK;
// SET I2S LINEAR PRESCALER TO 2
// SEE PAGE 893 OF RM
SPI2->I2SPR = 0x0002;
// GET THE I2SCFGR REGISTER VALUE
tmpreg = SPID2.spi->I2SCFGR;
// SET PLLI2S AS I2S CLOCK SOURCE
if ((RCC->CFGR & RCC_CFGR_I2SSRC) != 0)
{
// IF PLLI2S CLOCK IS NOT I2S CLOCK SOURCE, TURN IT ON
RCC->CFGR &= ~(uint32_t)RCC_CFGR_I2SSRC;
}
// GET THE PLLI2SN VALUE
plln = (RCC->PLLI2SCFGR & RCC_PLLI2SCFGR_PLLI2SN) >> 6;
// GET THE PLLI2SR VALUE
pllr = (RCC->PLLI2SCFGR & RCC_PLLI2SCFGR_PLLI2SR) >> 28;
// GET THE PLLM VALUE
pllm = (uint32_t)(RCC->PLLCFGR & RCC_PLLCFGR_PLLM);
// GET THE I2S SOURCE CLOCK VALUE
i2sclk = (uint32_t)(((HSE_VALUE / pllm) * plln) / pllr);
// COMPUTE THE REAL DIVIDER DEPENDING ON THE MCLK OUTPUT STATE WITH A FLOATING POINT
tmp = (uint16_t)(((((i2sclk / 256) * 10) / AudioFreq)) + 5);
// REMOVE FLOATING POINT
tmp = tmp / 10;
/* Check the parity of the divider */
i2sodd = (uint16_t)(tmp & (uint16_t)0x0001);
/* Compute the i2sdiv prescaler */
i2sdiv = (uint16_t)((tmp - i2sodd) / 2);
/* Get the Mask for the Odd bit (SPI_I2SPR[8]) register */
i2sodd = (uint16_t) (i2sodd << 8);
/* Test if the divider is 1 or 0 or greater than 0xFF */
if ((i2sdiv < 2) || (i2sdiv > 0xFF))
{
/* Set the default values */
i2sdiv = 2;
i2sodd = 0;
}
/* Write to SPI2 I2SPR register the computed value */
SPID2.spi->I2SPR = i2sdiv | i2sodd | I2S_MCLKOutput_Enable;
// CONFIGURE I2S
tmpreg |= SPI_I2SCFGR_I2SMOD | I2S_Mode_MasterRx | \
I2S_Standard_Phillips | I2S_DataFormat_16b | \
I2S_CPOL_Low | \
SPI_I2SCFGR_I2SE;
// WRITE CONFIGURATION TO SPI2 I2SCFGR REGISTER
SPID2.spi->I2SCFGR = tmpreg;
}The Pin Setup:
Code: Select all
void initI2S()
{
palSetPadMode(I2S_CLK_PORT, I2S_CLK_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_MODE_ALTERNATE(5) | PAL_STM32_OSPEED_HIGHEST);
palSetPadMode(I2S_DATA_PORT, I2S_DATA_PIN, PAL_MODE_INPUT | PAL_MODE_ALTERNATE(5) | PAL_STM32_OSPEED_HIGHEST);
palSetPadMode(I2S_WS_PORT, I2S_WS_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_MODE_ALTERNATE(5) | PAL_STM32_OSPEED_HIGHEST);
}The Caller Thread:
Code: Select all
// INITIALIZE WOLFSON CODEC REGISTERS
if( ! WM8974Init() )
{
printConsole("ERROR: Failure initializing Wolfson\n");
}
else
{
printConsole("INFO: Wolfson Initialized\n");
}
I2SSetup(8000);
I2SInit();