This report presents an overview of the software implementation of the Mikroe DSP Click daughter card based on an EFR32BG22 microcontroller. The DSP Click board houses a Coolaudio V1000 multi-effects processor, a Coolaudio V4220 A/D D/A converter, two Texas Instruments RC4580 operational amplifiers, and a Texas Instruments TPS65131 dual output DC-DC converter. The board includes two 3.5mm headphone jacks for analog input and output. The V1000 includes 16 built-in effects accessible via four program select pins, along with a serially programmable SRAM for developing custom audio effects. The DSP Click board is designed to mount to a mikroBUS socket which is available on the chosen microcontroller. Key challenges faced in the development of this project include the implementation of the custom serial interface, working with vague documentation, and dealing with poorly designed hardware.
Top view | Back view | ||
Digital signal processing (DSP) is the basis of any digital audio effects device. Digital audio effects are commonly used in a pedal format for instruments like guitar and piano but are also used for music production in music studios. The effects discussed in this report include reverb, delay, and chorus. Reverb is an effect which emulates the sound of a room. Typical reverb parameters include room size, reverb length, and wet/dry mix. In audio processing, the "wet" signal is the processed signal, and the "dry" signal is the unaffected signal. Delay is sometimes referred to as echo since it simply plays back the input audio at a given interval. Delay parameters typically include speed or rate, regeneration, and wet/dry mix. Regeneration refers to how long it takes for the repeating signal to fade out. Regeneration can also be set to infinity so that the repeating signal continues forever until the regeneration is turned down. Chorus is an audio effect which produces a shimmering sound by duplicating the audio, modulating its pitch and timing, then playing it back shortly after the original audio. The main goals of this project were to get the 16 built-in effects working and then program the SRAM to implement custom audio effects. With the complexity of programming the SRAM and the hardware issues encountered, the scope of this project was reduced to only accessing the internal programs and programming the SRAM with an open-source chorus effect.
When attempting to program the V1000 chip for the first time, it kept failing. It took a while to realize that the issue had to do with the hardware configuration. The V1000 has a pin labeled INT/EXT which controls whether the device is set to internal or external mode. In internal mode, the 16 internal programs become available over the program select pins. In external mode, the SRAM become available via the custom serial interface. When the INT/EXT pin is set to logic value 1, the board is set to internal mode. Likewise, when the INT/EXT pin is set to logic value 0, the board is set to external mode. This is where the issue lies. As you can see in the schematic below, the INT/EXT pin is directly connected to VCC which means that the board is locked into internal mode. To resolve this, one of the remaining unconnected GPIO pins - in my case the SCK pin – was connected directly to the INT/EXT pin on the V1000 chip. In addition, the trace running from FB1 to D3 was cut, effectively removing the connection from VCC to INT/EXT. You might be wondering about the pin 14, VDD. This pin powers the V1000 chip from the VDD source. However, the V1000 chip accepts a voltage range from 3-3.6 volts. VCC is a 5V source. D3 and D4 are diodes which reduce VCC to an acceptable voltage before it reaches the the VDD pin. Conveniently, a mikroBUS connector has a 3.3V source available. So this source was connected to VDD by running a wire from the 3.3V pin to the cathode of D4.
Schematic |
Software for this project was disigned in Simplicity Studio IDE under Gecko SDK v4.3.2 and GNU ARM Toolchain v.10.3.1
As previously mentioned, the V1000 chip can operate in two modes: internal and external. The software implementation varies slightly between each operating mode, however there is still a lot of functionality common between the two. The hardware initialization is almost identical for each mode. The first step to initializing the hardware is to initialize the DSP Click board. The proper power-up sequence is a power-on, followed by a reset. The power of the board is controlled by the RST (reset) pin and the reset sequence is: power off, delay, power on, delay. Below are the methods used to achieve this.
#define RST_PORT gpioPortC #define RST_PIN 6 void DSP_CLICK_init(void) { GPIO_PinModeSet(RST_PORT, RST_PIN, gpioModePushPull, 1); DSP_CLICK_reset(); } void DSP_CLICK_reset(void) { DSP_CLICK_off(); sl_udelay_wait(100000); DSP_CLICK_on(); sl_udelay_wait(100000); } void DSP_CLICK_on(void) { GPIO_PinOutSet(RST_PORT, RST_PIN); } void DSP_CLICK_off(void) { GPIO_PinOutClear(RST_PORT, RST_PIN); }
After the board has been initialized, the remaining GPIO pins need to be configured for either internal or external mode. For internal mode, pins P0 – P3 need to be configured for output, and INT/EXT needs to be set to 1. For external mode, the SCL and SDA pins need to be configured for output and the INT/EXT pins need to be set to 0. The GPIO_PinModeSet method is the only method needed to write both methods.
When the board is operating in internal mode, there are 16 built in programs available. The current effect can be changed by changing the output of the program select pins P0 - P3. For instance, setting all the pins to logic 0 selects the Small Hall Reverb program. For convenience, values were defined for each of the available programs and bit masks. The bit masks are designed to select a desired bit of a binary value. In this case, BIT_MASK_P0 is 0x01, BIT_MASK_P1 is 0x10, etc. To set the effect, a method was written which accepts a uint8_t, then sets the appropriate pins accordingly. This method is provided below.
void DSP_CLICK_set_effect(uint8_t effect) { if (effect > 0x0F) return; // invalid effect selection if (effect & BIT_MASK_P0) GPIO_PinOutSet(P0_PORT, P0_PIN); else GPIO_PinOutClear(P0_PORT, P0_PIN); if (effect & BIT_MASK_P1) GPIO_PinOutSet(P1_PORT, P1_PIN); else GPIO_PinOutClear(P1_PORT, P1_PIN); if (effect & BIT_MASK_P2) GPIO_PinOutSet(P2_PORT, P2_PIN); else GPIO_PinOutClear(P2_PORT, P2_PIN); if (effect & BIT_MASK_P3) GPIO_PinOutSet(P3_PORT, P3_PIN); else GPIO_PinOutClear(P3_PORT, P3_PIN); }
At this point the board is set up and able to process audio in real time. For internal mode, a button driver was implemented which increments the effect by one on each button press, allowing the user to cycle through all 16 effects with ease. In addition, a method which prints the current effect to a terminal was written so that when cycling through the effects, it is easy to see which effect is currently selected.
When running the board in external mode, the SCL and SDA pins are available for two-way communication with the V1000 chip. Given that the chip has a custom serial communication protocol, a custom implementation for communication is required. The protocol consists of 6 steps: attention, select, address, data, attention, deselect. Helper methods have been written to make the code easier to understand so each of these methods will be broken down. At the highest level, there is the V1000_write_effect method.
void V1000_write_effect(Word* effect, unsigned int length) { V1000_attn_select(); V1000_set_mode_write(); V1000_write_byte(0x00); // write starting address for (unsigned int i = 0; i < length; ++i){ // write data V1000_write_word(effect[i]); } V1000_attn_deselect(); }
The V1000_attn_select method is a combination of the first two steps. The attention signal is 0-1-0. This signal needs to be sent over the SDA (data) pin, in-between clock edges. It is unimportant what the current state of the clock pin is, only that the state of the clock changes before and after the attention signal is sent. In addition, the V1000 chip needs a short delay (10 microseconds) between each pin change. Then to select, the data pin needs to be set to 0 before flipping the clock, however since the attention signal ends with a 0, no change is required. All this can be seen in the V1000_attn and V1000_attn_select methods below.
void V1000_attn(void) { GPIO_PinOutToggle(SCL_PORT, SCL_PIN); // flip clock sl_udelay_wait(DELAY); GPIO_PinOutClear(SDA_PORT, SDA_PIN); // 0 sl_udelay_wait(DELAY); GPIO_PinOutSet(SDA_PORT, SDA_PIN); // 1 sl_udelay_wait(DELAY); GPIO_PinOutClear(SDA_PORT, SDA_PIN); // 0 sl_udelay_wait(DELAY); } void V1000_attn_select(void) { V1000_attn(); GPIO_PinOutToggle(SCL_PORT, SCL_PIN); // flip clock sl_udelay_wait(DELAY); }
Following the attention select is the read/write mode. To set the board to write, a 1 is sent over the data line. Conversely, to set the board to read mode, a 0 is sent over the data line. After the mode has been selected, the address to read or write to needs to be sent over the data line. When reading, the address needs to be sent every time whereas when writing, each write increments the address automatically. So, in the write method, only the starting address is sent. The V1000_write_byte method below is used to send the address to the chip from the most significant bit to the least. The way this method works is it checks whether there is a 1 in the leftmost (most significant) bit, and then writes a 1 if true, or 0 if false. Then the byte is left-shifted by one, putting the next bit to write in the leftmost position.
void V1000_write_byte(unsigned char byte) { for (unsigned int i = 0; i < 8; ++i) { if (byte & 0x80) { GPIO_PinOutSet(SDA_PORT, SDA_PIN); } else { GPIO_PinOutClear(SDA_PORT, SDA_PIN); } byte <<= 1; sl_udelay_wait(DELAY); GPIO_PinOutToggle(SCL_PORT, SCL_PIN); sl_udelay_wait(DELAY); } }
For those unfamiliar with a "word", it is simply a fixed-size unit of data. In the context of this project, a word is a typedef for an array of unsigned chars of size four (totaling 4 bytes). As one might expect, the V1000_write_word effect simply loops over the array of bytes, calling V1000_write_byte on each byte from the most significant byte to the least. Once all the data has been sent in the loop of V1000_write_effect, the communication must be ended with an attention deselect. The attention part is identical to the attention select, however, instead of selecting with 0, we deselect with 1. So, the full code for attention deselect is as follows.
void V1000_attn_deselect(void) { V1000_attn(); GPIO_PinOutSet(SDA_PORT, SDA_PIN); sl_udelay_wait(DELAY); GPIO_PinOutToggle(SCL_PORT, SCL_PIN); sl_udelay_wait(DELAY); }
Now that the procedure for writing a custom effect to the V1000, a custom
effect needs to be written. For inspiration, one might consider reviewing the
work at this repository:
https://github.com/nanaeffector/Coolaudio_V1000.
It contains a custom implemented chorus effect along with procedures to modify
different parameters of the effect. Due to the issues encountered during the
development of this project, the only effect written to the chip was this
effect. If this development of this project were to continue, the next step
would be to write custom effects.
The data sheet for the V1000 provides a detailed bitmap to construct commands to send to the chip, however, it excluded a very important detail. There is an entire set of assembly commands compatible with this device along with a custom assembler. This was discovered only because of the previously mentioned repository. This custom assembler along with its documentation have been included in this report for convenience.
Digital signal processing for audio has been an area of interest for me for a long time now. This project has allowed me the opportunity to gain a deeper appreciation for the engineering that goes into many of the audio effects devices I currently own. While I have enjoyed this project and plan to continue developing it, working with the DSP Click board has unfortunately been a frustrating experience. For anyone who wishes to pursue a project similar to this one, I would not recommend purchasing the DSP Click board. Rather I would recommend researching other DSP chips with better documentation and building a custom PCB to house that chip. If one truly wishes to use the V1000 chip, I also recommend building a custom PCB to house that chip. A properly designed interface for that chip and any other components on the board will give the user a much better experience.