0
\$\begingroup\$

In the following AVR code, why do I always see the same voltage for all pins of port A?

I am using ATMega32.

BTW, when I am grounding some pins of port A I do not see any change in the output. It seems AVR neglects what ever I give to port A of it.

#define F_CPU 8000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include "lib/hd44780.h"

void InitADC()
{
    ADMUX=(1<<REFS0);                         // For Aref=AVcc;
    ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); //Rrescalar div factor =128
}

uint16_t ReadADC(uint8_t ch)
{
    //Select ADC Channel ch must be 0-7
    ch=ch&0b00000111;
    ADMUX|=ch;

    //Start Single conversion
    ADCSRA|=(1<<ADSC);

    //Wait for conversion to complete
    while(!(ADCSRA & (1<<ADIF)));

    //Clear ADIF by writing one to it
    //Note you may be wondering why we have write one to clear it
    //This is standard way of clearing bits in io as said in datasheets.
    //The code writes '1' but it result in setting bit to '0' !!!

    ADCSRA|=(1<<ADIF);

    return(ADC);
}

void convert_text(double voltage,char *text)
{
    int voltage_n, voltage_f;
    voltage_n=(int)trunc(voltage);
    voltage_f=(int)trunc((voltage-voltage_n)*100);
    sprintf(text,"%d.%02d",voltage_n,voltage_f);
}

void convert_voltage_text(double voltage,int battery,char *text)
{
    char stext[20];
    convert_text(voltage,stext);
    sprintf(text,"V%d=%s",battery,stext);
}

void main ()
{

    char vtext1[20];
    char vtext2[20];
    char vtext3[20];
    char vtext4[20];
    char line1[20];
    char line2[20];
    uint16_t v_val1, v_val2, v_val3, v_val4;
    double voltage1, voltage2, voltage3, voltage4;

    InitADC();
    lcd_init();
    // DDRA=0;
    // PORTA=0;

    while(1)
    {
    _delay_ms(10);
        v_val1=ReadADC(1);           // Read Analog value from channel-0
    _delay_ms(10);
        v_val2=ReadADC(2);           // Read Analog value from channel-2
    _delay_ms(10);
        v_val3=ReadADC(3);           // Read Analog value from channel-3
    _delay_ms(10);
        v_val4=ReadADC(4);           // Read Analog value from channel-4

        voltage1=((double)v_val1)/1023*5;
        voltage2=((double)v_val2)/1023*5;
        voltage3=((double)v_val3)/1023*5;
        voltage4=((double)v_val4)/1023*5;

        convert_voltage_text(voltage1,1,vtext1);
        convert_voltage_text(voltage2,2,vtext2);
        convert_voltage_text(voltage3,3,vtext3);
        convert_voltage_text(voltage4,4,vtext4);

        sprintf(line1,"%-10s %s",vtext1,vtext2);
        sprintf(line2,"%-10s %s",vtext3,vtext4);

        lcd_clrscr();
        lcd_goto(LCD_LINE1_POS);
        lcd_puts("");
        lcd_puts(line1);
        lcd_goto(LCD_LINE2_POS);
        lcd_puts("");
        lcd_puts(line2);

        _delay_ms(100);             
    }

    return(0);
}
\$\endgroup\$
  • \$\begingroup\$ What is connected to the external Reference? Whats is the "I always see the same voltage" voltage? And from what I can tell, ReadADC() won't properly set the correct channel when called multiple times, because ADMUX|=ch; won't clear existing MUX3..0 bits. \$\endgroup\$ – Rev1.0 Jun 23 '15 at 6:51
  • \$\begingroup\$ @Rev1.0 Aref=VCC. How to clear existing Mux3..0 properly? \$\endgroup\$ – barej Jun 23 '15 at 6:54
  • 1
    \$\begingroup\$ Well ADMUX &= 0b11111000; should clear the lower 3 bits before assigning the new value from ch with ADMUX |= ch;. \$\endgroup\$ – Rev1.0 Jun 23 '15 at 7:03
1
\$\begingroup\$

I think previous poster is right about not clearing the channel bits in ADMUX. Here's my well tested code:

unsigned short read_proc_ad(byte adchan){
  //start a new conversion, wait for a result, and return it
  //unsigned short retval;
  ADMUX &= ~0X1F;
  ADMUX |= adchan;
  ADCSRA |= _BV(ADSC);
  while(bit_is_set(ADCSRA,ADSC))wdt_reset();
  return ADC;
}

Note also I don't look for ADIF to rise, instead I look for ADSC to fall. Also you might want to clear the ADIF flag just before the conversion rather than after.

\$\endgroup\$
  • \$\begingroup\$ Note that in most cases it should not be necessary to call wdt_reset(); in the while loop. The conversion does not take long, so that line probably ended up there because other code "before" already took long to execute. In general its best practice to only call wdt_reset() ONCE at the end or beginning of the while loop. There are exceptions though. \$\endgroup\$ – Rev1.0 Jun 24 '15 at 6:24
  • \$\begingroup\$ Regarding the ADC: While what you say is correct, polling ADIF should work as well. However, I would also use ADSC when using single conversion mode because it is easier to control. It would still be interesting why OPs code is not working. Clearing the ADIF before starting the conversion is worth a try. \$\endgroup\$ – Rev1.0 Jun 24 '15 at 6:27
  • \$\begingroup\$ Please update the answer with my solution: ch=ch&0b00000111; ADMUX&=~(0b00000111); ADMUX|=ch; \$\endgroup\$ – barej Jun 26 '15 at 23:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.