Too much noise and distortion out of ADC fed with guitar signal

This is a sample of the DAC output through my audio interface (lower your volume):

https://drive.google.com/file/d/1fVYVZq81MRJWpyGOoc1MXEjnBV_QT7qb/view?usp=sharing

This has an EQ filter applied, the raw output is much much worse (only that background noise can be heard).

1745013611021.png
 
You have serious problems. You need decent I2S signals to drive the dac. Then maybe just send it a test tone or something, or maybe just silence at first. Keep it real simple to find out where the problem is. If your I2S signals are full of jitter and noise then no surprise if it sounds grossly unacceptable. Look at the datasheet for the dac. It should show you what the waveforms need to look like. If you can get the dac working, then try to add the ADC and see what that does.

Remember, you are dealing the real, serious RF. Edge rates (risetimes) can involve Fourier frequency components up into the hundred's of MHz. You need a proper 4-layer PCB with a ground plane. It may never work with solderless breadboards.

Maybe try studying the attached.
 

Attachments

Last edited:
You have serious problems. You need decent I2S signals to drive the dac. Then maybe just send it a test tone or something, or maybe just silence at first. Keep it real simple to find out where the problem is. If your I2S signals are full of jitter and noise then no surprise if it sounds grossly unacceptable. Look at the datasheet for the dac. It should show you what the waveforms need to look like. If you can get the dac working, then try to add the ADC and see what that does.

Remember, you are dealing the real, serious RF. Edge rates (risetimes) can involve Fourier frequency components up into the hundred's of MHz. You need a proper 4-layer PCB with a ground plane. It may never work with solderless breadboards.

Maybe try studying the attached.

Do you think a single/dual layer PCB will at least make it acceptable? I'll be etching a board myself.

These pictures look to me as signatures of data alignment mismatch. Group of samples that supposed to be below zero “jump” to higher codes. I am guessing that ine of the devices sends/receives left justified, another - i2s (msb shift)
I tested switching the FMT of the DAC and still no success. I guess the FMT matching is not the issue.
 
Do you think a single/dual layer PCB will at least make it acceptable?
You should get this to work without PCB provided you keep the I2S wiring short (< 10cm).

Make ADC the master. Use a common ground for all boards (MCU, ADC, DAC).
Feed MCK (12.288MHz) from PC4 to ADC with a short wire.
Feed SCK, BCK, LRCK and DIN from ADC to DAC with short wires.

If this does not work or improve ADC data output it is possible that your ADC board does not work. Making a PCB does not help then.
 
Thanks for the help guys, you have been really hepful.

A little update: I tested using a PCM3060 codec, and I got the same behavior. I guess that excludes faulty ADC/DAC and shows that there really is a problem with the MCK clock path. I'll post an update when I have the board ready.
 
Hi guys. I etched a board using now a PCM3060 audio codec, but I still have that same issue:

1746332816328.png

44.1kHz, 24 bits, STM32 master
1746333088927.png

1746333168498.png


Plus, I have no DAC output from the codec when trying to transmit a fixed value to the DIN pin. VoutL+ stays fixed at 2.4V (as the datasheet states - DAC output is centered around 0.5*VCC, and because of that, I was expecting to get a constant DC value above 2.4V):

C:
/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file           : main.c
 * @brief          : Main program body
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2025 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <math.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define SAMPLE_RATE      44100
#define BUFFER_SIZE      512
#define DAC_MAX          0x7FFFFF
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

I2S_HandleTypeDef hi2s2;
DMA_HandleTypeDef hdma_spi2_rx;
DMA_HandleTypeDef hdma_spi2_tx;

QSPI_HandleTypeDef hqspi;

/* USER CODE BEGIN PV */
int32_t buffer[BUFFER_SIZE];
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MPU_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_I2S2_Init(void);
static void MX_QUADSPI_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void generate_test_tone(void) {
    for (int i = 0; i < BUFFER_SIZE; i++) {
        // PCM3060 expects 24 bit data aligned to the left inside a 32 bit word
        buffer[i] = DAC_MAX << 8;
    }
}
/* USER CODE END 0 */

/**
 * @brief  The application entry point.
 * @retval int
 */
int main(void) {

    /* USER CODE BEGIN 1 */

    /* USER CODE END 1 */

    /* MPU Configuration--------------------------------------------------------*/
    MPU_Config();

    /* 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();
    MX_QUADSPI_Init();
    /* USER CODE BEGIN 2 */
    generate_test_tone();
    HAL_I2S_Transmit_DMA(&hi2s2, (uint16_t*) buffer, BUFFER_SIZE);
    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1) {
        /* USER CODE END WHILE */

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

/**
 * @brief System Clock Configuration
 * @retval None
 */
void SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
    RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };

    /** Supply configuration update enable
     */
    HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);

    /** Configure the main internal regulator output voltage
     */
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

    while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {
    }

    /** Initializes the RCC Oscillators according to the specified parameters
     * in the RCC_OscInitTypeDef structure.
     */
    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 = 5;
    RCC_OscInitStruct.PLL.PLLN = 160;
    RCC_OscInitStruct.PLL.PLLP = 2;
    RCC_OscInitStruct.PLL.PLLQ = 4;
    RCC_OscInitStruct.PLL.PLLR = 2;
    RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
    RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    RCC_OscInitStruct.PLL.PLLFRACN = 0;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
        Error_Handler();
    }

    /** Initializes the CPU, AHB and APB buses clocks
     */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
            | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1
            | RCC_CLOCKTYPE_D1PCLK1;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != 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_FULLDUPLEX;
    hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
    hi2s2.Init.DataFormat = I2S_DATAFORMAT_24B;
    hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
    hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_44K;
    hi2s2.Init.CPOL = I2S_CPOL_LOW;
    hi2s2.Init.FirstBit = I2S_FIRSTBIT_MSB;
    hi2s2.Init.WSInversion = I2S_WS_INVERSION_DISABLE;
    hi2s2.Init.Data24BitAlignment = I2S_DATA_24BIT_ALIGNMENT_LEFT;
    hi2s2.Init.MasterKeepIOState = I2S_MASTER_KEEP_IO_STATE_DISABLE;
    if (HAL_I2S_Init(&hi2s2) != HAL_OK) {
        Error_Handler();
    }
    /* USER CODE BEGIN I2S2_Init 2 */

    /* USER CODE END I2S2_Init 2 */

}

/**
 * @brief QUADSPI Initialization Function
 * @param None
 * @retval None
 */
static void MX_QUADSPI_Init(void) {

    /* USER CODE BEGIN QUADSPI_Init 0 */

    /* USER CODE END QUADSPI_Init 0 */

    /* USER CODE BEGIN QUADSPI_Init 1 */

    /* USER CODE END QUADSPI_Init 1 */
    /* QUADSPI parameter configuration*/
    hqspi.Instance = QUADSPI;
    hqspi.Init.ClockPrescaler = 255;
    hqspi.Init.FifoThreshold = 1;
    hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE;
    hqspi.Init.FlashSize = 1;
    hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE;
    hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
    hqspi.Init.FlashID = QSPI_FLASH_ID_1;
    hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
    if (HAL_QSPI_Init(&hqspi) != HAL_OK) {
        Error_Handler();
    }
    /* USER CODE BEGIN QUADSPI_Init 2 */

    /* USER CODE END QUADSPI_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_Stream0_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
    /* DMA1_Stream1_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);

}

/**
 * @brief GPIO Initialization Function
 * @param None
 * @retval None
 */
static void MX_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = { 0 };
    /* USER CODE BEGIN MX_GPIO_Init_1 */

    /* USER CODE END MX_GPIO_Init_1 */

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOE_CLK_ENABLE();
    __HAL_RCC_GPIOH_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    /*Configure GPIO pin Output Level */
    HAL_GPIO_WritePin(GPIOB, LED_RED_Pin | LED_GREEN_Pin, GPIO_PIN_RESET);

    /*Configure GPIO pin : FSWITCH_Pin */
    GPIO_InitStruct.Pin = FSWITCH_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(FSWITCH_GPIO_Port, &GPIO_InitStruct);

    /*Configure GPIO pins : LED_RED_Pin LED_GREEN_Pin */
    GPIO_InitStruct.Pin = LED_RED_Pin | LED_GREEN_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* EXTI interrupt init*/
    HAL_NVIC_SetPriority(FSWITCH_EXTI_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(FSWITCH_EXTI_IRQn);

    /* USER CODE BEGIN MX_GPIO_Init_2 */

    /* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/* MPU Configuration */

void MPU_Config(void) {
    MPU_Region_InitTypeDef MPU_InitStruct = { 0 };

    /* Disables the MPU */
    HAL_MPU_Disable();

    /** Initializes and configures the Region and the memory to be protected
     */
    MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    MPU_InitStruct.Number = MPU_REGION_NUMBER0;
    MPU_InitStruct.BaseAddress = 0x0;
    MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
    MPU_InitStruct.SubRegionDisable = 0x87;
    MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
    MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
    MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
    MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
    MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    /* Enables the MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

}

/**
 * @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 */
    __disable_irq();
    while (1) {
    }
    /* 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,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

1746333050906.png

1746333232612.png
1746333249243.png

1746333340815.png

1746333359165.png

1746333375175.png

1746333393232.png
1746333403551.png


I am only hearing the raw guitar signal.
 
Like bohrok2610.and eclipsevl, I think it is a data integrity issue. For some reason the sign bit is sometimes read as 0 when it should be 1, causing a jump of +223. If that happened all the time, it could be due to incompatible formats, but it appears to be sometimes right and sometimes wrong.

Depending on how fast the logic is and if your clock and data lines are longer than a few centimetres, it might be necessary to add series termination resistors near the sources of the clock and data signals to get rid of transmission line resonances. At least I had some really weird issues due to transmission line resonances when I was working on a perfboard-and-adapter-boards version of the digital part of my valve DAC, a couple of series resistors solved those. If the layout is such that the return currents of the digital signals have to take a large detour, it might be necessary to add a few ground jumpers to solve that.

Could you show the layout of the clock and data lines?

Regarding your schematic, I assume you put the DAC in a mode where it gets its word and bit clock from the ADC and that using the CODEC reset input is optional. Is that correct?

R23 appears to be about 20 times too small for an electric guitar input. R25 appears to be generating thermal noise without doing anything useful (I assume dithering is taken care of inside the CODEC chip).

There is no series resistor between U7A and the output. A resistor of 100 ohm or so might be needed for stability when you drive a long cable.

Is there power supply decoupling close to the microcontroller?
 
Last edited:
According to PCM3060 datasheet both clocks (SCK1/2, BCK1/2 and LRCK1/2) are required unless registers 67 & 72 are set in serial-control mode. So pins 4-6 should be connected to 9-11 in HW mode if only 1 clock is used.

Thanks! That fixed it. I got it working.

Like bohrok2610.and eclipsevl, I think it is a data integrity issue. For some reason the sign bit is sometimes read as 0 when it should be 1, causing a jump of +223. If that happened all the time, it could be due to incompatible formats, but it appears to be sometimes right and sometimes wrong.
I think it has something to do with the cheap logic analyzer (it's one of those Saleae clones), because once I got it working with the etched board, the codec output on my audio interface didn't sound distorted and noisy as the logic analyzer output shows.
R23 appears to be about 20 times too small for an electric guitar input. R25 appears to be generating thermal noise without doing anything useful (I assume dithering is taken care of inside the CODEC chip).
Thanks for all that insight! I was going for 47k on R23 to have a 33Hz cutoff frequency high-pass filter. Is it too wide?
 
The RC time constant is actually determined by C6 and the series connection of R19 and the parallel connection of R23 and the guitar. It's therefore just over 100 ms, giving a cut-off frequency just below 1.6 Hz.

The reasons why I would recommend a much larger value for R23 are that it also forms a low-pass filter with the guitar inductance and that it injects thermal noise current. The low-pass has a cut-off frequency close to R/(2πL), where R is the parallel value of R19 and R23 and L is the inductance of the guitar. With, for example, L = 4 H, R needs to be at least 500 kohm to keep that at or above 20 kHz. You just meet that when R19 and R23 are both 1 Mohm resistors.
 
A very long time ago when i could hear 18kHz i tested lowpass filter before the power amp on an electric guitar.
Whith no lowpass i could hear some rests from white noise in the high frequency area. I tested where i could set a 12 db lowpass without affecting the guitar sound. I tested a few different sound types like typical jazz, rock or treble booster. Of course the most noise was with the treble booster but i heard no difference in the guitar sound with or without 10 kHz lowpass. At 8 kHz the difference was very low only to hear by switching on/off several times while playing the guitar. But the difference in the noise was clear to hear.
I used mostly an Altec Lansing 15" with metal dome. But the result was similar whith a Philips 12 inch double cone.
After that i build 10 kHz lowpass in the guitar amps. On the input i have 1M and 330kohm but i believe the guitar cable affects the high frequency more.
Here you can read about the capacitance in guitar cables: https://www.effectrode.com/knowledge-base/guitar-cable-specially-tuned-for-stratocasters/