provably a variable type problem

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

hello guys, I am stucked trying to make 2 misterious numbers in my lcd.
it is about the voltimeter. I copied the code but it didnt work.So i am trying to fix it.All seems to be working. but the values are only like this. 1.00 2.00 3.00, i mean... i never get 2.65...

#include 
#include 
#include "lcd.h"


//output format		1.2.34 indicates (channel 1's voltage is 2.34)


#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif


void InitADC(void);

long ReadChannel(int channel);

void DisplayReading(long Reading, int channel);

void UpdateLCDDisplay(int value, int displayindex);


int main(void)
{
	long Adc_reading;
	int i;
	
	lcd_init(LCD_DISP_ON);	
	lcd_clrscr();
	InitADC();
	
	
	i=7;					
	while(1)
	{		
		Adc_reading=ReadChannel(i);
		DisplayReading(Adc_reading,i);		
		lcd_gotoxy(0,1);
		lcd_putc(i+48);
		lcd_puts(" : Channel");		
	}
return 0;
}


void InitADC(void)
{
	ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX1) | _BV(MUX0);
	ADCSRA=0X83; 	
}

long ReadChannel(int channel)
{
	switch(channel)
	{
		case 0:
			ADMUX =_BV(REFS0); 
			break;
		case 1:
			ADMUX =  _BV(REFS0) | _BV(MUX0);
			break;
		case 2:
			ADMUX = _BV(REFS0) | _BV(MUX1); 
			break;
		case 3:
			ADMUX =  _BV(REFS0) | _BV(MUX1) | _BV(MUX0); 
			break;
		case 4:
			ADMUX =  _BV(REFS0) | _BV(MUX2);
			break;
		case 5:
			ADMUX =  _BV(REFS0) | _BV(MUX2) | _BV(MUX0); 
			break;
			case 6:
			ADMUX =   _BV(REFS0) | _BV(MUX2) | _BV(MUX2)| _BV(MUX1); 
			break;
			case 7:
			ADMUX = _BV(REFS0) | _BV(MUX2) | _BV(MUX2)| _BV(MUX1)| _BV(MUX0); 
			break;
		
	}
	
	ADCSRA |= _BV(ADSC); 
	while (ADCSRA & (1 << ADSC));
 	
	ADCSRA |= _BV(ADSC); 
	while (ADCSRA & (1 << ADSC)); 	
		
	return ADC;	
}

void DisplayReading(long Reading, int channel)
{

		
	char d2=0;
	char d3=0;
	char d4=0;
	float voltage1;
	float voltage2;
	float voltage3;
/////////////////////////////////////////////
	voltage1=(((Reading*5)/1023));
	d2=voltage1;
	
	voltage2=10*(((Reading*5)/1023)-d2);
	d3=(int)voltage2;


	voltage3=(voltage2-d3)*10;
	d4=voltage3;
//////////////////////////////////////////////////////

	
	lcd_gotoxy(1,0);
	UpdateLCDDisplay(d2,0);	
	
	lcd_gotoxy(2,0);
	lcd_putc('.');
	
	lcd_gotoxy(3,0);
	UpdateLCDDisplay(d3,0);
	
	lcd_gotoxy(4,0);
	UpdateLCDDisplay(d4,0);

	
	_delay_us(200);
}

void UpdateLCDDisplay(int value, int displayindex)
{
	int v;		
	lcd_putc(value+48);		
}



I think is some variable type problem.
due to that seem that the result of the operation for 3 and d4 is equal to 0.

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

osaka wrote:
the result of the operation for 3 and d4 is equal to 0.
I didn't immediately see what the specific problem is but it seems to me that you're going about the task in a rather difficult way. It would be much easier (and probably more efficient) to convert the ADC reading to millivolts and then produce the digits that you want from that value. No floating point operations are needed.

void DisplayReading(long Reading, int channel)
{
  int idx;
  char digits[4];

  // convert the ADC reading to millivolts
  Reading = (Reading * 5 * 1000) / 1023;

  // convert to individual digit values
  for (idx = sizeof(digits) - 1; idx >= 0; idx--)
  {
    digits[idx] = Reading % 10;
    Reading /= 10;
  }

  // output the decimal result to two decimal places
  lcd_gotoxy(1,0);
  UpdateLCDDisplay(digits[0],0);

  lcd_gotoxy(2,0);
  lcd_putc('.');

  lcd_gotoxy(3,0);
  UpdateLCDDisplay(digits[1],0);

  lcd_gotoxy(4,0);
  UpdateLCDDisplay(digits[2],0);

   _delay_us(200);
}

As a side note, the switch statement for setting the ADMUX value can be simplified greatly by observing that the MUXn bits occupy the least significant bit positions of the ADMUX register.

#define CHAN_MASK 0x07
ADMUX = _BV(REFS0) | (channel & CHAN_MASK); 

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

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

oooooooooooooooooooooo

thanks a lot man. a free beer for you.
you are so accurate, so efficient. I just replace my code with yours...and is working with an erro of 30mV.

I lost all the day with this, Is is so frustrating....
, but finally I found some help.

THANK YOU DKINZER

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

I discovered what was my problem. "Reading" was a long type variable. I had to make a division and the result of the operation could be something like 3.36 but long does not have fractions, so with my previous code I would get 3V as a result. That's why my result was always an integer.
Forcing the operation to float to keep fractions I can get all the other numbers.

BAD CODE

voltage1=(((Reading*5)/1023)); 

GOOD CODE

voltage1=(float)(((Reading*5)/1023)); 

Anyway, keeping this code the program size is about 4000 bytes, against you code's 1000 bytes.

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

osaka wrote:
I discovered what was my problem.
Now that you have it working, one other change you should make is to scale the reading by dividing by 1024 instead of 1023. This will make it slightly more accurate.

osaka wrote:
Anyway, keeping this code the program size is about 4000 bytes, against you code's 1000 bytes.
I suspect that much of the difference is due to your code needing the floating point operation code. Generally speaking, you'll do well to avoid using floating point operations unless you absolutely have to have them.

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

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

Another change to consider is to round the reading to the nearest 10 millivolts. This may be appropriate since you are only displaying the result to two decimal places.

  // convert the ADC reading to millivolts,
  // rounded to 10 millivolts precision
  Reading = (Reading * 5 * 1000) / 1023 + 5;

If you do this, the fourth entry in the digits array (i.e., the millivolts digit) should not be used.

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net