This version (12 Jun 2020 14:41) was approved by Chad Wentworth.The Previously approved version (24 Jun 2019 18:43) is available.Diff

Tutorial: Creating drivers for audio components using SigmaStudio

The bare metal framework is very extensible. In this tutorial, we’re going to explore adding new audio components (e.g. a codec, a MEMS microphone, etc.) to our system. Most audio components have both an I2S interface for audio and either an SPI or I2C interface for control.

This tutorial will cover how to configure a new audio component, with SigmaStudio, and then use this configuration in the bare metal framework to initialize it.

Creating Drivers for Audio Components

Part 1: Creating and initializing the audio component over TWI/I2C

If you are using an audio component from Analog Devices, SigmaStudio can very likely be used to generate a set of configuration parameters with which the bare metal framework can initialize the component.

First, if you don’t already have SigmaStudio installed, get the latest version from

It's important to download and install SigmaStudio and not SigmaStudio for SHARC.

In this example, we will show how to configure and connect the ADI SSM3582 (a Class D amplifier) to our system over I2C.

Step 1: Create and configure the component in SigmaStudio

Open up SigmaStudo. It will default to a blank project. If not, create a new project with File→New Project. We’re going to save our project within the framework so we can easily access the files that we export. Save your project into the following path:

C:\Analog Devices\SAM_BareMetal_SDK-Rel1.0.0\framework\drivers\bm_adau_driver\configurations\ss_schematics

Let's call our project SSM3582_basic.dspproj. You will see other projects in that directory, they correspond to preexisting drivers in the framework (more on this later).

Look for the component we’re interested in within the table on the left hand side and drag an instance of that component into our hardware configuration tab. Once we’ve dragged it in, click on the grey text within the box (IC initially), and give the component a more descriptive name. Our exported files will use this stub so it will make it easier to keep track of things.

Adding a component

Click on the configuration tab for this component at the bottom of the Hardware Configuration pane. Click on the Chip/SAI/DAC Control tab. You will see a graphical interface with a number of options. Note that there are several tabs across the top, each with more options.

Configuring the component

In our case, the default options are what is needed. So, without changing these settings, carry on reading through Step 2 to export the configuration files.

Step 2: Export the configuration

In the Action menu, select Link Compile Download.

It will appear that nothing has happened but SigmaStudio has indeed compiled the configuration options for this project.

After this, select Export System Files (immediately below Link Compile Download).


This will bring up a file dialog to create a folder for your project; name it SSM3582-basic. Within this folder, create another one called Exported Init Files. Press Save to export the files into this folder.

If you open this folder, you will see a number of files generated by SigmaStudio. We are concerned with two in particular, that contain the blocks of I2C initialization data for the bm_adau_driver driver to initialize the new part.

Init Files

Now that we have generated the initialization files, let's connect these to the bm_adau_driver driver.

Step 3: Connect the init files into the bare metal framework

A set of intermediate files provide a C struct to access information held in the two .dat files generated at Step 2. This enables us to pass a pointer to this struct rather than managing links to the configuration files when we initialize our new driver.

Import the bare metal framework projects in CCES (instructions here) and navigate to ${Core_0_Project}\src\drivers\bm_adau_driver\configurations. You will find a number of .c files in this directory, each corresponding to a particular device. Notice that there is already a file for the ssm3582_configuration.c. Open it.

Copy the “2ch_i2s_slave” configuration variables (shown below) and paste them again into the same file.

uint8_t ssm3582_2ch_i2s_slave_TxBuffer[] = {
#include "drivers/bm_adau_driver/configurations/ss_schematics/Class_D_Fin/Exported_init_files/TxBuffer_SSM3582_1.dat"
uint16_t ssm3582_2ch_i2s_slave_NumBytes[] = {
#include "drivers/bm_adau_driver/configurations/ss_schematics/Class_D_Fin/Exported_init_files/NumBytes_SSM3582_1.dat"
BM_ADAU_DEVICE_INIT_DATA ssm3582_2ch_i2s_slave = {.data_tx_buffer = ssm3582_2ch_i2s_slave_TxBuffer,
                                                  .data_num_bytes = ssm3582_2ch_i2s_slave_NumBytes,
                                                  .total_lines = sizeof(ssm3582_2ch_i2s_slave_NumBytes) / sizeof(uint16_t),
                                                  .ignore_first_byte_of_init_file = true};

Give new unique names to the copied configuration variables, and change the file paths to point to the .dat files generated at Step 2.

The code snippet below shows an example of the copied configuration code, with new variable names:

// My basic SSM3582 configuration
uint8_t SSM3582_basic_slave_TxBuffer[] = {
#include "drivers/bm_adau_driver/configurations/ss_schematics/SSM3582-basic/Exported_init_files/TxBuffer_SSM3582.dat"
uint16_t SSM3582_basic_NumBytes[] = {
#include "drivers/bm_adau_driver/configurations/ss_schematics/SSM3582-basic/Exported_init_files/NumBytes_SSM3582.dat"
BM_ADAU_DEVICE_INIT_DATA SSM3582_basic_slave = {.data_tx_buffer = SSM3582_basic_slave_TxBuffer,
                                                  .data_num_bytes = SSM3582_basic_NumBytes,
                                                  .total_lines = sizeof(SSM3582_basic_NumBytes) / sizeof(uint16_t),
                                                  .ignore_first_byte_of_init_file = true};

We can now reference the C struct called SSM3582_basic_slave when we need to reference this new configuration.

Step 4: Create driver instance and initialize the component

Next, let's create an external reference to this struct. Open bm_adau_device.h, located one directory up from the file we edited at Step 3. The code snippet shows the syntax to declare the reference; copy the statement below the external reference to aSSM3582_2ch_i2s_slave into your 'bm_adau_device.h' source:

// SSM3582 (2 channel class D chip)
extern BM_ADAU_DEVICE_init_Data SSM3582_2ch_i2s_slave;
extern BM_ADAU_DEVICE_init_Data SSM3582_basic_slave;    // Our new SSM3582 configuration

From this point, the init files data, generated at step 2, can be read from the C structure in any source file within the framework. All we need to do to initialize the new device is to create an instance of the bm_adau_device driver and initialize it like so:

// Create an instance of an bm_adau_device driver structure and initialize component
#define SSM35822_I2C_ADDR   (0x10)
BM_ADAU_DEVICE        ssm3582_basic;
if (res = adau_initialize( &ssm3582_basic,        // Address of our ADAU driver instance
                           TWI0,                  // Which TWI/I2C port we're connected to
                           SSM35822_I2C_ADDR,     // The I2C address of the SSM5822
                           &SSM3582_basic_slave,  // Address of our init file struct we created above
                           SSM3582_ADDR_BYTES)    // byte configuration of init files
         // Handle error here

adau_initialize() will then initialize the SSM3582 over I2C using the init files exported from SigmaStudio.

In the bare metal framework, the audio component initialization is done within the audio framework file. If you’re using the standard bare metal framework, the init function can be found in the ARM project, within ${Core_0_Project}\src\audio_frameworks\audio_framework_8ch_sam_and_audioproj_fin_arm.c. And if you’re using the Linux version of the framework where Linux is running on the ARM, then SHARC core 1 is responsible for audio component initialization. In this case, the init functionality can be found in core 1 (SHARC) within this file: src/audio_frameworks/audio_framework_8ch_sam_and_audioproj_fin_core1.c.

A number of other bm_adau_driver struct declarations can be found at the top of these files. The adau_initialize code can be found within the audioframework_initialize() function.

Reading / Writing Control Registers

The bm_adau_driver also includes functions to read/write to control registers of the device. For example, the followng code snippet shows how to change the Class D amp gain to 0dB:

adau_write_ctrl_reg( &ssm3582_basic, 0x7, 0x40 );  // Set left channel to 0dB
adau_write_ctrl_reg( &ssm3582_basic, 0x8, 0x40 );  // Set right channel to 0dB

And we can use the same approach to read control registers:

uint16_t leftGain = adau_read_ctrl_reg( &ssm3582_basic, 0x7 );  // Read left channel gain

If the audio component has a SigmaDSP core, this same driver can also be used to read and write parameter memory for the SigmaDSP. Use the adau_read_parameter_ram() and adau_write_parameter_ram() functions to access parameter RAM.

Navigation - SHARC Audio Module

resources/tools-software/sharc-audio-module/baremetal/driver-creation-tutorial.txt · Last modified: 12 Jun 2020 14:40 by Chad Wentworth