|
|
View previous topic :: View next topic |
Author |
Message |
ppeterson1 Guest
|
A/D conversion on pic16f767 problem |
Posted: Tue May 11, 2004 10:53 am |
|
|
In the following code, I am trying to read an analog value from A/D channel 1, and output the value received onto a terminal through rs-232. The problem that occurs is for an input voltage to A/D channel 1 from 0-5 volts a value of 18368 is displayed. When I go slightly above 5 volts, a value of 23552 is displayed. The output to the terminal is correct, because the mission clock is displayed.
Here is an output example:
Mission, Value
000001, 18368,
000002, 18368,
000003, 23552,
Below is the code:
Code: | #include <16F767.H>
#DEVICE ADC=10 // Defines as a 10 bit A/D resolution device
#fuses EC_IO, NOWDT, PUT, NOPROTECT, NOBROWNOUT, BORV27, NOMCLR, NODEBUG
#use delay(clock=19660800)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, PARITY=N, BITS=8)
#include <stdlib.h>
#priority timer1, ad
// Global variables:
int16 value[11]; // Declaration to store 11 10-bit A/D channels
int8 channel; // Selects channel to be read
int1 ReadADC; // Determines whether A/D is read in while statement
int8 InterruptEpoch=0; // Interrupt counter that resets at 50 interrupts
int8 i; // Simple counter
// Mission Clock variables
int8 RawTime[6];
// A/D register defines
#bit ADGO = 0x1F.2 // References the ADGO bit in the ADCON0 Register
#bit ADON = 0x1F.0 // ADON bit in the ADCON0 Register
#bit ADIF = 0x0C.6 // References the ADIF (A/D interrupt flag) in PIR1 Register
#bit PEIE = 0x0B.6 // References the PEIE bit in the INTCON register
#byte ADRESL = 0x9E // References ADRESL register in bank 1
#byte ADRESH = 0x1E // References ADRESL register in bank 0
char buffer[8];
// Function Prototypes
void InitAD(); // Initialize A/D engine
void doADReadings(); // Reads from all 8 A/D sensors and sends them to value[]
// Main function driver
void main()
{
RawTime[0] = 9;
RawTime[1] = 9;
RawTime[2] = 9;
RawTime[3] = 9;
RawTime[4] = 9;
RawTime[5] = 9;
// Calls initialize A/D converter function
InitAD();
// Initialize timers and interrupt frequencies
// program TIMER1 to generate 16 Hz interrupts, then turn it on
// ( Fosc = 19.6608 MHz ) / 4 / 64 = 76.8 kHz
// 76.8 kHz / 192 = 400 Hz
setup_timer_1( T1_INTERNAL | T1_DIV_BY_8 );
set_timer1( 65536 - 192*8 );
enable_interrupts( INT_TIMER1 );
enable_interrupts( GLOBAL );
// Turn on A/D
ADON = true;
// Slight delay to allow capacitor to fully charg
delay_us(15);
set_adc_channel( 1 );
output_high(PIN_B7);
while(input(PIN_B6)) {
// Perform interrupts
if(ReadADC==1)
{
output_high(PIN_C0);
doADReadings();
// value[0] &= 0x03FF;
delay_ms(50);
output_low(PIN_C0);
ReadADC=0;
}
RawTime[5]++;
if(RawTime[5] == 10) {
RawTime[5]=0;
RawTime[4]++;
if(RawTime[4] == 10) {
RawTime[4] = 0;
RawTime[3]++;
if(RawTime[3] == 10) {
RawTime[3]=0;
RawTime[2]++;
if(RawTime[2] == 10) {
RawTime[2] = 0;
RawTime[1]++;
if(RawTime[1] == 10) {
RawTime[1] = 0;
RawTime[0]++;
if(RawTime[0] == 10 ) {
RawTime[0] = 0;
}
}
}
}
}
}
// Test to see if correct variables are sent
// Format: Mission clock, A/D channels 1,2,3,4,5,6,7,8
for(i=0; i < 6; i++)
printf( "%C", (RawTime[i] | 0x30));
printf(",");
for(channel=0; channel < 1; channel++) {
// Put value to a character stream and output to display
sprintf( buffer,"%lu", value[channel] );
printf( "%C%C%C%C%C%C", buffer[0], buffer[1], buffer[2],buffer[3],buffer[4],buffer[5]);
putchar(',');
}
printf("\n\r");
}
}
// This interrupt is called 400 times/second
#int_timer1
void Int16HZ(){
set_timer1( 65536 - 192*8 ); // Sets timer0 counter to 192
// DisplayValuetoLEDs_debug(fractionSeconds);
InterruptEpoch++;
if(InterruptEpoch==50)
InterruptEpoch = 0;
// A/D Readings
if(InterruptEpoch==2 || InterruptEpoch==27) // Operates at 16 Hz ((400Hz/50) * 2 = 16)
ReadADC=1; // Flag that lets function in main() do A/D readings
}
//===========Initialization Functions==================
void InitAD() {
setup_adc_ports( ALL_ANALOG | VSS_VDD );
setup_adc( ADC_CLOCK_DIV_64 );
set_tris_A( 1 );
}
//==============Program Functions==================
void doADReadings() {
// preconditions: ADC is set to a channel number for reading
// postconditions: value is set to proportional to the
// voltage reading for corresponding input channel
//Pins AN5, AN6, AN7 are not available on the 767, so skip
disable_interrupts( GLOBAL );
enable_interrupts( INT_AD );
// Enable the peripheral interrupt enable
PEIE = 1;
// Ensure the interrupt flag is clear
ADIF = false;
// Start conversion
ADGO = true; //Start A/D conversion cycle0
while(ADGO);
disable_interrupts( INT_AD );
enable_interrupts( GLOBAL);
// Assign received conversion to value
value[0] = make16(ADRESH, ADRESL);
} |
Thanks for any help on this.
Regards, Paul |
|
|
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
|
Posted: Tue May 11, 2004 12:10 pm |
|
|
The interupt enable flag and the interupt flag are independant. The interupt flag is set when the conversion is complete reguardless of weather jumpin to interupt code is enabled. You can see some of this by looking at the list file where the interupts are enabled and disabled.
In your doADReadings function you only need this;
Code: |
ADGO = true; //Start A/D conversion cycle0
while(ADGO);
value[0] = make16(ADRESH, ADRESL);
|
You will get the same results using the built in read_adc() function.
Code: |
value[0] = read_adc()
|
All of the built in adc functions work quite well and make code that is easier to read. Try them and look at the list file to see what they are doing. Also what channel are you reading?
Here is an example for reading a different channel every time the function is called.
Code: | Int8 Analog_Channel;
#bit Analog_0 = Analog_Channel.0
#bit Analog_1 = Analog_Channel.1
#bit Analog_2 = Analog_Channel.2
#bit Analog_3 = Analog_Channel.3
#bit Analog_4 = Analog_Channel.4
#bit Analog_5 = Analog_Channel.5
#bit Analog_6 = Analog_Channel.6
#bit Analog_7 = Analog_Channel.7
Int1 Analog_Been_Initialized=0;
/***********************************************************
* Analog *
***********************************************************/
#inline
void Analog_Service(void)
{ if(!Analog_Been_Initialized)
{ setup_port_a( RA0_ANALOG );
setup_adc_ports( ALL_ANALOG );
setup_adc(ADC_CLOCK_INTERNAL);
Analog_Been_Initialized=1;
Analog_Channel=0;
}
else
{ if((Analog_Channel<<1)==0) Analog_Channel++; // Advance to the next channel
if(Analog_0)
{ set_adc_channel(0);
GO_DONE=1;
Analog_0_Work+=Analog_0_RAW;
Analog_0_Filtered=(Analog_0_Work>>2);
Analog_0_Work-=Analog_0_Filtered;
While(GO_DONE);
Analog_0_RAW=read_adc();
}
if(Analog_1)
{ set_adc_channel(1);
GO_DONE=1;
Analog_1_Work+=Analog_1_RAW;
Analog_1_Filtered=(Analog_1_Work>>2);
Analog_1_Work-=Analog_1_Filtered;
While(GO_DONE);
Analog_1_RAW=read_adc();
}
if(Analog_2)
{ set_adc_channel(2);
GO_DONE=1;
Analog_2_Work+=Analog_2_RAW;
Analog_2_Filtered=(Analog_2_Work>>2);
Analog_2_Work-=Analog_2_Filtered;
While(GO_DONE);
Analog_2_RAW=read_adc();
}
if(Analog_3)
{ set_adc_channel(3);
GO_DONE=1;
Analog_3_Work+=Analog_3_RAW;
Analog_3_Filtered=(Analog_3_Work>>2);
Analog_3_Work-=Analog_3_Filtered;
While(GO_DONE);
Analog_3_RAW=read_adc();
}
if(Analog_4)
{ set_adc_channel(4);
GO_DONE=1;
Analog_4_Work+=Analog_4_RAW;
Analog_4_Filtered=(Analog_4_Work>>2);
Analog_4_Work-=Analog_4_Filtered;
While(GO_DONE);
Analog_4_RAW=read_adc();
}
if(Analog_5)
{ set_adc_channel(5);
GO_DONE=1;
Analog_5_Work+=Analog_5_RAW;
Analog_5_Filtered=(Analog_5_Work>>2);
Analog_5_Work-=Analog_5_Filtered;
While(GO_DONE);
Analog_5_RAW=read_adc();
}
if(Analog_6)
{ set_adc_channel(6);
GO_DONE=1;
Analog_6_Work+=Analog_6_RAW;
Analog_6_Filtered=(Analog_6_Work>>2);
Analog_6_Work-=Analog_6_Filtered;
While(GO_DONE);
Analog_6_RAW=read_adc();
}
if(Analog_7)
{ set_adc_channel(7);
GO_DONE=1;
Analog_7_Work+=Analog_7_RAW;
Analog_7_Filtered=(Analog_7_Work>>2);
Analog_7_Work-=Analog_7_Filtered;
While(GO_DONE);
Analog_7_RAW=read_adc();
}
}
}
|
I do some math operations while the channel is changing.
Last edited by Neutone on Tue May 11, 2004 1:18 pm; edited 2 times in total |
|
|
Humberto
Joined: 08 Sep 2003 Posts: 1215 Location: Buenos Aires, La Reina del Plata
|
|
Posted: Tue May 11, 2004 12:11 pm |
|
|
Hi Paul,
Code: |
void InitAD()
{
setup_adc_ports( ALL_ANALOG | VSS_VDD );
setup_adc( ADC_CLOCK_DIV_64 );
set_tris_A(1); // ?????????????????
}
|
CCS functions for A/D conversions are very straightforward.
Just to test that convertion is done properly, replace this function
Code: |
void doADReadings() {
// preconditions: ADC is set to a channel number for reading
// postconditions: value is set to proportional to the
// voltage reading for corresponding input channel
//Pins AN5, AN6, AN7 are not available on the 767, so skip
disable_interrupts( GLOBAL );
enable_interrupts( INT_AD );
// Enable the peripheral interrupt enable
PEIE = 1;
// Ensure the interrupt flag is clear
ADIF = false;
// Start conversion
ADGO = true; //Start A/D conversion cycle0
while(ADGO);
disable_interrupts( INT_AD );
enable_interrupts( GLOBAL);
// Assign received conversion to value
value[0] = make16(ADRESH, ADRESL);
}
|
With this:
Code: |
set_adc_channel(1);
read_adc(ADC_START_ONLY);
delay_us(10);
value[0] = read_adc();
|
HTH,
Humberto |
|
|
Humberto
Joined: 08 Sep 2003 Posts: 1215 Location: Buenos Aires, La Reina del Plata
|
|
Posted: Tue May 11, 2004 12:15 pm |
|
|
Sorry Neutone, almost the same but 1 minute later ...
Humberto |
|
|
ppeterson1 Guest
|
|
Posted: Tue May 11, 2004 12:51 pm |
|
|
OK, I have tried the above, but it seems as though the compiler references the wrong bit, bit 1 of register 0xF1 (which is the CHS bit), when it should reference bit 2, the ADON bit. So the PIC16F767 goes into an infinite loop. Below is a snippet of the listing file:
.................... value[0] = read_adc();
0017: BSF 1F.1
0018: BTFSC 1F.1
0019: GOTO 018
001A: BSF 03.5
001B: MOVF 1E,W
001C: BCF 03.5
001D: MOVWF 25
001E: MOVF 1E,W
001F: MOVWF 26
It seems as if I need to access the bits directly. So what should I do? |
|
|
ppeterson1 Guest
|
|
Posted: Tue May 11, 2004 12:54 pm |
|
|
*Correction: The compiler should reference bit 2,GO_DONE, not bit 0, ADON. |
|
|
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
|
Posted: Tue May 11, 2004 1:34 pm |
|
|
I compiled the example file EX_RMSDB.C with the device type changed to 16f767 and it compiles corectly.
Code: |
.................... value = Read_ADC();
0846: BSF ADCON0.2
0847: BTFSC ADCON0.2
0848: GOTO 047
0849: MOVF ADRESH,W
084A: MOVWF 23
|
Maybe your compiler is older had bug. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue May 11, 2004 1:49 pm |
|
|
One explanation for the values that you were getting is the
setting of the ADFM bit. This controls the left or right justification
of the A/D result. Do a search on the forum for ADFM_BIT
and you will find the solution.
---------------
Also, one additional issue could be the comparator module.
It should be turned off. Normally you can do this with
setup_comparator(NC_NC_NC_NC);
But this may not work with your version of the compiler.
You may have to check the .LST file, and see if the code
is correct. If not, you need to put in code to turn the
comparators off by using direct register access. |
|
|
ppeterson1 Guest
|
|
Posted: Tue May 11, 2004 5:56 pm |
|
|
OK, I have updated from 3.188 to 3.190, and now the A/D works great.
Thanks! |
|
|
ppeterson01 Guest
|
|
Posted: Tue May 11, 2004 5:57 pm |
|
|
The ADFM was also a problem before. I have set that bit to '1' and now it is right aligned. Before it seemed to be left aligned as a default. |
|
|
|
|
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
|