Fast Signal Sampling (Part 7)

Part 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 11, 12

Erratum. This one is a really stupid bug which took me quite some time to understand and find. Once in a while, I recieved an uploaded data value of 1023, so as to say a full scale value, preferably while scanning at high speed rates (above 32 kHz).

It appeared to be a #$%&#@*ยง typo kind of bug which in fact was decreasing the dwell time from the a/d converter! For some reasons, I divided te prescaler by two before writing it to the register. Plain stupid. The nice thing about it, is that the observed signal to noise ratio has now improved by almost 50%. Which is confirming the theory (S/N is proportional to the sqrt of the dwell time).

Here is the corrected code. For sake of easiness of use, I also simplified the timer prescaler too, to the cost of limiting the scanning speed from 256Hz to 64000Hz.

void PlainSCAN::SetSCANParameters(uint16_t samplingFrequency, uint16_t samples, uint8_t refVoltage) 
/* Set acquisition parameters */
{
	/* Stop data acquisition engine if necessary */
	if (_dataAcqStatus != SCAN_DISABLED) {
		DisableSCANEngine();
	}
	_refVoltage = refVoltage;
	_samples = samples;
	_samplingFrequency = samplingFrequency;
	/* Timing parameters */
	uint16_t timeInterval = uint16_t(1000000.0 / samplingFrequency); /* In us from 50000 (20 hz) to 16 (64 kHz) */
	cli();
	/* Clear control registers */
	ADCSRA = 0x00;
	ADCSRB = 0x00;
	ADMUX  = 0x00;
	/* Right aligned data, for 10 bits records */
	ADMUX &= ~(1 << ADLAR); 
	/* Set reference voltage */
	ADMUX |= (_refVoltage << 6);
	/* Compute adc prescaler value automatically, allowed value are 2^1 to 2^7 */
	_adcPrescalerExp = 7; 
	/* Decrease prescaler down to 16; for some reasons, decreasing below 16 drives to unaccurate timing */
	/* Prescaler = 1/((Freq * 13)/F_CPU) */
	while ((1 << _adcPrescalerExp) > uint16_t((timeInterval * 16.0) / 13.0)) {
		_adcPrescalerExp -= 1;
	}
	/* Set prescaler */
	ADCSRA |= _adcPrescalerExp;
	/* Enable ADC */
	ADCSRA |= (1 << ADEN); 
	/* ADC Auto Trigger Enable */
	ADCSRA |= (1 << ADATE); 
	/* ADC interrupt Enable */
	ADCSRA |= (1 << ADIE); 
	/* ADC Auto Trigger Source Selection */
	ADCSRB |= ((1 << ADTS0) | (1 << ADTS2)); 
	/* Set Timer1 */
	/* Reset Timer/Counter1 control registers and Interrupt Mask Register */
	TCCR1A = 0; 
	TCCR1B = 0; 
	TIMSK1 = 0; 
	/* Set Clear Timer on Compare Match (CTC) Mode */
	TCCR1B |=  (1 << WGM12); 
	/* Set fixed prescaler (256 to 64000 Hz) */
	TCCR1B |= 1; 
	/* Set upper count value: (F_CPU / (freq * Prescaler) */
	OCR1A = uint16_t(16000000.00 / samplingFrequency) - 1;
	/* Reset Timer/Counter2 */
	TIMSK2 = 0; 
	/**/
	sei();
};

Next post on same subject

Leave a Reply

You must be logged in to post a comment.