2020-05-13 06:09 AM
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.
Solved! Go to Solution.
2020-05-13 12:28 PM
> 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
2020-05-13 09:49 AM
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
2020-05-13 12:21 PM
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,
2020-05-13 12:28 PM
> 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
2020-05-15 03:51 AM
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>© 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 configuration
I2S configuration
DMA configuration
Output
2020-05-15 03:53 AM
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