In this blog I would like to describe technique which I found when developing Christmas Card with Lights and Sound but later did not used it. Even I did not use it, I still think it is interesting. I will show how to get additional Digital-to-Analog (DAC) converters on AVR DA, DB, DD, EA and EB microcontrollers with just using two external low-accurate passives. Documentation says that in case of DA, DB, DD and EB family there is only one DAC and in case of EA family there is no DAC, but is it true?
Where are the hidden DACs?
The secret hidden DACs actually are not dedicated peripheral like the only main DAC, and they are no 10-bit, but they are 8-bit. They are “hidden” in Analog Comparator (AC) peripheral. They are mentioned using very mysterious term here:
And on diagram here:
In theory, for every comparator you can get one additional DAC but there are some limitations. For example, on DB family there 3 ACs, but their outputs are routed to two pins only. But this most probably can be workarounded using CCL.
You can even select reference voltage, but selection affects all additional DAC and can’t be done independently per additional DAC.
Exposing AC DAC to pin
The little problem is that output of this DAC is connected to input of AC and not to output pin. But we can hack it. Idea is that we will have capacitor connected between ground and positive input of AC. Negative input will be connected to DACREF, of course. In this setup AC will compare voltage on capacitor with internal DAC voltage. If the capacitor voltage is lower than DAC voltage, it will output logical 0 and if the capacitor voltage is higher, it will output logical 1. Now we need to charge capacitor somehow. We basically need to charge it when the voltage is bellow DAC voltage, stop charging it when it reaches DAC voltage and possibly, we need to discharge it when its voltage is higher than it should be. Because AC do comparison we need, we can use output of AC for driving charger/discharger. Or in other words, we will charge/discharge capacitor from the output of AC which comparing its voltage. For limiting charging/discharging current we use resistor. Here is the schematics of the circuit:
There is one more requirement. We need to invert output signal, but this is not issue because AC in AVR Dx/Ex MCU support configurable output signal inversion.
Capacitor and Resistor requirements
Circuit basically works with any capacitor and resistor. The values in the schematics are my recommendations for starting point. Values influence output noise and setting time. AC work asynchronously on system clock so when reached correct voltage, AC start oscillating. While speed is high it is not unlimited at there are pulse duration limits which causes overshoots and undershoots which cause noise on output voltage.
Higher resistance/capacitance cause lower output noise because circuit is charging/discharging slower, but naturally, response on changes is also slower.
You basically should use as high values as possible as long as rise/fall times are acceptable for you.
Since circuit work with any capacitor and resistor, you do not need to worry about tolerances of parts. You can use cheapish parts.
Code
Following code (very short) configures AC0 in way described above. It is written for AVR128DB48 MCU.
#include <avr/io.h> int main(void) { // PC6 = OUT // PE0 = AC_INP // connect C=100nF between GND and PE0 // and R=1k between PC6 and PE0 PORTMUX.ACROUTEA = PORTMUX_AC0_ALT1_gc; PORTC.DIRSET = PIN6_bm; VREF.ACREF = VREF_REFSEL_2V500_gc; AC0.MUXCTRL = AC_MUXPOS_AINP1_gc | AC_MUXNEG_DACREF_gc | AC_INVERT_bm; AC0.CTRLA = AC_POWER_PROFILE0_gc | AC_OUTEN_bm | AC_ENABLE_bm; volatile uint8_t dacref1 = 0x40; volatile uint8_t dacref2 = 0xC0; while (1) { AC0.DACREF = dacref1; for (volatile int i = 0; i < 10000; i++) {} AC0.DACREF = dacref2; for (volatile int i = 0; i < 10000; i++) {} } }
I chose PC6 and PE0 pins because they are near on Curiosity Nano Board. AC positive input pin is AIN1 (PE0 by default) and output pin is PC6 (which is alternative configuration set using PORTMUX). You can of course adjust these settings. Reference is 2.5V, and you can use 1.024V, 2.048V, 4.096V, Vcc or Vrefa. In main loop, the program alternates between two DAC levels (0x40 which corresponds 64/256 of 2.5V which correspond to 25% of 2.5V or 0.625V and 0xC0 which correspond to 75% of 2.5V which is 1.875V).
Tests
I tested output signal on oscilloscope. Here is the output (yellow is DAC output, blue on later screenshots is AC output):
With changing resistor from 1k to 68ohm you get much faster rise time (9.2 us from 0.625V to 1.875V) but also more significant overshoots and undershoots causing higher (horrible) output noise.
On following screenshot, there are zoomed AC output. As you can see oscillator work independently on system clock. Pulses are very short and non-deterministic. Most pulses are 40ns long which correspond to 25 MHz frequency which is much faster than system clock (4 MHz) and there are even shorter pulses spuriously.
Power consumption
Is horrible. Because circuit oscillates at very high frequency there is terrible dynamic power consumption. I measured circuit with and without this DAC configured. I measured it using Nordic Power Profiler Kit 2 (PPK). Current consumption is 1.17mA higher when enabled and running. Note that internal 10-bit DAC has power consumption of 120uA typical according to datasheet, so our DIY additional DAC channel consume about 10x more (and have 4x time lower resolution).
EMI
Is horrible. Circuit which relies on oscillation influenceable by anything nearby … ha ha ha.
Further tuning
I did several experiments with circuit. Here in the blog are presented only most successful ways. Most important are tweaks of resistor and capacitor values. I also tried different setups. For example, I tried adding series diode with charging resistor. This allows charging, but once charge it will not actively discharge capacitor. The problem is when you change DAC output lower than it was, then nothing will discharge capacitor. It will self-discharge, but very slowly. Also, in this setup it was sensible to touches and other external sources (possibly usable as DIY capacitive buttons?). I tried similar setup also with P-channel MOSFET (maybe this circuit was referred as open-source, not related to software but complementary to open-drain) and it has slightly better properties than series diode (lower noise), but problem with lack of active discharge at right time was still there. Benefit of this alternative circuits was that occurrence of random charging pulses was highly reduced (which consequently led to lower output noise) but with different properties on rise and (non-functional) fall it is mostly unusable, I think. But I gave these ideas to the public. Maybe somebody invent something smarter than I did.
Comparing with PWM
Naturally we can compare our exposed DAC with PWM which is most common way to generate analog looking voltage from digital value. In comparison with PWM output from these DACs is more stable. In case of PWM (and RC filter) you always have rising phase when digital output is high (charging RC filter capacitor) and falling phase when it is low (discharging RC filter capacitor). You can reduce it by improving RC filter or increasing frequency, but you never get rid of it at all. In opposition exposed DAC do not have these phases at all and it maintains stable voltage by generating pulse as soon as it deviates from desired level (in both directions). The cost of this approach are pulses which may happen randomly, and their frequency is hard to characterize. In case of PWM, this is not issue at all because frequency is constant. Different is also resolution. In case of PWM resolution depends on timer which generates it and frequency which limits resolution. It can range very broadly and can be worse or better in comparison with exposed DAC. Exposed DAC has static 8-bit resolution. At last, there is different accuracy because of voltage reference used. PWM signals are usually generated from digital power supply which powers whole chip (or even more parts) and most regulators are not very accurate. In opposition, exposed DAC is referenced to precise internal (or possibly even more precise external) voltage refence which are generally much more accurate than voltage which powers chip.
Closing
This is all. Thank you for reading this blog as far. In this blog you have seen method for getting additional DACs on AVR Dx and Ex microcontrollers using analog comparator and just two inaccurate passives. I am not sure, if it is practically usable for production deployment, but it basically works and do its job. In the resource section below, you can download Atmel Studio project for AVR128DB48 MCU which you can use if you want to try it yourself.