Bukit Mertajam, Penang

I2S for PIC32MX/MZ – Introduction

Skill level: Advanced, with C and 32-bit experience. SMD soldering experience is a must too!

This tutorial requires an oscilloscope to view the output audio signals!

What do all digital audio players, smart devices and sound cards have in common?

They all have an Audio DAC inside. Digital to Analog Converters (DAC) converts digital signals to analog signals. In this tutorial, we will use an audio DAC to convert the digital signals of a microcontroller/processor to audible audio signals.

In case if you are wondering, why are we using an Audio DAC and not a regular DAC? Even though an ordinary DAC or a microcontroller’s PWM (Pulse Width Modulation) can generate audio signals, they lack quality and clarity.

Furthermore, Audio DACs are specialized DACs which contain extra hardware to ensure that the resulting audio signals are clear, with very little noise. Audiophiles purchase very expensive external audio DACs for their computers or Hi-Fi sets just so they can listen to crystal clear sound.

There is a wide range of DACs that we can choose from. Higher end DACs could contain small a DSP (Digital Signal Processor) to process the output digital audio or have customizable DAC settings. Some DACs even include a ADC (Analog to Digital converter) for converting analog signals from a microphone to digital signals that can be processed by a microcontroller/processor.

In this tutorial, we focus on basic Audio DACs, for simplicity. The Audio DAC is connected to a microcontroller by I2S (Inter-IC Sound) bus. Note that not all DACs have a I2S interface – this will be explained in the other tutorial!

I2S Protocol

The I2S bus is a type of serial connection designed by Philips/NXP. According to the “I2S Specification” from the company, the bus has three major lines:

  • Continuous Serial Clock (SCK)
  • Word Select (WS)
  • Serial Data (SD)

The generation of the clock (SCK) and the word select (WS) are by the Master, which is the microcontroller.

Here is the diagram for the basic I2S transmission, from the Microchip PIC32MX Reference Manual. Note that the SCK in the microcontroller is called BCLK (bit clock), and the WS is called LRCK (left-right clock). Different microcontrollers have different labels for the SCK, WS and SD but in the end they have the same meanings respectively.

Since audio DAC mainly operates in stereo mode, there is a left channel and a right channel. When the left audio data is being pushed into the DAC, the WS/LRCK is low. When the right audio data is being pushed into the DAC, the WS/LRCK is high.

If the audio data is 16-bit, 16 clock pulses are sent out together with the audio data per channel. And if the audio data is 32-bit, 32 clock pulses are sent out together with the audio data per channel.

You also notice that in this microcontroller, the I2S line is shared with the SPI line. In order to switch to I2S mode, you need to change the SPI settings during initialization. Here’s how to do this with the help of the PIC32 SPI Reference Manual:

SPI1CON2 = 0x00000080; // I2S Mode, AUDEN = 1, AUDMON = 0
SPI1CON2bits.IGNROV = 1; // Ignore Receive Overflow bit (for Audio Data Transmissions)
SPI1CON2bits.IGNTUR = 1; //  Ignore Transmit Underrun bit (for Audio Data Transmissions) 1 = A TUR is not a critical error and zeros are transmitted until thSPIxTXB is not empty 0 = A TUR is a critical error which stop SPI operation
SPI1BRG = 38;
SPI1CON = 0x00000060; // Master mode, SPI ON, CKP = 1, 16-bit audio channel
SPI1CONbits.STXISEL = 0b11;
SPI1CONbits.DISSDI = 1; // 0 = Disable SDI bit
SPI1CONSET = 0x00008000;

In this example, we are using SPI1. This code can be used for PIC32MX/MZ, but for configuring the interrupts, you have to refer to the PIC32MX/MZ datasheet.

How about the SPI1BRG? This register determines the bit rate for the I2S transmission. Say that we want to configure this transmission at 8kHz audio sample rate, at 16-bit data per channel, with a 20MHz peripheral bus.

  • FPB = 20MHz
  • Sampling rate: 8kHz
  • Data: 16-bit (signed)

Using the following equation:

The baud rate is calculated as: 8000 * 32 (The 32 is because in this example, combining the left and right data to form one audio frame gives you total of 32 bits) Calculating the SPI1BRG, we get 38.0625 which is rounded to 38.

After all the calculation, we start the I2S module:

SPI1CONSET = 0x00008000;

Sending audio data into the DAC

Of course, you need to put audio data into the SPI transmit buffer, or else there is no output at all! However, you must continuously send the audio data into the buffer every time the transmit is complete or else you would not be hearing the correct sound output. Using interrupts, we allow the program to jump into the interrupt vector once the SPI module is done transferring the data.

void __ISR(_SPI1_VECTOR, ipl7AUTO) _IntHandlerDrvI2SInstance0(void)
    if(channel) {
      SPI1BUF = data_left;
    else {
      SPI1BUF = data_right;
    channel ^= 0x01;       
    IFS1bits.SPI1TXIF = 0; // Clear the SPI1 interrupt.

In the example code, when the interrupt is up, it loads a value into the SPI1BUF. A transmission is only successful once the value of both the left channel and right channel has been transmitted. Therefore, the ‘channel’ variable is toggled every time the interrupt is triggered.

Also, the data for I2S in this tutorial are signed 16-bit, so the buffer can only take values between -32767 and 32767.

Connecting the PIC32 to the Audio DAC

Here is the general block diagram of how you should connect the PIC32 to the Audio DAC. If your Audio DACs has an ADC inside, the converted ADC data is fed back into the SDIx. But since in this tutorial we are only focusing on the audio output, the SDIx is not used.

We are using an NXP UDA1334ATS Audio DAC in this tutorial. You are free to use other audio DAC, but make sure you need to configure the Audio DAC into I2S mode first. Unfortunately, the NXP UDA1334ATS DAC does not come in the form of DIP package, therefore you need a SMD breakout board. Information of how to configure the DAC is on the datasheet:

In addition, you should:

1.) Ground both SFOR0 and SFOR1 inputs to select this DAC to I2S mode.

2.) Ground DEEM/CLKOUT.

3.) Ground MUTE.

4.) Connect the digital and analog supply voltage to +3.3V.

5.) Ground SYSCLK/PLL1.

When all are done (on the breadboard),  do not run or compile code yet! Reserve this “platform” for some cool sound and music guides in Cytron tutorial!


1.) http://chipkit.net/forum/viewtopic.php?f=6&t=3137&start=10

2.) UDA1334ATS datasheet: www.nxp.com/docs/en/data-sheet/UDA1334ATS.pdf

3.) PIC32 SPI Reference Manual: ww1.microchip.com/downloads/en/DeviceDoc/61106G.pdf



Related Posts

Leave a comment