Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

PIC 18F ADC Problem (plz help)

Status
Not open for further replies.
I still can't figure out the problem. When I change the input voltage, the characters in the hyperterminal changes, which means something is working. Might there be any problem with ASCII conversion? Please help....
 

Thank u so much for your suggestion Bjuric. I tried the following code:
[CODE TXSTA = 0x20;
SPBRG = 77;
TXSTAbits.TXEN = 1;
RCSTAbits.SPEN = 1;
Send_Value();

SPBRG=77 will set the bud rate at 4006 bps

Then what value you are setting in Hyperterminal? In Hyperterminal , BPS values are available like 1200, 2400, 4800, 9600 etc. Also set the same bud rate for COM port of your PC
 

SPBRG=77 will set the bud rate at 4006 bps

Then what value you are setting in Hyperterminal? In Hyperterminal , BPS values are available like 1200, 2400, 4800, 9600 etc. Also set the same bud rate for COM port of your PC

Actually I am using a 20MHz crystal but as you can see in the previous post in the forum by bigdogguru, my PIC is running a Fosc of 48 MHz. So I used the formula and got SPBRG = 77 for baud rate 9600. And its actually working fine with the serial communication. I tested by sending simple character to the hyperterminal and its working fine. The main problem is when i try to get the ADC output data to the hyperterminal, i am getting strange characters in hyperterminal. So I think it is either, ADC problem or ASCII conversion problem in my code. Can you please help.
 

SPBRG=77 will set the bud rate at 4006 bps
What frequency are you looking at?
Thank u so much for your help. I used SPBRG = 77; and the serial communication is up and running


I still can't figure out the problem. When I change the input voltage, the characters in the hyperterminal changes, which means something is working. Might there be any problem with ASCII conversion? Please help....

You forgot to set the ADCON2 register.
Try this for main().
Code:
TRISA  = 0b00000001;
ADCON0 = 0b00000001;	
	// A/D is ON
ADCON1 = 0b00001110;	
	// Vref- set to Vss
	// Vref+ set to Vdd
	// AN0 is analog - others are digital
ADCON2 = 0b10111110;
	// right justify
	// Acq time 20TAD
	// clk Fosc/64
TXSTA = 0b10100010;
	// 8-bit
	// async
	// low speed (BRGH=0)
RCSTA = 0b10010000;
SPBRG = 77;

I didn't test this.
 
Last edited:

What frequency are you looking at?





You forgot to set the ADCON2 register.
Try this for main().
Code:
TRISA  = 0b00000001;
ADCON0 = 0b00000001;	
	// A/D is ON
ADCON1 = 0b00001110;	
	// Vref- set to Vss
	// Vref+ set to Vdd
	// AN0 is analog - others are digital
ADCON2 = 0b10111110;
	// right justify
	// Acq time 20TAD
	// clk Fosc/64
TXSTA = 0b10100010;
	// 8-bit
	// async
	// low speed (BRGH=0)
RCSTA = 0b10010000;
SPBRG = 77;

I didn't test this.
Thank u for the reply. For TXSTA I was using 0x20 before and was working well for serial communication. Do I need to change it?
And about the reference voltage, I was giving a 5V reference voltage to AN1 i.e. 3rd pin. Do I need to give it to the AN3 i.e. 5th pin?

---------- Post added at 10:15 ---------- Previous post was at 09:52 ----------

I used as you said in main:
Code:
void main
{
TRISA  = 0b00000001;
ADCON0 = 0b00000001;	
	// A/D is ON
ADCON1 = 0b00001110;	
	// Vref- set to Vss
	// Vref+ set to Vdd
	// AN0 is analog - others are digital
ADCON2 = 0b10111110;
	// right justify
	// Acq time 20TAD
	// clk Fosc/64
TXSTA = 0b10100010;
	// 8-bit
	// async
	// low speed (BRGH=0)
RCSTA = 0b10010000;
SPBRG = 77;
Read_ADC();
Send_Value(voltage);
}

The output is as shown in attachment below. In this case, input voltage was 2.7V, so it is showing value in range of 200, I think its problem with decimal places. I checked with other values as well, the values are quite similar (except the decimal place) but when input voltage was 3.2, it was showing like 664.698.679.664 etc.

 

Thank u for the reply. For TXSTA I was using 0x20 before and was working well for serial communication. Do I need to change it?
No, it's OK. I just did it from scratch, but the result is the same.

And about the reference voltage, I was giving a 5V reference voltage to AN1 i.e. 3rd pin. Do I need to give it to the AN3 i.e. 5th pin?
Vref is set at RA3(AN3), so it would be the 5th pin.. Check the pinout for the MCU.

2011-12-30_10:12:21_668x386.png

In the ADCON1, Vref is set to Vdd internally, so don't connect anything externally. All pins except RA0(AN0) are set as digital outputs, as you can see in the TRISA config. If you set RA3 to Vdd, and is is set to output low it might burn the pin.


BTW, just tested the code on the P18F2420, it is working.
 

Ok I just gave the input voltage to AN0 and no reference voltage and I used the following code:
Code:
#include <p18f4550.h>
#include <delays.h>

void Read_ADC(void);
void Send_Value (unsigned char c);

unsigned long voltage;

void main(void)
{
	TRISA  = 0b00000001;
	ADCON0 = 0b00000001;	
		// A/D is ON
	ADCON1 = 0b00001110;	
		// Vref- set to Vss
		// Vref+ set to Vdd
		// AN0 is analog - others are digital
	ADCON2 = 0b10111110;
		// right justify
		// Acq time 20TAD
		// clk Fosc/64
	TXSTA = 0b10100010;
		// 8-bit
		// async
		// low speed (BRGH=0)
	RCSTA = 0b10010000;
	SPBRG = 77;
	Read_ADC();
	Send_Value(voltage);	

}

void Read_ADC (void)
{
	voltage = 0;		//globally defined as unsigned long
	ADCON0bits.GO = 1;		//start converting
	while (ADCON0bits.DONE == 1);		//wait for EOC
	voltage = (ADRESH << 8) | ADRESL;		//combines two bytes into a long int
	voltage = voltage * 5000;			//Vref is in milliVolts
	voltage = voltage/1023;			//10 bit ADC
}

void Send_Value (unsigned char c)
{
	char ch;
	ch = (voltage/1000) % 10;		//extract thousands digit
	ch = ch + 48;			// now it is in ASCII

	while (PIR1bits.TXIF == 0);
	TXREG = ch;
	
	while (PIR1bits.TXIF == 0);
	TXREG = '.';		//We need a decimal point

	ch = (voltage/100) % 10;		//extract hundredth digit
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;

	ch = (voltage/10) % 10;		//extract tenth digit
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;

	ch = voltage % 10;		
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;
}

But the result is still weird. I am not getting the input voltage in my hyperterminal. I've attached below a screenshot of hyperterminal when the input voltage was 3.72V. Since, the code worked for you, can there be any problem with my hardware? I think its working well because i tried serial communication and led glow test with the same circuit.

 

In the Send_Value routine, at the end, put
Code:
while (PIR1bits.TXIF == 0);
	TXREG = 0x0D;
This will give you new line for every result

In Hyper Terminal:
File -> Properties -> Settings tab -> ASCII Setup
Enable the setting 'Append line feeds to incoming line ends'


You are sending unsigned long to Send_Char routine, but Send_Calue accepts char values.
You don't need any of those.

Send_Value(voltage);
The voltage variable is degined globally, no need to send it.

void Send_Value (unsigned char c)
The c variable is not used within the routine, you don't need it there. Set to void.
 
Last edited:

I made changes as you said. Here's the new code:
Code:
#include <p18f4550.h>
#include <delays.h>

void Read_ADC(void);
void Send_Value (void);

unsigned long voltage;

void main(void)
{
	TRISA  = 0b00000001;
	ADCON0 = 0b00000001;	
		// A/D is ON
	ADCON1 = 0b00001110;	
		// Vref- set to Vss
		// Vref+ set to Vdd
		// AN0 is analog - others are digital
	ADCON2 = 0b10111110;
		// right justify
		// Acq time 20TAD
		// clk Fosc/64
	TXSTA = 0b10100010;
		// 8-bit
		// async
		// low speed (BRGH=0)
	RCSTA = 0b10010000;
	SPBRG = 77;
	Read_ADC();
	Send_Value();	

}

void Read_ADC (void)
{
	voltage = 0;		//globally defined as unsigned long
	ADCON0bits.GO = 1;		//start converting
	while (ADCON0bits.DONE == 1);		//wait for EOC
	voltage = (ADRESH << 8) | ADRESL;		//combines two bytes into a long int
	voltage = voltage * 5000;			//Vref is in milliVolts
	voltage = voltage/1023;			//10 bit ADC
}

void Send_Value (void)
{
	char ch;
	ch = (voltage/1000) % 10;		//extract thousands digit
	ch = ch + 48;			// now it is in ASCII

	while (PIR1bits.TXIF == 0);
	TXREG = ch;
	
	while (PIR1bits.TXIF == 0);
	TXREG = '.';		//We need a decimal point

	ch = (voltage/100) % 10;		//extract hundredth digit
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;

	ch = (voltage/10) % 10;		//extract tenth digit
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;

	ch = voltage % 10;		
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;

	while (PIR1bits.TXIF == 0);
	TXREG = 0x0D;
}

But still the output is not correct. I've attached a screenshot of hyperterminal when the input voltage is 3.72, its still showing weird values and nothing in front of decimal. What should be done? please help..



---------- Post added at 11:28 ---------- Previous post was at 11:19 ----------

Here's the screen shot of hex value when input was 3.72 V. What might be the reason for wrong display in hyperterminal? please help...

---------- Post added at 11:37 ---------- Previous post was at 11:28 ----------

Here's the corresponding hex value for the data in hyperterminal. What might be the problem for incorrect display in hyperterminal. Please help me...

9_1325240839.jpg
 

I think the last character does not show because TXREG gets filled, that is while() doesn't work properly. Try adding empty instruction cycle in while loop.
Code:
while (PIR1bits.TXIF == 0){ Nop(); }
This should work in MPLAB.

Try adding some delay between readings, do it like this in a while loop... just, I again don't know how to do a delay in MPLAB, you're on your own on that.
I also got the same weird output without the delay (mine was .50 ).
Code:
main(void)
{
....
....

  while(1)
  {
    Read_ADC();
    Send_Value();
    // call delay routine here
  }
}
 

Thank u for the reply. I can not test it now. I'll test it tomorrow and let you know. Thanks for the help.
 

I think the last character does not show because TXREG gets filled, that is while() doesn't work properly. Try adding empty instruction cycle in while loop.
Code:
while (PIR1bits.TXIF == 0){ Nop(); }
This should work in MPLAB.

Try adding some delay between readings, do it like this in a while loop... just, I again don't know how to do a delay in MPLAB, you're on your own on that.
I also got the same weird output without the delay (mine was .50 ).
Code:
main(void)
{
....
....

  while(1)
  {
    Read_ADC();
    Send_Value();
    // call delay routine here
  }
}

I used the delay as you said but I didn't understand where to put the empty instruction cycle. So, I used it as follows:
Code:
#include <p18f4550.h>
#include <delays.h>

void Read_ADC(void);
void Send_Value (void);

unsigned long voltage;

void main(void)
{
	TRISA  = 0b00000001;
	ADCON0 = 0b00000001;	
		// A/D is ON
	ADCON1 = 0b00001110;	
		// Vref- set to Vss
		// Vref+ set to Vdd
		// AN0 is analog - others are digital
	ADCON2 = 0b10111110;
		// right justify
		// Acq time 20TAD
		// clk Fosc/64
	TXSTA = 0b10100010;
		// 8-bit
		// async
		// low speed (BRGH=0)
	RCSTA = 0b10010000;
	SPBRG = 77;
	
	while (1)
	{
		while (PIR1bits.TXIF == 0);
		Nop();
		Read_ADC();
		Send_Value();
		Delay10KTCYx(240);		//Delay 200 ms
		Delay10KTCYx(240);		//Delay 200 ms
	}

}

void Read_ADC (void)
{
	voltage = 0;		//globally defined as unsigned long
	ADCON0bits.GO = 1;		//start converting
	while (ADCON0bits.DONE == 1);		//wait for EOC
	voltage = (ADRESH << 8) | ADRESL;		//combines two bytes into a long int
	voltage = voltage * 5000;			//Vref is in milliVolts
	voltage = voltage/1023;			//10 bit ADC
}

void Send_Value (void)
{
	char ch;
	ch = (voltage/1000) % 10;		//extract thousands digit
	ch = ch + 48;			// now it is in ASCII

	while (PIR1bits.TXIF == 0);
	TXREG = ch;

	while (PIR1bits.TXIF == 0);
	TXREG = '.';		//We need a decimal point
	

	ch = (voltage/100) % 10;		//extract hundredth digit
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;
	

	ch = (voltage/10) % 10;		//extract tenth digit
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;


	ch = voltage % 10;		
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;

	while (PIR1bits.TXIF == 0);
	TXREG = 0x0D;
	
}

I used 400 ms display. Still I couldn't get the right value. I have attached an screenshot of the output when the input was 2.133V. Where shall i put the empty instruction cycle in the code to get it right. Please help...

23_1325483552.jpg
 

I think Nop(); is also for delay. What can I do to get the correct output? Please help.
 

Nop() is one empty cycle. It's used when you have to have a command in some place without actually doing anything.

Use the command as I posted:
Code:
while (PIR1bits.TXIF == 0){ Nop(); }

I see you removed brackets, which is OK, but than you need to remove the semicolon at the end of the while statement
Code:
while (PIR1bits.TXIF == 0)  //semicolon removed from here
    Nop();

With that semicolon in place, you've got the same code as before, only one empty cycle after.
Without the semicolon, while statement will execute Nop() over and over again.


That's why I use brackets... removes this hard to spot mistakes.
 

Nop() is one empty cycle. It's used when you have to have a command in some place without actually doing anything.

Use the command as I posted:
Code:
while (PIR1bits.TXIF == 0){ Nop(); }

I see you removed brackets, which is OK, but than you need to remove the semicolon at the end of the while statement
Code:
while (PIR1bits.TXIF == 0)  //semicolon removed from here
    Nop();

With that semicolon in place, you've got the same code as before, only one empty cycle after.
Without the semicolon, while statement will execute Nop() over and over again.


That's why I use brackets... removes this hard to spot mistakes.

Thank u for ur suggestion. I made modification as you said. Here's the code:
Code:
#include <p18f4550.h>
#include <delays.h>

void Read_ADC(void);
void Send_Value (void);

unsigned long voltage;

void main(void)
{
	TRISA  = 0b00000001;
	ADCON0 = 0b00000001;	
		// A/D is ON
	ADCON1 = 0b00001110;	
		// Vref- set to Vss
		// Vref+ set to Vdd
		// AN0 is analog - others are digital
	ADCON2 = 0b10111110;
		// right justify
		// Acq time 20TAD
		// clk Fosc/64
	TXSTA = 0b10100010;
		// 8-bit
		// async
		// low speed (BRGH=0)
	RCSTA = 0b10010000;
	SPBRG = 77;
	
	while (1)
	{
		while (PIR1bits.TXIF == 0)
		{
			Nop();
		}
		Read_ADC();
		Send_Value();
		Delay10KTCYx(240);		//Delay 200 ms
		Delay10KTCYx(240);		//Delay 200 ms
	}

}

void Read_ADC (void)
{
	voltage = 0;		//globally defined as unsigned long
	ADCON0bits.GO = 1;		//start converting
	while (ADCON0bits.DONE == 1);		//wait for EOC
	voltage = (ADRESH << 8) | ADRESL;		//combines two bytes into a long int
	voltage = voltage * 5000;			//Vref is in milliVolts
	voltage = voltage/1023;			//10 bit ADC
}

void Send_Value (void)
{
	char ch;
	ch = (voltage/1000) % 10;		//extract thousands digit
	ch = ch + 48;			// now it is in ASCII

	while (PIR1bits.TXIF == 0);
	TXREG = ch;

	while (PIR1bits.TXIF == 0);
	TXREG = '.';		//We need a decimal point
	

	ch = (voltage/100) % 10;		//extract hundredth digit
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;
	

	ch = (voltage/10) % 10;		//extract tenth digit
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;


	ch = voltage % 10;		
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;

	while (PIR1bits.TXIF == 0);
	TXREG = 0x0D;

	
}

But still result is the same. When the input is 3.715 V, it is displaying .17
I think I am doing something wrong because you said you got it right with the same code. Since, the input is 3.715V, the output should be 6 characters including "0D" for carriage return but I am getting only 4 characters in the hyperterminal including "0D". Can it be due to wrong configuration bits because in the configuration bit, the oscillator input is 20 MHz, but due to the internal oscillator, the overall frequency is 48 MHz? Please help.

 

I tried to tested the code, so I directly send the ASCII values to check by using the code below:
Code:
#include <p18f4550.h>
#include <delays.h>

void Read_ADC(void);
void Send_Value (void);

unsigned long voltage;

void main(void)
{
	TRISA  = 0b00000001;
	ADCON0 = 0b00000001;	
		// A/D is ON
	ADCON1 = 0b00001110;	
		// Vref- set to Vss
		// Vref+ set to Vdd
		// AN0 is analog - others are digital
	ADCON2 = 0b10111110;
		// right justify
		// Acq time 20TAD
		// clk Fosc/64
	TXSTA = 0b10100010;
		// 8-bit
		// async
		// low speed (BRGH=0)
	RCSTA = 0b10010000;
	SPBRG = 77;
	
	while (1)
	{
		while (PIR1bits.TXIF == 0);
		Nop();
		Read_ADC();
		Send_Value();
		Delay10KTCYx(240);		//Delay 200 ms
		Delay10KTCYx(240);		//Delay 200 ms
	}

}

void Read_ADC (void)
{
	voltage = 0;		//globally defined as unsigned long
	ADCON0bits.GO = 1;		//start converting
	while (ADCON0bits.DONE == 1);		//wait for EOC
	voltage = (ADRESH << 8) | ADRESL;		//combines two bytes into a long int
	voltage = voltage * 5000;			//Vref is in milliVolts
	voltage = voltage/1023;			//10 bit ADC
}

void Send_Value (void)
{
	char ch;
	ch = (voltage/1000) % 10;		//extract thousands digit
	ch = ch + 48;			// now it is in ASCII

	while (PIR1bits.TXIF == 0);
	TXREG = 'T';

	while (PIR1bits.TXIF == 0);
	TXREG = '.';		//We need a decimal point
	

	ch = (voltage/100) % 10;		//extract hundredth digit
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = 'E';
	

	ch = (voltage/10) % 10;		//extract tenth digit
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = 'S';


	ch = voltage % 10;		
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = 'T';

	while (PIR1bits.TXIF == 0);
	TXREG = 0x0D;

}

The output was as attached. So even the last character is not displayed in hyperterminal. What might the reason be? Please help..

5_1325571713.jpg
 

Supose u apply 2.5 volt to ADC
So it will conver it into binary (0111111111) or it will eqaul to 511 in decimal.

Voltage = 511 * 5 / 1023 // it is not 5000
or Voltage = 2.497 (It is not an integer)

So in your code,
unsigned long voltage;
voltage = voltage * 5000; //Vref is in milliVolts
voltage = voltage/1023;
What will be stored in voltage? Think yourself.
Well , I think the value may be truncated but that is not very importent in this case.
(got confused at first sight)
 
Last edited:

Supose u apply 2.5 volt to ADC
So it will conver it into binary (0111111111) or it will eqaul to 511 in decimal.

Voltage = 511 * 5 / 1023
or Voltage = 2.497 (It is not an integer)

So in your code,
What will be stored in voltage? Think yourself.

So you think I should use voltage as a float? But I think for modulus operation, it should be integer. What shall I change in my code to make it work? Please help..
 

long adc;
int voltage;
float result;

result=(float)(voltage*5)/1023;
adc=result*1000;

Now send the digits of adc one by one (remember the decimal place)

Or you may leave it as it is as mentioned in post 37
 
Last edited:

long adc;
int voltage;
float result;

result=(float)(voltage*5)/1023;
adc=result*1000;

Now send the digits of adc one by one (remember the decimal place)

Thank u for the reply. I made modifications in the code as you said:

Code:
#include <p18f4550.h>
#include <delays.h>

void Read_ADC(void);
void Send_Value (void);

unsigned long adc;
unsigned float result;
unsigned int voltage;

void main(void)
{
	TRISA  = 0b00000001;
	ADCON0 = 0b00000001;	
		// A/D is ON
	ADCON1 = 0b00001110;	
		// Vref- set to Vss
		// Vref+ set to Vdd
		// AN0 is analog - others are digital
	ADCON2 = 0b10111110;
		// right justify
		// Acq time 20TAD
		// clk Fosc/64
	TXSTA = 0b10100010;
		// 8-bit
		// async
		// low speed (BRGH=0)
	RCSTA = 0b10010000;
	SPBRG = 77;
	
	while (1)
	{
		while (PIR1bits.TXIF == 0)
		{
			Nop();
		}
		Read_ADC();
		Send_Value();
		Delay10KTCYx(240);		//Delay 200 ms
		Delay10KTCYx(240);		//Delay 200 ms
	}

}

void Read_ADC (void)
{
	voltage = 0;		//globally defined as unsigned long
	ADCON0bits.GO = 1;		//start converting
	while (ADCON0bits.DONE == 1);		//wait for EOC
	voltage = (ADRESH << 8) | ADRESL;		//combines two bytes into a long int
	result = (float)(voltage * 5)/1023;
	adc = result * 1000;
	//voltage = voltage * 5000;			//Vref is in milliVolts
	//voltage = voltage/1023;			//10 bit ADC
}

void Send_Value (void)
{
	char ch;
	ch = (adc/1000) % 10;		//extract thousands digit
	ch = ch + 48;			// now it is in ASCII

	while (PIR1bits.TXIF == 0);
	{
		TXREG = ch;
	}

	while (PIR1bits.TXIF == 0);
	TXREG = '.';		//We need a decimal point
	

	ch = (adc/100) % 10;		//extract hundredth digit
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;
	

	ch = (adc/10) % 10;		//extract tenth digit
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;


	ch = adc % 10;		
	ch = ch + 48;
	while (PIR1bits.TXIF == 0);
	TXREG = ch;

	while (PIR1bits.TXIF == 0);
	TXREG = 0x0D;

	
}

The value in hyperterminal changed but still its not correct. I have attached a screenshot for the input voltage of 3.712V. What might be the problem? Please help.

 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top