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

multiple Adc simultaneously

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



Joined: 08 Sep 2011
Posts: 202

View user's profile Send private message

multiple Adc simultaneously
PostPosted: Tue Jan 10, 2012 11:31 am     Reply with quote

How can I use multiple (three) adc simultaneously.
1st adc for input sense.
2nd adc for output sense.
3rd adc for overload sense.

When 3rd adc (overload) sense, mcu wait 10 min, after 10 min if 3rd adc (overload) sense now then it will go over load cut off case, otherwise stay current case.

In this 10 min if 2nd adc (output) sense high volt it will go hi cut off case, otherwise stay current case.

& also in this 10 min 1st adc (input) sense it will work as i define for its function.

ADC sense as
Code:

/********************************************************
  Function getchreading
  Calculate minivolts on selected channel
 ********************************************************/
int16 getchreading(int channel)
{
       int32 tlong;
        int1 done; // Put local variable at beginning of code block
        int x; // Number of times to average ADC value
        int16 temp_result, adc_value;

        set_adc_channel(channel); // Select Channel

        // Average ADC value
        temp_result = 0;
        for (x=1; x<=ADC_AVERAGE_COUNT; x++)
        {
                delay_us(ADC_DELAY); //Charge capacitor
                read_adc(ADC_START_ONLY); //Do A/D conversion

                done == adc_done();

                while(!done) {
                done = adc_done();
                }

                temp_result += read_adc(); // Read A/D register . Pin A0 in an input
                delay_ms(ADC_2TAD); // Wait 2 TADs before doing another A/D conversion
        }

        adc_value = temp_result / ADC_AVERAGE_COUNT;

        tlong = (int32)ADC_value*5000; //Convert the result in millivolts
        tlong = tlong/1023; // 0..1023 -> 0-5000mV
       
        return (int32) tlong;
}

/*******************************************************
 Reads Form AN0 ADC_Value
 and DOES NOT RETURN A VALUE
*******************************************************/
void Process_ch_0(void)
{
   // o\p lod sence here is done here
 
   if ((ch_0<=5000)&&(ch_0>=0010))
   {
      if (ch_0 < 550)
      {
         output_low(RL1);
                 break;           
      }

         if (ch_0 >= 700)  //  > 95
             {
             output_high(RL1);
             break;
            }

/******************************************************
  Function ProcessMains_volt
  Reads Form AN1 ADC_Value and returns NORMAL_VOLTAGE, HIGH_VOLTAGE, LOW_VOLTAGE
*******************************************************/
int ProcessMains(void)
 {
   
    ch_2 = getchreading(0);
    Process_ch_2();     // Do something with ch_0 o\p lod sence
     if (ch_2>800)      // lod 130 % Wait 6 min
         {int x = 0;
         if(x<600)
            {
    ch_1 = getchreading(1);     //       WHICH MEANS THIS NEVER IS.
    Process_ch_1();      // Do something with ch_1 o\p volt
    if (ch_1>1000)
       return(HIGH_VOLTAGE);
 
    if (ch_1<900)
       return(NORMAL_VOLTAGE);
             Delay1000ms();
             }         
       return(OVER_LOD_SENCE);     
      }
    ch_1 = getchreading(1);          //  WHICH MEANS THIS NEVER IS.
    Process_ch_1();      // Do something with ch_1 o\p volt
    if (ch_1>1000)
       return(HIGH_VOLTAGE);
 
    if (ch_1<900)
       return(NORMAL_VOLTAGE);
 
 }

My problem is when ch_2 stay here:
Code:

 if (ch_2>800)      // lod 130 % Wait 10 min
         {int x = 0;
         if(x<600)
            {

Code:

  if (ch_1>1000)
       return(HIGH_VOLTAGE);

Not work .
_________________
sahu
temtronic



Joined: 01 Jul 2010
Posts: 9205
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Jan 10, 2012 11:58 am     Reply with quote

First, No PIC can simultaneously read 3 ADC channels, but that's not your real problem, just a language thing...

Second this is not a complete program so I can't comment on the 'flow' or logic, or HOW you've assigned variables.

Comments...

1) You should use 'real names' for the variables like Input_sense, Output_sense, Overload_Sense as CH_3 doesn't make me think of 'overload' reading.

2)Your actual reading an ADC channel and getting the average reading is very poorly written. I'd suggest using say take 8 readings , then average those. Very fast for the compiler to cut efficient code.

3) You don't say whether the ADC is in 8 bit or 10 bit mode.

4) your 'if statement logic for all conditions is very,very 'messy' and almost impossible to follow. I suggest you delete everything and start all over. Create code to read and display the raw ADC values,either to LCD or PC, to confirm the numbers you're getting are correct

Then add code (switch statement logic or simple if...then ..else ) for each condition, ONE condition , at a time. It will be cleaner and easier to follow,especially to check for mistakes in logic !


You should ALWAYS include PIC type and Compiler version.There are some known issues that we may spot given this data.

You need some kind of real time clock interrupt for your program. There's a nice software RTC in the code library, easy to follow, easy to use, a good choice for your program.
SherpaDoug



Joined: 07 Sep 2003
Posts: 1640
Location: Cape Cod Mass USA

View user's profile Send private message

PostPosted: Tue Jan 10, 2012 12:48 pm     Reply with quote

A couple more suggestions;

1) Read_adc() automatically waits for the A/D to finish conversion. You rarely if ever need to use adc_done().

2) Use select case instead of all those if() statements.

3) Look up "Olympic Average" on this forum for a simple fast way to average A/D readings and get rid of outlyers. At least make sure your ADC_AVERAGE_COUNT is a power of 2 so the division is easy.

4) tlong is declared as int32 so you should cast it to int16 before returning.
_________________
The search for better is endless. Instead simply find very good and get the job done.
sahu77



Joined: 08 Sep 2011
Posts: 202

View user's profile Send private message

PostPosted: Tue Jan 10, 2012 1:09 pm     Reply with quote

temtronic wrote:
First, No PIC can simultaneously read 3 ADC channels, but that's not your real problem, just a language thing...
Second this is not a complete program so I can't comment on the 'flow' or logic, or HOW you've assigned variables.
Comments...

1) You should use 'real names' for the variables like Input_sense, Output_sense, Overload_Sense as CH_3 doesn't make me think of 'overload' reading.

OK i improve it in my code.

temtronic wrote:

2)Your actual reading an ADC channel and getting the average reading is very poorly written. I'd suggest using say take 8 readings , then average those. Very fast for the compiler to cut efficient code.

Only need take 8 readings, then average those. Is sufficient. pl give best example for actual reading an ADC. Hope you do it sure ....

temtronic wrote:

3) You don't say whether the ADC is in 8 bit or 10 bit mode.

10 bit mode

temtronic wrote:

4) your 'if statement logic for all conditions is very,very 'messy' and almost impossible to follow. I suggest you delete everything and start all over. Create code to read and display the raw ADC values,either to LCD or PC, to confirm the numbers you're getting are correct
Then add code (switch statement logic or simple if...then ..else ) for each condition, ONE condition , at a time. It will be cleaner and easier to follow,especially to check for mistakes in logic !

ok , but i think no requirement it now. I think you understand my English very -2 poor.

temtronic wrote:

You should ALWAYS include PIC type and Compiler version.There are some known issues that we may spot given this data.

I use PIC16F676 & Compiler version V4.057

temtronic wrote:

You need some kind of real time clock interrupt for your program. There's a nice software RTC in the code library, easy to follow, easy to use, a good choice for your program.

If you don't mind can you explain real time clock interrupt or give me a link where I can find complete code for real time clock interrupt?
_________________
sahu
sahu77



Joined: 08 Sep 2011
Posts: 202

View user's profile Send private message

PostPosted: Tue Jan 10, 2012 1:23 pm     Reply with quote

SherpaDoug wrote:
A couple more suggestions;
1) Read_adc() automatically waits for the A/D to finish conversion. You rarely if ever need to use adc_done().

Not understand pl explain more.

SherpaDoug wrote:

2) Use select case instead of all those if() statements.

i'm use like
Code:
    while (1)
   {
        if (BlinkMains_flag==0)
     voltage_type = ProcessMains();
     // time_elasped set in interrupt
    // Quick start here if SW1 is  on during start up
     if ( (QuickStart_flag == 1) && (seconds<STARTUP_SEC) && (voltage_type != HIGH_VOLTAGE) )
                 voltage_type = NORMAL_VOLTAGE;
     
      switch(voltage_type)
      {
         case HIGH_VOLTAGE:
          output_low(RL5);       // Relay Off
         ErrorConditionExists_flag = 1;
            BlinkLED3();
            break;
           
         case LOW_VOLTAGE:
            output_low(RL5);     // Relay Off
         ErrorConditionExists_flag = 1;
            BlinkLED2();
            break;
           
           
         case OVER_LOD_SENCE:  //130% YET 10 MIN
          output_low(RL5);     // Relay Off 
         ErrorConditionExists_flag = 1;
        // ErrorConditionExists_lod_flag =1;
            BlinkLED4();
            break;           
         
          case NORMAL_VOLTAGE:
            output_low(BZR);
         ErrorConditionExists_flag = 0;
         //ErrorConditionExists_lod_flag =0;
            if ((BlinkMains_flag==1 || QuickStart_flag == 1 ))
         {
               output_high(L1);
               output_high(RL5);
         }
            output_low(L2);
            break;
         
         default:
            //We shouldn't get here
           
            break;
      }
   
// Delay to make sure a minimum of 2 TADs have gone by before we do another measurement.
   delay_ms(5); //1=5
   } // Loop Forever 


} // End Main

SherpaDoug wrote:

3) Look up "Olympic Average" on this forum for a simple fast way to average A/D readings and get rid of outlyers. At least make sure your ADC_AVERAGE_COUNT is a power of 2 so the division is easy.

OK I try "Olympic Average" ,if u don't mine can u fulfill it with & ADC_AVERAGE_COUNT is a power of 2 also.

SherpaDoug wrote:

4) tlong is declared as int32 so you should cast it to int16 before returning.

It is wrong ???
_________________
sahu
SherpaDoug



Joined: 07 Sep 2003
Posts: 1640
Location: Cape Cod Mass USA

View user's profile Send private message

PostPosted: Wed Jan 11, 2012 8:31 am     Reply with quote

Consider this code:
Code:

int16 olympic_ten(void) {
    int16 imax=0;
    int16 imin=1023;
    int16 isum=0;
    int16 itemp;
    int8 ctr;

    for (ctr=10;ctr>0;ctr--) {
        itemp=read_adc();
        if (itemp<imin) imin=itemp;
        if (itemp>imax) imax=itemp;
        isum+=itemp;
        delay_us(250);
    }
    //Now we have the sum of ten readings, and the max  & min readings
    isum-=imin;
    isum-=imax; //subtract the min and max from the sum, now only 8 are left

    itemp=isum/8; //compiler will automatically optimise this to a shift
    return itemp;
}


There is no distinct waiting for the A/D to convert. read_adc() does not return until the A/D is done. Checking the done flag is only useful if you are going to do other work while the A/D is busy.

The for() loop ends at zero so the code only has to compare to zero on each cycle, not do a subtraction and then compare to zero.

Note the declared size of itemp which matches the size of the declared function return value:
int16 olympic_ten(void) {
int16 itemp;
return itemp;

I usually find most of my A/D readings are OK, and one or two are really bad. Averaging just lets the bad readings contaminate the whole lot. This method removes the worst first, then averages the rest. I invented it while watching Olympic figure skating as I was working on code. The Olympic rules threw out the high and low figure skating judges scores and averaged the rest.
_________________
The search for better is endless. Instead simply find very good and get the job done.
sahu77



Joined: 08 Sep 2011
Posts: 202

View user's profile Send private message

PostPosted: Wed Jan 11, 2012 9:07 am     Reply with quote

now
Code:
int16 olympic_ten(void)
 {
     int32 tlong;
     int16 imax=0;
     int16 imin=1023;
     int16 isum=0;
     int16 itemp;
     int8 ctr;

     for (ctr=10;ctr>0;ctr--) {
         itemp=read_adc();
         if (itemp<imin) imin=itemp;
         if (itemp>imax) imax=itemp;
         isum+=itemp;
         delay_us(250);
     }
     //Now we have the sum of ten readings, and the max  & min readings
     isum-=imin;
     isum-=imax; //subtract the min and max from the sum, now only 8 are left

     itemp=isum/8; //compiler will automatically optimise this to a shift
     return itemp;

        adc_value = itemp;

        tlong = (int32)ADC_value*5000; //Convert the result in millivolts
        tlong = tlong/1023; // 0..1023 -> 0-5000mV
       
        return (int32) tlong;
 }

but confused how to set channels (AN0 ,AN1 & AN2). Before I use as
Code:
ch_0 = getchreading(0);
ch_1 = getchreading(1);
ch_2 = getchreading(2);

_________________
sahu
SherpaDoug



Joined: 07 Sep 2003
Posts: 1640
Location: Cape Cod Mass USA

View user's profile Send private message

PostPosted: Wed Jan 11, 2012 11:52 am     Reply with quote

Code:
int16 olympic_ten(int channel)
 {
     int32 tlong;
     int16 imax=0;
     int16 imin=1023;
     int16 isum=0;
     int16 itemp;
     int8 ctr;

     set_adc_channel(channel); // Select Channel
     delay_us(ADC_DELAY); // Time to settle, depends on sensor impedance

     for (ctr=10;ctr>0;ctr--) {
         itemp=read_adc();
         if (itemp<imin) imin=itemp;
         if (itemp>imax) imax=itemp;
         isum+=itemp;
         delay_us(ADC_DELAY);
     }
     //Now we have the sum of ten readings, and the max  & min readings
     isum-=imin;
     isum-=imax; //subtract the min and max from the sum, now only 8 are left

     itemp=isum/8; //compiler will automatically optimise this to a shift
     return itemp;

        adc_value = itemp;

        tlong = (int32)ADC_value*5000; //Convert the result in millivolts
        tlong = tlong/1023; // 0..1023 -> 0-5000mV
       
        return (int16) tlong; // cast result to fit return size
 }


Note if speed is more important than accuracy you could divide by 1024 instead of 1023. The error would be less than 0.1% but the slow 32 bit divide would turn into a quick bit shift.
_________________
The search for better is endless. Instead simply find very good and get the job done.
sahu77



Joined: 08 Sep 2011
Posts: 202

View user's profile Send private message

PostPosted: Wed Jan 11, 2012 12:07 pm     Reply with quote

which right ?
this
Code:
       adc_value = itemp;

         tlong = (int32)ADC_value*5000; //Convert the result in millivolts
         tlong = tlong/1023; // 0..1023 -> 0-5000mV
         
         return (int16) tlong; // cast result to fit return size

OR
Code:
       adc_value = itemp;

         tlong = (int16)ADC_value*5000; //Convert the result in millivolts
         tlong = tlong/1023; // 0..1023 -> 0-5000mV
         
         return (int16) tlong; // cast result to fit return size


but confused how to set channels (AN0 ,AN1 & AN2). Before I use as Code:
ch_0 = getchreading(0);
ch_1 = getchreading(1);
ch_2 = getchreading(2);
_________________
sahu
SherpaDoug



Joined: 07 Sep 2003
Posts: 1640
Location: Cape Cod Mass USA

View user's profile Send private message

PostPosted: Wed Jan 11, 2012 1:47 pm     Reply with quote

This line will not work:

tlong = (int16)ADC_value*5000; //Convert the result in millivolts

If ADC_value has the value 100 then 100 * 5000 will not fit a an int16 so it will be clipped and the accuracy lost. The PIC must first evaluate the right side of the equation before it assigns it to tlong. As ADC-value is not declared I would use:

tlong = (int32)itemp * 5000; //Convert the result in millivolts

The part (int32)itemp tells the compiler to use int32 size math for the multiplication.


This line:

return (int16) tlong; // cast result to fit return size

makes sure the int32 size tlong is properly converted to int16 size before it is returned from the function. The compiler will probably convert it automatically, but if it does not the bug could be very hard to find. It also might confuse someone else reading your code years from now.
_________________
The search for better is endless. Instead simply find very good and get the job done.
sahu77



Joined: 08 Sep 2011
Posts: 202

View user's profile Send private message

PostPosted: Wed Jan 11, 2012 6:17 pm     Reply with quote

Thanks for reply. But my 2nd question is rest. How can I set AN0, AN1, AN3 ?
_________________
sahu
SherpaDoug



Joined: 07 Sep 2003
Posts: 1640
Location: Cape Cod Mass USA

View user's profile Send private message

PostPosted: Wed Jan 11, 2012 9:04 pm     Reply with quote

How about this:
Code:
ch_0 = olympic_ten(0);
ch_1 = olympic_ten(1);
ch_2 = olympic_ten(2);


If you actually want 0,1,3 you may have a problem. Some PICs are limited in the analog vs digital combinations the hardware will support. I don't know the details of your PIC.
_________________
The search for better is endless. Instead simply find very good and get the job done.
Brandon03



Joined: 11 Jan 2012
Posts: 3

View user's profile Send private message

PostPosted: Thu Jan 12, 2012 12:12 am     Reply with quote

there is a way to do true simultaneous sampling. It onvolves using many discrete Sample and Hold circuits. You just connect S/h input to your signals, outputs to analog pins of PIC and tie all of write strobe pins together to single pin of PIC. This way you can trigger many samplings at exactly the same time and then digitalize them one by one with ADC.
RF_Developer



Joined: 07 Feb 2011
Posts: 839

View user's profile Send private message

PostPosted: Thu Jan 12, 2012 3:47 am     Reply with quote

sahu77 wrote:
which right ?
Code:
       adc_value = itemp;

         tlong = (int32)adc_value*5000; //Convert the result in millivolts
         tlong = tlong/1024; // 0..1023 -> 0-5000 (1023/1024) mV
         
         return (int16) tlong; // cast result to fit return size


but confused how to set channels (AN0 ,AN1 & AN2). Before I use as Code:
ch_0 = getchreading(0);
ch_1 = getchreading(1);
ch_2 = getchreading(2);

You should be able to select ADC channels that way. It should work with that code. The setup_adc_ports() in main() must set up all the ADC channels you wish to use. That varies on different PICs, so check your datasheet and device .h file.

I don't know why getchreading() has this wait:
delay_ms(ADC_2TAD); // Wait 2 TADs before doing another A/D conversion
after reading the ADC result. Its a) pointless and b) far too long as it wait for milliseconds and ADC_2TAD is not a meaningful value in ms (its related to ADC clock cycles which are generally a bit longer than a us if the ADC is optimally configured). Also, as others have said there's no point in this routine for tirggering the conversion with ADC_START_ONLY then waitng for it to complete. Simply use read_adc() with no parameters and it will wait for your. If your PIC supports hardware TAD waiting then also use that and you won't need to wait after channel selection, the hardware will do it for you.

There are two errors:

Use consistent case for your variables. CCS doesn't care, it thinks adc_value is the same as ADC_value, but it should care, C IS case sensitive. Other C compilers would give compile errors.

The other thing is another example of the classic ADC range error Rolling Eyes

I'd not do this by delays Shocked I'd sample all the signals every loop of the main code, averaging as required. I'd have a single timer giving me a tick every 10 or 100ms or so. I'd then monitor the values and count ticks to time the minutes as needed. The loop time would mainly be goverened by how long it took to sample the ADC channels.

RF Developer
SherpaDoug



Joined: 07 Sep 2003
Posts: 1640
Location: Cape Cod Mass USA

View user's profile Send private message

PostPosted: Thu Jan 12, 2012 7:25 am     Reply with quote

To achieve simultaneous sampling you could try several ways.

1) Brandon03 describes how to do it with external hardware.
2) You could use several small PICs, one for each input and synchronize them.
3) If you are averaging anyway, you could interleave the channels in the averaging loop. It is not true simultaneous sampling but pretty close.

Sample 1 channel A
Sample 1 channel B
Sample 1 channel C
Sample 2 channel A
Sample 2 channel B
Sample 2 channel C
.
.
.
Sample N channel A
Sample N channel B
Sample N channel C

Then average the N samples for each channel.
_________________
The search for better is endless. Instead simply find very good and get the job done.
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