CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

A/D conversion on pic16f767 problem

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
ppeterson1
Guest







A/D conversion on pic16f767 problem
PostPosted: Tue May 11, 2004 10:53 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue May 11, 2004 12:10 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue May 11, 2004 12:11 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue May 11, 2004 12:15 pm     Reply with quote

Sorry Neutone, almost the same but 1 minute later ... Very Happy

Humberto
ppeterson1
Guest







PostPosted: Tue May 11, 2004 12:51 pm     Reply with quote

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







PostPosted: Tue May 11, 2004 12:54 pm     Reply with quote

*Correction: The compiler should reference bit 2,GO_DONE, not bit 0, ADON.
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

PostPosted: Tue May 11, 2004 1:34 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue May 11, 2004 1:49 pm     Reply with quote

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







PostPosted: Tue May 11, 2004 5:56 pm     Reply with quote

OK, I have updated from 3.188 to 3.190, and now the A/D works great.

Thanks!
ppeterson01
Guest







PostPosted: Tue May 11, 2004 5:57 pm     Reply with quote

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.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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