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.

I found that for analog pins I have to use the C port of ATMega, and I tried by testing the code below. 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.

• Why not use analogRead()? May 31, 2021 at 8:04
• I have to program an ATMega using AVR codevision and as I noticed I have to use port and register manipulation, it's not like arduino ide or smth else and i am trying to get how to do that May 31, 2021 at 15:41

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:

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.

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.

• So basically, I guess I have to do something like this guy does starting with min 11 youtube.com/watch?v=IhtD_VSoURk Sorry if I don't get everything you say, I'm beginner with arduino embedded programming May 30, 2021 at 23:09
• @Shortcircuit Potentially; I'm having a bit of trouble following what's going on in the video, but that looks right. Getting a better understanding of the concepts (i.e. what the registers mean and how to interact with them) should help clarify things. May 30, 2021 at 23:17
• yeah, same, it's pretty hard to follow it but I find it like a visual way to follow while reading your instructions and might help I guess, thank you! May 31, 2021 at 15:39

The complete details are described in the atmega328 datasheet starting on page 205.

As @nanofarad mentions, particular attention needs to be paid attention to setting up the ADC clock which should be between 50 kHz and 200 kHz. The ADC clock is derived from the system clock using one of eight pre-scalar settings set in the ADCSRA register.

Except for setting the ADC clock frequency, this gist shows how to perform an analog read:

https://gist.github.com/Wollw/2396604

There are bits in the ADMUX register which need to be set up, a conversion is started by setting the ADSC bit in the ADCSRA register and then you have to wait until the ADSC bit is set to zero.

You can also have an interrupt generated when a conversion is completed. See this blog post for more details:

http://www.glennsweeney.com/tutorials/interrupt-driven-analog-conversion-with-an-atmega328p

There are additional features available when controlling the ADC directly such as selecting the source for AREF, having a conversion automatically start on an event, left adjusting the conversion result.

• @ErikR so basically the mistake i am doing is using PORT/PIN/DDR C instead of ADC and ADMUX and their configuration and setup. Thought if B and D ports can be used just like that the analog pins are alike May 30, 2021 at 22:47
• Yes, the 10-bit result of an ADC conversion is found in the ADCL and ADCH registers (available as the 16-bit ADC register in C). What's your question about the B and D ports? May 30, 2021 at 22:53