cancel
Showing results for 
Search instead for 
Did you mean: 

TIM triggered I2S DMA transfer

akosodry
Associate II
Posted on June 18, 2016 at 14:12

Hello.

I have aSTM32F407 based board where the DAC chip communicates through I2S. I'd like to make the signal generation similar to the example project named ''DAC_SignalsGeneration'' in the Cube firmware package, e.g. when the timer overflows it triggers the I2S DMA transfer and sends the next sample of the signal table. I checked theRM0090 andAN4640 documents but could not really figure out the way to link the timer triggering mechanism to the I2S (For Example: in case of DAC there is the

sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;

which enables the connection, in case of ADC there is the

hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO

which also enables.) Can you give me an example code how should i connect the timer with the I2S? Here are the relevant code parts i have so far. I configured TIM4 for triggering:

static void MX_TIM4_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
htim4.Instance = TIM4;
htim4.Init.Prescaler = TIM4_PRESCALER;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = TIM4_PERIOD;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}

Here is the I2S init:

static void MX_I2S2_Init(void)
{
hi2s2.Instance = SPI2;
hi2s2.Init.Mode = I2S_MODE_MASTER_TX;
hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
hi2s2.Init.DataFormat = I2S_DATAFORMAT_24B;
hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_48K;
hi2s2.Init.CPOL = I2S_CPOL_LOW;
hi2s2.Init.ClockSource = I2S_CLOCK_PLL;
hi2s2.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;
if (HAL_I2S_Init(&hi2s2) != HAL_OK)
{
Error_Handler();
}
}

And here is the MSPInit of the I2S

void HAL_I2S_MspInit(I2S_HandleTypeDef* hi2s)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(hi2s->Instance==SPI2)
{
/* USER CODE BEGIN SPI2_MspInit 0 */
/* USER CODE END SPI2_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_SPI2_CLK_ENABLE();
/**I2S2 GPIO Configuration 
PB10 ------> I2S2_CK
PB15 ------> I2S2_SD
PC6 ------> I2S2_MCK
PB9 ------> I2S2_WS 
*/
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_15|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* Peripheral DMA init*/
hdma_spi2_tx.Instance = DMA1_Stream4;
hdma_spi2_tx.Init.Channel = DMA_CHANNEL_0;
hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi2_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_spi2_tx.Init.Mode = DMA_CIRCULAR;
hdma_spi2_tx.Init.Priority = DMA_PRIORITY_HIGH;
hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hi2s,hdmatx,hdma_spi2_tx);
/* Peripheral interrupt init */
HAL_NVIC_SetPriority(SPI2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI2_IRQn);
/* USER CODE BEGIN SPI2_MspInit 1 */
/* USER CODE END SPI2_MspInit 1 */
}
}

Thank you in advance! Best regards. #dma #i2s #trigger #timer #stm32
7 REPLIES 7
Posted on June 18, 2016 at 16:23

The internal DAC uses the TIM to define the sample rate. With I2S the sample rate is defined by the bit clock, and the DMA transfer to the I2S peripheral simply sustains the bit stream. What purpose would you running a TIM serve in this case?

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
mark239955_stm1
Associate II
Posted on June 19, 2016 at 04:43

I'll add to what clive has said by pointing out that the 407 has a separate and independent PLL for I2S clocking.  Using the I2S PLL instead of a timer running off an APB clock derived from HCLK would allow you to achieve very fine-grained control of I2S sample timing and so optimal spectral performance when generating arbitrary waveforms.

akosodry
Associate II
Posted on June 19, 2016 at 11:28

Hello.

The TIM would be used to fix the frequency 

akosodry
Associate II
Posted on June 19, 2016 at 11:35

Hello.

Thanks for the feedback. The TIM would have been used for timing, e.g. when to send the sine table values. (I have a uint32_t vector of sine wave values, that shall be sent through I2S to the DAC in order to generate the sine wave).

But now i am a bit confused. I will make some test based on your comments. 

I would appreciate if you have any other comments related to the topic.

Thanks in advance.

akosodry
Associate II
Posted on June 19, 2016 at 11:42

Addition comment:

I2S PLL is well defined to the desired frequency:

PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
PeriphClkInitStruct.PLLI2S.PLLI2SN = 240;
PeriphClkInitStruct.PLLI2S.PLLI2SR = 2;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}

Posted on June 19, 2016 at 14:10

Again, it doesn't need a timer.

The I2S is synchronous, the bit clock being a multiple of the sample rate. Set that clock correctly and set the DMA frame over the table, and the I2S will pull data in the manner/rate to fill the request.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
akosodry
Associate II
Posted on June 19, 2016 at 16:56

Hello,

thanks for the feedback. I removed the TIM from the project. Is it enough if i call

HAL_I2S_Transmit_DMA(&hi2s2, (uint16_t*) sinTable, 512)

in the main? (before the while(1)) the table is defined as:

const uint32_t sinTable[256]={....};