Path: Home => AVR-EN => Applications => R/2R-DAC => Sine wave generators   Diese Seite in Deutsch: Flag DE Logo

DAC8 Tutorial for learning avr assembler language of
AVR-single-chip-processors AT90S, ATtiny, ATmega, ATxmega

of ATMEL using practical examples.

8-bit-digital-to-analog-converter using R/2R-networks

A R/2R network as Digital-to-Analog Converter (DAC)

If you need fixed or variable sine wave generators and if you do not want to be limited to a maximum frequency and you do not need triangles such as in this signal generator, you can follow this page.

Accessing the sine wave table

This page provides a versatile algorithm to generate sine waves with a R/2R network. It uses a 256 byte long sine wave table. Up to 3.676 kHz this table is accessed byte by byte, above that every second/fourth/eigth/sixteenth/thirtysecond byte is accessed to increase the generated frequency, but that reduces the resolution of the network.

The algorithm works as follows. It requires two values, which can either be located in a constant or, as double byte values, in two registers. Here the two values are assumed to be in the 16-bit registers X and Y, but they can be as well be in other registers or defined as constants (with slighlty differing code).

  nop ; Short delay, +1 = 4 * cCnt + 8
  ldi ZH,High(2*SineTable) ; Restart table, +1 = 4 * cCnt + 9
  ldi ZL,Low(2*SineTable) ; +1 = 4 * cCnt + 10
  ; Next table value, 4 * Cnt + 10
  lpm rmp,Z ; Read table value, +2 = 4 * cCnt + 11
  out pR2RO,rmp ; 1 cycle = reference point, +1 = 1 or 4 * cCnt + 13
  ; -----------------------------------------------
  ; Start delay loop
  mov rCntH,YH ; Int delay counter, MSB, +1
  mov rCntL,YL ; dto., LSB, +1
  sbiw rCntL,1 ; Decrease counter, +1 = 2 delay loop
  brne SineLoop2 ; +2 when jumping, +1 if not
  ; Loop delay = 1 + 1 + 4 * (cCnt - 1) + 3
  ;  = 4 * cCnt + 1
  ; -----------------------------------------------
  add ZL,XL ; Next value address, +1 = 4 * cCnt + 3
  adc ZH,XH ; +1 = 4 * cCnt + 4
  cpi ZL,Low(2*SineTableEnd) ; +1 = 4 * cCnt + 5
  breq SineLoop ; +2 when jumping = 4 * cCnt + 7
                ; +1 when not, = 4 * cCnt + 6
  nop ; An additional delay, +1 = 4 * cCnt + 7
  nop ; Two instructions delay, +1 = 4 * cCnt + 8
	rjmp SineLoop1 ; +2 = 4 * cCnt + 10

The whole algorithm does the following:
  1. First, the Z pointer is set to the start of the sine wave table.
  2. The table value is read with LPM and written to the R/2R port.
  3. Then a delay loop starts: a 16-bit counter in R25:R24 is loaded with a start value from Y, then decreased until it reaches zero. That loop requires (4 * Counter value) + 1 clock cycles.
  4. To the address in Z is then added a fixed displacement in X, which can be 1/2/4/8/16/32.
  5. If the end of the table has been reached, the loop restarts from the beginning.
  6. If not, there is some delay added to get a fixed loop time and the next table value is issued.
The number of clock cycles for the whole loop is 4 * Counter value + 13. If the counter value is one, the loop needs 17 cycles. The maximum delay is 4 * 65536 + 13 = 262,157 cycles, which prolongs the routine and reduces the generated frequency by the 15,421-fold.

The frequency generated is
fsine = clock / resolution / (4 * delay + 13)

the delay counter value can be calculated from
delay = (clock / resolution / fsine - 13) / 4

The algorithm can be used in every AVR that has ADIW implemented. If you need X and Y for other purposes: these values can also be located in lower registers or in constants (if you do not need to change them at runtime).

The reason, why the loop starts with a NOP, is: if you have to react on an external interrupt, you'll have to have a mechanism that diverts out of that routine. If you use the T flag in the status register for that purpose, the NOP would change to BRTS ExtJmpLabel. As there is normally no jump, this consumes one clock cycle. If you do not need that diversion routine, just remove the NOP from line 1 and one NOP at the end, so that the time comsumption is balanced. In the above formulas, the 13 changes to 12 then. That is the case with the version described here.

The sheet Clocking in the LibreOffice Calc file here shows the frequencies that can be generated. First select the clock frequency of the controller: the drop-down list offers all crystals that are available in the market. Select one of these. Then select the cycle length of the algorithm to be used, either 12 or 13 (+4 * Delay). The frequency ranges that can be generated with the different resolutions of the sine table access appear.

The limitations of operational amplifiers

Operational amplifiers that work with 5V are only linear in certain voltage areas. The design of the sine table has to account for that.

To measure linearity properties I have written fixed values to the R/2R network with a ATmega324PA and measured the input and output voltages of the opamp types 741 and CA3140.

In- and output voltages of a 741 The measured voltages of the 741 are listed on the sheet 741 in the LibreOffice calc file here.

The voltage on the input (thick in red) in Volt differs only slightly from the value that should occur (small in black), the difference is displayed in the yellow curve in mV and is constant over the whole voltage range. The 1% resistors work very linear.

The output of the 741 does not follow the input voltage below 2.0V: the output always remains at 2.0V.

From 2.0V on the output follows the input (green curve in V). This only changes at 4.3V, above the output goes into saturation. The difference (violet curve in mV) gets negative.

In- and output voltages of a CA3140 This is the same curve for a CA3140. That already works linear from 0.0V on, but has its end of linearity already at 3.0V.

In- and output voltage range of a TL071 The respective curve for a CMOS-OPAMP TL071 looks very different from the CA3140. The usable linear range is more similar to a 741.

As it is unnecessary to use the whole voltage range from 0 to 5V, we can simply create a sine table that respects the usable area. We then do not need any special ICs (so-called rail-to-rail opamps).

The sine wave table

Generating the sine table for certain opamps Due to the described linearity, the sine wave table used needs a little bit attention. Depending from the type of operation amplifier to be used and the desired amplitude of the sine the values have to be selected. The LibreOffice document sinegenerator_table provides a calculation scheme for that.

Select an opamp from the respective drop-down-list first. If your opamp is not listed here, just add it, with its specific upper and lower usable voltage, to the list and select it.

If considering smaller operating voltages than +5V, keep in mind that the operation amplifier also has to work with this.

On the right side, the produced table values appear. Just select the cells F9 to F42, copy those with Ctrl-C and paste this to your source code text file.

LC or RC filters for the output?

LC filter for sine wave generator The 8-bit sine wave generator produces rectangles with 256 * fsine, or the 128/64/32/16/8-fold, if so selected, with all uneven harmonics of that. To filter those rectangles, an LC filter can be used, if the range of the frequencies is limited. To calculate those LC filters you can use the sheet "LCfilter" in the LibreOffice calc document referenced above. Just input your selected parameters and see what is produced from that.

RC filter at base frequency 1 kHz RC filter at 17 kHz I additionally tested an RC filter for this purpose. See the sheet RC_filter in the LibreOffice Calc file here. The RC filter reduces the output amplitude only slightly (88%), while it suppresses the R/2R rectangles and their harmonics perfectly.

Sine signal raw Sine signal filtered To the left, the raw signal can be seen with a resolution of 7 bits (128 single values). The digital part of the signal can still be seen.

To the right, the same signal with an RC filter is seen. The digital parts are not there any more, but the signal is a little bit malformed from the filter.

Output capacitor

The same with the output capacitor that removes the DC from the sine wave. This must be at least as large as the desired frequency and the impedance of the following stage requires. Use the same calculation sheet to calculate the minimum capacity.

A fixed sine wave generator

If you need a precise sine wave generator at a certain fixed frequency you can use this here. It works with an ATtiny24 in a small 14-pin DIL package. All you need for that can be found on the referenced page.

Adjustable sine wave generator with LCD

If you rather want to have a Mercedes, which then you have to have a slightly higher device and you come to that here.

To the top of that page

©2020 by