I2S driver tutorial with MPLAB Harmony

A tutorial on how to use I2S driver library in MPLAB Harmony

I2S stands for Inter IC Sound and it’s a standardized protocol to transfer audio data between two or more IC(Integrated Circuit). If you want to create embedded system with audio application then you might want to have to use I2S. This is because most of the audio DAC and other audio IC now requires I2S interface. Then if you want to develop with microcontroller then one of the choice you have is to use PIC32 32 bit microcontrollers from Microchip. And to use the I2S of the PIC32 you can use the MPLAB Harmony framework. The MPLAB harmony framework is based on drivers, middlewares and direct peripheral access. I2S driver is just one of them.

To start using the I2S driver you should learn about the operation and functionality of I2S driver. This includes interrupt and DMA modes, various clock settings(BCLK, MCLK, LRCK), sampling rate audio protocol modes(I2S Audio, Left and Right Adjusted or PCM/DSP mode)etc.Then you should learn how to create a MPLAB Harmony project and how to set the I2S driver using MHC. The PIC32MX MPLAB Harmony Tutorial blog post guides you how to create a MPLAB Harmony project.

Here we will give you the basic idea of using I2S driver. As explained in the earlier tutorials like MPLAB Harmony System and Client Driver Handle and How to configure CODEC driver using MPLAB Harmony, the programming of PIC32 microcontroller via MPLAB harmony is based on initializing and operating the drivers via system and client interface.

I2S driver tutorial with MPLAB Harmony

So there are functions relating to the I2S driver for system and clients. Client here means the user application. So first you have to learn how to create system interface to the I2S driver and then how to create client interface to the same I2S driver.

I2S driver interaction with the System

Creating a system interface to the I2S driver just means to create initialization data structure, using that data structure to initialize the driver and then finally using the handle so obtained during the initialization to maintain the state machine of the I2S driver. Now these steps are explained in the order.

The first step is to create the I2S driver initialization data structure. The data structure is created using the DRV_I2S_INIT structure that includes members for specifying device requested power state, peripheral ID, transmit and receive queue size, interrupt sources, DMA settings etc.

An example of such structure is below(found in system_int.c file):

After the I2S data structure is created, it is then passed to the DRV_I2S_Initialize API as parameter to initialize and obtain I2S driver object handle for the system.

The object handle of type SYS_MODULE_OBJ is first declared in the system_definition.h file.

Then this handle is used as return of the function DRV_I2S_Initialize function in the system_init.c file inside the SYS_Initialize() function as shown below.

After obtaining the handle to the I2S driver for the system, it should be used to routinely call the driver by the system. In this case, the driver can be called in polled or interrupt method. Either way the functions or API DRV_I2S_Tasks or DRV_I2S_TasksError should be called passing the handle to it.

For example in polled environment, the functions are called in the SYS_Tasks() function in the system_tasks.c file and in the interrupt environments the functions are called in the ISR(Interrupt Service Routine) in the system_interrupt.c file.

I2S Driver interaction with the Client

The next part of using the I2S driver relates to client application. While the system interface to the drivers are usually automatic via MHC, the client interface to the driver such as I2S is semi automatic and the user or the developer has to write the code him or herself.

The first step in doing this is to create I2S object handle to the client in the app.h file.

Then we can use the DRV_I2S_Open function to open the I2S driver in app.c file usually inside one of the application state inside the APP_Tasks() function. The return of the function is client handle(appData.i2sHandle).

The open function requires us to pass the index of the I2S driver or the instance, the read, write or both operation and the blocking or non-blocking operation.

After obtaining the client handle(appData.i2sHandle) we can use it to perform various testing and using client function to use the I2S driver for read and write operation, handling errors and other driver related events.

The client read and write operation with I2S driver is classified as non-buffered and buffered operation. As can be inferred, buffered read and write operation involves using buffers for I2S read and write and non-buffered operation does not use buffer.

Non-Buffered I2S operation

Non-buffered I2S operation is the basic type of audio data operation which is used when there is no requirement for buffer operation. The functions used for non-buffered I2S data operation are the DRV_I2S_Write and DRV_I2S_Read operation. These non-buffered operation can be blocking or non-blocking depending on the mode it was opened(see above). If the driver was opened in blocking mode then the function(DRV_I2S_Write or DRV_I2S_Read) will not return until all the audio data has been processed or if error occurred. If the driver was opened in non-blocking mode then the function will return without waiting until all data have been processed with the number of bytes that has been processed. This is similar to the blocking and non-blocking USART driver operation(see USART read write tutorial using MPLAB Harmony).

If you are using non-buffered I2S operation then you cannot use the interrupt and DMA of I2S driver feature.

Now the following shows non-buffered blocking and non-buffered non-blocking mode of operation for the I2S driver.

Example 1: Blocking non-buffered I2S driver operation

Example 2: Non-Blocking Non-Buffered I2S driver operation

Buffered I2S operation

The buffered I2S operation uses buffers and the functions to do that are DRV_I2S_BufferAddWrite, DRV_I2S_BufferAddRead and DRV_I2S_BufferAddWriteRead. Unlike the non-buffered operations, where the I2S driver could be used in blocking and non-blocking mode, in case of buffered operation, the I2S driver is also non-blocking.

Another notable difference between buffered and non-buffered operation is that in case of buffered I2S operation, a I2S driver buffer event handler set is required. This user application event handler function is registered using DRV_I2S_BufferEventHandlerSet. If the event handler function is called say App_BufferEventHandler(), then various I2S driver buffer events can be registered such as DRV_I2S_BUFFER_EVENT_COMPLETE, DRV_I2S_BUFFER_EVENT_ERROR and DRV_I2S_BUFFER_EVENT_ABORT. The I2S buffer driver notifies the user application when such event occurs and the user application can take appropriate action(s).

The buffered I2S operation could be invoked in polled or interrupt manner. Depending upon whether the I2S driver is used in polled or interrupt fashion, the DRV_I2S_Tasks and DRV_I2S_TasksError are called from system tasks(SYS_Tasks() function) or ISR(__ISR() function).

The following two examples shows how the I2S driver is initialized, opened and how the buffered read and write operation are performed in polled mode and interrupt mode.

Example 1: Polled mode buffered transmit

 

Example 1: Interrupt mode buffered transmit

 

 

 

What do you think?

11 points
Upvote Downvote

Total votes: 0

Upvotes: 0

Upvotes percentage: 0.000000%

Downvotes: 0

Downvotes percentage: 0.000000%

Leave a Reply

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

How to configure CODEC driver using MPLAB Harmony

How to configure CODEC driver using MPLAB Harmony

What is Callback function in MPLAB Harmony