Fast Signal Sampling (Part 8)

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

For the Arduinoos fans. Recent optimization works lead me to update my fast signal sampling library. First of all, I am myself creating some contradictions that I hate with others software editors: change software names! So, for now and then, the library which contains the fast signal sampling code is nammed PlainADC. The new release includes:

  • Bugs correction
  • Fractional frequencies
  • Optimized auto prescalers
  • Wider frequency range: 0.25 Hz up 150 kHz
  • Single shot data acquisition with programmable dwell time
  • Please check this page if you are interested in the code.

    For those who cannot wait ;-), this is an abstract from the revised library containing the most critical part of the code: parameters setting including auto ranging.

    void PlainADC::SetAcquisitionEngine(uint8_t adcChannel, uint8_t refVoltage, double samplingFrequency, uint16_t samples) 
    /* Set acquisition parameters */
    {
    	if (_dataAcqStatus == DAC_DISABLED) {
    		/* Set global variables */
    		_refVoltage = refVoltage;
    		_lastSamples = _samples;
    		_samples = samples;
    		_samplingFrequency = samplingFrequency;
    		/* Data vector */
    		if (_lastSamples != _samples) {
    			if (vData != NULL) {
    				free(vData);
    			}
    			vData = (uint16_t*)malloc(_samples * sizeof(uint16_t));
    		}
    		/* Disable all interrupts */
    		cli();
    		/* Clear ADC control registers */
    		ADCSRA = 0x00;
    		ADCSRB = 0x00;
    		ADMUX  = 0x00;
    		/* Set reference voltage */
    		ADMUX |= (_refVoltage << 6);
    		/* Set ADC channel */
    		ADMUX |= adcChannel;
    		/* Compute ADC prescaler */
    		double divisionFactor = ((13.0 * _samplingFrequency * 1.0E+6) / 16.0);
    		uint8_t adcPrescaler = 7; /* Default division factor exponent */
    		while ((divisionFactor > PowerTwo(adcPrescaler)) && (adcPrescaler > 2)) {
    			adcPrescaler -= 1;
    		}
    		/* Set ADC prescaler */
    		ADCSRA |= adcPrescaler;
    		/* 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 = 0x00; 
    		TCCR1B = 0x00; 
    		TIMSK1 = 0x00; 
    		/* Set Clear Timer on Compare Match (CTC) Mode */
    		TCCR1B |=  (1 << WGM12); 
    		/* Compute clock timer prescaler */
    		uint16_t preScalers[] = {1, 8, 64, 256, 1024};
    		uint8_t timerPrescaler = 0;
    		uint32_t upperCount;
    		do {
    			upperCount = uint32_t(F_CPU / (_samplingFrequency * preScalers[timerPrescaler])) - 1 ;
    			timerPrescaler += 1;
    		} while ((upperCount > 0xFFFF) && (timerPrescaler < 5));
    		OCR1A = uint16_t(upperCount);
    		/* Set timer prescaler */
    		TCCR1B |= timerPrescaler; 
    		/* Enable timer1 interrupt  */
    		TIMSK1 |= (1 << OCIE1B);
    		/* Set acquisition status */
    		_dataAcqStatus = DAC_IDLE; 
    		/* Enable all interrupts */
    		sei();
    	}
    };
    

    The timer1 prescaler is now automatically set depending upon the sampling frequency. While the lower limit frequency is set by the ATMega specification (Maximum prescaler for timer1 = 1024, so Fmin = F_CPU/((MaxUpCounts+1)*1024) ≈ 0.238), the upper limit depends upon the speed of the a/d converter and to the related code.

    Next post on same subject

    Leave a Reply

    You must be logged in to post a comment.