The analog to digital converters on the Arduino are not the best.

In fact, if you want to use them at an acceptable sample rate (>40kHz) you can only use one channel. Not very practical, so most people pair their Arduino with an external analog to digital converter. A popular and easy to use device is the Microchip MCP3002 Dual Channel 10-Bit A/D Converter with SPI Serial Interface. The data sheet states the device can achieve a sample rate of 200 kHz which is more than adequate for our purposes. However, the maximum sample rate will be set by the Arduino. As long as we don’t exceed the maximum sample rate of the MCP3002 we’ll be fine.

Here is the circuit I used for this experiment.

This is a little sketch to test how fast we can get our Arduino to complete one sampling cycle. Without coding in assembly, there is no easy way to compute the clock cycles per instruction, and thus the sample rate. The easiest way to figure out our sample rate is to toggle and output and measure the frequency with our oscilloscope.

/* Start of audiouino project. Intial experiment using SPI buss connected to Microchip MCP3002 see: Click to access 21294C.pdf Slave Select - Pin 8 */ #include const int slaveSelect = 6; //Slave Select const int LED = 7; const byte READ = 0x60; //Channel 0 Read, MSBF byte MSB, LSB; word ChannelA; void setup(){ pinMode(slaveSelect, OUTPUT); //Sets slave pin as output pinMode(LED, OUTPUT); Serial.begin(9600); //Start serial monitor SPI.begin(); //Starts SPI SPI.setBitOrder(MSBFIRST); //Sets MSB first PORTD = 0xFF; } void loop(){ digitalWrite(slaveSelect, LOW); //pulls slaveSelect pins low, readying the ADC to read MSB = SPI.transfer(READ); //Pulls in the MSB LSB = SPI.transfer(0); //Pulls in the LSB digitalWrite(slaveSelect, HIGH); //turns ADC off ChannelA = word(MSB,LSB); //combines the to bytes into a word if (ChannelA > 500) { PORTD = PORTD ^ 0x80; } else { } }

This is probably a good place to talk about port manipulation. Port manipulation is a way to directly access the output/input registers (as well as a few other registers, check out the link). When using PORTD, you can assign any 8-bit number to the output and its binary representation will be shown on the output of PORTD. For example

PORTD = 28;

has the binary representation.

0001 1100

This means that outputs 2,3, and 4 are activated.

The setup section goes through some fairly typical things. Some global constants and variable are assigned. The Slave Select pin and LED pin are set as outputs and the SPI buss is started (ignore the Serial.begin, it’s not used). SPI bit order is also set and Port D (Pins 0-7) are set as high.For this code setting PORTD all high doesn’t really accomplish anything practical. In later methods it’ll ensure proper SPI behavior, but now it’s superfluous.

Now we start performing the actual sampling. First the slaveSelect line is pulled low, readying the ADC. The READ command is sent to the ADC and the MSB is returned. Then the LSB is sent in by transferring any data (in this case a 0) over the data line. Then the MSB and LSB are combined to create a word. Then the LED is toggled by XORing output 7 via PORTD.

Since disabling serial monitoring, I need a way to ensure that the ADC is working. Since I’m varying the voltage to the ADC my turning a pot, the value of ChannelA should change. The if statement will toggle the output as long as ChannelA is greater than 500; if it’s less than 500 nothing will happen. This will allow us to monitor the sampling frequency and to still see if we’re sampling.

So let’s look at the LED output.

As you can see we’ve achieved a sample rate of about 24kHz, hardly spectacular. This sample rate still might be useable for our sidechaining application since it’ll give us a maximum frequency response of about 12kHz. I can’t think of too many applications where a sidechain frequency of greater than 12kHz would be practical. I imagine most compression is triggered somewhere between 40-2000Hz. However we can still do better. Which brings me to our next point.

digitalWrite is slow, REALLY slow.

How slow? Let’s look at some modified code.

/* Start of audiouino project. Intial experiment using SPI buss connected to Microchip MCP3002 see: Click to access 21294C.pdf Slave Select - Pin 8 */ #include <SPI.h> const int slaveSelect = 6; //Slave Select const int LED = 7; const byte READ = 0x60; //Channel 0 Read, MSBF byte MSB, LSB; word ChannelA; void setup(){ pinMode(slaveSelect, OUTPUT); //Sets slave pin as output pinMode(LED, OUTPUT); SPI.begin(); //Starts SPI SPI.setBitOrder(MSBFIRST); //Sets MSB first PORTD = 0xFF; //initializes pins 0-7 high. } void loop(){ PORTD = PORTD ^ 0x40; //toggles pin 6 low MSB = SPI.transfer(READ); //Pulls in the MSB LSB = SPI.transfer(0); //Pulls in the LSB PORTD = PORTD ^ 0x40; //toggles pin 6 high ChannelA = word(MSB,LSB); //combines the to bytes into a word if (ChannelA > 500) { PORTD = PORTD ^ 0x80; //toggle pin 7 } else { } }

This code is functionally the same as before but instead of using digitalWrite I’m directly manipulating the ports. Let’s see if there is a speed improvement.

There is a definite improvement with this method. We’re effectively sampling at 61kHz. If this isn’t evidence as of to why not to use digitalWrite I don’t know what is.

The code I’ve provided might not be the fastest out there. It might be possible to get the SPI buss to run faster using the SPI.setClockDivider() function, however documentation on this is thin and this is just a starting point. Also I might be able to shave a few clock cycles by using the compound assignment XOR instead of calling PORTD then XORing with the toggle mask and then assigning it to itself again.