Now we use the I2S transmitter and the AXI-Stream interface to output a media file from an SD card over a speaker.
In the last part of the I2S tutorial, the created I2S transmitter was equipped with an AXI stream interface in order to be able to connect it to the processing system. In this part of the tutorial we want to use this interface to read wave files from an SD card using the processing system and output the music using a CS4344 D/A converter via a connected speaker.
The following IP cores are required for the project:
The I2S transmitter with the AXI-Stream Interface
A Processing System to read the data from the SD card and write them into a FIFO
An AXI-Stream FIFO
A Clocking Wizard to generate the audio clock
The Clocking Wizard generates the clock, which is then used as the master clock for the CS4344. The output clock can be adapted to the sampling rate of the audio file via the AXI-Lite interface. The Clocking Wizard is initialized with a 12.288 MHz clock for 48 kHz audio signals.
The AXI-Stream FIFO serves as a link between the processing system and the I2S transmitter. The processing system writes data to the FIFO via the AXI-Lite (or AXI) interface and this then streams the data to the I2S transmitter.
A bitstream is created from the design and after that, the software can be developed.
The xilffs FAT library from Xilinx is required to read the SD card, which must be integrated into the Board Support Package of the Vitis project:
Xilinx's FAT library is based on Elm Chan's FAT library, which I have already used for my implementation of an SD card for the AVR.
In the first step, the software uses the AudioPlayer_Init function to initialize the audio player and thus the FIFO, the GIC, and the interrupt handler, as well as the clocking wizard and the SD card.
When the audio file has been loaded and the clocking wizard's output frequency has been adjusted, the first block of data is read from the wave file and copied to the FIFO:
As soon as the FIFO triggers a TFPE interrupt (Transmit FIFO Programmable Empty), the FIFO is filled with new data from the internal buffer. When the transfer from the processing system to the FIFO is complete, a TC interrupt (transmit complete) is triggered and the next block of data is read from the SD card. This will repeat until the file is completely played.
static void AudioPlayer_CopyBuffer(void)
{
u32 Bytes = 0x00;
for(u32 i = 0x00; i < 256; i += _File.Format.BlockAlign)
{
u32 Word = 0x00;
for(u8 Byte = 0x00; Byte < _File.Format.BlockAlign; Byte++)
{
Word |= _FifoBuffer[i + Byte];
Word <<= 0x08;
}
if(XLlFifo_iTxVacancy(&_Fifo))
{
XLlFifo_TxPutWord(&_Fifo, Word);
Bytes += sizeof(u32);
}
}
XLlFifo_iTxSetLen(&_Fifo, Bytes);
}
Now a wave file is needed. Simple test signals are available in the repository or can e.g. be generated at wavtones.com.
The respective file then only has to be copied to the SD card under the name Audio.wav and you're ready to go.