cancel
Showing results for 
Search instead for 
Did you mean: 

Problem on I2S output using HAL

KAJIK.1
Associate III

Hi,

​

I wrote a simplest program (below) to send sine wave data to PCM5102A (DAC) via I2S.

​However, I found only L-ch data is valid and R-ch has no data.

Analogue (DAC) output of L-ch is somewhat sine wave, but it is sometimes de-formed. And if I look it closer, I found some noise on the signal. (below captured screen shot)

I use HAL and STM32F401. And I2S settings are half-duplex master transmit (only).

Kindly enlighten me what is wrong with my program.

int main(void) {
	HAL_Init();
	SystemClock_Config();
 
	MX_GPIO_Init();
	MX_DMA_Init();
	MX_I2S2_Init();
	HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET); // LED Off
 
	int16_t Wave_Data[n_data * 2];
 
	for (int i = 0; i < n_data; i++) {
		Wave_Data[i * 2]     = (int16_t) (sin(2. * M_PI *  8. * i / 1000.) * 50) ; // L-ch
		Wave_Data[i * 2 + 1] = (int16_t) (sin(2. * M_PI * 10. * i / 1000.) * 50) ; // R-ch
	}
	while (1) {
		if (Wave_buf_empty == 1) {
			while ((&hi2s2)->State != HAL_I2S_STATE_READY)
				;
			if (HAL_I2S_Transmit_DMA(&hi2s2, (uint16_t*) Wave_Data,
					(uint16_t) n_data * 2) != HAL_OK) {
				Error_Handler();
			}
			Wave_buf_empty = 0 ;
		}
	}
}
 
//////////// Call Back ///////////////
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) {
	Wave_buf_empty = 1;
	HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);  // check if DMA is working
}

Screen capture of USB scope.

0693W000000XN2BQAW.png

​

​

1 ACCEPTED SOLUTION

Accepted Solutions

> I use HAL with STM32CubeIDE(CubeMX), and all settings/initialization have done by the generated code.

> Therefore, I do not need to manipulate Registers (I suppose).

But you say it does not work. The microcontroller does not care about the code generator or source code, it works out of the registers.

You can get the I2S registers content by dereferencing the hi2s2's Instance, similarly you go to the hdmatx and dereference Instance there. So read them out and post them.

JW

View solution in original post

5 REPLIES 5

Read out and check/post the content of DMA and SPI/I2S registers. Check/post also content of the array you are sending, and check if the address in DMA_SxM0AR of the respective DMA stream indeed points to that array.

JW

KAJIK.1
Associate III

JW,

Thank you for your immediate reply.

I use HAL with STM32CubeIDE(CubeMX), and all settings/initialization have done by the generated code.

Therefore, I do not need to manipulate Registers (I suppose).

As for the DMA stream, it is very simple sine waves. (See attached Excel graph. Data is acquired by printf().)

Debgger shows Wave_Data array and I2S_HandelTypeDef hi2s2 as below. (Same value as above and address is correct.)

 Name : Wave_Data

 Details:{0, 0, 2, 3, 5, 6, 7, 9, 9, 12, 12, 15, 14, 18, 17, 21, 19, 24, 21, 26, 24, 29, 26, 31, 28, 34, 30, 36, 32, 38, 34, 40, 36, 42, 37, 43, 39, 45, 40, 46, 42, 47, 43, 48, 44, 3...}

 Default:0x2000f04c

Name : hi2s2

 Details:{Instance = 0x40003800, Init = {Mode = 512, Standard = 0, DataFormat = 0, MCLKOutput = 0, AudioFreq = 8000, CPOL = 0, ClockSource = 0, FullDuplexMode = 0}, pTxBuffPtr = 0x2000f04c, TxXferSize = 2000, TxXferCount = 2000, pRxBuffPtr = 0x0, RxXferSize = 0, RxXferCount = 0, IrqHandlerISR = 0x8002aa9 <I2S_IRQHandler>, hdmatx = 0x20002240 <hdma_spi2_tx>, hdmarx = 0x0, Lock = HAL_UNLOCKED, State = HAL_I2S_STATE_BUSY_TX, ErrorCode = 0}

If someone kindly upload simple working code of I2S output using STM32CubeIDE/HAL, it will be great help for me or a newbie.

Thanks,

> I use HAL with STM32CubeIDE(CubeMX), and all settings/initialization have done by the generated code.

> Therefore, I do not need to manipulate Registers (I suppose).

But you say it does not work. The microcontroller does not care about the code generator or source code, it works out of the registers.

You can get the I2S registers content by dereferencing the hi2s2's Instance, similarly you go to the hdmatx and dereference Instance there. So read them out and post them.

JW

KAJIK.1
Associate III

Dear JW and ALL,

Worked!!! :grinning_face: :grinning_face: :smiling_face_with_smiling_eyes:

I fount my code works well when I built the project from scratch with STM32CubeIDE.

Code itself is completely identical, however, there must have some glitch when I was working around the code for a week or two.

To share the result with the community, I put entire code and settings screen shots below. (How easy to use I2S with CubeMX!!)

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
 
/* Includes ------------------------------------------------------------------*/
#include "main.h"
 
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
#include <math.h>
 
#define n_data 1000
 
/* USER CODE END Includes */
 
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
 
/* USER CODE END PTD */
 
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
 
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
 
/* USER CODE END PM */
 
/* Private variables ---------------------------------------------------------*/
I2S_HandleTypeDef hi2s2;
DMA_HandleTypeDef hdma_spi2_tx;
 
/* USER CODE BEGIN PV */
 
uint8_t Wave_buf_empty = 1 ;
 
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_I2S2_Init(void);
/* USER CODE BEGIN PFP */
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s);
/* USER CODE END PFP */
 
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
 
/* USER CODE END 0 */
 
/**
  * @brief  The application entry point.
  * @retval int
  */
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 */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2S2_Init();
  /* USER CODE BEGIN 2 */
 
  // With 48kHz sampling rate, L-ch will be 384Hz and R-ch will be 480Hz
 
	int16_t Wave_Data[n_data * 2];    // n_data is defined as 1000
 
	for (int i = 0; i < n_data; i++) {
		Wave_Data[i * 2] = (int16_t) (sin(2. * M_PI * 8. * i / 1000.) * 500); // L-ch (x 500 is amplitude)
		Wave_Data[i * 2 + 1] =
				(int16_t) (sin(2. * M_PI * 10. * i / 1000.) * 500); // R-ch
		printf("%d, %d\n", Wave_Data[i * 2], Wave_Data[i * 2 + 1]);
		HAL_Delay(1);
	}
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	while (1) {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
		if (Wave_buf_empty == 1) {
			while ((&hi2s2)->State != HAL_I2S_STATE_READY)
				;
			if (HAL_I2S_Transmit_DMA(&hi2s2, (uint16_t*) Wave_Data,
					(uint16_t) n_data * 2) != HAL_OK) {
				Error_Handler();
			}
			Wave_buf_empty = 0;
		}
 
	}
  /* USER CODE END 3 */
}
 
/**
  * @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 = 25;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  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_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
  PeriphClkInitStruct.PLLI2S.PLLI2SN = 192;
  PeriphClkInitStruct.PLLI2S.PLLI2SR = 2;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
}
 
/**
  * @brief I2S2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2S2_Init(void)
{
 
  /* USER CODE BEGIN I2S2_Init 0 */
 
  /* USER CODE END I2S2_Init 0 */
 
  /* USER CODE BEGIN I2S2_Init 1 */
 
  /* USER CODE END I2S2_Init 1 */
  hi2s2.Instance = SPI2;
  hi2s2.Init.Mode = I2S_MODE_MASTER_TX;
  hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
  hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
  hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;
  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();
  }
  /* USER CODE BEGIN I2S2_Init 2 */
 
  /* USER CODE END I2S2_Init 2 */
 
}
 
/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void) 
{
 
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA1_Stream4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
 
}
 
/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
 
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
 
}
 
/* USER CODE BEGIN 4 */
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) {
	Wave_buf_empty = 1 ;
}
 
/// enable "printf()" through SWO signal line ///
 
int _write(int file, char *ptr, int len)
{
  int DataIdx;
  for(DataIdx=0; DataIdx<len; DataIdx++)
  {
    ITM_SendChar(*ptr++);
  }
  return len;
}
 
/* USER CODE END 4 */
 
/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
 
  /* USER CODE END Error_Handler_Debug */
}
 
#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
 
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
 

Clock configuration0693W000001cKVsQAM.png

I2S configuration0693W000001cKW2QAM.png

DMA configuration0693W000001cKWCQA2.png

Output0693W000001cKZzQAM.png

KAJIK.1
Associate III

Dear JW,

I woud like to say thank you to JW.

I learnd how I can read the registers with Debugger. (Without your uncompromizing "see register" suggestion, I would not have learned it.)

FYI, below is the Registers related with I2S/DMA I got.

Many thanks,

hi2s2	I2S_HandleTypeDef	{...} (Hex)	
	Instance	SPI_TypeDef *	0x40003800 (Hex)	
		CR1	volatile uint32_t	0	
		CR2	volatile uint32_t	0	
		SR	volatile uint32_t	2	
		DR	volatile uint32_t	0x0 (Hex)	
		CRCPR	volatile uint32_t	0x7 (Hex)	
		RXCRCR	volatile uint32_t	0	
		TXCRCR	volatile uint32_t	0	
		I2SCFGR	volatile uint32_t	0xa00 (Hex)	
		I2SPR	volatile uint32_t	0x11f (Hex)	
	Init	I2S_InitTypeDef	{...} (Hex)	
		Mode	uint32_t	512	
		Standard	uint32_t	0	
		DataFormat	uint32_t	0	
		MCLKOutput	uint32_t	0	
		AudioFreq	uint32_t	48000	
		CPOL	uint32_t	0	
		ClockSource	uint32_t	0	
		FullDuplexMode	uint32_t	0	
	pTxBuffPtr	uint16_t *	0x2000f04c	
	TxXferSize	volatile uint16_t	2000	
	TxXferCount	volatile uint16_t	2000	
	pRxBuffPtr	uint16_t *	0x0	
	RxXferSize	volatile uint16_t	0	
	RxXferCount	volatile uint16_t	0	
	IrqHandlerISR	void (*)(struct __I2S_HandleTypeDef *)	0x80028bd <I2S_IRQHandler>	
	hdmatx	DMA_HandleTypeDef *	0x2000009c <hdma_spi2_tx>	
		Instance	DMA_Stream_TypeDef *	0x40026070	
			CR	volatile uint32_t	0x12c5f (Hex)	
			NDTR	volatile uint32_t	2000	
			PAR	volatile uint32_t	0x4000380c (Hex)	
			M0AR	volatile uint32_t	0x2000f04c (Hex)	
			M1AR	volatile uint32_t	0	
			FCR	volatile uint32_t	0x0 (Hex)	
		Init	DMA_InitTypeDef	{...}	
			Channel	uint32_t	0	
			Direction	uint32_t	64	
			PeriphInc	uint32_t	0	
			MemInc	uint32_t	1024	
			PeriphDataAlignment	uint32_t	2048	
			MemDataAlignment	uint32_t	8192	
			Mode	uint32_t	0	
			Priority	uint32_t	65536	
			FIFOMode	uint32_t	0	
			FIFOThreshold	uint32_t	0	
			MemBurst	uint32_t	0	
			PeriphBurst	uint32_t	0	
		Lock	HAL_LockTypeDef	HAL_LOCKED	
		State	volatile HAL_DMA_StateTypeDef	HAL_DMA_STATE_BUSY	
		Parent	void *	0x200000fc <hi2s2>	
		XferCpltCallback	void (*)(struct __DMA_HandleTypeDef *)	0x8002759 <I2S_DMATxCplt>	
		XferHalfCpltCallback	void (*)(struct __DMA_HandleTypeDef *)	0x800279b <I2S_DMATxHalfCplt>	
		XferM1CpltCallback	void (*)(struct __DMA_HandleTypeDef *)	0x0	
		XferM1HalfCpltCallback	void (*)(struct __DMA_HandleTypeDef *)	0x0	
		XferErrorCallback	void (*)(struct __DMA_HandleTypeDef *)	0x80027b7 <I2S_DMAError>	
		XferAbortCallback	void (*)(struct __DMA_HandleTypeDef *)	0x0	
		ErrorCode	volatile uint32_t	0	
		StreamBaseAddress	uint32_t	1073897476	
		StreamIndex	uint32_t	0	
	hdmarx	DMA_HandleTypeDef *	0x0	
	Lock	volatile HAL_LockTypeDef	HAL_LOCKED	
	State	volatile HAL_I2S_StateTypeDef	HAL_I2S_STATE_BUSY_TX	
	ErrorCode	volatile uint32_t	0