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();