This article is going to more “difficult” than the previous 3. That’s because we need to dive into the ADAU1701 design in enough detail to understand how to program the device, which happens in the next article. None of that programming discussion is going to make much sense unless we explain the hardware and how the SigmaStudio compiler allocates the ADAU1701 resources. This probably won’t feel like baking a cake from a box mix–this is going to more like a chemistry class to understand the composition of that mix.
ADAU1701 Block Diagram
The blocks at the top part of the diagram control voltages and internal clocks, and they require a few components on the board to work properly. But they aren’t relevant to us right now. Let’s start at the bottom.
On the bottom left side, there is a mode select block that is associated with reset. There are two boot-up modes. One mode is self-boot, and the other is I2C control. For self-boot mode, the ADAU1701 loads the program and data and control registers from an EEPROM connected to that I2C interface (next block over). So for this mode, we would need to get all of that programming into the EEPROM. The SigmStudio program can program the EEPROM for us, so that step isn’t difficult. But we aren’t going to use self-boot mode. Instead, we are going to boot the ADAU1701 from a microprocessor that is connected to the I2C bus. Using a microprocessor to load the ADAU1701 will give us a lot more flexibility, as it will allow us to change the DSP parameters in real time, or to easily reconfigure the processing in response to switches, IR remote controls, or from other input devices. Self-boot mode is generally used for large-scale production runs of DSP-based products, but for a DSP test bed we want more flexibility.
That control interface shows that you can control the ADAU1701 using either I2C or SPI, which are both low-level protocols for communicating between IC’s. We are going to use I2C because it is simpler (just two wires) and well supported with an Arduino software library. I2C stands for “inter-integrated circuit”, and a good tutorial on how it works is at this link. We will be discussing the microprocessor code for this control interface in the next article, so if I2C is new to you, you should take a look at that tutorial.
The next 4 blocks include the serial audio data inputs and outputs. These blocks allow connecting external A/D converters, DAC’s, or other DSP’s. The test bed doesn’t use these inputs or outputs, but one of the designs in an upcoming case study uses a serial output to send subwoofer audio to an external DAC. The other two blocks are a very cool feature of the device. The ADAU1701 can read switches or potentiometers by using the “Aux ADC” or “General Purpose I/O” pins. And with the SigmaStudio code, you can use the value of those inputs to control various DSP blocks, and you can control devices with the outputs. That’s a powerful capability that is especially useful for stand-alone DSP-based products such as Bluetooth speakers or soundbars. We won’t use these inputs or outputs for the testbed or cast studies, but it’s worth knowing that these features are available.
The next block up is the large one in the middle: the Audio Processor Core. That’s where the biquads are implemented and where the other DSP functions are executed. This block is fully programmable, and it won’t make any sense unless we look at the programming tool, SigmaStudio. At this point you should install SigmaStudio if you haven’t already done so. SigmaStudio is available for download at this link.
Analog Devices has an excellent set of Wiki pages that will help you get started and master SigmaStudio, with lots of examples. As of November, 2016, there are still a couple of sections that are “under construction”, but there is enough material there to cover the fundamentals of designing active speakers using the ADAU1701. We are going to show some screen shots of SigmaStudio, but we aren’t going to show the detailed steps of how we created the example. So if you aren’t familiar with using SigmaStudio, you might want to walk through the Development Environment section of the Wiki at this point.
So lets start with a simple example. We’ll make a two-way crossover using one biquad for each driver and we’ll add a volume control and a programmable delay for each driver. Then we’ll run the compiler and take a look at the code and see how the compiler assigned the Parameter RAM. We haven’t talked about Parameter RAM yet, but we will look at it toward the end of this article.
The first step is to start a new project and then drag an ADAU1701 processor onto the hardware window.
Then select the schematic tab and start picking out the DSP components. You will need an input block to get data from the A/D converter (the A/D is the top two connections on the input block) and you will need a volume control (right-mouse button click and select “grow algorithm” to make it a stereo volume control). Connect using wires and you’ll have something that looks like this on your screen:
Now add a biquad filter for each channel. They are in the “Filters” folder, under “Second Order”, “Double Precision”, “General (2nd order)”. If we were going to have the ADAU1701 use self-boot mode, we would double click on the biquad filters and specify the type of filter we want, whether it is Parametric, Low Pass, High Pass, or whatever. But we are going to program these filters, so we select the “IIR Coefficient” type. It actually doesn’t matter which filter type we choose because we are going to overwrite whatever SigmaStudio puts in these filters, but choosing “IIR Coefficient” programs them initially to have a transfer function of “1”–that is, the filter defaults to acting like a piece of wire. So after adding the filters and specifying the biquads, we have the makings of a two-way crossover, where one channel can be the tweeter and the other channel is the woofer:
Let’s add an audio routing block so if we want to change which channel is the tweeter and which is the woofer, we can change it from the microprocessor rather than resoldering speaker wires. To do this, select a multiplexer block and use the T connection from the Schematic Design menu. The T connection is needed whenever a signal gets sent to more than one block. We’ll send both the left and right channels to the multiplexer, and it will choose which channel goes to the biquad block–either a L/R combination or a R/L combination. And then we’ll add a delay and an output to convert the signal back to analog. The delay is useful for time-aligning the drivers. Each unit of delay is equivalent to moving the driver back about 1/4″. We need to set the maximum delay value so the compiler will know how much Data RAM to allocate–we’ll just pick 20, which will give us a range of about 5 inches. The delay block is buried in the “Basic DSP” folder. So here’s what we have so far:
Now we compile this design by selecting “Link/Compile/Download” from the “Action” menu, and then select the “Export System Files” from the same menu. Pick a name and location for those files–we’ll use the name “tutorial”. The compiler creates the ADAU1701 program, plus it also generates about 10 files that tell us all the information we need to load and the control the ADAU1701 while it is running that program. So let’s take a look at those files. The first one we want to look at is tutorial_IC_1.h. This file has the ADAU1701 program. But a more useful view of the same information is in tutorial.xml. Take a look at this file to get a bigger picture of the ADAU1701 program. This is actually only about 1/3 of the file:
Hmmm… there are about 50 “useful” instructions out of the 1024 instructions that the ADAU1701 does every audio sample. So most of the time the ADAU1701 is doing what we call NOP’s, which is short for no operations, or instructions that don’t do anything. That’s a waste of good processing resources, so let’s change the program to do something useful during that time. We’ll add a row of biquads that we can use for making an 11-channel EQ. We’ll add another biquad that can provide overall compensation for the baffle step diffraction, or BSC. And we’ll add a lot more biquads to each channel, to allow making steep crossover filters or EQ for each driver. Each biquad is second order, so 8 biquads per driver should be plenty. And since there are 4 DAC’s, we’ll provide 4 channels with analog output, and for future applications we’ll add two serial streams to use an external DAC for a sub. The DAC has two channels, so we will also be able to use this design as a stereo 3-way crossover. Add in a little more signal routing and volume controls on each channel, and we get the following design:
This is the design that gets loaded into the DSP test bed. This design is very flexible–it is better than just about any audio DSP chip from several years ago. In fact, it is so flexible that we really shouldn’t need to ever change it for building many active speakers. And since we’ve already got the code for this design, we really don’t need to know how to use SigmaStudio–we just poke in the right parameters from the microprocessor to use all of these nice DSP resources. That’s an important point worth repeating: you only need to use SigmaStudio if you want to use different DSP blocks than what are available in this design. But if you do want to change the design for the DSP test bed, or for some other application, the SigmaStudio project file for this design is available from the Downloads link.
There was one thing we did for this design that we didn’t discuss, and that is going back and giving “useful” names to each DSP block. In the next article we are going to describe how to change the biquad filters, or change the delay , or select left vs right channels, and to do that we need a standard language to specify each block. So we renamed the filters that are shared, shown at the top of the design, SharedFilter1, SharedFilter2, and so forth. And we name the Biquads for each channel as Gen_FilterX_Y, where X is the channel number and Y is the biquad number of that channel. This way, we will have standard names for the DSP blocks, or cells, and we can refer to the cells in our code using these standard names. The need for this naming convention will become clearer when we look at both the Arduino code for directly controlling the ADAU1701 and the ASD software that provides control from the PC.
SigmaStudio Parameter Data
We’ve looked at the program data, but in order to really understand how the ADAU1701 works, we need to look at the parameter data files that the compiler generates. We can either look at the tutorial_IC_1_PARAM.h file or tutorial.params. The second file is a little easier to read, so we’ll use that. Here are the first two sections of that file:
Cell Name = SW vol 1 Parameter Name = SWGain1940DBAlg1target Parameter Address = 2 Parameter Value = 1 Parameter Data : 0x00 , 0x80 , 0x00 , 0x00 , Cell Name = SW vol 1 Parameter Name = SWGain1940DBAlg1step Parameter Address = 3 Parameter Value = 0.000244140625 Parameter Data : 0x00 , 0x00 , 0x08 , 0x00 ,
What this tells us is that the cell or DSP block that was named “SW vol 1” got assigned two Parameter Addresses, 2 and 3, and two default values. The first address is actually the volume level for this stereo control. The second address is the slew rate, or step size. What’s important here is that if we want to change the volume, we need the microprocessor to change the value at Parameter Address 2. We’ll see in the next article how to do this.
Let’s keep looking at the tutorial.params file. Here is the section that describes the first biquad, which we named “Gen Filter1”:
Cell Name = Gen Filter1 Parameter Name = EQ1940Single10B1 Parameter Address = 4 Parameter Value = 1 Parameter Data : 0x00 , 0x80 , 0x00 , 0x00 , Cell Name = Gen Filter1 Parameter Name = EQ1940Single11B1 Parameter Address = 5 Parameter Value = 0 Parameter Data : 0x00 , 0x00 , 0x00 , 0x00 , Cell Name = Gen Filter1 Parameter Name = EQ1940Single12B1 Parameter Address = 6 Parameter Value = 0 Parameter Data : 0x00 , 0x00 , 0x00 , 0x00 , Cell Name = Gen Filter1 Parameter Name = EQ1940Single11A1 Parameter Address = 7 Parameter Value = 0 Parameter Data : 0x00 , 0x00 , 0x00 , 0x00 , Cell Name = Gen Filter1 Parameter Name = EQ1940Single12A1 Parameter Address = 8 Parameter Value = 0 Parameter Data : 0x00 , 0x00 , 0x00 , 0x00 ,
You’ve got to look carefully at the Parameter Name for the cell, but you can see that there are 5 values: B0, B1, B2, A1 and A2. These are the values of the coefficients for that biquad equation that we discussed in article number 2. And we can see the addresses that the compiler assigned: they are addresses 4 through 8. So we now have a high-level understanding of how to change the biquad properties–we just need to write the right data to these addresses.
Another thing to notice from this parameter data is the set of numbers at the end of each block. Notice that there are 4 Hex numbers (“0x” is the prefix used for hexadecimal notation) for each coefficient. That’s a total of 32 bits of binary data, and our C code in the Arduino or PC is going to generate standard floating point numbers. So we will have some formatting to do, and we need to figure out how to send 4 Hex numbers to a single address. But that’s coming up in the next article.
We need to look at another important ADAU1701 detail before moving on to the microprocessor and code samples–that is how the memory is organized in the ADAU1701. As shown below, the Processing Elements of the Core uses 4 different memory spaces: Program RAM, Parameter RAM, Data RAM, and Control Registers. We can write directly to the Program RAM and the Parameter RAM, but the Data RAM is only accessible to the processing elements. The Program RAM, Parameter RAM and Control Registers all have different fixed starting addresses that must be added to the cell addresses.
We can write directly to the Parameter RAM to change a DSP block, but it is preferable to write to the Parameter RAM through the safeload registers. These registers buffer up multiple parameters and load them into the Parameter RAM in a burst at the start of each audio sample. So if we need to update the biquad coefficients, the safeload registers will allow us to change them all at once, to prevent pops or clicks. This feature allows us to update the filters while still playing music–we don’t have to stop the processor to make the updates.
And finally, we need to understand that this memory is structured differently than what we are accustomed to seeing in a processor. Each word in the Program RAM is 40 bits wide. The Parameter RAM is 32 bits, and the Control Registers are variable width–from 8 to 40 bits, depending on the register. So when we read or write to these locations, we will need to know the number of bytes required for each transfer.