View previous topic :: View next topic |
Author |
Message |
sahu77
Joined: 08 Sep 2011 Posts: 202
|
multiple Adc simultaneously |
Posted: Tue Jan 10, 2012 11:31 am |
|
|
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: 9246 Location: Greensville,Ontario
|
|
Posted: Tue Jan 10, 2012 11:58 am |
|
|
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
|
|
Posted: Tue Jan 10, 2012 12:48 pm |
|
|
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
|
|
Posted: Tue Jan 10, 2012 1:09 pm |
|
|
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
|
|
Posted: Tue Jan 10, 2012 1:23 pm |
|
|
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
|
|
Posted: Wed Jan 11, 2012 8:31 am |
|
|
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
|
|
Posted: Wed Jan 11, 2012 9:07 am |
|
|
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
|
|
Posted: Wed Jan 11, 2012 11:52 am |
|
|
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
|
|
Posted: Wed Jan 11, 2012 12:07 pm |
|
|
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
|
|
Posted: Wed Jan 11, 2012 1:47 pm |
|
|
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
|
|
Posted: Wed Jan 11, 2012 6:17 pm |
|
|
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
|
|
Posted: Wed Jan 11, 2012 9:04 pm |
|
|
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
|
|
Posted: Thu Jan 12, 2012 12:12 am |
|
|
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
|
|
Posted: Thu Jan 12, 2012 3:47 am |
|
|
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
I'd not do this by delays 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
|
|
Posted: Thu Jan 12, 2012 7:25 am |
|
|
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. |
|
|
|