Arduino Software, Part 1

As noted in the Arduino Software Overview, part 1 covers the software that is normally not seen when you open the sketch, as it consists of the library files that support the high-level commands. In the current code, the library files “ADAU1466_CMD.cpp” and its header file “ADAU1466_CMD.h” are kept in the sketch directory for easy access, but eventually they will be moved to the library folder. We’ll return to this reason for keeping the ADAU1466_CMD files in the main sketch in a couple of paragraphs. These library files can be downloaded from this link.

The library files make up the inner layers of that software block diagram that was shown in the overview. These inner layers are shown below:

Let’s take a look at that ADAU1466_CMD.h file to see what these library files can do. For simplicity, we have left out the function argument types and the function types, and we’ve also done some editing to improve readability.

class ADAU1466_cmd {
  Main_Volume(reg_num, value_code)
  Mute(reg_num, value_code)
  Input_select(reg_num, value_code)
  Chan_vol_trim(reg_num, value_code)
  Update_Delay(reg_num, value_code)
  Mute_all(mute_addresses[number_of_mute_addresses], mute_state)
  
  T_W_Xover(freq_code, Xover_type_code, biquad_addresses[5][2])
  W_S_Xover(freq_code, Xover_type_code, biquad_addresses[4][2])
  Xover_T_polarity(T_Pol_Addr, value_code)
  Xover_W_polarity(W_Pol_Addr, value_code)
  Xover_S_polarity(S_Pol_Addr, value_code)

  EQ_Filter(reg_num, Freq, value_code)
  BSC_Freq_or_Gain(reg_num, freq_code, gain_code)
  Rumble_filter(reg_num, freq_code, Q_code, Rumble_enabled)
  Peak_filter(reg_num, freq_code, Gain_code, Peak_enabled)
  Custom_filter(reg_num, f_type, f_freq, f_gain, f_Q)
  Custom_filter_index(reg_num, t_code, f_code, g_code, Q_code)
  
  ADAU1466_Source_select(regnum, source_code, sweep_reg)
  ADAU1466_Sine(regnum, freq_code)
  ADAU1466_Sweep(regnum, range_code, steps_code, speed_code)

  Curvature(curvature, t_delay_regs[12], w_delay_regs[7])
  Shading(vol_taper, t_shading_regs[12], w_shading_regs[7])
  Speaker_select(speaker_code, t_mute_regs[12], w_mute_regs[7])

That’s a nice set of functions for controlling most active line arrays, so we could put this ADAU1466_CMD file in the library to simplify the rest of the sketch. However, what if we want to build a new line array that is open baffle and needs compensation for the dipole cancellation? Or what if we have a speaker that can swap the phase of the rear speakers electronically? I’ve already done this, and I found that I needed three new functions:

  Set_Dipole_Comp(reg_num, comp_code, filter_freq)
  Set_Dipole_Gain(reg_num, gain_code)
  Dipole_On_Off(On_Off_code, Phase_Switches[5])

Or, what if we wanted to add the Analog Devices SuperBass algorithm or the Dynamic Bass algorithm? I had these functions implemented in the library files for the ADAU1701 but removed them from the ADAU1466 library because it usually isn’t needed for the larger speakers and because it was going to take a lot of work to implement those functions for the ADAU1466. To add those features, we would need two additional functions:

  SuperBass(reg_num, freq_code, Intensity_code, Gain_code)
  Dynamic_Bass(reg_num, Thresh_code, TC_code, Boost_code, F_code)

We said earlier that we would return to the reason for keeping the ADAU1466_CMD code in the sketch rather than the library, and now it should be obvious: every time we need some new capability, we need to add some new commands. So, as long as we keep adapting this code to do new and interesting things, it makes sense to keep the ADAU1466 files with the sketch. But someday, when we stop innovating, we’ll put those files in the library where they belong. Yeah, right.

The details for these library files are described in the sections below, starting at the innermost layer. The library files (excluding ADAU166_CMD for now) are in this zip file. These files go in the “library” folder in the Arduino documents. The link to the sketch files, which currently include the ADAU1466_CMD files, is in Part 2.

SPI

The Arduino SPI library has the ability to initiate, transfer and complete the electrical transactions on the SPI bus. We simply need to pass in the pin numbers for the clock, chip select, data in and data out, and the SPI library will allow us to move data bytes into and out of the ADAU1466.

ADAU1466_SPI

This library provides the interfaces to write to the ADAU1466 registers and memory spaces. The ADAU1466 registers and memory are all 32-bit wide, so all ADAU1466 programming requires transferring 4 bytes at a time. The functions in this layer allow transferring blocks of data into and out of the ADAU1466 and to load biquad data into the Parameter RAM using the ADAU1466 Safeload protocol. The complete list of functions provided by this library file is listed in ADAU1466_SPI.h. This list is shown below, with the types removed and some additional editing for clarity:

class ADAU1466_SPI {
  SPI_write_Mult(addr_MSB, addr_LSB, count, command_buffer[])
  SPI_read_reg(addr_MSB, addr_LSB)
  SPI_write_reg_string(addr_MSB, addr_LSB, command_string)
  SPI_write_reg_int(addr_MSB, addr_LSB, value)
  safeload_addr(reg_num)
  safeload_data(safeload_data_array[])
  biquad_safe_load(filter_reg, ADAU1466_coefs[5][4])
  safeload_xfer(count)
}

These functions make it much easier to communicate with the ADAU1466. For example, the programmer can specify a buffer of coefficients for an IIR biquad filter, and this library takes care of putting them into the right locations using the Safeload Registers.

These interfaces are primarily used by the ADAU1466_DSP library, but they can also be used by other code modules. For example, there is a “String write” feature that allows writing text data into user bits on the SPDIF output. This function is useful for sending messages from one speaker to the other in a stereo pair.

ADAU1466_DSP

The primary function of this library is to calculate the crossover coefficients. It does that by determining how many biquad stages are needed for a given crossover type and which filter types are needed for each biquad. After all the biquads are defined, it uses the Bilinear Transform library to calculate the coefficients for each biquad.

This library also provides a general “filter_calc” function for calculating the coefficients for other filters.

Bilinear Transform

The bilinear transform library has just one function, which calculates the IIR filter coefficients for a specified filter. The function declaration (without the types) is:

coef_calc(FC, gain, Q, filtertype, (& coef_array)[6])

This function allows us to convert a given filter type at specific center frequency (FC) and with a specified gain and Q to an array of floating-point coefficients for a digital biquad filter. The math required to implement this transform was copied from the Apogee application notes published for their DSP cores, but it is essentially the same as what was published in the old DSP Cookbook.

Shared_Enums

The Shared_Enums file is just a header file without corresponding code. It defines structures need to specify biquads and filters. It also enumerates the crossover types and what drivers are associated with a specific crossover.

Lookup Tables

If you look at the interfaces in ADAU1466_CMD.h, you will see a lot of function arguments that are called “value_code”. These parameters are indexes to a table that converts the code to the series of bytes that need to be sent to the DSP Parameter RAM. For example, the master volume setting used in the HCI is a number ranging from 0 to 89. We need this number to correspond to a volume setting from -80 dB to +6 dB. This conversion is done by using a lookup table. The look-up table is generated using a spreadsheet that calculates the “dB equivalent” of the volume steps in a format suitable for directly loading into the Parameter RAM for the DSP volume control cell.

There a quite a few other tables in the ADAU1466_Lookup tables.h file that convert the options presented in the HCI menus to the actual values that we need to put in the DSP Parameter RAM. As another example, the HCI allows selecting 5 different levels of electronic curvature, either concave or convex. These 5 options are used as an index to an array of delay values for each of the 20 channels that are used to control the curvature.

If you need to add more tables like these or modify any of the existing tables, there is an Excel worksheet in the zipped library files that can be used to update the lookup tables. The spreadsheet does the math to convert values such as volume or delay into the necessary 32-bit words, and then it formats the words into the hex data bytes used for the table.

ADAU1466_CMD

We already looked at the functions exposed by this library in the introduction to this section. But it is worth pointing out a few additional features of these functions to better understand how the user can call them.

First, every exposed function passes a “register number” or an array of addresses into these functions. This register or address is the cell number or block of cells extracted from the SigmaStudio compiler output. As noted in the SigmaStudio Software section, we needed to label each cell with a meaningful name and use a utility to find the SPI address for the Parameter RAM for all of the cells. The cell addresses were placed in the file “Cell_map.h”. For example, we labeled the cell we used for the input selection “Input_SW”. The corresponding entry in the Cell_map file is 704. That means the compiler assigned Parameter RAM address 704 to that cell. So, if we want to change the input, we will need to call this function with the reg_num equal to 704.

Second, some of the functions need an array of Parameter RAM addresses. For example, the tweeter delay cell addresses are defined as:

tweeter_delay_addresses[12] = {T1_Delay, T2_Delay, T3_Delay, T4_Delay, T5_Delay, T6_Delay, T7_Delay, T8_Delay, T9_Delay, T10_Delay, T11_Delay, T12_Delay}

This array of addresses is then passed into the Curvature function, along with the woofer delay addresses.

And third, the other arguments for these functions are usually a code corresponding to some position in an HCI menu, although that is not always the case. As noted in the section on Lookup Tables, these codes get converted to the actual values used by the DSP to control volume, delay, input, etc. However, for some of the functions, the arguments are enumerated types, such as the argument for the T_W_Xover function. This function has “Xover_type_code” as an argument, which specifies a specific Crossover topology such as Butterworth_1, Linkwitz_Riley_4, or some order of Bessel Crossover.

And in still other cases, such as the Custom Filter function, the arguments are actual numerical values rather than codes or indices that need to be translated with a lookup table. The fact that the arguments to these functions can be an index into a lookup table, an enumeration, or a numerical value means there are probably some opportunities to improve these library functions for more consistency, but that will be left as an “exercise for the reader”.