|
|
View previous topic :: View next topic |
Author |
Message |
RatFink
Joined: 01 May 2004 Posts: 49
|
LM34, A/d conversion and getting a 10 bit result. |
Posted: Thu May 20, 2004 1:11 am |
|
|
I'm using an LM34 for tempurature reading up tp 300' F (10 mV/degree farenheit), then printing the results on an LCD, Here's my code;
Code: | #include <16f877A.h>
#device ICD=TRUE
#fuses HS, NOWDT, NOPROTECT,PUT,BROWNOUT, NOLVP
#byte TRISA = 0x85
#byte PORTA = 0x05
#byte TRISB = 0x86
#byte PORTB = 0x06
#byte TRISD = 0x88
#byte PORTD = 0x08
#byte TRISE = 0x89
#byte PORTE = 0x09
#locate READ = 0x20
#locate HEAT = 0x21
#byte ADCON0 = 0x1F
#byte ADCON1 = 0x9F
#byte ADRESH = 0x1E
#byte OPTION = 0x81
#bit T0IF = 0x0B.2
#bit GO = 0x1F.2
#bit ADIF = 0x0C.6
/* Define Temp */
#bit TEMP = PORTE.0
/* Define Outputs*/
#bit Bake = PORTB.3
#bit Vent = PORTB.4
#bit Timed1 = READ.0
#bit Timed2 = READ.1
/* Define Inputs */
#bit PB_Preheat = PORTB.0
#bit PB_Plastic = PORTB.1
#bit PB_Metal = PORTB.2
#use delay(clock=20000000)
//#use rs232(DEBUGGER)
//#use rs232(baud=9600, xmit=PIN_c6,rcv=PIN_c7)
#include <lcd3.c>
void main(void)
{
lcd_init();
ADCON0 = 0x69; /* Fosc/8, A/D enabled */
OPTION = 0x07; /* TMR0 prescaller, 1:256, PORTB pullups */
ADCON1 = 0x0E; /* Left justify, 1 analog channel, VDD and Vss references */
TRISB = 0x07; /* PORTD Inputs and 0utputs */
PORTB =0x07; /*Sets up PORTD*/
TRISE = 0x01;
Timed1 =0;
Timed2 = 0;
HEAT = 0; /* clears HEAT */
BACK: do
{;}
while (T0IF == 0); /* Stay in loop until T0IF sets */
/* Exit loop */
T0IF = 0; /* Clear the T0IF flag */
GO = 1; /* Start A/D conversion */
do
{;}
while (ADIF == 0 ); /* Wait for conversion to complete */
HEAT = ADRESH<<1; /* Load HEAT with A/D value */
lcd_gotoxy(0,2);
printf(lcd_putc, "TEMP %1u", HEAT);
goto BACK; /* Check again */
} |
I know there is a lot of stuff before the MAIN, that is all from the rest of my code that this will be going with when it's working.
what I have here works fine up to 255 degrees, but then it roles over to zero again. I know this is because "HEAT" is only 8 bits, and so is ADRESH. Is there a way I can get this to work? Maybe combining ADRESH and ADRESL into a 16 bit adress.
I need to be able to multiplay the result by 2
In order to get the real tempurature.
Thank you again, you guys, you guys have been a big help to me. |
|
|
valemike Guest
|
Missing a #device ADC=10 |
Posted: Thu May 20, 2004 5:41 am |
|
|
This is what I do to get 10-bit A/D...
Code: |
#include <16F876.h>
#device ADC=10
#fuses HS, NOWDT, NOPROTECT etc.
|
Don't forget the #device ADC=10
directive. You need that for 16F/16C/ family of PICs. |
|
|
rwyoung
Joined: 12 Nov 2003 Posts: 563 Location: Lawrence, KS USA
|
|
Posted: Thu May 20, 2004 7:16 am |
|
|
You need a line
#device ADC=10
at the top to tell the compiler that results from the ADC are 10-bits long and not 8. Your code is just using the top 8 out of 10 bits.
And your variable "HEAT" is only 8 bits so it can never hold anything greater than 255 anyway.
I also suggest you use the library functions for manipulating the ADC so that your code is more portable to other PICs. _________________ Rob Young
The Screw-Up Fairy may just visit you but he has crashed on my couch for the last month! |
|
|
RatFink
Joined: 01 May 2004 Posts: 49
|
|
Posted: Thu May 20, 2004 10:59 am |
|
|
Thanks valemike and rwyoung, I read through everything I could find here last night and found references to exactly what you guys are saying.
rwyoung wrote: | I also suggest you use the library functions for manipulating the ADC so that your code is more portable to other PICs. |
When you say this, are you talking about these?
Code: | ////////////////////////////////////////////////////////////////// ADC
// ADC Functions: SETUP_ADC(), SETUP_ADC_PORTS() (aka SETUP_PORT_A),
// SET_ADC_CHANNEL(), READ_ADC()
// Constants used in SETUP_ADC_PORTS() are:
#define NO_ANALOGS 0x86 // None
#define ALL_ANALOG 0x80 // A0 A1 A2 A3 A5 E0 E1 E2 Ref=Vdd
#define ANALOG_RA3_REF 0x81 // A0 A1 A2 A5 E0 E1 E2 Ref=A3
#define A_ANALOG 0x82 // A0 A1 A2 A3 A5 Ref=Vdd
#define A_ANALOG_RA3_REF 0x83 // A0 A1 A2 A5 Ref=A3
#define RA0_RA1_RA3_ANALOG 0x84 // A0 A1 A3 Ref=Vdd
#define RA0_RA1_ANALOG_RA3_REF 0x85 // A0 A1 Ref=A3
#define ANALOG_RA3_RA2_REF 0x88 // A0 A1 A5 E0 E1 E2 Ref=A2,A3
#define ANALOG_NOT_RE1_RE2 0x89 // A0 A1 A2 A3 A5 E0 Ref=Vdd
#define ANALOG_NOT_RE1_RE2_REF_RA3 0x8A // A0 A1 A2 A5 E0 Ref=A3
#define ANALOG_NOT_RE1_RE2_REF_RA3_RA2 0x8B // A0 A1 A5 E0 Ref=A2,A3
#define A_ANALOG_RA3_RA2_REF 0x8C // A0 A1 A5 Ref=A2,A3
#define RA0_RA1_ANALOG_RA3_RA2_REF 0x8D // A0 A1 Ref=A2,A3
#define RA0_ANALOG 0x8E // A0
#define RA0_ANALOG_RA3_RA2_REF 0x8F // A0 Ref=A2,A3
// Constants used for SETUP_ADC() are:
#define ADC_OFF 0 // ADC Off
#define ADC_CLOCK_DIV_2 1
#define ADC_CLOCK_DIV_8 0x41
#define ADC_CLOCK_DIV_32 0x81
#define ADC_CLOCK_INTERNAL 0xc1 // Internal 2-6us
// Constants used in READ_ADC() are:
#define ADC_START_AND_READ 7 // This is the default if nothing is specified
#define ADC_START_ONLY 1
#define ADC_READ_ONLY 6 |
I tried using these at first, but I'm actually using PORTE.0 as the A/D input because the driver for my LCD uses the first 3 bits of PORTA as the control lines, and using the above library funcions didn't seem to give me the option for setting only PORTE for the A/D conversion. Am I missing something here?
rwyoung wrote: | And your variable "HEAT" is only 8 bits so it can never hold anything greater than 255 anyway. |
Yes, I understand that this is my problem with both "Heat" and "ADRESH"
Both of you wrote: | You need a line
#device ADC=10 |
I saw this in a few of the threads I read, but it was my understanding that the PIC16F877A always used 10 bit A/D, my problem is having those 10 bits to use. If I use the above library functions, and the "ADC_READ" in what location can I perform the math funtions I need to give me a reading to the LCD that I want?
Thank you |
|
|
RatFink
Joined: 01 May 2004 Posts: 49
|
|
Posted: Thu May 20, 2004 2:19 pm |
|
|
OK, I spent a long time trying to change LCD drivers to get one that did have the control line on PORTA so I could use it for A/D and the standard libraries.... I can't get one to work.
So I'm trying to join ADRESH and ADRESL.
Code: | BACK: do
{;}
while (T0IF == 0); /* Stay in loop until T0IF sets */
/* Exit loop */
T0IF = 0; /* Clear the T0IF flag */
GO = 1; /* Start A/D conversion */
do
{;}
while (ADIF == 0 ); /* Wait for conversion to complete */
int16 HEAT;
HEAT = make16(ADRESH, ADRESL);
HEAT = HEAT<<1; /* Load HEAT with A/D value */
lcd_gotoxy(0,2);
printf(lcd_putc, "TEMP %1u", HEAT);
goto BACK; /* Check again */
} |
but when I try to compile I get an error message
Quote: | A numeric expression must appear here
|
associated with
Why? |
|
|
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
|
Posted: Thu May 20, 2004 3:01 pm |
|
|
I don't think that you can declare a variable just anywhere in code but I'm not really sure. |
|
|
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Thu May 20, 2004 3:01 pm |
|
|
Try commenting out sections of your code until it goes away. int16 HEAT might not be what's causing the problem. It's usually something before the error that's causing the problem. I've had phantom errors before and had to enter the code from scratch because some kind of character was in there (control code?) that I couldn't see.
Ronald |
|
|
prwatCCS
Joined: 10 Dec 2003 Posts: 70 Location: West Sussex, UK
|
|
Posted: Thu May 20, 2004 3:05 pm |
|
|
Here is a code snip from my AD implementations ..
u16 is typedefed elsewhere but is unsigned int16
All variables are globals - I dont understand why you force HEAT to be at 0x20 in your code ...
Code: |
#device ADC=10
// current and voltage monitors
typedef unsigned int16 u16;
u16 V_Out1;
u16 V_Out2;
u16 I_Out1;
u16 I_Out1Max;
u16 I_Out1Min;
u16 I_Out2;
u16 I_Out2Max;
u16 I_Out2Min;
u8 analog_channel;
// the above is from my header file
#int_AD
void ADC_irq(void)
{
disable_interrupts( INT_AD );
switch (analog_channel++)
{
case AN0:
V_Out1 = read_adc(ADC_READ_ONLY);
break;
case AN1:
I_Out1 = (I_Out1 + read_adc(ADC_READ_ONLY))/2;
if (I_Out1 > I_Out1Max)
I_Out1Max = I_Out1;
else if (I_Out1 < I_Out1Min)
I_Out1Min = I_Out1;
break;
case AN2:
V_Out2 = read_adc(ADC_READ_ONLY);
break;
case AN3:
I_Out2 = (I_Out2 + read_adc(ADC_READ_ONLY))/2;
if (I_Out2 > I_Out2Max)
I_Out2Max = I_Out2;
else if (I_Out2 < I_Out2Min)
I_Out2Min = I_Out2;
break;
case AN4:
analog_channel = AN0;
break;
default:
analog_channel = AN0;
}
set_adc_channel(analog_channel); // next port to sample
}
|
This code has been used on 16F877 and 18F252 projects without any problems - albeit that I have irq's running and collect data from multiple ADC ports.
in your case I would use
u16 HEAT;
HEAT = read_adc(ADC_READ_ONLY);
HEAT = HEAT << 1; //multiply heat * 2 as required
Also I think Neutine is right about placement of int16 HEAT; - move it to the top of main() _________________ Peter Willis
Development Director
Howard Eaton Lighting Ltd UK |
|
|
RatFink
Joined: 01 May 2004 Posts: 49
|
|
Posted: Thu May 20, 2004 3:08 pm |
|
|
Neutone, Thanx
now I'm having trouble with the printf format. |
|
|
languer
Joined: 09 Jan 2004 Posts: 144 Location: USA
|
|
Posted: Thu May 20, 2004 3:22 pm |
|
|
Try,
Code: | printf(lcd_putc, "TEMP %l", HEAT); |
And if you keep the #locate statement in,
Code: | #locate READ = 0x20
#locate HEAT = 0x21 | I think the READ may overflow into the HEAT memory location if it is more than a byte-long.
And you should still be able to use,
Code: | heat = read_adc( ADC_READ_ONLY ); | instead of,
Code: | HEAT = make16(ADRESH, ADRESL); | Just a thought. |
|
|
RatFink
Joined: 01 May 2004 Posts: 49
|
|
Posted: Thu May 20, 2004 3:30 pm |
|
|
languer wrote: | Try,
Code: | printf(lcd_putc, "TEMP %l", HEAT); |
|
Thwnx I'll try that (**edit**, nope, I still get a wrong format message)
languer wrote: | And if you keep the #locate statement in,
Code: | #locate READ = 0x20
#locate HEAT = 0x21 | I think the READ may overflow into the HEAT memory location if it is more than a byte-long. |
I actually have that commented out now, (Read actually just holds values in 2 bits)
languer wrote: | And you should still be able to use,
Code: | heat = read_adc( ADC_READ_ONLY ); | instead of,
Code: | HEAT = make16(ADRESH, ADRESL); | Just a thought. |
Even though I'm not using the other A/D library functions? Which I would if I could define porte.o as my input. |
|
|
RatFink
Joined: 01 May 2004 Posts: 49
|
|
Posted: Thu May 20, 2004 4:01 pm |
|
|
WOOOT! I got it to work, it may be ugly, but it does what it should.
Code: | void main(void)
{
int16 HEAT;
lcd_init();
ADCON0 = 0x69; /* Fosc/8, A/D enabled */
OPTION = 0x07; /* TMR0 prescaller, 1:256, PORTB pullups */
ADCON1 = 0x0E; /* Left justify, 1 analog channel, VDD and Vss references */
TRISB = 0x07; /* PORTD Inputs and 0utputs */
PORTB =0x07; /*Sets up PORTD*/
TRISE = 0x01;
Timed1 =0;
Timed2 = 0;
//HEAT = 0; /* clears HEAT */
BACK: do
{;}
while (T0IF == 0); /* Stay in loop until T0IF sets */
/* Exit loop */
T0IF = 0; /* Clear the T0IF flag */
GO = 1; /* Start A/D conversion */
do
{;}
while (ADIF == 0 ); /* Wait for conversion to complete */
HEAT = make16(ADRESH, ADRESL);
HEAT = HEAT*(5.0/65535)*100; /* Load HEAT with A/D value, convert to correct temp*/
lcd_gotoxy(0,2);
printf(lcd_putc, "TEMP %lu", HEAT);
goto BACK; /* Check again */ |
Thanx to all that helped, now I just have to learn TIMER0 interupts then the code for my project will be done. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|