Electronic – Arduino PORT manipulation analogRead()

analogarduinoport

I am trying to figure out on my Arduino UNO how to read analog input using port manipulation. I have found a lot of tutorials on how to do that for digitalRead / digitalWrite, but nothing with analogRead / Write.

I want to read the analog data coming from the FSR sensor but I cannot figure out how to do that.
FSR sensor

I found that for analog pins I have to use the C port of ATMega, and I tried by testing the code below.
Code
but the only thing I get as result is either 0 or 1 when I get over half of the read values.

How can I get the values as an integer by using port manipulation? Or a double/float, the format of the result is not that important as long it is not boolean.

Best Answer

The analog readings for pins are obtained using the ADC peripheral, not a GPIO port. Additionally, you might not get the performance benefits you are looking for, since the relative overhead of analogRead is not as large as the overhead of digitalRead, and most of the time is spent actually waiting for the ADC to do a conversion.

The exact registers and details are given in the atmega328p datasheet, but I'll give an overview of a simple approach. Of course, you'll want to consult the datasheet for full details and explanations.

First, you'll need to enable the ADC and set the desired prescaler for an ADC clock of between 50 and 200 kHz by setting the ADEN, ADPS2, ADPS1, and ADPS0 bits in ADCSRA. For an Arduino clocked at 16 MHz, this gives an ADC clock of 125 kHz, well within the required range:

enter image description here Datasheet page 258

You'll then need to set the high bits of ADMUX, which specify the analog reference you want to use (external, 5 V AVCC, or 1.1 V internal reference). The default behavior of Arduino's analogRead is the 5 V AVCC reference, meaning that you'd need to set the REFS0 and clear the REFS1 bit.

enter image description here Datasheet page 257

You can then select the input you want to read by setting the lower four bits of ADMUX (following table 24-4 on page 258), and then set the ADSC (A/D Start Conversion) of ADCSRA to start a conversion. You can then loop waiting for ADSC to return to 0 indicating the conversion finished (or set an interrupt instead); once the conversion is done the results will be present in ADCH and ADCL.

However, if you're looking to optimize the time for reading a single analog port, you'll want to use a free-running mode. By setting ADATE in ADCSRA to 1 and leaving ADCSRB at all-zeros (default indicating free-running), the ADC will automatically perform analog reads, putting the result into ADCH and ADCL when it's ready -- you can read it at any time, for only the cost of two port reads. Note that in this case, you'll want to read ADCH and ADCL atomically by reading ADC (a syntactic sugar that leads to the compiler reading the two registers atomically):

 uint16_t result = ADC;

If you wish to save power, you may want to set the appropriate bits of DIDR0 as well; digital input circuitry can sometimes use excess power if an analog input not close to 0 or 5 V is provided, and disabling the digital input using DIDR0 will prevent this excess power consumption.