[SOLVED] Cannot Operate ADC Correctly using AVR-Lib

Hello there !

I am working on an external testcase (not using arduino IDE and arduino core) using avr core and avr dude on arduino nano atmega328p, i am facing a problem in which i cannot read the value of the joystick module from ADC0 (also tried ADC1), the value of ADC0 is always 1023 when combining both ADCL and ADCH on a uint16_t.

Here is the UART result when the joystick is in neutral position:

1010

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023

1023
...

Here is my connection:

  1. Arduino AREF -> 5v.
  2. POT-GND -> Arduino GND.
  3. POT-VCC -> Arduino 5v.
  4. POT-SIGNAL -> Arduino ADC0.

Here is my code:

#include<Serial.h>

void Serial::UART::startProtocol() {
	UCSR0B = (1 << TXEN0) | (1 << RXEN0); // TXEN_BIT = 1, enables the transmitter buffer register.
    UCSR0C = (1 << USBS0) | (3 << UCSZ00); // enables the UCSZ0, UCSZ1 and URSEL
    UBRR0 = 0x10; // 0x10 (16) for BR = 57600 // 0x33 (51) for 9600
}

void Serial::UART::stopProtocol() {
    UCSR0B = 0x00; 
}

uint8_t Serial::UART::read() {
    while (!(UCSR0A & (1 << RXC0)));
	return UDR0;
}
 
void Serial::UART::cprint(char& data) {
    while (!(UCSR0A & (1 << UDRE0)));
	UDR0 = data;
}

void Serial::UART::cprintln(char& data) {
    cprint(data);
    sprint(NEW_LINE_CARRIAGE_R);
}

void Serial::UART::sprint(char* data) {
    int i = 0;
    while (i < strlen(data)) {
        cprint(data[i++]);
    }
}

void Serial::UART::sprintln(char* data) {
    sprint(data);
    sprint(NEW_LINE_CARRIAGE_R);
}

void Serial::UART::print(const uint64_t& data, const uint8_t& base) {
    char* strBuffer;
    if (base == BIN_RADIX) {
	    strBuffer = (char*) calloc(1, CHAR_OF_BIN);
    } else if (base == DEC_RADIX) {
	    strBuffer = (char*) calloc(1, CHAR_OF_DEC);
    } else {
	    return;
    }
    // convert input to string
    itoa(data, strBuffer, base);
    int i = 0;
    while (i < strlen(strBuffer)) {
        cprint(strBuffer[i++]);
    }
    free(strBuffer);
}

void Serial::UART::println(const uint64_t& data, const uint8_t& base) {
    print(data, base);
    sprint(NEW_LINE_CARRIAGE_R);
}
#include<Analog.h>

void Analog::Adc::startProtocol() {
    // setup ADCSRA
    ADCSRA = (1 << ADEN) /*enable adc protocol*/ | (1 << ADIE) /*enable interrupt service*/ 
                | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) /*set clock prescaler to clk/128*/; 
}

void Analog::Adc::stopProtocol() {
    ADCH = 0x00;
    ADCL = 0x00;
    ADCSRA = 0x00;
}

void Analog::Adc::startConversion(const uint8_t& PIN) {
     // setup ADMUX
    ADMUX = 0b00000000 | PIN; 

    ADCSRA |= (1 << ADSC); // the last step: start conversion
}

uint16_t Analog::Adc::analogRead() { 
    volatile uint8_t adcl = ADCL;
    volatile uint8_t adch = ADCH;
    return ((0x00 | adch) << 8) | adcl; // concatenate the 2 (8-bit registers) in a 16-bit software register
}
#define F_CPU 16000000UL 

#include<avr/io.h>
#include<util/delay.h>
#include<string.h>
#include <avr/interrupt.h>

#include<Serial.h>
#include<Analog.h>

Serial::UART* uart = Serial::UART::getInstance();
Analog::Adc* adc = Analog::Adc::getInstance();

ISR(ADC_vect){
	uart->println(adc->analogRead(), 10);
	adc->startConversion(0b0000);
	_delay_ms(500);
}

int main(void) {
	uart->startProtocol();
	sei();
	adc->startProtocol();
	adc->startConversion(0b0000);
	while (1);	
	return 0;
}

I hope i am not doing a silly mistake as usual :sweat_smile:.
Thank you !

Why this odd way? Have You tried using the IDE and the most common way? That would be easier for several helpers to analyze.
Until then, I'm out.

1 Like

Thanks for your reply, ofc i have used arduino core and arduino IDE in several testcases before and even with java JSerialComm, but i am doing some testcases with avr core for learning purposes (i still haven't wired up my avr, so using my arduino nano for now).

Okey. Only Your question is outside my comfort zone and I need to step back.

Ehh.... Do You have any tools for test printing the ADC reading?

I am using the terminal (echo command) and arduino IDE serial monitor, but will write a tool soon.

EDIT:
I posted the test above before my code, the ADC0 read is always 1023 when using an external AREF Voltage (on ADMUX = 0b00xxxxxx, the first 2 bits are zero) and connecting the REF pin to 5v pin.

Something i don't understand on the arduino nano circuit diagram about AREF is this:
image

Fine! Do that!
During many years I was a hired guy thrown into large, unknown systems to find bugs and correct them.
Using test prints from inside the code made every job successful. Debugging, gossiping out data from the inside. Incredibly useful. Often several stages of printing was needed to find the code where the bug was.

1 Like

Yeah, debugging is an absolute rule of software development, so this is the UART println (i did a small lib to print chars, integers, large integers and strings).

@Railroader Alright, i figured it out with extra debugging and testing against this arduino simple code (as it's very similar to mine) and a regular potentiometer instead of the joystick module for simple output:

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.println(analogRead(A0));
  delay(500);
}

And then i discovered that there are some pins that weren't correctly secured in place including the ADC ones, now my code runs fine, i have a github repo for it and going to post it soon (for experimental usages), here is the output:

1023

1022

1021

1023

1021

1021

1005

526

520

543

1021

1021

1020

1019

1022

1020

1023

1023

1022

1021

1021

849

680

545

489

386

275

236

201

2

0

0

0

0

0

0

0

0

Thanks for the help.

1 Like

As far as I know the default reference is Vcc. When You want another reference, connect it to the desired source. Know that I'm an UNO guy so far.

1 Like

Well done! Usually a joystick contains 2 pure resistive pots. Connecting the proper pins correctly is of course essential.

Fine You got up on the tracks! (Railroad driver as well.)

1 Like

You can flag the reply helping You as "the solution".

1 Like

There is an extra step on AVR which is setting the ADMUX register bits (REFS1 and REFS0 reference selection bits) through ADMUX = 0b00000000 | PIN the other part (| PIN) is selecting the MUX code for the ADC pin.

And this is the truth table for it:

AFAIK, Arduino has the same analogy but simpler using the analogReference() directly....but i haven't tried it before.

May be this circuit corresponds to setting the REFS1 = 0 and REFS0 = 1 which is the same as the Vcc.

That's more than I know. My guess would have been that there are ready declarations to use when selecting the ref.
Anyway, feels good You're up and running. That's the goal.
Time to get horizontal regarding tomorrow duties, driving veteran trains.

1 Like

I can only agree.

1 Like

And that is why people (are encouraged to) use the Arduino IDE. :grin:

Indeed, so you do not need that external connection and it is advised against in case you select the internal 1.1 V reference as apparently whatever the reference is, it appears on AREF.

1 Like

As promised, here is my working code with the documentation:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.