2016-06-18 05:12 AM
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 thesConfig.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
2016-06-18 07:23 AM
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?
2016-06-18 07:43 PM
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.
2016-06-19 02:28 AM
Hello.
The TIM would be used to fix the frequency2016-06-19 02:35 AM
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.2016-06-19 02:42 AM
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();
}
2016-06-19 05:10 AM
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.2016-06-19 07:56 AM
Hello,
thanks for the feedback. I removed the TIM from the project. Is it enough if i callHAL_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]={....};