M48 ADC again!

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Freaks,
I am trying to use the M48 ADC to display a value on the LCD. I have used this before with a temp. sensor and the entire circuit works fine. Here is what I am trying now:

               
VDD   -----+-
5V         |     
           |           
           /        
           \ R1= 10K    
           /          
           |           
           |           
           |        10K     To pin 24 (ADC1)
           +------/\/\/\---------+ 
           |                  
           /                  
           \ R2= 10K              
           /
           |
          GND 

Here is my code:

void ADC_init()

   {
      DDRC = 0x00;
      ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADATE); // Set ADC prescaler to 128 - 125KHz sample rate @ 16MHz
       ADMUX |= (1 << REFS0); // Set ADC reference to AVCC
     //using all 10 bit of ADC; so do not left justify the ADC value
       DIDR0 = (1 << ADC0D); //turn off the digital driver
   // No MUX values needed to be changed to use ADC0
       ADCSRA |= (1 << ADEN);  // Enable ADC
       ADCSRA |= (1 << ADIE);  // Enable ADC Interrupt
        sei();   // Enable Global Interrupts
        ADCSRA |= (1 << ADSC);  // Start A2D Conversions; make sure the ADC is turned on after all the init functions are completed
 
}

/***************ADC ISR*****************************/
ISR(ADC_vect)
{
    
        
	ADC_buffer = ADC;
	                                         sprintf(ADC_value_LCD,"%03dV",ADC_buffer);//MEASURE VOLTAGE
	 ADC_value_LCD[4] = '\0';
         LCD_set_x_y(1,2);
         LCDString(ADC_value_LCD);
}


 int main()
   {

  
  	 DDRB = 0xFF;
	 DDRC = 0x00; 
         LcdInitialize();
	 timer_init(); 
         ADC_init();
         ext_interrupt_init();


          for(;;)
            {

               go_to_sleep();
            }
     }

The timer_init() and the ext_interrupt_init() are other functions that I am using.

I measured the voltage at pin 24 and it is 2.47V. I have connected AVCC to VCC. The LCD is showing me 062V instead of 511. What could be wrong?

Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
ADMUX |= (1 << REFS0);

should be

ADMUX = (1 << REFS0) | 1;

In your code you did not select channel 1, default after reset is channel 0.
Furthermore I would use = instead of |= (at least in the first assignment).

I would never ever place such a code- and time-intensive routine like

sprintf(ADC_value_LCD,"%03dV",ADC_buffer);//MEASURE VOLTAGE 
ADC_value_LCD[4] = '\0'; 
LCD_set_x_y(1,2); 
LCDString(ADC_value_LCD);

inside an ISR!

Edit: and of course you should disable the digital driver from ADC1 instead of ADC0.

/Martin.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for your feedback, Martin.

I made those changes here is my code now:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

volatile unsigned char count;
volatile unsigned char ADC_value_LCD[5];
volatile unsigned char ADC_value_LCD_cel[5];
volatile unsigned char ADC_buffer;
volatile unsigned char ADC_flag;
unsigned char address;


/*******************BEGIN LCD code******************/

#define LCD_DATA_PORT PORTD
#define LCD_DATA_DDR  DDRD
#define LCD_CTRL_PORT PORTD
#define LCD_CTRL_DDR  DDRD
#define LCD_RS        PD0 //2
#define LCD_RW        PD1//3
#define LCD_E         PD2//4
#define LCD_FUNCTION_SET      0x38 // 0b00110000
#define LCD_FUNCTION_SET_4BIT 0x28 // 0b00101000
#define LCD_DISPLAY_OFF       0x08 // 0b00001000
#define LCD_DISPLAY_ON        0x0F // 0b00001111
#define LCD_DISPLAY_CLEAR     0x01 // 0b00000001
#define LCD_ENTRY_MODE_SET    0x06 // 0b00000110
#define LCD_CURSOR_HOME       0x02 // 0b00000010
#define LCD_CURSOR_BLINK_OFF  0x0E
#define LCD_HOME_FOR_TEMP     0x88
#define BYTE unsigned char


void LcdSendNibble( uint8_t nibble )

{

   _delay_ms(20);

   // Output upper nibble on the data ports upper bits

   LCD_DATA_PORT = (nibble & 0xF0) | (LCD_DATA_PORT & 0x0F);

 

   // Toggle the E line

   LCD_CTRL_PORT |= (1<<LCD_E);   // Going up..

   LCD_CTRL_PORT &= ~(1<<LCD_E);  // ..and down.

}

void LcdSendByte(uint8_t theByte)

{

   // Send the high nibble

   LcdSendNibble(theByte);

 

   // Shift theByte to get lower nibble in upper part...

   theByte = theByte << 4;

   // ...and send it

   LcdSendNibble(theByte);

}


void LcdSendInstruction( uint8_t theInstruction )

{

   // RS low for instructions

   LCD_CTRL_PORT &= ~(1<<LCD_RS);

 

   // Send the instruction

   LcdSendByte(theInstruction);

}



void LcdSendCharacter(uint8_t theChar)

{

   // RS high for characters to display

   LCD_CTRL_PORT |= (1<<LCD_RS);

 

   // Send the command

   LcdSendByte(theChar);

}


void LcdInitialize(void)

{

   // initialize LCD control lines

   LCD_CTRL_PORT &= ~(1<<LCD_RS);    // RS low

   LCD_CTRL_PORT &= ~(1<<LCD_RW);    // R/W low

   LCD_CTRL_PORT &= ~(1<<LCD_E);    // E low

 

   // initialize LCD control lines to output

   LCD_CTRL_DDR |= (1<<LCD_RS);

   LCD_CTRL_DDR |= (1<<LCD_RW);

   LCD_CTRL_DDR |= (1<<LCD_E);

 

   // initialize LCD data port to input

   LCD_DATA_DDR |= 0xF0;      // Data on high four bits of port for now...

    _delay_ms(15);

   LcdSendNibble( LCD_FUNCTION_SET );

   _delay_ms(5);

   LcdSendNibble( LCD_FUNCTION_SET );

   _delay_us(100);

   LcdSendNibble( LCD_FUNCTION_SET );

 

   // Now, still in 8-bit mode, set the display to 4-bit mode

   LcdSendNibble( LCD_FUNCTION_SET_4BIT );

 

   // We are now in 4-bit mode.

   // Do the rest of the init sequence.

   LcdSendInstruction( LCD_FUNCTION_SET_4BIT );

   _delay_ms(500);

   LcdSendInstruction( LCD_DISPLAY_OFF );

   _delay_ms(500);

   LcdSendInstruction( LCD_DISPLAY_CLEAR );

   _delay_ms(500);

   LcdSendInstruction( LCD_ENTRY_MODE_SET );

   _delay_ms(500);

   LcdSendInstruction( LCD_DISPLAY_ON );

   _delay_ms(500);

}

void LCDString(char *str) //New function to send string to LCD

 {

   while(*str)

     LcdSendCharacter(*str++);
 }


void LCD_set_x_y(BYTE x, BYTE y)
{

 if(x == 0)
 {
  
  address = 0x80;
  }
  else if (x == 1)
  {

   address = 0xC0;
   }

   if (y < 16)
   address += y;

  LcdSendInstruction( address );

  }

/********************ADC INIT FUNCTION***************/
void ADC_init()

{
   DDRC = 0x00;
   //PORTC = 0x00;
   ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADATE); // Set ADC prescaler to 128 - 125KHz sample rate @ 16MHz
   ADMUX = (1 << REFS0) | 1; // Set ADC reference to AVCC
   //ADMUX |= (1 << ADLAR);
   //using all 10 bit of ADC; so do not left justify the ADC value
   DIDR0 = (1 << ADC1D); //turn off the digital driver
   // No MUX values needed to be changed to use ADC0
   ADCSRA |= (1 << ADEN);  // Enable ADC
   ADCSRA |= (1 << ADIE);  // Enable ADC Interrupt
   sei();   // Enable Global Interrupts
   ADCSRA |= (1 << ADSC);  // Start A2D Conversions; make sure the ADC is turned on after all the init functions are completed
 
}

/***************ADC ISR*****************************/
ISR(ADC_vect)
{
    
        

         ADC_flag = 1;
	 ADC_buffer = ADC;
}
 int main()
{

  
  	 DDRB = 0xFF;
     LcdInitialize();
     ADC_init();

  for(;;)
  {

     if(ADC_flag == 1)
	  {

	    sprintf(ADC_value_LCD,"%03dV",ADC_buffer);//MEASURE VOLTAGE
		ADC_value_LCD[4] = '\0';
		LCD_set_x_y(1,2);
        LCDString(ADC_value_LCD);
      }

   }
}

Now something strange is happening :)

When I connect a multimeter to measure the voltage at the ADC1 pin, I am getting 2.5V on the meter, but the value seen on the LCD is 256. When I remove the meter, it goes back to 006V.

I even tried removing the DDRC = 0x00 statement and it did not make any difference.Is this because of some kind of loading/impedance issue?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

if(ADC_flag == 1)

And where is the flag getting cleared?

If you are indeed doing continuous conversions, there really isn't a need for the flag--there will be many, many conversions done every display operation.

The read of "ADC_buffer" in the mainline muse be done as an atomic operation--with global interrupts off.

(None of the above addresses your 006V situation. Check for common ground.)

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's a good point, Lee.

Ok I made sure my grounds were ok and now I am getting a value of 255V. Could this be because of a char limitation? The expected value is 512 which is outside the char range. Should I make my array an integer array?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also when I remove R2 completely, I get 255.
With a 22k I get 198. With a 1k I get 92.
What could be wrong?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Could this be because of a char limitation? The expected value is 512 which is outside the char range. Should I make my array an integer array?

Unless you know some miracle way to squeeze a ten bit reading into an eight bit variable then, yes, the destination variables need to be the next size up from 8 bit which means 16 bit.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok I changed ADC_buffer from unsigned char to uint and it worked. That was a stupid mistake..:)

I am looking for only one single conversion. Is my ADC set up to do that? I know ADCSRA |= (1 << ADSC); is supposed to start the conversion but is it a single conversion? I see the LCD getting refreshed continuously but does that mean I am doing multiple conversions?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

And where is the flag getting cleared?

Quote:

I see the LCD getting refreshed continuously but does that mean I am doing multiple conversions?

Are these two things related I wonder?

(yes it's single conversion - you only ever trigger ADSC once and it's not configured to free run - it's just that once the ISR sets the flag your test in the main() for() loop is now always true so each time round the for() it rewrites the display with the one and only reading ever taken (a few microseconds after power was applied)

To be honest I simply don't see the point of the interrupts in this - why not just loop polling the thing?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

it's not configured to free run

???
Quote:

| (1 << ADATE);

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Cliff,
I removed the interrupt and am now collecting the ADC value in the ADC_buffer variable in a for loop in main.

@Lee,
That makes sense.
I re-read the datasheet and it says when ADATE is set to 1, the AVR looks for a trigger source which is decided by the ADTS bit in the ADCSRB register. Since ADTS(2:0) is zero, it has selected free running mode.

So to make a true single conversion, should I leave the ADATE bit cleared when I init my ADC?