cancel
Showing results for 
Search instead for 
Did you mean: 

I2S not working on 32-bit data with DMA

enpassant
Visitor

Hi,

I am using a STM32F407 with the TLV320AIC3204 audio codec. I have verified that the I2S works with a logic analyzer:

image (12).png
I am trying to read the data using DMA (I tried both circular and normal).

Here is my I2S configuration:

enpassant_0-1725576629077.png

 

enpassant_1-1725576649643.png


I made sure to add the following line to fix the DMA bug:
__HAL_RCC_DMA1_CLK_ENABLE();

 

Here is the code that is giving me problems:

 

 

#include "dsp.h"

#define BUFSIZE 8192
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

static volatile int64_t sin_a;
static volatile int64_t cos_a;
static volatile int64_t sin_b;
static volatile int64_t cos_b;

uint16_t adcBuf[BUFSIZE];

static volatile int done;
static int bufLen;

static int32_t combineUint(uint16_t a, uint16_t b){
	return (int32_t)(((uint32_t)a << 16))| (uint32_t)b ;
}

static int processArray(uint16_t* arr, int len){
	for (int i = 0 ; i < len; i+= 16){
		cos_a += combineUint(arr[i], arr[i+1]);
		cos_b += combineUint(arr[i+2], arr[i+3]);
		sin_a += combineUint(arr[i+4], arr[i+5]);
		sin_b += combineUint(arr[i+6], arr[i+7]);
		cos_a -= combineUint(arr[i+8], arr[i+9]);
		cos_b -= combineUint(arr[i+10], arr[i+11]);
		sin_a -= combineUint(arr[i+12], arr[i+13]);
		sin_b -= combineUint(arr[i+14], arr[i+15]);
	}
}

void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s2){
	if (bufLen >= 128){
		//processArray(adcBuf, bufLen );
	}
}

void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s2){
	if (bufLen >= 128){
	//	processArray(adcBuf+bufLen , bufLen );
	} else {
	//	processArray(adcBuf, bufLen * 2);
	}
	done = 1;

}


int dspDone(){
	return done;
}

void startDSP(int avg){
	bufLen = avg * 8;
	if (bufLen * 2 > BUFSIZE ) return;
	sin_a = 0;
	sin_b = 0;
	cos_a = 0;
	cos_b = 0;
	done = 0;
//	while (HAL_GPIO_ReadPin (GPIOB, GPIO_PIN_12));
//	while (!HAL_GPIO_ReadPin (GPIOB, GPIO_PIN_12)); // Wait for WS = LOW

	HAL_I2S_Receive_DMA(&hi2s2, adcBuf, bufLen);
}

static float magnitude (int64_t a, int64_t b){
	float c = a;
	float d = b;
	return sqrtf(c*c+d*d);
}


void getResults(float *mag, float *ang){
	*mag = magnitude(sin_a, cos_a)/magnitude(sin_b, cos_b);
	*ang = atan2(sin_a, cos_a) - atan2(sin_b , cos_b);
}

 

 


With lines 63 and 64 commented, I get random data sometimes (see picture below where I breakpointed on line 78, which is called after done = 1. You can see that the data is bit shifted by a random amount. There is nothing connected to the ADC, so the MSB should be close to 0xFFFF or 0x0000. However, these values are not.

enpassant_3-1725577153988.png

I was able to fix the problem somewhat by uncommenting lines 63 and 64, which only allows the I2S to start when WS is low. (Trying it when WS=high gave me garbage every time). The issue now is that I am seeing the data offset by 16 bits sometimes.

 

For example, here, the MSB are in the even positions (all of the numbers in the even positions don't change very much, and are close to 0xFFFF):

enpassant_4-1725577280675.png

However, if I wait until the breakpoint is triggered again, I get 

enpassant_5-1725577353982.png

 

Now the MSB is in the odd positions. Any idea why this is happening? With lines 63 and 64 uncommented, on most runs the MSB is in the even positions the first time, and every time after that the MSB is in the odd positions. 

 

Here is my main function:

 

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
  __HAL_RCC_DMA1_CLK_ENABLE();
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2S2_Init();
  MX_DAC_Init();
  MX_I2C1_Init();
  MX_SPI3_Init();
  MX_USART1_UART_Init();
  MX_ADC1_Init();
  MX_DMA_Init();
  MX_USB_DEVICE_Init();
  MX_ADC2_Init();
  /* USER CODE BEGIN 2 */
  resetRegisters();
  HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
  HAL_DAC_Start(&hdac, DAC_CHANNEL_2);
  setVoltage(-1.5, 1);
  setVoltage(-1.5, 2);
  HAL_Delay(100);
  tlv320aic3204_init();
  resetLMX();
  int counter = 10;
  float voltage = -2;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if (counter > 226){
		  counter = 10;
	  }
	  if (voltage > -1.45){
		  voltage = -2;
	  }
	  setFrequency(counter * 100 * MHz, 2);
	  HAL_Delay(20);
	  counter++;
	  voltage += 0.1;
	  setVoltage(voltage, 2);
	  setVoltage(voltage, 1);
	  startDSP(16);
	  while (!dspDone());
	  float m;
	  float a;
	  getResults(&m, &a);


    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 

 

 

3 REPLIES 3

For the need to wait for a certain WS level, see errata.

And use circular DMA.

JW

I tried that by using this:

 

//	while (HAL_GPIO_ReadPin (GPIOB, GPIO_PIN_12));
//	while (!HAL_GPIO_ReadPin (GPIOB, GPIO_PIN_12)); // Wait for WS = LOW

 

Is there a smarter way of doing it? Right now I'm just waiting for the WS pin to change from high to low, and then I start I2S. I also still have issues where sometimes the data is shifted by 16 bits. Usually, the first time the breakpoint is triggered the data is correct, but on the next breakpoint trigger, there is a 16 bit offset with the data.

 

I also tried using circular DMA. However, if I stop the DMA and start it again, the problem comes back. I need to stop the DMA because I am trying to build a crude network analyzer, and the data is only valid after the frequency of the oscillators settles. My plan was to stop the DMA after the amount of data needed is obtained, and then start it again once the frequency of the oscillators changes to a new one. Basically I need to change the frequency of another component, wait a bit, start DMA to collect some points, stop DMA after the points are collected, and then repeat with other frequencies.

If you want WS to be low, shouldn't your statements be reversed?

// WS is high or low here
while (!HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12)); // wait while WS is low
// WS is high here, but could be mid-transaction
while (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12)); // wait while WS is high
// WS just became low

 

> MX_DMA_Init();

This statement should be before the I2S gets initialized. Is this an old project? This was a bug that was fixed a few years ago. See:

Solved: MX_DMA_Init order in the main.c file generated by ... - STMicroelectronics Community

 

> There is nothing connected to the ADC, so the MSB should be close to 0xFFFF or 0x0000

I wouldn't ascribe any significance to the ADC result if nothing is connected to the input.

If you feel a post has answered your question, please click "Accept as Solution".