More on the R-2R ladder as a DAC. This time some work on the firmware.

 

What I'm doing here is the reverse of what happens when we digitize a signal; at regular intervals I have to come up with

the value for the waveform at that moment in time rather than read the value. When filtered, that will then reconstruct the

waveform.

 

To do that I need an accurate time interval. The only sensible way to do that on the Arduino is with interrupts, so I had

to have a quick read on how that works. It's not too complicated - just a short set-up of the timer and an interrupt

routine. Here's my test sketch modified to use timer 2 to generate an interrupt every 10uS and put out a value to the DAC.

 

/* AD Test */
unsigned int tableOffset = 0;             // output count
unsigned int tableStep = 5120;
//
//  --- Sine table - generated by sineTable.exe 
//
unsigned int sineTable[512] = {
   0x8000,0x8192,0x8324,0x84b6,0x8647,0x87d9,0x896a,0x8afb,0x8c8b,0x8e1b,0x8fab,0x9139,0x92c8,0x9455,0x95e2,0x976d,
   0x98f8,0x9a82,0x9c0b,0x9d93,0x9f19,0xa09f,0xa223,0xa3a6,0xa528,0xa6a8,0xa826,0xa9a3,0xab1f,0xac98,0xae11,0xaf87,
   0xb0fb,0xb26e,0xb3de,0xb54d,0xb6ba,0xb824,0xb98c,0xbaf2,0xbc56,0xbdb8,0xbf17,0xc073,0xc1ce,0xc325,0xc47a,0xc5cd,
   0xc71c,0xc869,0xc9b4,0xcafb,0xcc3f,0xcd81,0xcebf,0xcffb,0xd133,0xd269,0xd39b,0xd4ca,0xd5f5,0xd71d,0xd842,0xd964,
   0xda82,0xdb9d,0xdcb4,0xddc7,0xded7,0xdfe3,0xe0ec,0xe1f1,0xe2f2,0xe3ef,0xe4e8,0xe5dd,0xe6cf,0xe7bd,0xe8a6,0xe98c,
   0xea6d,0xeb4a,0xec24,0xecf9,0xedca,0xee96,0xef5f,0xf023,0xf0e2,0xf19e,0xf255,0xf307,0xf3b5,0xf45f,0xf504,0xf5a5,
   0xf641,0xf6d9,0xf76c,0xf7fa,0xf884,0xf909,0xf98a,0xfa05,0xfa7d,0xfaef,0xfb5d,0xfbc5,0xfc29,0xfc89,0xfce3,0xfd39,
   0xfd8a,0xfdd6,0xfe1d,0xfe5f,0xfe9d,0xfed5,0xff09,0xff38,0xff62,0xff87,0xffa7,0xffc2,0xffd8,0xffe9,0xfff6,0xfffd,
   0xffff,0xfffd,0xfff6,0xffe9,0xffd8,0xffc2,0xffa7,0xff87,0xff62,0xff38,0xff09,0xfed5,0xfe9d,0xfe5f,0xfe1d,0xfdd6,
   0xfd8a,0xfd39,0xfce3,0xfc89,0xfc29,0xfbc5,0xfb5d,0xfaef,0xfa7d,0xfa05,0xf98a,0xf909,0xf884,0xf7fa,0xf76c,0xf6d9,
   0xf641,0xf5a5,0xf504,0xf45f,0xf3b5,0xf307,0xf255,0xf19e,0xf0e2,0xf023,0xef5f,0xee96,0xedca,0xecf9,0xec24,0xeb4a,
   0xea6d,0xe98c,0xe8a6,0xe7bd,0xe6cf,0xe5dd,0xe4e8,0xe3ef,0xe2f2,0xe1f1,0xe0ec,0xdfe3,0xded7,0xddc7,0xdcb4,0xdb9d,
   0xda82,0xd964,0xd842,0xd71d,0xd5f5,0xd4ca,0xd39b,0xd269,0xd133,0xcffb,0xcebf,0xcd81,0xcc3f,0xcafb,0xc9b4,0xc869,
   0xc71c,0xc5cd,0xc47a,0xc325,0xc1ce,0xc073,0xbf17,0xbdb8,0xbc56,0xbaf2,0xb98c,0xb824,0xb6ba,0xb54d,0xb3de,0xb26e,
   0xb0fb,0xaf87,0xae11,0xac98,0xab1f,0xa9a3,0xa826,0xa6a8,0xa528,0xa3a6,0xa223,0xa09f,0x9f19,0x9d93,0x9c0b,0x9a82,
   0x98f8,0x976d,0x95e2,0x9455,0x92c8,0x9139,0x8fab,0x8e1b,0x8c8b,0x8afb,0x896a,0x87d9,0x8647,0x84b6,0x8324,0x8192,
   0x8000,0x7e6d,0x7cdb,0x7b49,0x79b8,0x7826,0x7695,0x7504,0x7374,0x71e4,0x7054,0x6ec6,0x6d37,0x6baa,0x6a1d,0x6892,
   0x6707,0x657d,0x63f4,0x626c,0x60e6,0x5f60,0x5ddc,0x5c59,0x5ad7,0x5957,0x57d9,0x565c,0x54e0,0x5367,0x51ee,0x5078,
   0x4f04,0x4d91,0x4c21,0x4ab2,0x4945,0x47db,0x4673,0x450d,0x43a9,0x4247,0x40e8,0x3f8c,0x3e31,0x3cda,0x3b85,0x3a32,
   0x38e3,0x3796,0x364b,0x3504,0x33c0,0x327e,0x3140,0x3004,0x2ecc,0x2d96,0x2c64,0x2b35,0x2a0a,0x28e2,0x27bd,0x269b,
   0x257d,0x2462,0x234b,0x2238,0x2128,0x201c,0x1f13,0x1e0f,0x1d0d,0x1c10,0x1b17,0x1a22,0x1930,0x1842,0x1759,0x1673,
   0x1592,0x14b5,0x13db,0x1306,0x1235,0x1169,0x10a0,0x0fdc,0x0f1d,0x0e61,0x0daa,0x0cf8,0x0c4a,0x0ba0,0x0afb,0x0a5a,
   0x09be,0x0926,0x0893,0x0805,0x077b,0x06f6,0x0675,0x05fa,0x0582,0x0510,0x04a2,0x043a,0x03d6,0x0376,0x031c,0x02c6,
   0x0275,0x0229,0x01e2,0x01a0,0x0162,0x012a,0x00f6,0x00c7,0x009d,0x0078,0x0058,0x003d,0x0027,0x0016,0x0009,0x0002,
   0x0000,0x0002,0x0009,0x0016,0x0027,0x003d,0x0058,0x0078,0x009d,0x00c7,0x00f6,0x012a,0x0162,0x01a0,0x01e2,0x0229,
   0x0275,0x02c6,0x031c,0x0376,0x03d6,0x043a,0x04a2,0x0510,0x0582,0x05fa,0x0675,0x06f6,0x077b,0x0805,0x0893,0x0926,
   0x09be,0x0a5a,0x0afb,0x0ba0,0x0c4a,0x0cf8,0x0daa,0x0e61,0x0f1d,0x0fdc,0x10a0,0x1169,0x1235,0x1306,0x13db,0x14b5,
   0x1592,0x1673,0x1759,0x1842,0x1930,0x1a22,0x1b17,0x1c10,0x1d0d,0x1e0e,0x1f13,0x201c,0x2128,0x2238,0x234b,0x2462,
   0x257d,0x269b,0x27bd,0x28e2,0x2a0a,0x2b35,0x2c64,0x2d96,0x2ecc,0x3004,0x3140,0x327e,0x33c0,0x3504,0x364b,0x3796,
   0x38e3,0x3a32,0x3b85,0x3cda,0x3e31,0x3f8c,0x40e8,0x4247,0x43a9,0x450d,0x4673,0x47db,0x4945,0x4ab2,0x4c21,0x4d91,
   0x4f04,0x5078,0x51ee,0x5367,0x54e0,0x565c,0x57d9,0x5957,0x5ad7,0x5c59,0x5ddc,0x5f60,0x60e6,0x626c,0x63f4,0x657d,
   0x6707,0x6892,0x6a1d,0x6baa,0x6d37,0x6ec6,0x7054,0x71e4,0x7374,0x7504,0x7695,0x7826,0x79b8,0x7b49,0x7cdb,0x7e6d};
void setup() {
  
  // set the digital pins as outputs:
  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(A0, OUTPUT);
  pinMode(A1, OUTPUT);
  pinMode(A2, OUTPUT);
  pinMode(A3, OUTPUT);
  digitalWrite(A0, HIGH);   
  digitalWrite(A1, HIGH);   
  digitalWrite(A2, HIGH);   
  digitalWrite(A3, HIGH);   
  // timer 2 set up
  cli();                  // disable interrupts
  TCCR2A = 0;             // control register all 0
  TCCR2B = 0;             // control register all 0
  TCNT2 = 0;              // set count to 0
  OCR2A = 159;            // period = 160 x 1/16MHz = 10uS
  TCCR2A |= (1 << WGM21); // mode is clear on match
  TCCR2B |= (1 << CS20);  // no prescaler   
  TIMSK2 |= (1 << OCIE2A);  // enable interrupt on match
  sei();                  // enable interrupts
}
ISR(TIMER2_COMPA_vect) {
  tableOffset = tableOffset + tableStep;
  PORTD = (sineTable[tableOffset >> 7]) >> 8;
}
void loop() {
}

 

Why 10uS? It needs to be fast enough to give several samples for each cycle at the highest frequency I want to generate

(20kHz) but if I run it too fast the processor won't keep up. This is far from perfect - even a third-order filter can't

take out the sample frequency to the extent that we would like - but remember this is cheap and cheerful and there's a

limit to how good a £2 [plus an Arduino] piece of test equipment can be.

 

I changed the sine table to be 512 entries so that it now spans a full cycle (before it was just one quadrant). I did that

because of the limited time that there is to do arithmetic in the interrupt routine.

 

To generate different frequencies, I step along the sine table at a rate that's appropriate for the particular frequency

we're trying to achive. The variable that holds the table index is a 16-bit unsigned int. The top 9 bits are the index into

the table. The lower 7 bit are a fraction of an entry - I'm doing arithmetic here with an implied fixed point as that

allows better resolution of frequency. When a particular frequency is asked for via a SCPI message there will need to be

some arithmetic done to work out the step value, but once that's done the process of reading out the table values is simply

by adding the step to the current offset which is nice and fast within the interrupt routine. There isn't even a need to

test for the end of table as the natural overflow from working 16 bits brings the index round to the start again.

Here's the DAC output on the 'scope. You can see the samples coming out at 10uS intervals, so I must have got the timer

set-up code right.

 

 

Below is another view where you can see several cycles - because the step value in this case doesn't have a simple

relationship to the cycle, the samples are different for each cycle, but when filtered they'll give the frequency that we

want.

 

 

Finally, here's an interesting trace - this is the previous one accumulated. This one shows that, though the step isn't a

simple divion of the table size, it does repeat after a few cycles. (The filtered waveform is blurred because the trigger

is on the 10uS steps of the yellow trace which don't have a simple relationship to the frequency generated.)

 

 

Next part will be combining that with the SCPI parser and seeing what happens.

 

Part one: Arduino: R-2R Experiment

Part two: Arduino: R-2R: Sine On You Crazy Diamond

Part three: Arduino: R-2R: Buffer, Attenuate, and Filter

Part four: Arduino: R-2R: "We Interrupt This Programme..."

Part five: Arduino: R-2R: "Resistance is..."?

Part six: Arduino: R-2R: Setting the Output Frequency

Part seven: Arduino: R-2R; "A Sweep is as Lucky, as Lucky Can Be..."

Part eight: Arduino: R-2R: Setting the Signal Amplitude