STM32 Audio Tutorial (Part 2): Codec I2C Setup

In this STM32 audio tutorial, we will take a look at how the audio codec is configured via I2C and initialized to be able to play back audio. Audio codecs differ from DACs – they need to be set up because of the complex audio routing mechanisms, mixers and I2S decoders they contain.

The STM32 must initialize the codec to prepare it for accepting I2S audio data. This primarily involves setting up the codec analog peripherals, setting up clocks and configuring the I2S format for audio data. Read on to find details on how you can do this with the PCB Artists ES8388 codec module.

Reading and Writing to Codec Registers

Reading and writing to the audio codec via I2C is an essential part of initializing the codec for audio playback. The I2C registers serve a variety of purposes such as:

  • Setting sampling rate for audio stream
  • Ensuring all bias circuits are configured properly
  • Setting audio routing and gain stages
  • Setting audio data communication format

Unlike many other codecs such as the SGTL5000, this codec does not require you to have any other clock inputs (MCLK or BCK) before using the I2C port. The I2C on ES8388 works even if all clock sources are missing.

Using HAL_I2C_Mem_Write (STM32Cube HAL API Function)

The HAL_I2C_Mem_Write() function greatly simplifies the task of writing to I2C slave device registers. Without this HAL function, you would need to write two bytes to the codec – the register address and the register data.
If you want to, you can use the regular “I2C write bytes” function as well. Note that it is not recommended that you use the I2C write-with-interrupt functions. If you do that, you may end up pipelining multiple commands and the codec may reject commands pipelined like that, especially at 400kHz (as some commands take time to execute).

In case of the ES8388 codec, the I2C registers can be written using the following format.

stm32 es8388 audio codec i2c register write
ES8388 Codec I2C Register Write Format

Writing to the I2C register map as needed is achieved within the es8388_reg_write() function.
The debug printf statements printed via SWO trace output and can help troubleshoot I2C issues without using debugger breakpoints too often.

The following code snippet is what you need for writing to ES8388 codec registers via I2C.

HAL_StatusTypeDef es8388_reg_write(uint8_t reg, uint8_t data)
	HAL_StatusTypeDef err;
	err = HAL_I2C_Mem_Write (es8388_i2c, ES8388_ADDR << 1, reg, I2C_MEMADD_SIZE_8BIT, &data, 1, 10);
	if (err == HAL_OK)
		printf ("es8388_reg_write: REG_0x%02x = 0x%02x\r\n", reg, data);
		printf ("es8388_reg_write: FAILED\r\n");
	return err;

Using HAL_I2C_Mem_Read (STM32Cube HAL API Function)

The HAL_I2C_Mem_Read() function simplifies reading of I2C slave device registers by eliminating the need for coding in an I2C bus restart condition after sending the target codec register address.

Reading from a slave device requires the I2C host to generate a bus restart condition after sending the register address. The register data content is then read out. The ES8388 register read sequence is shown below. This entire register read sequence is carried out by the HAL_I2C_Mem_Read API.

read i2c register stm32 hal_i2c_mem_read
Reading I2C Registers from Audio Codec

Again, some debug printf calls are placed within the I2C register read function to help with logging responses. The function to read ES8388 audio codec registers is shown below.

uint8_t es8388_reg_read(uint8_t reg, uint8_t *data)
	HAL_StatusTypeDef err;

	return HAL_I2C_Mem_Read (es8388_i2c, ES8388_ADDR << 1, reg, I2C_MEMADD_SIZE_8BIT, data, 1, 10);
	if (err == HAL_OK)
		printf ("es8388_reg_read: REG_0x%02x = 0x%02x\r\n", reg, *data);
		printf ("es8388_reg_read: FAILED\r\n");
	return err;

Testing the I2C link is simple. ES8388 codec does not provide a device ID or scratchpad register like many other codecs too. However, you can still write to registers such as ADC or DAC volume setting registers to check if the writes and reads are functioning properly.

Here is an example of checking whether writing to a register succeeded. If one write succeeds, it is safe to assume all subsequent I2C accesses will succeed as long as HAL_OK is returned by HAL_I2C_Mem_Write() or HAL_I2C_Mem_Read() functions.

es8388_reg_write (DAC_POWER, 0x3C);
// Random check
es8388_reg_read (DAC_POWER, &temp);
printf ("Read check: 0x%02x\r\n", temp);

You can now proceed to setting up the codec for audio playback. In this example, we will configure the codec as follows:

  • Power up all necessary internal bias and clock circuits
  • Enable DAC for audio output and link it to audio output pins
  • Set up MCLK to Fs (sample clock or LRCLK) ratio to 256
  • Set I2S data format to 16 bits per channel stereo and 48 kHz data rate

Setting up Audio Codec for Audio Playback

The process of setting up the ES8388 codec for audio playback via DAC is rather simple. Here is a flow chart on how to set up the codec for playback – assuming the codec was just powered up and all registers are at their defaults.

es8388 audio playback initialization sequence
Initialization Sequence for PCB Artists ES8388 Audio Codec Module

This setup sequence has been provided in the PCB Artists STM32 Audio Codec library for initializing the codec in audio playback mode. You can look at the full source code here.

Change Log
  • Initial Release: 29 September 2021

You may also like

Leave a Comment

eighteen − 5 =