Working with STM32 and I2S Part 3: Sending Data using DMA

In the previous guide (here), we took a look how to configure the I2S and transmit data using poling mode. In this guide, we shall use DMA to transfer data over I2S bus.

In this guide, we shall cover the following:

  • I2S Configuration.
  • DMA Configuration.
  • Sending Data over DMA.
  • Interrupt handler.
  • Main code.
  • Results.

12. I2S Configuration:

Within I2S_init function in I2S.c source file, before enabling the I2S, enable TX using DMA as following:

	/*Enable SPI_TX_DMA*/
	SPI3->CR2|=SPI_CR2_TXDMAEN;

Enable SPI3 interrupt in NVIC (will need it later):

	/*Enable SPI3 interrupt in NVIC*/
	NVIC_EnableIRQ(SPI3_IRQn);

13. DMA Configuration:

After enabling the interrupt for SPI3, we shall configure the DMA, first we need to know which DMA and Stream is related to SPI3_TX, from the DMA1 request mapping, we can find the following:

We have the following:

  • DMA1 is needed.
  • Either Stream5 or Stream7 can be used.
  • Channel 0.

Hence, we start by enable clock access to DMA1 as following:

From the block diagram, we can find that DMA1 is connected to AHB1 bus.

Hence, we can enable the clock access to DMA1 as following:

	/*Enable Clock Access to DMA1*/
	RCC->AHB1ENR|=RCC_AHB1ENR_DMA1EN;

Configure the DMA with the following parameters:

  • Channel is channel 0.
  • Memory increment mode.
  • Direction is Memory to peripheral.
  • Enable transfer complete interrupt.

	DMA1_Stream7->CR|=(0x00<<DMA_SxCR_CHSEL_Pos)|DMA_SxCR_MINC|DMA_SxCR_DIR_0|DMA_SxCR_TCIE;

Enable DMA1_Stream7 in the NVIC:

	/*Enable DMA1_Steam5 Interrupt in nVIC*/
	NVIC_EnableIRQ(DMA1_Stream7_IRQn);

14. Sending Data using DMA:

We start by declaring the following function:

void I2S_SendData_DMA(uint16_t *data, uint16_t len)

The function takes two parameters:

  • Point to the buffer that contains the data to be send.
  • Length of the data to be transmitted.

Within the function:

Set the peripheral address of the DMA to be SPI3->DR as following:

	/*Set Peripheral address to SPI3->DR*/
	DMA1_Stream7->PAR=(uint32_t)&SPI3->DR;

Set Memory address to be the buffer passed by the user:

	/*Set the memory address*/
	DMA1_Stream7->M0AR=(uint32_t)data;

Set number of transfer to be the length passed by the user:

	/*Set the length*/
	DMA1_Stream7->NDTR=len;

Launch the DMA:

	/*Launch the DMA*/
	DMA1_Stream7->CR|=DMA_SxCR_EN;

15. Interrupt Handler :

For the interrupt handler of the DMA:

Declare this function:

void DMA1_Stream7_IRQHandler(void)

Within the function:

Check if the source of the interrupt is transfer complete:

	/*Check the Intrrupt source of DMA1_Stream7*/
	if((DMA1->HISR & DMA_HISR_TCIF7) ==DMA_HISR_TCIF7 )

Within the if condition, enable SPI_TX buffer is empty interrupt:

		/*Enable SPI Tx buffer empty interrupt*/
		SPI3->CR2|=SPI_CR2_TXEIE;

Clear the pending flag of the DMA:

		/*Clear Pending flag*/
		DMA1->HIFCR=DMA_HIFCR_CTCIF7;

Declare the following function:

__WEAK void SPI3_DMA_TX_Completed(void)
{

}

This function shall be overwritten by the user in his application.

For the SPI interrupt handler:

void SPI3_IRQHandler(void)

Check if TX buffer is empty and it is not busy:

if ((SPI3->SR & SPI_SR_TXE) && ((SPI3->SR & SPI_SR_BSY)==0))

Call the weak function:

		/*Call SPi3_DMA_TX_Completed function*/
		SPI3_DMA_TX_Completed();

Disable TX buffer interrupt empty:

		/*Disable SPI Tx Buffer empty interrupt*/
		SPI3->CR2&=~SPI_CR2_TXEIE;

Finally, add the following function in the header file:

void I2S_SendData_DMA(uint16_t *data, uint16_t len);

16. Main Code:

In main.c file:

#include "delay.h"
#include "stdlib.h"
#include "i2s.h"

#define dataSize 10
uint16_t data[dataSize] = {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888, 0x9999, 0xAAAA};

volatile uint8_t I2S_Finished=0;


int main(void)
{
	delay_init(168000000);
	I2S3_Pins_Init();
	I2S3_Init();


	while(1)
	{
		I2S_SendData_DMA(data, dataSize);
		while(I2S_Finished==0);
		I2S_Finished=0;
		delay(10);
	}


}

void SPI3_DMA_TX_Completed(void)
{
	I2S_Finished=1;

}

17. Results:

By connecting logic analyzer to the clock, data and word select, you should get the following:

Since have successfully transmitted data over I2S bus.

Next, we shall configure CS43L22 to generate some audio.

Add Comment

Your email address will not be published. Required fields are marked *