cancel
Showing results for 
Search instead for 
Did you mean: 

Is this a normal level of noise to see for I2S with DMA?

NNagy.1
Senior

I'm getting a lot of noise from my STM32 design, which is just supposed to be direct line-in/out I2S with DMA.

I'm feeding in a 440Hz sine wave to my CS5343 A/D converter:

0693W000001qSuUQAU.jpg

And this is the output I'm seeing from my CS4344 D/A converter:

0693W000001qSutQAE.jpg

If I zoom out a bit, my oscilloscope does interpret the signal as 440Hz:

0693W000001qSv8QAE.jpg

And when I plug the output into an amp I at least get a slightly imperfect A440 (it seems to go a little flat or sharp from time to time).

I know that there are algorithms for filtering/smoothing signals, but before implementing any of those I just want to make sure that this is a pretty standard level of noise to see, and that there's not something egregiously wrong with my setup.

5 REPLIES 5
NNagy.1
Senior

If it's important, this is my code:

#include "main.h"
 
I2S_HandleTypeDef hi2s2;
I2S_HandleTypeDef hi2s3;
DMA_HandleTypeDef hdma_spi2_rx;
DMA_HandleTypeDef hdma_spi3_tx;
 
uint16_t rxBuf[AUDIO_BUFFER_LENGTH];
uint16_t txBuf[AUDIO_BUFFER_LENGTH];
 
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_I2S2_Init(void);
static void MX_I2S3_Init(void);
 
int main(void)
{
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
 
  MX_DMA_Init();
 
  MX_I2S3_Init();
  MX_I2S2_Init();
 
  HAL_I2S_Transmit_DMA(&hi2s3, (uint16_t*)&txBuf, AUDIO_BUFFER_16BIT_LENGTH);
  HAL_I2S_Receive_DMA(&hi2s2, (uint16_t*)&rxBuf, AUDIO_BUFFER_16BIT_LENGTH);
 
  while (1)
  {
  }
}
 
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
 
  /** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
  PeriphClkInitStruct.PLLI2S.PLLI2SN = 96;
  PeriphClkInitStruct.PLLI2S.PLLI2SP = RCC_PLLP_DIV2;
  PeriphClkInitStruct.PLLI2S.PLLI2SR = 2;
  PeriphClkInitStruct.PLLI2S.PLLI2SQ = 2;
  PeriphClkInitStruct.PLLI2SDivQ = 1;
  PeriphClkInitStruct.I2sClockSelection = RCC_I2SCLKSOURCE_PLLI2S;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
}
 
/**
  * @brief I2S2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2S2_Init(void)
{
  hi2s2.Instance = SPI2;
  hi2s2.Init.Mode = I2S_MODE_MASTER_RX;
  hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
  hi2s2.Init.DataFormat = I2S_DATAFORMAT;
  hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
  hi2s2.Init.AudioFreq = I2S_SAMPLE_RATE;
  hi2s2.Init.CPOL = I2S_CPOL_LOW;
  hi2s2.Init.ClockSource = I2S_CLOCK_PLL;
  if (HAL_I2S_Init(&hi2s2) != HAL_OK)
  {
    Error_Handler();
  }
}
 
/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
 
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
}
 
/**
  * @brief I2S3 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2S3_Init(void)
{
  hi2s3.Instance = SPI3;
  hi2s3.Init.Mode = I2S_MODE_SLAVE_TX;
  hi2s3.Init.Standard = I2S_STANDARD_PHILIPS;
  hi2s3.Init.DataFormat = I2S_DATAFORMAT;
  hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;
  hi2s3.Init.AudioFreq = I2S_SAMPLE_RATE;
  hi2s3.Init.CPOL = I2S_CPOL_LOW;
  hi2s3.Init.ClockSource = I2S_CLOCK_PLL;
  if (HAL_I2S_Init(&hi2s3) != HAL_OK)
  {
    Error_Handler();
  }
}
 
/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void) 
{
 
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA1_Stream1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
  /* DMA1_Stream5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
 
}
 
void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {
	ARM_COPY((Q*)&rxBuf, (Q*)&txBuf, AUDIO_BUFFER_LENGTH>>1);
}
 
void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {
}
 
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) {
 ARM_COPY((Q*)&rxBuf[AUDIO_BUFFER_LENGTH>>1], (Q*)&txBuf[AUDIO_BUFFER_LENGTH>>1], AUDIO_BUFFER_LENGTH>>1);
}
 
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) {
}

Definitions of custom macros are in two separate files:

i2s_audio_settings.h

#ifndef I2S_AUDIO_SETTINGS_H
#define I2S_AUDIO_SETTINGS_H
 
#include "stm32f7xx_hal.h"
 
#define NUM_CHANNELS					 2
#define AUDIO_DATA_SIZE                 16
#define AUDIO_SAMPLE_RATE               44
#define AUDIO_BUFFER_SAMPLES_PER_CHANNEL 16
 
#if AUDIO_DATA_SIZE == 24
#define I2S_DATAFORMAT I2S_DATAFORMAT_24B
#define AUDIO_BUFFER_T int32_t
#define AUDIO_BUFFER_16BIT_LENGTH AUDIO_BUFFER_SAMPLES_PER_CHANNEL << (NUM_CHANNELS)
#define AUDIO_BUFFER_LENGTH AUDIO_BUFFER_16BIT_LENGTH >> 1 // should treat lenght as int32
#else
#define I2S_DATAFORMAT I2S_DATAFORMAT_16B
#define AUDIO_BUFFER_T int16_t
#define AUDIO_BUFFER_16BIT_LENGTH AUDIO_BUFFER_SAMPLES_PER_CHANNEL << (NUM_CHANNELS - 1)
#define AUDIO_BUFFER_LENGTH AUDIO_BUFFER_16BIT_LENGTH
#endif
 
#define AUDIO_BUFFER_PTR_T AUDIO_BUFFER_T*
 
// TODO: extend
#if AUDIO_SAMPLE_RATE == 44
#define I2S_SAMPLE_RATE I2S_AUDIOFREQ_44K
#else
#define I2S_SAMPLE_RATE I2S_AUDIOFREQ_96K
#endif
 
#endif

dsp.h

#ifndef __DSP_H
#define __DSP_H
 
#define ARM_MATH_CM7
 
#include <stdio.h>
#include <stdlib.h>
#include "arm_math.h"
#include "i2s_audio_settings.h"
 
#if AUDIO_DATA_SIZE > 16
 
#define UINT uint32_t
#define Q    q31_t
#define ARM_RFFT_INSTANCE               arm_rfft_instance_q31
#define ARM_CFFT_RADIX4_INSTANCE arm_cfft_radix4_instance_q31
#define ARM_COPY(in, out, size)         arm_copy_q31(in, out, size)
#define ARM_RFFT_INIT(rfftInstancePtr, size) arm_rfft_init_q31(rfftInstancePtr, size, 0, 1)
#define ARM_RFFT(instance, in, out)     arm_rfft_q31(instance, in, out)
#define ARM_SHIFT(in, shift, out, size) arm_shift_q31(in, shift, out, size)
 
#else
 
#define UINT uint16_t
#define Q    q15_t
#define ARM_RFFT_INSTANCE               arm_rfft_instance_q15
#define ARM_CFFT_RADIX4_INSTANCE arm_cfft_radix4_instance_q15
#define ARM_COPY(in, out, size)         arm_copy_q15(in, out, size)
#define ARM_RFFT_INIT(rfftInstancePtr, size) arm_rfft_init_q15(rfftInstancePtr, size, 0, 1)
#define ARM_RFFT(instance, in, out)     arm_rfft_q15(instance, in, out)
#define ARM_SHIFT(in, shift, out, size) arm_shift_q15(in, shift, out, size)
 
#endif
 
#endif

Divide and conquer.

Fill a buffer with a easily recognizable pattern - a sawtooth - and play it back.

JW

Tried a TX sawtooth with the above configurations and changing the following:

void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {
	for (int i = 0; i < AUDIO_BUFFER_LENGTH>>1; i++) {
		txBuf[i] = i;
	}
}
 
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) {
	for (int i = AUDIO_BUFFER_LENGTH>>1; i < AUDIO_BUFFER_LENGTH; i++) {
		txBuf[i] = i;
	}
}

0693W000001qYLpQAM.jpg

The green data line is the output from the board. It transmits all indexes of the txBuf (0->15). But then between each transmit there is 16 cycles of zero.

I tried changing AUDIO_BUFFER_SAMPLES_PER_CHANNEL to a different number (2) and the same pattern showed: the system transmits 0, 1, then 2 cycles of zero, then repeats.

I'm not near my device at the moment but I think this might be the culprit:

  HAL_I2S_Transmit_DMA(&hi2s3, (uint16_t*)&txBuf, AUDIO_BUFFER_16BIT_LENGTH);
  HAL_I2S_Receive_DMA(&hi2s2, (uint16_t*)&rxBuf, AUDIO_BUFFER_16BIT_LENGTH);

I thought that the "Size" parameter was supposed to be the total number of samples transmitted per DMA cycle, but looks like it's actually the number of samples per half DMA cycle.

/**
  * @brief  Transmit an amount of data in non-blocking mode with DMA
  * @param  hi2s pointer to a I2S_HandleTypeDef structure that contains
  *         the configuration information for I2S module
  * @param  pData a 16-bit pointer to the Transmit data buffer.
  * @param  Size number of data sample to be sent:
  * @note   When a 16-bit data frame or a 16-bit data frame extended is selected during the I2S
  *         configuration phase, the Size parameter means the number of 16-bit data length
  *         in the transaction and when a 24-bit data frame or a 32-bit data frame is selected
  *         the Size parameter means the number of 16-bit data length.
  * @note   The I2S is kept enabled at the end of transaction to avoid the clock de-synchronization
  *         between Master and Slave(example: audio streaming).
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_I2S_Transmit_DMA(I2S_HandleTypeDef *hi2s, uint16_t *pData, uint16_t Size)

So I will need to verify when home, but I think this would fix the issue:

  HAL_I2S_Transmit_DMA(&hi2s3, (uint16_t*)&txBuf, AUDIO_BUFFER_16BIT_LENGTH>>1);
  HAL_I2S_Receive_DMA(&hi2s2, (uint16_t*)&rxBuf, AUDIO_BUFFER_16BIT_LENGTH>>1);

Also, somebody else in a different thread pointed out that my memory alignment settings might be wrong, and I think they're correct!

I tried to re-write the CubeMX-generated code to be flexible with me changing the data size settings (in i2s_audio_settings.h) but looks like I missed a spot (in stm32f7xx_hal_msp.c) (lines 47,48,107,108):

/**
* @brief I2S MSP Initialization
* This function configures the hardware resources used in this example
* @param hi2s: I2S handle pointer
* @retval None
*/
void HAL_I2S_MspInit(I2S_HandleTypeDef* hi2s)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hi2s->Instance==SPI2)
  {
  /* USER CODE BEGIN SPI2_MspInit 0 */
 
  /* USER CODE END SPI2_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_SPI2_CLK_ENABLE();
  
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    /**I2S2 GPIO Configuration    
    PB12     ------> I2S2_WS
    PB13     ------> I2S2_CK
    PB15     ------> I2S2_SD
    PC6     ------> I2S2_MCK 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_15;
    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);
 
    /* I2S2 DMA Init */
    /* SPI2_RX Init */
    hdma_spi2_rx.Instance = DMA1_Stream1;
    hdma_spi2_rx.Init.Channel = DMA_CHANNEL_9;
    hdma_spi2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_spi2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi2_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_spi2_rx.Init.Mode = DMA_CIRCULAR;
    hdma_spi2_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_spi2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_LINKDMA(hi2s,hdmarx,hdma_spi2_rx);
 
  /* USER CODE BEGIN SPI2_MspInit 1 */
 
  /* USER CODE END SPI2_MspInit 1 */
  }
  else if(hi2s->Instance==SPI3)
  {
  /* USER CODE BEGIN SPI3_MspInit 0 */
 
  /* USER CODE END SPI3_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_SPI3_CLK_ENABLE();
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    /**I2S3 GPIO Configuration    
    PA4     ------> I2S3_WS
    PB2     ------> I2S3_SD
    PC10     ------> I2S3_CK 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
 
    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
 
    /* I2S3 DMA Init */
    /* SPI3_TX Init */
    hdma_spi3_tx.Instance = DMA1_Stream5;
    hdma_spi3_tx.Init.Channel = DMA_CHANNEL_0;
    hdma_spi3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_spi3_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi3_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_spi3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_spi3_tx.Init.Mode = DMA_CIRCULAR;
    hdma_spi3_tx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_spi3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi3_tx) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_LINKDMA(hi2s,hdmatx,hdma_spi3_tx);
 
  /* USER CODE BEGIN SPI3_MspInit 1 */
 
  /* USER CODE END SPI3_MspInit 1 */
  }
 
}

I don't use Cube/HAL so can't comment, sorry.

Would you use native access to the mcu, things might be perhaps a bit clearer.

JW