AudioArduino-AnalogBoard - SdPhd



AudioArduino - Analog I/O board (v1)

This project continues the treatment of the DIY approach to soundcards - as an example of an overall approach to PC-based digital audio hardware development - started with ExtendingISASoundcard. Specifically, it discusses the analog-to-digital (A/D) and digital-to-analog (D/A) conversion capabilities of the AudioArduino project - by discussing an extension board, implemented as a single sided printed circuit board (PCB).

Completed and unconnected analog I/O board for AudioArduino

In particular, the focus is on how can the default analog input-output (I/O) of AudioArduino, be made to conform to standard analog audio I/O interfaces (as found on typical soundcards), exemplified here by the line-level standard (i.e., 'line in' and 'line out').


AudioArduino-AnalogBoard poster


  • See below for videos (for help with videos, click ).


Find link to schematics and PCB layout below.

The Kicad project (schematic and layout source files) can be browsed on here, or checked out from svn through:

svn co


Building and running

This example board has been implemented on a UV-photosensitive single-sided PCB. The board design follows a 'surface-mounted' philosophy: that is, there are no through-holes (or vias) - instead, both standard through-hole and SMD elements are soldered on surface.

The board is simply a collection of standalone modules - to establish interconnections, so-called 'Single Row Pin Header Sockets/Receptacles' (a.k.a 'Boardmount Sockets', such as 3M™'s 929 Series 929974-01-10-RK) can be used to 'chip away' individual pin sockets using a knife, which can then be soldered at termination points on the surface of the PCB; which will then allow simple interconnection using wire (something like 'inverse' wire-wrapping). Such 'receptacles' can sometimes be carried under the IDC moniker, although that is a more specific connector which is not usable for the purpose - accurate DIY 'cutting' is only possible with single-row sockets, with small indentations what would guide the knife; that is, the so-called "break away" (or "chip-away") variety is needed.

While this approach allows for quite fast PCB development and easy experimentation, it is also not very strong mechanically - typically, the single pin sockets are fragile, and can easilly pull the copper beneath on application of small force, especially if the copper trace is rather thin. If this happens, hot glue can be used for mechanical stabilisation, as shown below:

bent soldered socket pin bent socket pin falls off fallen socket pin re-soldered, and reinforced with a drop of hot glue at the base

For this reason, you would be well advised to increase the width of the thinnest copper traces on the PCB layout, if you intend to rebuild this board.

Since the board is modular, and actual functionality depends on the wire interconnections present at any given time - check out the videos, and the discussion further on, for information on how the board is utilised. Note that the board requires external 5V power supply (however, it performs better is the supply is closer to 7V).


While the main discussion is in the paper, a few additional notes are included below.

Analog audio standards - line-level

Finding accurate standards for analog audio is a tricky and expencive endeavor, maybe best outlined in the article by Dennis A. Bohn, "The bewildering wilderness – navigating the complicated and frustrating world of audio standards". This also makes it more difficult to integrate such standards in projects of open-source character (like this one).

However, for the purposes of this project, we can take that a line-level signal is an audio-encoded voltage signal, whose instantaneous values swing in the range between -2V and 2V. Considering that the Arduino accepts analog input between 0V and 5V; and as analog output generates a PWM signal, which is a binary voltage signal pulsing at either 0V or 5V - additional signal processing is needed before AudioArduino's analog I/O can be said to conform to line-level standard.

Since Arduino's analog input samples voltage, its domain is the same as the domain of a 'line-in' signal - the only difference is the range, which can be addressed by signal scaling (multiplication and constant addition). On the other hand, Arduino's analog output is a PWM (pulse-width modulated) signal, which is not a voltage-encoded signal (instead, useful information is encoded in its duty cycle), and hence is not in the same domain (not the same type) as the 'line-out' signal.

For the purposes of this project, we will call voltage-encoded signals 'SH' (for 'sampled & held'), referring to a quantized analog voltage signal. Note that some introductory material may often refer to this kind of a signal as 'PCM' (see for instance this ALSA page); however, seen as an analog signal, PCM (pulse-code modulation) refers to a specific binary encoding of digital values (which has, for instance, RZ (return-to-zero) and NRZ (non-return-to-zero) variants). For this reason, note that this SH signal is sometimes (wrongly) labeled as 'PCM' in the media on this page.

In other words, to conform to 'line-level' on the output, we need to employ some sort of a PWM-to-SH (analog) conversion; this board demonstrates a simple, 'first principles' implementation of a PWM-to-SH converter.

Board modules

As mentioned, this version of the board is modular, and the actual functionality is implemented by interconnecting modules using wire and available pin sockets. The modules are outlined in the diagram below:

Diagram of board modules

After buffering the PWM output from the Arduino with a buffer (1), the PWM to SH conversion is performed by a XOR pulse extractor (2) and PWM to SH circuit (3). There are three types of amplifiers: an 'analog' speaker amplifier (4), and PWM-oriented H-bridge (5) and Class-D (6) amps. There are three filters; passive RC (7), passive LC (8) and active 'bass/treble' filter (9); and for handling input, there is a mic preamp (10), mixer or summing amplifier (11) and a DC level shifter (12).

Thus, this board should allow a standard analog audio I/O interface expected of a usual soundcard:

  • line-in signals can be brought directly to the mixer/summer amplifier, and get scaled within the analog input range for the Arduino
  • mic-in can be brought to a mic preamp first, before being taken to the same mixer/summer and get scaled within the analog input range for the Arduino)
  • line-out signals can be obtained by feeding end-of-period signals - and the PWM output - of the Arduino, into the PWM-to-SH conversion circuit (which includes the XOR pulse extractor); the SH signal then needs just a coupling capacitor on the output to have its DC level removed, so it's centered around zero
  • speaker-out signals can be obtained by either feeding the SH signal into the 'analog' speaker amp - or by feeding the PWM signal to either the H-bridge or the class-D amplifier.

The videos show implementations of some of these arrangements. (note that the video camera sound recording 'cheats' a bit, as I personally found the direct PWM from the Arduino to result with the cleanest sound; however, that is not always captured on the videos.)

Since the board design and implementation is rather basic, it suffers from issues such as HF leaking (cross-talk), distortion, and difficulty of tuning of parameters via trimpots - some of which are discussed in more detail below. Otherwise, in principle, it should be able to perform an analog duplex loopback test with this board and AudioArduino - however, as it can be seen from the corresponding section, the end results are rather poor for now.

Buffering of PWM output

The first thing one may want to do in a design like this, is to buffer the PWM output from Arduino's digital out 6 - performed in the sense of 'impedance matching', so the Arduino sees an large impedance on Dout6, and does not lose much current itself (which otherwise comes from the PC's USB port, and is most likely limited at 250 mA). While here a usual op-amp (wired as a, say, voltage follower) can be appropriate, this project opted to utilize a 74XX (7400) series TTL XOR quad buffer, known as 7486. The reason for this is that a XOR circuit can be made into either a buffer follower or a buffer inverter, just by changing one of its inputs from GND to VCC; and assuming the propagation delay in both XOR circuits is the same, then one could derive both buffered PWM (BPWM) and buffered inverted PWM (BIPWM) signals - which are in phase - from a single PWM signal. The schematic of such an arrangement is shown below:

schematic of XOR buffer

To utilize this unit, simply establish the following connection with wire:

(Arduino) Dout6 -> d6_OC0A-PWM (board)

and with it in place (and the board powered), the signals B_PWM_1, B_IPWM_1, B_PWM_2 should be available. B_PWM_1 and B_IPWM_1 should be in-phase and inverted buffered PWM signals, which are delayed once (times the propagation delay of a XOR gate) in respect to the input PWM signal; B_PWM_2 represents a signal inverted twice - hence it should be delayed twice (times the propagation delay of a XOR gate) in respect to the input PWM signal. At this point, it should be mentioned that the 74XX series have different families, which encompass parts with different implementation technologies properties (among them, propagation delay) - from Digital I/O Boards: "basic TTL: 10ns; Low-power TTL (L): 33ns; High-speed TTL (H): 6ns; Schottky TTL (S): 3ns; Low-power Schottky TTL (LS): 9.5ns".

Considering that during test, the device used was 74LS86, one would expect a propagation delay of 9.5 ns - and crudely measuring with oscilloscope, there are apparently no problems:

PWM vs Buffered PWM - zoomed in on right, with delay measure

... however, upon closer inspection, even a rough measurement of delay (using the approximate midpoints of the repsective ramp regions) gives a delay closer to 32 ns! And, if the normal and inverted buffered PWM outputs are compared:

PWM vs Buffered PWM vs Buffered Inverted PWM - with delay measure

... it is visible that the non-inverted buffered signal has a higher propagation delay (~44ns) than the inverted one (~14ns)! (note: while it is likely I had made a mistake, and measured B_PWM_2 instead of B_PWM_1 in the above image -- I remember myself being surprised at the result and double-checking; so that should indeed be B_PWM_1 marked as 'BPWM' on the above image).

Another thing visible from the capture above, is that the rise times of the buffered PWM signals have also changed (in respect to the original PWM) - which can be seen as an additional cause of distortion. (note that the above image is composited {using GIMP} from individual oscilloscope screen captures, where the common point of reference is the PWM signal, in particular its rising edge; the same technique is used in other images below).

The H-bridge chip requires two inputs in antiphase (the non-inverted and inverted) as PWM input, which is the reason for trying to obtain B_PWM_1 and B_IPWM_1 in phase; in other circuits that require PWM input, B_PWM_1 has been used (instead of using Arduino's Dout6 directly).

XOR pulse extraction

The PWM-to-SH conversion outlined for this board, depends on the existence of two signals, representing exclusive pulses that mark the end of PWM period (EOP for end-of-period); where we count on having three of ATMega328's output compare (OC) units for generating these signals (in addition to OC unit A of Timer0 - or OC0A - which is used for generating the PWM signal). (Thus, the particular PWM-to-SH conversion process outlined here, relies specifically on the availability of four 8-bit OC units {divided among two timers} on Arduino's ATMega328; where one OC unit is used to deliver PWM, and the other three are used for EOP signals).

However, the only viable way to utilise OC0B, OC2A and OC2B as EOP markers, is to set them to high when the counter approaches the MAX value (255 for 8-bit); and reset them to GND when the counter wraps (is reset to 0). Unfortunately, this means that these EOP signals direct from the ATMega328 will overlap in time, and thus not be exclusive. To generate exclusive and buffered EOP pulses (BEOP) from Arduino's OC0B, OC2A and OC2B, the XOR pulse extractor circuit is used, whose schematic is shown below:

schematic of XOR pulse extractor

To utilize this unit, simply establish the following connections with wire:

(Arduino) Dout11 -> d12_OC2A-3 (board)
(Arduino) Dout03 -> d03_OC2B-2 (board)
(Arduino) Dout05 -> d05_OC0B-1 (board)

and with them in place (and the board powered), the signals xBEOP-3, xBEOP-2, xBEOP-1, should be available. (Note that the needed Arduino pin is Dout11 - however, because of a typo in the schematic, you may find references to d12 instead)

The difference between the input Arduino signals (left) and the output XOR pulse extractor signals (right) is shown on the image below (note, the OC units are set to trigger, when the counter reaches the following values: OCR2A = 252; OCR2B = 253; OCR0B = 254):

composite image of PWM vs OC end-of-period signals composite image of PWM vs BEOP signals

Note that the ATMega is rather precise in terms of turning off the EOP signals right at the start of PWM period; while the output from the pulse extractor suffers from the same problem identified for the XOR circuit: there is delay, and the rise time of the pulses is longer. In fact, that causes the (x)BEOP-1 pulse to extend until after the PWM period has started (which makes it somewhat unusable for the PWM-to-SH converter); but as only two pulses are needed there, (x)BEOP-2 and (x)BEOP-3 fit the requirements, as they are exclusive, and terminate before the next PWM period starts.

However, there is another problem - (x)BEOP-2 does not have the same amplitude as other pulses, and (x)BEOP-1 exhibits a DC level for its OFF state, that shouldn't be there - this is more obvious in the zoomed-out scope capture below:

PWM vs BEOP - zoomed out, showing leakage pulses and DC levels

Note: The above captures were obtained with the (x)BEOP signals floating (unconnected); it is likely that in this case, the XOR circuit enters a metastable point, and possibly starts oscillating in tact with the PWM signal. Seemingly, having a load in the processing chain remedies this; however captures for (x)BEOP for that case are, as of yet, not taken.

Besides this 'metastable' DC problem, it is obvious from the above capture that there is a severe 'leakage' (or 'crosstalk') problem - as the falling edge of the PWM signal is also imprinted on the (x)BEOP signals (although there is a small chance that it is a crass-talk occuring between the oscilloscope probes themselves).

PWM-to-SH conversion

  • Video: MVI_4372_audard_anboard_PCM_LM386_test.ogv

The PWM-to-SH converter used here, is based on the principle that (ideally) integrating a PWM signal with TTL levels, 0V and 5V, would result with:

  • a linear ramp during PWM signal's duty cycle (when the signal is high); and
  • a constant level outside the duty cycle of the signal (when the signal is 0).

Thus, if the integration process is reset at each start of PWM period - then the integrate value that is reached at the end of a PWM period, will have a linear correspondence with the duration of the duty cycle; or in other words, we would have obtained a signal with information encoded in the voltage level - a SH signal. In order to use reset the integration before the PWM period starts, we use two (x)BEOP signals as co-called COPY and CLEAR signals. The schematic for this circuit is shown below:

schematic of PWM to SH converter

To utilize this unit, simply establish the following connections with wire:

(board) xBEOP-2 -> xBEOPo-3/COPY (board)
(board) xBEOP-3 -> xBEOPo-2/CLEAR (board)
(board) B_PWM_1 -> B_PWM_Int (board)

and with them in place (and the board powered), the signal B_PCM_OUT should be available. The circuit consists of an integrator, which feeds a capacitor constant current while the PWM signal is high, so the capacitor develops a linear ramp voltage during duty cycle; a transistor for emptying the integrator capacitor, triggered by the CLEAR (CLR) signal; the integrator capacitor voltage is buffered by a voltage follower opamp; and brought to a pass-transistor structure, triggered by the COPY signal. When the pass transistor is open, the integrator cap voltage is 'copied' to the 'copy buffer' capacitor; the voltage of the 'copy buffer' capacitor is buffered once more by voltage follower opamp, whose output is B_PCM_OUT. Thus, we simply need to ensure that CLEAR hits just before the PWM period starts - and that COPY hits just before CLEAR, in order to have a SH representation of the PWM signal.

Note that with just the above connection in place, and without a load (B_PCM_OUT floating), a decent measurement for B_PCM_OUT couldn't be obtained; it is likely the 'metastability' of the (x)BEOP signals is still present in this case. The captures below, that show that the above operation does indeed take place, could first be obtained with B_PCM_OUT connected to the 'analog' amp (terminated with a speaker)

PWM vs Vcint (or BPCM) - with or without CLR signal

The image above on the right shows signals obtained when the CLEAR signal is disconnected - it is visible that a SH signal is still produced; however, one can easily hear that the sound is more distorted, than when the CLEAR signal is in place. Also, it is visible that there is high-frequency leakage corresponding to the falling edge of the PWM signal on the output B_PCM_OUT; which will require low-pass filtering. Finally, the buffer output SH signal also has quite a modest amplitude, centered at around 2.8V DC level; the DC level can be removed with a coupling capacitor, and the amplitude subsequently brought to 'line' levels by using an amplifier.

Note that the parts utilised for the PWM-to-SH converter on the board were in no way specifically chosen - TL074 as opamp, and BC337-25 as (BJT) transistor were used simply because they were easily available at the moment. It is likely that the performance of the circuit might improve by choosing parts more suited for this kind of operation - with lower turn on thresholds, and lower rise times.

Amplification of SH output - LM386

  • Video: MVI_4372_audard_anboard_PCM_LM386_test.ogv

The National LM386 is an audio amplifier IC chip, which can be said to accept SH (voltage-encoded) audio signals, and is capable of driving a small loudspeaker. The utilized schematic is taken from the datasheet.

To utilize this unit, for instance, simply establish the following connections with wire:

(board) B_PCM_OUT -> IN_RC_FLT (board)
(board) OUT_RC_FLT -> IN_AMP_A (board)
(board) SPK_OUT_A -> (speaker, which has other end on GND)

and with them in place (and the board powered), sound (played back in high-level audio software throuh AudioArduino) should be reproduced on the speaker.

Note that in the above setup, also the RC filter is utilised; however, given that all of the filters on board are rather simple, they are not very succesful at removing the HF content - and because of these problems with the obtained SH signal, a speaker connected directly to PWM output of Arduino still sounds better than this arrangement (with SH output amplified by LM386).

Amplification of PWM output - H-bridge, class-D

  • Video: MVI_4373_audard_anboard_hbridge_test.ogv, MVI_4374_pwm_bpwm_emptyclassd_test.ogv

As seen in AudioArduino, the default analog ouput from the Arduino - the PWM output - is usable, as a format, for driving a loudspeaker, and delivering sound of tolerable quality. In the audio industry, there is a whole class of amplifiers, known as "Class-D", which utilize PWM as means of delivering audio power to loudspeaker.

These types of amplifiers use a so-called H-bridge structure, which has full-bridge and half-bridge variants; where a half-bridge is a structure of two transistors 'one above the other', used as switches. This structure is capable of delivering bidirectional current to a load, even if the power supply is unidirectional (i.e. single supply: only positive voltage is present as supply). However, "H-bridge" is a term more commonly used in context of motor control (for instance, in robotics). Note that the H-bridge structure usually suffers from the possibility of having switches turned on in a such a way that short-circuits the power supply; this is often remedied by introducing 'dead time': where after a transistor has been active and turned off, both transistors must stay off for a brief period of time.

To experiment with this possibility for handling audio output (which could correspond to a 'speaker out' on an off-the-shelf soundcard), there are two units on board, that are intended to deliver amplified PWM output: a Rohm BD6211HFP "H-bridge" driver for motors with 400 ns 'dead time'; and a International Rectifier IRS20954 "Class-D" amplifier with adjustable dead time from 15 to 45 ns (and delay of 90 ns).

Note that H-bridge transistor structures can be identified not only in the BD6211 and the IRS20954 - but also in the LM386, which is shown in the image below (composed from diagrams in respective datasheets):

bridge structures in amp chips (indicated for comparison)

... however, given that the LM386 works with analog signals, presence of such a structure classifies it as class B amplifier (as opposed to class D, which whould work with PWM signals).

To utilize the BD6211, simply establish the following connections with wire:

(board) B_PWM_1  -> U3_FIN (board)
(board) B_IPWM_1 -> U3_RIN (board)
(board) U3_OUT1  -> (one input of loudspeaker)
(board) U3_OUT2  -> (other input of loudspeaker)

and with them in place (and the board powered), sound (played back in high-level audio software throuh AudioArduino) should be reproduced on the speaker.

The utilized schematic is taken from the datasheet. Measurement of the two output signals from BD6211 is shown below:

PWM vs pin Fout,Rout of BD6211

Note that the non-inverted output is delayed almost a whole millisecond from the Arduino's PWM signal, and the rise-time is quite visibly extended - and it most certainly incorporates both the problems from the PWM buffering with XOR, and the large dead time of the chip. Needless to say, the sound produced cannot be said to be fantastic - however, it is loud (which can also be seen by the nearly 200 mA power usage when this unit is actively playing a loudspeaker).

To utilize the IRS20954, simply establish the following connections with wire:

(board) B_PWM_1  -> U3_FIN (board)
(board) B_IPWM_1 -> U3_RIN (board)
(board) U3_OUT1  -> (one input of loudspeaker)
(board) U3_OUT2  -> (other input of loudspeaker)

and with them in place (and the board powered), sound (played back in high-level audio software throuh AudioArduino) should be reproduced on the speaker.

The utilized schematic for IRS20954 is a sort of a minimal cross-section between the schematic in the datasheet, and examples given in posts "Philips UCD application note - Page 39/Post #388 - diyAudio" and "UcD like topology amp. - Page 18/Post #172 - diyAudio". In particular, the needed output MOSFET transistors have not been added to the board, in order to look at the behavior of the internal bridge transistors.

Without the output MOSFETs, the circuit will not work properly (as it apparently also senses the current through these MOSFETs for correct operation); and the only measurable signal is on the LO output pin:

PWM vs pin LO of IRS20954 (without output MOSFET)

Note that, while the LO pin does generate a signal which is apparently SH, this signal also does sound distorted. However, it seems that with the output MOSFETs, and by using the raw PWM output from the Arduino directly as input - it is likely this circuit could produce the best PWM amplification, and loudspeaker sound quality, among the alternatives on the board.

Amplification of line input

As mentioned, handling the input - whether line or microphone level - so it fitst the Arduino input range, mathematically requires simply a linear scaling operation; and hence this was not a subject of deeper focus in this project. The microphone preamplifier is copied verbatim from ExtendingISASoundcard, while the mixer is the simplest example of an inverting mixer/summer.

The main problem with these circuits on the board, is that the amplification is set via trimmers - and it can be rather difficult to tune these trimmers exactly, so the output signal fits a given range. Additionally, generic TL074 are used as opamps, which are indended to work with symmetric power supply (that is, both positive {+Vcc} and negative {-Vcc} voltages are present as power supplies); the board distributes only a positive power supply, and the tests were conducted in a single supply environment (although the board, via socket pins, allows connecting additional negative power supply to the opamps). In other words, better user interaction with the input section needs a more involved electronic design than the one outlined in this project.

Analog duplex loopback test

  • Video: MVI_4375_analog-loopback01.ogv, MVI_4375_analog-loopback02.ogv, MVI_4375_analog-loopback03.ogv

As mentioned in the discussion of the digital duplex loopback test in AudioArduino, the ultimate test for a soundcard would be to loop back the line-level analog output into the line-level analog input - and perform a full-duplex (simultaneous) playback and capture in high-level audio software (Audacity). In ideal conditions, the captured audio should be a delayed, but otherwise equal version of the original, played back audio.

To create an example analog duplex loopback, simply establish the following connections with wire:

(board) B_PCM_OUT -> IN_RC_FLT (board)
(board) OUT_RC_FLT -> IN_AMP_A (board)
(board) SPK_OUT_A -> AIn0 (Arduino)

and with them in place (and the board powered), it should be possible to capture a played-back file in Audacity. Note that the mic preamplifier could also be utilized in the loopback chain.

As it is obvious from the three video attempts, it is rather difficult to obtain a decent capture quality through analog duplex loopback (which is not surprising, in light of the problems outlined so far).

For instance, here is a file recorded during one of the videos, in 16-bit, 44.1 kHz, mono format (Audacity having performed the conversion of 8-bit to 16-bit samples automatically):

  • capture-bpcm-filter-analog-loopback.wav
    • Notice the audible HF noise content in the file (even if no HF content is directly visible in the wave file) - which almost completely covers the entire music content.

Thus, the failure of the 'analog duplex loopback' test is an obvious pointer that engineering beyond basic concepts / 'first principles' is needed, in order to deliver analog I/O performance expected of a typical, off-the-shelf soundcard in context of AudioArduino.


Choose skin to view site in: 0 1 2 3 4 5 6 7 8 9 10 11