Skip to main content
Commonmark migration
Source Link
More complete initialization.
Source Link
Edgar Bonet
  • 44.3k
  • 4
  • 41
  • 79

If you want your sampling to be at 2 kHz with cycle-accurate timings, you have to send to this pin a train of pulses which are at least 40 ns wide and have a period of 500 µs, i.e. 8,000 CPU cycles. This can be done with any of the three timers of the Uno. For example, you can set Timer 2 to repeatedly count from 0 to 124 with a prescaler of 64 (untested code). Your initialization would have something like this:

// Initial transfer to configure the ADC.
PORTB &= ~_BV(SS_ADC);
SPI.transfer(ADCbyte1);
SPI.transfer(ADCbyte2);
PORTB |= _BV(SS_ADC);

// Configure Timer 2 for PWM on pin OC2B = PD3 = digital 3.
DDRD  |= _BV(PD3);    // pin PD3 as output
TCCR2A = 0;           // undo the Arduino core's configuration
TCCR2B = 0;           // ditto
OCR2A  = 125 - 1;     // period = 64 * 125 CPU cycles = 500 us
OCR2B  =   2 - 1;     // high for 64 *   2 CPU cycles =   8 us
TCNT2  = 122;         // first pulse in 8 to 12 us
TIFR2 |= _BV(OCF2B);  // clear the output compare flag
TCCR2A = _BV(COM2B1)  // non-inverting PWM on pin OC2B
       | _BV(WGM20)   // mode 7: fast PWM, TOP = OCR2A
       | _BV(WGM21);  // ditto
TCCR2B = _BV(WGM22)   // ditto
       | _BV(CS22);   // clock at F_CPU/64

Edit 1: I would just want to add that cycle accurate timing does not meannot mean perfectly uniform timing. The Arduino Uno is clocked off a ceramic resonator. This kind of resonator, in addition to having bad accuracy, has poor frequency stability. To get an idea of how bad it can be, see the Allan deviation plots on the article Arduino clock frequency accuracy, by Joris van Rantwijk.

Edit 2: After some tests, I wrote a more complete initialization:

  • An initial SPI transaction is used to prime the ADC, as suggested by Chris Stratton
  • the PWM pin is configured as an output
  • the Arduino core's Timer setup is undone before applying the new configuration
  • the very first pulse on CONVST is scheduled to happen not too long after the initialization, yet after tACQ
  • the output compare flag is cleared during the initialization.

If you want your sampling to be at 2 kHz with cycle-accurate timings, you have to send to this pin a train of pulses which are at least 40 ns wide and have a period of 500 µs, i.e. 8,000 CPU cycles. This can be done with any of the three timers of the Uno. For example, you can set Timer 2 to repeatedly count from 0 to 124 with a prescaler of 64 (untested code):

// Configure Timer 2 for PWM on pin OC2B = digital 3.
OCR2A  = 125 - 1;     // period = 64 * 125 CPU cycles = 500 us
OCR2B  =   2 - 1;     // high for 64 *   2 CPU cycles =   8 us
TCCR2A = _BV(COM2B1)  // non-inverting PWM on pin OC2B
       | _BV(WGM20)   // mode 7: fast PWM, TOP = OCR2A
       | _BV(WGM21);  // ditto
TCCR2B = _BV(WGM22)   // ditto
       | _BV(CS22);   // clock at F_CPU/64

Edit: I would just want to add that cycle accurate timing does not mean perfectly uniform timing. The Arduino Uno is clocked off a ceramic resonator. This kind of resonator, in addition to having bad accuracy, has poor frequency stability. To get an idea of how bad it can be, see the Allan deviation plots on the article Arduino clock frequency accuracy, by Joris van Rantwijk.

If you want your sampling to be at 2 kHz with cycle-accurate timings, you have to send to this pin a train of pulses which are at least 40 ns wide and have a period of 500 µs, i.e. 8,000 CPU cycles. This can be done with any of the three timers of the Uno. For example, you can set Timer 2 to repeatedly count from 0 to 124 with a prescaler of 64. Your initialization would have something like this:

// Initial transfer to configure the ADC.
PORTB &= ~_BV(SS_ADC);
SPI.transfer(ADCbyte1);
SPI.transfer(ADCbyte2);
PORTB |= _BV(SS_ADC);

// Configure Timer 2 for PWM on pin OC2B = PD3 = digital 3.
DDRD  |= _BV(PD3);    // pin PD3 as output
TCCR2A = 0;           // undo the Arduino core's configuration
TCCR2B = 0;           // ditto
OCR2A  = 125 - 1;     // period = 64 * 125 CPU cycles = 500 us
OCR2B  =   2 - 1;     // high for 64 *   2 CPU cycles =   8 us
TCNT2  = 122;         // first pulse in 8 to 12 us
TIFR2 |= _BV(OCF2B);  // clear the output compare flag
TCCR2A = _BV(COM2B1)  // non-inverting PWM on pin OC2B
       | _BV(WGM20)   // mode 7: fast PWM, TOP = OCR2A
       | _BV(WGM21);  // ditto
TCCR2B = _BV(WGM22)   // ditto
       | _BV(CS22);   // clock at F_CPU/64

Edit 1: I would just want to add that cycle accurate timing does not mean perfectly uniform timing. The Arduino Uno is clocked off a ceramic resonator. This kind of resonator, in addition to having bad accuracy, has poor frequency stability. To get an idea of how bad it can be, see the Allan deviation plots on the article Arduino clock frequency accuracy, by Joris van Rantwijk.

Edit 2: After some tests, I wrote a more complete initialization:

  • An initial SPI transaction is used to prime the ADC, as suggested by Chris Stratton
  • the PWM pin is configured as an output
  • the Arduino core's Timer setup is undone before applying the new configuration
  • the very first pulse on CONVST is scheduled to happen not too long after the initialization, yet after tACQ
  • the output compare flag is cleared during the initialization.
+ note on bad frequency stability of the ceramic resonator.
Source Link
Edgar Bonet
  • 44.3k
  • 4
  • 41
  • 79

the ADC is LTC1859 and triggers on the slave select falling edge.

No, it doesn't. Here is a link to its datasheet. It has a pin labeled CONVST, for “conversion start”. According to the section Pin functions, “This active high signal starts a conversion on its rising edge.”

If you want your sampling to be at 2 kHz with cycle-accurate timings, you have to send to this pin a train of pulses which are at least 40 ns wide and have a period of 500 µs, i.e. 8,000 CPU cycles. This can be done with any of the three timers of the Uno. For example, you can set Timer 2 to repeatedly count from 0 to 124 with a prescaler of 64 (untested code):

// Configure Timer 2 for PWM on pin OC2B = digital 3.
OCR2A  = 125 - 1;     // period = 64 * 125 CPU cycles = 500 us
OCR2B  =   2 - 1;     // high for 64 *   2 CPU cycles =   8 us
TCCR2A = _BV(COM2B1)  // non-inverting PWM on pin OC2B
       | _BV(WGM20)   // mode 7: fast PWM, TOP = OCR2A
       | _BV(WGM21);  // ditto
TCCR2B = _BV(WGM22)   // ditto
       | _BV(CS22);   // clock at F_CPU/64

Note that the timer will also set a flag when the pulse is done, at which time you can start the data transfer. You would then start your data-taking loop with:

// Wait for the output compare flag.
loop_until_bit_is_set(TIFR2, OCF2B);

// Clear the flag.
TIFR2 |= _BV(OCF2B);

// Now transfer the data.
...

Edit: I would just want to add that cycle accurate timing does not mean perfectly uniform timing. The Arduino Uno is clocked off a ceramic resonator. This kind of resonator, in addition to having bad accuracy, has poor frequency stability. To get an idea of how bad it can be, see the Allan deviation plots on the article Arduino clock frequency accuracy, by Joris van Rantwijk.

the ADC is LTC1859 and triggers on the slave select falling edge.

No, it doesn't. Here is a link to its datasheet. It has a pin labeled CONVST, for “conversion start”. According to the section Pin functions, “This active high signal starts a conversion on its rising edge.”

If you want your sampling to be at 2 kHz with cycle-accurate timings, you have to send to this pin a train of pulses which are at least 40 ns wide and have a period of 500 µs, i.e. 8,000 CPU cycles. This can be done with any of the three timers of the Uno. For example, you can set Timer 2 to repeatedly count from 0 to 124 with a prescaler of 64 (untested code):

// Configure Timer 2 for PWM on pin OC2B = digital 3.
OCR2A  = 125 - 1;     // period = 64 * 125 CPU cycles = 500 us
OCR2B  =   2 - 1;     // high for 64 *   2 CPU cycles =   8 us
TCCR2A = _BV(COM2B1)  // non-inverting PWM on pin OC2B
       | _BV(WGM20)   // mode 7: fast PWM, TOP = OCR2A
       | _BV(WGM21);  // ditto
TCCR2B = _BV(WGM22)   // ditto
       | _BV(CS22);   // clock at F_CPU/64

Note that the timer will also set a flag when the pulse is done, at which time you can start the data transfer. You would then start your data-taking loop with:

// Wait for the output compare flag.
loop_until_bit_is_set(TIFR2, OCF2B);

// Clear the flag.
TIFR2 |= _BV(OCF2B);

// Now transfer the data.
...

the ADC is LTC1859 and triggers on the slave select falling edge.

No, it doesn't. Here is a link to its datasheet. It has a pin labeled CONVST, for “conversion start”. According to the section Pin functions, “This active high signal starts a conversion on its rising edge.”

If you want your sampling to be at 2 kHz with cycle-accurate timings, you have to send to this pin a train of pulses which are at least 40 ns wide and have a period of 500 µs, i.e. 8,000 CPU cycles. This can be done with any of the three timers of the Uno. For example, you can set Timer 2 to repeatedly count from 0 to 124 with a prescaler of 64 (untested code):

// Configure Timer 2 for PWM on pin OC2B = digital 3.
OCR2A  = 125 - 1;     // period = 64 * 125 CPU cycles = 500 us
OCR2B  =   2 - 1;     // high for 64 *   2 CPU cycles =   8 us
TCCR2A = _BV(COM2B1)  // non-inverting PWM on pin OC2B
       | _BV(WGM20)   // mode 7: fast PWM, TOP = OCR2A
       | _BV(WGM21);  // ditto
TCCR2B = _BV(WGM22)   // ditto
       | _BV(CS22);   // clock at F_CPU/64

Note that the timer will also set a flag when the pulse is done, at which time you can start the data transfer. You would then start your data-taking loop with:

// Wait for the output compare flag.
loop_until_bit_is_set(TIFR2, OCF2B);

// Clear the flag.
TIFR2 |= _BV(OCF2B);

// Now transfer the data.
...

Edit: I would just want to add that cycle accurate timing does not mean perfectly uniform timing. The Arduino Uno is clocked off a ceramic resonator. This kind of resonator, in addition to having bad accuracy, has poor frequency stability. To get an idea of how bad it can be, see the Allan deviation plots on the article Arduino clock frequency accuracy, by Joris van Rantwijk.

Source Link
Edgar Bonet
  • 44.3k
  • 4
  • 41
  • 79
Loading