View previous topic :: View next topic |
Author |
Message |
reptile404
Joined: 01 Apr 2011 Posts: 17
|
4 Adc channels?? |
Posted: Fri Jan 13, 2012 12:50 pm |
|
|
Hello !!
I would like to know a method that makes the pic read 4 adc values , evry 2ms it reads from a chennel , for example for 2 channels we can use this:
Code: |
int a=0,val,va2;
Timer2 (2ms)
if (a%2)
{
set_adc_channel(0);
delay_us(16);
val=read_adc();
a=a+1;
}
else {
set_adc_channel(1);
delay_us(16);
val2=read_adc();
a=a+1;
}
|
THNX _________________ REPTILE404
55AA55AA55
Last edited by reptile404 on Fri Jan 13, 2012 2:35 pm; edited 2 times in total |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Jan 13, 2012 1:58 pm |
|
|
what you show is NOT simultaneous - as the sampling periods occur at different times. i know of no way using THE PIC ALONE to do what you ask for. the PIC hardware is not structured to be able to do that. |
|
|
reptile404
Joined: 01 Apr 2011 Posts: 17
|
|
Posted: Fri Jan 13, 2012 2:27 pm |
|
|
Yes, I know that its impossible but I meant a method like I gave but reads 4 channels without using delay function (ofc not 16us). _________________ REPTILE404
55AA55AA55 |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Jan 13, 2012 2:49 pm |
|
|
Code: |
unsigned int16 val[4]={0,0,0,0};
// later
unsigned int8 i;
for (i=0; i<4;i++){
set_adc_channel(i);
val[i]=read_adc();
}
|
done in minimum time
can you see how to adapt the "ALL read"
i just showed to one after another ;-)) |
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Fri Jan 13, 2012 3:25 pm |
|
|
To get any accuracy if the 4 inputs are at differing voltages you will need a delay between the channel selection and ADC reading: Code: | unsigned int16 val[4]={0,0,0,0};
// later
unsigned int8 i;
for (i=0; i<4;i++){
set_adc_channel(i);
delay_us(16);
val[i]=read_adc();
} |
_________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
reptile404
Joined: 01 Apr 2011 Posts: 17
|
|
Posted: Sat Jan 14, 2012 3:32 am |
|
|
Thnx Guys ! But I didn't mean to use a loop (for). I just want to know a mathematical method. For example, I used (%) for two channels, using timers interruptions. For example, it reads from a channel every 2ms(overflow) loop, its not optimal.
thnks _________________ REPTILE404
55AA55AA55 |
|
|
reptile404
Joined: 01 Apr 2011 Posts: 17
|
|
Posted: Sat Jan 14, 2012 3:40 am |
|
|
Is it possible in ccs to know rest of division (a%4) ?
If a=5, so result is 1 and rest is 1. _________________ REPTILE404
55AA55AA55 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Sat Jan 14, 2012 4:06 am |
|
|
OK. I think I get what you are asking. Details will depend on your chip, clock etc., but something like:
Code: |
int16 adc_vals[4]; //Storage for results
enum adc_states {SELECT, START, MATHS};
#define MAX_CHAN (4)
#int_timer2
void tick_handler(void) {
static enum adc_states state=SELECT;
static int8 chan_num=0;
static int16 sums[4], tempint;
switch (state) {
case SELECT:
if (++chan_num)==MAX_CHAN) chan_num=0; //0...3 output
set_adc_channel(chan_num); //select next channel
state++;
break;
case START:
read_adc(ADC_START_ONLY); //start the adc, and exit immediately
state++;
break;
case MATHS:
sums[chan_num]+=read_adc(ADC_READ_ONLY); //get the reading
tempint=sums[chan_num]<<2; //generate the average (/4)
sums[chan_num]-=tempint; //update the sum
adc_vals[chan_num]=tempint; //store result
state=SELECT;
break;
}
}
|
All you do is setup timer2, to tick at (say) 100Hz, and enable the interrupts. Then the code first selects the next adc channel and exit's.
On the next interrupt (so allowing plenty of time for the ADC to acquire), it starts the acquisition (which now carries on between the interrupts).
On the next interrupt, it takes the reading, and performs a rolling average. This last interrupt takes the longest, mainly because of performing four array accesses, but still only perhaps 60 instructions. Obviously without averaging, the work is much simpler - up to you whether this is needed.
At any time in the main code you can get the current reading by just looking at the values in 'adc_vals'. Only 'caveat' here is that since these are 16bit values, you should either disable interrupts for a couple of cycles while you read the value, or test that the value you get matches what is in the array. So:
Code: |
int16 get_adc_val(int8 n) {
int16 local_temp;
do {
local_temp=adc_vals[n];
} while (local_temp!=adc_vals[n]);
return(local_temp);
}
//OR
int16 get_adc_val(int8 n) {
int16 local_temp;
disable_interrupts(GLOBAL);
local_temp=adc_vals[n];
enable_interrupts(GLOBAL);
return(local_temp);
}
|
Either version makes sure you get the value avoiding problems with 'half' the value being updated between the two reads. In the first case, if the value changes between the read and the test, then an update occurred
on one of them, so 'try again', while in the second, the interrupts are disabled just for the array access.
You can actually shorten the code, combining the 'select' with the end of the 'maths' phases, so a reading is taken every other interrupt, instead of every third interrupt. However if you do, you need to perform the first 'select' in the main code before the interrupt is enabled, and give long enough for acquisition to occur before starting the interrupt.
As it stands, it'll take a total of 12 interrupts for all the values to update, but at any time you can just read the array and get the last updated value..
Best Wishes |
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Sat Jan 14, 2012 9:50 pm |
|
|
reptile404 wrote: | Is it possible in ccs to know rest of division (a%4) ?
If a=5, so result is 1 and rest is 1. |
Is this what you are looking for?
a = 5
a/4 = 1
a%4 = 1
a=6
a/4 = 1
a%4 = 2
a=24
a/4 = 6
a%4 = 0 _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Sun Jan 15, 2012 2:42 am |
|
|
Look at the div operator.
This returns both quotient and remainder. So:
Code: |
#include <stdlib.h>
div_t idiv;
idiv=div(6,4);
//Then idiv.quot will be '1' - the integer division result and
//idiv.rem will be '2' - the remainder
|
Saves having to do both division and modulus when you want both parts. Changed it to '6', rather than '5', so that the two results are different, so you can see what is going on.
Best Wishes |
|
|
reptile404
Joined: 01 Apr 2011 Posts: 17
|
|
Posted: Sun Jan 15, 2012 10:40 am |
|
|
Thanks Mr.Ttelmah
i think its what i was looking for , i just need to know the remainder cauz i will do division on 4 and remainder values will be (0,1,2,3) whatever variable A is , then rem=0 => set_adc_channel(0);
rem=1=> set_adc_channel(1);
.
.
rem=3=> set_adc_channel(3);
as you said idiv.rem will return the remainder _________________ REPTILE404
55AA55AA55 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Sun Jan 15, 2012 10:56 am |
|
|
Seriously, look at how I do the channel select maths in the example I posted:
Code: |
if (++chan_num)==MAX_CHAN) chan_num=0; //0...3 output
|
With 'MAX_CHAN' set to 5,
chan_num will then count, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4 etc..
This takes about four machine instructions, whereas using a division or modulus takes possibly about 100.....
Rather a large difference.
Best Wishes |
|
|
|