Tips and tricks (Part 23)

Previous T&T

Although the situation is not very common, you may face like me the following situation: I needed to read analog signals applied to some of the 6 ports from an Arduino UNO. But some of the readings would use the Vcc reference (so as to say the analogReference(DEFAULT))  and some others would use the 1.1 V internal reference (so as to say the analogReference(INTERNAL)).

Jumping from one reference to another is not without risks of getting very confusing analog measurements. Arduino reference manual says:

After changing the analog reference, the first few readings from analogRead() may not be accurate.

Good point, however how much is “few”? If you drop an eye on the ATMEGA 328 Datasheet, you may read that: “the user must allow the (internal) reference to start up before the output is used…. … Refer to ”Internal Voltage Reference” on page 51 for details on the start-up time.”

So far so good. And well, I decided that I would do something special to make 100% sure that my readings are correct, whatever the operating conditions. The best way to do that would be to use a reference voltage, attach its output to a dedicated analog port and perform measurements as long as the gap between to consecutive measurement is not null. Although this option is bulletproof, it is expensive in terms of addtional components and commissioning of analog ports.

After some more reading in the ATMEGA data sheet, one can read at the MUX section that 6 among the the 14 theoretical ports are used by arduino, one (actually port 8) allows access to the chip temperature (the famous hidden thermometer) and another one (port 14) reads the internal 1.1 V! Hurray! We have a built in reference!


Here is is the function that I wrote to perform the analog to digital startup. The comments in the code should suffice to explain the way it works.

void InitAdc(uint8_t ref)
	ADMUX = 0x00; /* Clear register */
	analogReference(ref); /* Set reference */
	analogRead(0); /* Read any channel once in order to set parameters */
	ADMUX |= 0x0E; /* Set channel 14 so as to say 1.1 V ref */
	const uint8_t ADSCmask = (1 << ADSC); /* Compute mask once to save time */
	/* Set comparison parameters */
	const uint8_t maxReadings = 100;
	const uint8_t maxDelta = 4;
	/* Local variables */
	uint8_t readings = 0; /* Readings prevent infinite looping */
	uint16_t lastReading = 0;
	uint16_t reading = 0;
	do {
		readings += 1; /* Record readings */
		lastReading = reading; /* Record last reading */
		ADCSRA |= (ADSCmask | (1 << ADEN)); /* Start single conversion */
		while((ADCSRA & ADSCmask) == ADSCmask); /* Wait for conversion to complete */
		reading = (ADCL | (ADCH << 8)); /* Compute resulting adc counts */
	} while ((abs(reading - lastReading) > maxDelta) && (readings < maxReadings));		



Next T&T

Leave a Reply

You must be logged in to post a comment.