ESP32 SPI control of ADAU1467 parameter RAM on EVAL-ADAU1467Z — writes not taking effect
I'm trying to control an ADAU1467 on the EVAL-ADAU1467Z evaluation board from an ESP32 microcontroller via SPI on the J1 slave control port header (the same header used by the USBi).
Setup:
EVAL-ADAU1467Z with ADAU1467 self-booting from EEPROM (SELFBOOT enabled)
SigmaStudio 4.7, program compiled and written to EEPROM via USBi
ESP32-WROOM-32 (3.3V logic) connected to J1: GPIO23→Pin 8 (MOSI), GPIO19→Pin 5 (MISO), GPIO18→Pin 7 (SCLK), GPIO5→Pin 9 (SS), GND→Pin 10
USBi ribbon cable disconnected
SPI settings: 1 MHz clock, Mode 3 (also tried Mode 0), MSB first
SPI chip address byte: 0x01 (SigmaStudio shows SPI 0x1 ADR0)
I toggle SS three times at startup to switch the slave port from I2C to SPI mode
What works:
SigmaStudio via USBi can successfully toggle mute blocks (MuteL at address 0x0275, MuteR at 0x0276) using the Capture Window — confirmed the writes use 4 data bytes (0x00000000 for mute, 0x00000001 for unmute)
The ESP32 SPI write format matches exactly: [0x01][0x00][0x02][0x75][0x00][0x00][0x00][0x00]
What doesn't work:
ESP32 writes to parameter RAM addresses 0x0275/0x0276 have no effect on the audio — mute blocks do not respond
Reads from parameter RAM return inconsistent/garbage values (e.g., 0xFFFFF9AF, 0xFF0000BC) suggesting the SPI communication may not be properly established
No audible clicking or artifacts during writes (there was clicking earlier with incorrect wiring, which was resolved, but funnily enough pops up again from time to time)
What I've tried:
SPI Mode 0 and Mode 3
Chip address 0x00 and 0x01
Direct writes and safeload writes (addresses 0x6000/0x6005/0x6006 and 0x0001/0x0006/0x0007)
Confirmed EEPROM contains latest compilation (re-exported system files, re-wrote EEPROM)
Power cycling ADAU1467 first, then connecting ESP32 after self-boot completes
SS toggle sequence (3 toggles) before any SPI transactions
Questions:
Is there anything specific to the EVAL-ADAU1467Z board that would prevent an external MCU from accessing the slave control port via J1 when self-booted? Any switches or jumpers I need to set?
Does the ADAU1467 require the SS toggle sequence when using the J1 header, or does the eval board hardware configure the port as SPI by default (since the USBi uses SPI)?
For writing to parameter RAM on a self-booted ADAU1467, should I use direct writes or safeload? The ADI wiki page on microcontroller interfacing mentions that gain/mute changes on Sigma300/350 don't require safeload.
Is there a specific SPI timing requirement (setup/hold time, delay between bytes) that the USBi satisfies but a standard microcontroller SPI peripheral might not?
Can anyone share a working example of an ESP32 or Arduino writing to ADAU1467 parameter RAM via SPI on the eval board?
Any guidance would be greatly appreciated. Thank you.
Audio Signal Processors
ADAU1467 Evaluation board
hardware
adau1467
spi communication
esp32
SigmaDSP Audio Processors
Show More
Hello MDThommo81,
MDThommo81 said:
SPI chip address byte: 0x01 (SigmaStudio shows SPI 0x1 ADR0)
The chip address should be 0x0 for write and 0x1 for read.
In the USBi, SPI 0x1 ADR0, 0x1 is SPI latch (SS) number (since USBi has 5 latches) and ADR0 is the address for the ADAU1467.
I can see in your below statement that you tried 0x0 too, so let's move forward with the issue.
1)Turn OFF the self-boot switch, once the DSP is self-booted from the EEPROM then try to write to the registers.
2)Yes, the toggle sequence is necessary, since you pressed reset after self-boot is complete, now the control port is back to its default mode which is I2C. so you must toggle the SS pin to put it back to SPI mode before trying to write anything.
3) Yes, for mute or gain blocks the safe load is not needed, but for updating filter coefficients you must use safe load method.
I have recently replied to one of the customers with an Arduino code who has difficulty in SPI read function, so you try or refer than code.
Did you try writing or reading from the DSP before self-boot from EEPROM? did you try to boot the DSP from the MCU?
The below file has the Arduino code and SS project.
SPI_READ1.zip
Regards,
Harish
Thanks so much, Harish. The problem for me was the SPI transaction framing. Seeing the example code helped immediately. I naively used my similar use of controlling external SPI devices using a separate chip address byte, and therefore had 4 header bytes (chip address, read/write indicator, address hi, address lo). Changing it to the 3 header bytes worked.
I didn't get to test your recommended item #1, can I ask what was the purpose of doing this? Do I need to disable the self-boot switch normally after the DSP is running?
Harishgowtham said:
The chip address should be 0x0 for write and 0x1 for read.
Harish, I believe that this is only true for I2C, because SPI does not have an address (on the bus). An I2C device with a 7-bit address of 0x0 would have its 8-bit address as 0x0 for write and 0x1 for read.
The SPI bus does not have a chip address, but rather a dedicated chip select, and the chip select does not change between write and read. As you mention, USBi has 5 select lines for SPI, although the 10-pin connector only has 1 (the other 4 must be hand-wired).
When the ADAU14xx chip is in I2C mode, it's 7-bit I2C address can be any of 4 values, depending upon the ADDR0 and ADDR1 input pins. Thus, its 7-bit address can be 0x0, 0x1, 0x2, or 0x3. The corresponding 8-bit write addresses would be 0x0, 0x2, 0x4, or 0x6 and the corresponding read addresses would be 0x1, 0x3, 0x5, or 0x7.
Hi Harish, I'd also like to just build my understanding a little more to update biquad filter coefficients from the ESP32 at runtime. You mention a safe load process. I assume this is:
Step 1 - write data to safeload data registers (0x6000 - 0x6004)
Step 2 - write target address to safeload address register (0x6005)
Step 3 - write number of words to safeload count register (0x6006)
I'll need to confirm the exact register addresses of course. And I believe there are a maximum of 5 safeload slots.
I can see the SafeLoadModule addresses in the exported param file (0x6000–0x6007). Could you provide or point me to example code showing the correct sequence for writing 5 biquad coefficients via safeload on the ADAU1467? Specifically: the exact write sequence, whether writing NUM_SAFELOAD_LOWER triggers the transfer, and any timing considerations.
Hello MDThommo81,
I currently have a working example of the Safe-Load mute sequence implemented on an STM32 microcontroller communicating with the DSP via I²C.
Please find the code snippet below for your reference.
void DSP_SafeLoad_Mute(uint16_t targetAddr, uint32_t value)
{
uint8_t data[4];
// Convert 32-bit value to byte array (MSB first)
data[0] = (value >> 24) & 0xFF;
data[1] = (value >> 16) & 0xFF;
data[2] = (value >> 8) & 0xFF;
data[3] = value & 0xFF;
// Step 1: Write data to SafeLoad Data Register 0 (0x6000)
SIGMA_WRITE_REGISTER_BLOCK(DEVICE_ADDR_IC_1,
0x6000,
4,
data);
// Step 2: Write target parameter address to 0x6005
uint8_t addrBuf[4] = {0x00, 0x00,
(targetAddr >> 8) & 0xFF,
targetAddr & 0xFF};
SIGMA_WRITE_REGISTER_BLOCK(DEVICE_ADDR_IC_1,
0x6005,
4,
addrBuf);
// Step 3: Write word count = 1 to 0x6006
uint8_t countBuf[4] = {0x00, 0x00, 0x00, 0x01};
SIGMA_WRITE_REGISTER_BLOCK(DEVICE_ADDR_IC_1,
0x6006,
4,
countBuf);
}
// mute
DSP_SafeLoad_Mute(0x0015, 0x00000000);
DSP_SafeLoad_Mute(0x0014, 0x00000000);
//unmute
DSP_SafeLoad_Mute(0x0015, 0x00000001);
DSP_SafeLoad_Mute(0x0014, 0x00000001);
If required, I can also prepare and share the Safe-Load implementation for updating biquad coefficients.
mute_test_.dspproj
Regards
Harish
Hello again,
As I mentioned in my first reply, for changing a mute or gain value in run time doesn't need safe load Operation, the Sigma300/350 DSPs handle it automatically during end of the frame.
The above attached code snippet is to give you an idea about how safe load works.
For updating filter coefficients in runtime, you must use safe load Operation.
Let me know if you need an example for it.
Regards,
Harish
Thank you, Harish. This is great. Yes please, if you have an example for controlling a set of parametric filters, and perhaps some HPFs etc, that would be good. It will just make sure that I approach it in the correct manner at this end.
Kind regards,
Mark
Please log in to reply.