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

Fast ADC code
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
future



Joined: 14 May 2004
Posts: 330

View user's profile Send private message

Fast ADC code
PostPosted: Sun Nov 28, 2004 9:13 pm     Reply with quote

Hi all,

I have some working code and am trying to improve it.

Every 1ms I set the adon bit and wait to get the interrupt, but the dispatcher overhead is big for a simple int.

So I am looking for a simpler way to get the AD data. Something like this but in the main code... a function that every time I call it it returns the last adc read.

Code:
  bit_set(adcon0,adon);    // read next a/d channel

#int_ad
void ad_isr(void) {
  adcraw[adch]=adres;     // return value in a/d result reg
  bit_clear(++adch,3);    // rollover after 7
  set_adc_channel(adch);  // write channel to adcon0
}


Any snippets would be good.

Thank you.
Charlie U



Joined: 09 Sep 2003
Posts: 183
Location: Somewhere under water in the Great Lakes

View user's profile Send private message

PostPosted: Mon Nov 29, 2004 7:48 am     Reply with quote

This depends on how recent your version of the compiler is, but you could try something like this:

Code:

void my_adc_read(void) {
  adcraw[adch]=read_adc(ADC_READ_ONLY);     
  bit_clear(++adch,3);    // rollover after 7
  set_adc_channel(adch);
  // add delay here for data acquisition time
  // the 18F452 data sheet has an example
  // that shows approximately 13 microseconds
  delay_us(13);
  read_adc(ADC_START_ONLY);// write channel to adcon0
}


You must make certain that there is enough time between calls to this function to allow for the conversion time.

Also, somewhere before calling this function, you must start a conversion on the first channel to get things started.
future



Joined: 14 May 2004
Posts: 330

View user's profile Send private message

PostPosted: Mon Nov 29, 2004 1:51 pm     Reply with quote

This is where lives my problem, I cant stop processing data and wait 13uS, I have a 20uS timer interrupt.

I will have to look on the oscilloscope how the waveforms will be after this change.

The program controls 16 pulse outputs with a 20uS resolution.
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

PostPosted: Mon Nov 29, 2004 3:49 pm     Reply with quote

I would guess you have plenty of code space. You can call this as often as you wish without using interupts and the channels will have enougn time to settle before reading. It also filters the inputs over time creating a 12 bit reading from a 10 bit input.

Code:
#define ADCON0 0xFC2
#bit GO_DONE = ADCON0.1

Int16 Analog_0_RAW;
Int16 Analog_0_Work;
Int16 Analog_0_Filtered;

Int16 Analog_1_RAW;
Int16 Analog_1_Work;
Int16 Analog_1_Filtered;

Int16 Analog_2_RAW;
Int16 Analog_2_Work;
Int16 Analog_2_Filtered;

Int16 Analog_3_RAW;
Int16 Analog_3_Work;
Int16 Analog_3_Filtered;

Int16 Analog_4_RAW;
Int16 Analog_4_Work;
Int16 Analog_4_Filtered;

Int16 Analog_5_RAW;
Int16 Analog_5_Work;
Int16 Analog_5_Filtered;

Int16 Analog_6_RAW;
Int16 Analog_6_Work;
Int16 Analog_6_Filtered;

Int16 Analog_7_RAW;
Int16 Analog_7_Work;
Int16 Analog_7_Filtered;

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();
      }
   }
}
future



Joined: 14 May 2004
Posts: 330

View user's profile Send private message

PostPosted: Mon Nov 29, 2004 6:51 pm     Reply with quote

Neutone:

Thanks for the reply.

Code:
 if((Analog_Channel<<1)==0)   Analog_Channel++; // Advance to the next channel


I don't get how it walks through the channels, I think this would work only for the 0 going to 1 channel, but how it will increment to 2?

I will use 8 bit a/d without any filtering.

Another thing, why use GO_DONE=1 if read_adc() will do it all again?
Ttelmah
Guest







PostPosted: Tue Nov 30, 2004 3:49 am     Reply with quote

future wrote:
This is where lives my problem, I cant stop processing data and wait 13uS, I have a 20uS timer interrupt.

I will have to look on the oscilloscope how the waveforms will be after this change.

The program controls 16 pulse outputs with a 20uS resolution.

In all honesty, if you have a 20uSec interrupt, use it for the ADC as well.
The big problem though is that even at 40MHz, a 20uSec interrupt will not lave much time for anything else. The overhead on the normal interrupt handler, is about 60-80 instruction times (once all the registers are saved, the interrupt checked, the subroutine called, and the registers restored). Even at 10MIPS, nearly half the processor time is going to be used in the interrupt.
That being said, run a single byte state machine, on the first interrupt, select the channel. On the second, trigger the conversion. On the third, read it. If the data is written into an array, it can be accessed asynchronously as needed.
If you use a switch statement, with no 'default', though it looks bulky, it'll translate into a table based jump, which is quite efficient. Avoid using variables to access the array. Making the switch longer in the table based form, adds no extra time, so something like:
Code:

switch (state++) {
case 0:
   set_adc_channel(0);
   break;
case 1:
   read_adc(ADC_START_ONLY);
   break;
case 2:
   adval[0]=make16(ADRESH,ADRESL);
   break;
case 3:
   set_adc_channel(1);
   break;
case 4:
   read_adc(ADC_START_ONLY);
   break;
case 5:
   adval[1]=make16(ADRESH,ADRESL);
   break;

..... repeat for all eight cases.

case 23:
   adval[7]=make16(ADRESH,ADRESL);
   state=0;
   break;
}


Now though this is bulky, it is actually quick. The compiler will generate a table based jump for this code, and each entry, only involves a few of instructions. Using 'make16', rather that 'ADC_READ_ONLY', implies the compiler will not bother to check the 'conversion done' flag, and simply transfer the two bytes to fixed addresses (an access to an array using a variable, involves calculating the address to use at run time, and is slow - doing the same using a constant, is solved at compile time, and is therefore fast). Since the interrupts are at 20uSec, enough time exists for each of the phases involved (charge and read), without having to delay.
It may be that you are handing the global interrupt event yourself, and thereby reducing the interrupt overhead, by saving less registers. If so, you'l have to add the table access registers used for the switch statement to this.

Best Wishes
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

PostPosted: Tue Nov 30, 2004 9:39 am     Reply with quote

future wrote:
Neutone:

Thanks for the reply.

Code:
 if((Analog_Channel<<1)==0)   Analog_Channel++; // Advance to the next channel


I don't get how it walks through the channels, I think this would work only for the 0 going to 1 channel, but how it will increment to 2?

I will use 8 bit a/d without any filtering.

Another thing, why use GO_DONE=1 if read_adc() will do it all again?

This
Code:
Analog_Channel<<1

Shifts a bit left through a byte. If the resulting byte is zero the byte is incremented. The byte has 8 different values.

This
Code:
GO_DONE=1

will be cleared 12 TAD later allowing time for the input to settle.
future



Joined: 14 May 2004
Posts: 330

View user's profile Send private message

PostPosted: Tue Nov 30, 2004 2:33 pm     Reply with quote

Ttelmah, that's what I was talking about! The code works perfect, thank you.

This 20uS interrupt is really hard for the cpu I know, but the highest resolution I can get better are the results.

There are 14 outputs controlled by this interrupt.

I tried to use FAST INTS but I lost the fight... got weird behaviour from the program. It works most of the time but sometimes it computes weird data.

Neutone, does if((Analog_Channel<<1)==0) changes the value of Analog_Channel?
Ttelmah
Guest







PostPosted: Tue Nov 30, 2004 4:37 pm     Reply with quote

Glad it worked.
To use the fast interrupt, you really need to go through the assembler code generated for the routines you call, and verify exactly which registers are used. Remember the fast stack, only saves the very 'main' registers, and you manually need to save the 'scratch' area if you do any arithmetic, or subroutine calls that are not 'inline'. You also need to save the indirect addressing registers if you access an array etc..
I have used it on half a dozen occasions now, and in and example like yours, you can probably save about 25% of the overhead (especially if there is only one fast interrupt, since then you don't have to check any interrupt flags).
There are problems with the way it is implemented by CCS at present, which makes it difficult to be efficient in this area (I usually end up with some assembler added).

Best Wishes
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Tue Nov 30, 2004 6:34 pm     Reply with quote

future wrote:
Ttelmah, that's what I was talking about! The code works perfect, thank you.

This 20uS interrupt is really hard for the cpu I know, but the highest resolution I can get better are the results.

There are 14 outputs controlled by this interrupt.

I tried to use FAST INTS but I lost the fight... got weird behaviour from the program. It works most of the time but sometimes it computes weird data.

Neutone, does if((Analog_Channel<<1)==0) changes the value of Analog_Channel?


No only this line
Code:
Analog_Channel++
changes Analog_Channel
future



Joined: 14 May 2004
Posts: 330

View user's profile Send private message

PostPosted: Tue Nov 30, 2004 6:55 pm     Reply with quote

So it will only increment the channel if it was 0b10000000 as there is no other Analog_Channel++.
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Tue Nov 30, 2004 8:13 pm     Reply with quote

Or if it is 0b00000000. I think he made a mistake. It should be (Analog_Channel<<=1)

These elses should make it faster as well
Code:

#define ADCON0 0xFC2
#bit GO_DONE = ADCON0.1

Int16 Analog_0_RAW;
Int16 Analog_0_Work;
Int16 Analog_0_Filtered;

Int16 Analog_1_RAW;
Int16 Analog_1_Work;
Int16 Analog_1_Filtered;

Int16 Analog_2_RAW;
Int16 Analog_2_Work;
Int16 Analog_2_Filtered;

Int16 Analog_3_RAW;
Int16 Analog_3_Work;
Int16 Analog_3_Filtered;

Int16 Analog_4_RAW;
Int16 Analog_4_Work;
Int16 Analog_4_Filtered;

Int16 Analog_5_RAW;
Int16 Analog_5_Work;
Int16 Analog_5_Filtered;

Int16 Analog_6_RAW;
Int16 Analog_6_Work;
Int16 Analog_6_Filtered;

Int16 Analog_7_RAW;
Int16 Analog_7_Work;
Int16 Analog_7_Filtered;

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();
      }
      else 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();
      }
      else 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();
      }
      else 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();
      }
      else 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();
      }
      else 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();
      }
      else 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();
      }
      else 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();
      }
   }
}
future



Joined: 14 May 2004
Posts: 330

View user's profile Send private message

PostPosted: Thu Dec 02, 2004 1:51 pm     Reply with quote

Optimized a little:

Code:
         switch (adch++)                                          // 01xxx001
         {  case 0:
               adcon0=0b01000001;                                 // set_adc_channel(0);
               break;
            case 1:
               read_adc(ADC_START_ONLY);
               break;
            case 2:
               adcraw[0]=adres;
               adcon0=0b01001001;                                 // set_adc_channel(1);
               break;
            case 3:
               read_adc(ADC_START_ONLY);
               break;
            case 4:
               adcraw[1]=adres;
               adcon0=0b01010001;                                 // set_adc_channel(2);
               break;
            case 5:
               read_adc(ADC_START_ONLY);
               break;
            case 6:
               adcraw[2]=adres;
               adcon0=0b01011001;                                 // set_adc_channel(3);
               break;
            case 7:
               read_adc(ADC_START_ONLY);
               break;
            case 8:
               adcraw[3]=adres;
               adcon0=0b01100001;                                 // set_adc_channel(4);
               break;
            case 9:
               read_adc(ADC_START_ONLY);
               break;
            case 10:
               adcraw[4]=adres;
               adcon0=0b01101001;                                 // set_adc_channel(5);
               break;
            case 11:
               read_adc(ADC_START_ONLY);
               break;
            case 12:
               adcraw[5]=adres;
               adcon0=0b01110001;                                 // set_adc_channel(6);
               break;
            case 13:
               read_adc(ADC_START_ONLY);
               break;
            case 14:
               adcraw[6]=adres;
               adcon0=0b01111001;                                 // set_adc_channel(7);
               break;
            case 15:
               read_adc(ADC_START_ONLY);
               break;
            case 16:
               adch=0;
               adcraw[7]=adres;
               break;
         }


Now I can work on an old idea, having both 10bit and 8bit channels working at the same time.
future



Joined: 14 May 2004
Posts: 330

View user's profile Send private message

PostPosted: Tue Dec 07, 2004 5:14 pm     Reply with quote

Done, configure adc as 10bit and one can have 0~5v with 20mV resolution and 0~1.25v with 5mV resolution.


Code:
    #define result_left()   bit_clear(adcon1,7)   // result left justified
    #define result_right()  bit_set(adcon1,7)     // result right justified

    adcon0=0b01100001;             // channel(4);
    if(highres==true)
               { result_right(); }
           else { result_left(); }
    break;
   case 9:
    read_adc(ADC_START_ONLY);
    break;
   case 10:
    if(highres==true)
                { adcraw=adresl; } // 0~1.25v
           else { adcraw=adresh; } // 0~5v
djwallace



Joined: 26 Nov 2004
Posts: 10

View user's profile Send private message Send e-mail MSN Messenger

Quick Question about ADCs
PostPosted: Tue Jan 11, 2005 2:57 pm     Reply with quote

If I do not specify the line

#device adc=X

where X is the resolution. will it use 10Bit resolution? I am currently using a PIC16F88 with a 10 Bit ADC.

Thanx,

Darryl
_________________
darryl
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
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