|
|
View previous topic :: View next topic |
Author |
Message |
MO
Joined: 07 Nov 2008 Posts: 4
|
Read analog sensors in the background? |
Posted: Thu Sep 03, 2009 3:42 am |
|
|
Hallo,
I want to read 6 analog sensors and update their accosiated globals. I tried using int_ad with the next a/d conversion started at the end of the ad_isr, but this kept my program almost exclusively in the ad_isr leaving very little time for main() and other routines.
I'm using a PIC18F4620 with 20MHz crystal. I use setup_adc_ports (AN0_TO_AN7|VSS_VDD) with setup_adc(ADC_CLOCK_DIV_32). I am already using int_rda, int_ext1, int_rb and timer0(20ms timebase), timer2(pwm) and timer3(software pwm).
I want to update the analog readings so that my main() and other routines can use the most current readings without having to query and wait for the a/d convertion to finish.
What is a good strategy for doing this? Please point me in an efficient direction.
Thanks for the guidance!
Regards,
Madri |
|
|
Ttelmah Guest
|
|
Posted: Thu Sep 03, 2009 4:59 am |
|
|
Have a timer 'tick' interrupt. Interval to suit you, but typically perhaps about 50 to 100Hz.
Then have this use a state machine, something like:
Code: |
int16 ad_readings[4]; //Have enough entries for the channel numbers in
//'chans' below.
#INT_TIMERx
void tick(void) {
static int8 state=0;
static int8 channel=0;
const int8 chans[] = {0,1,4,6,255}; //put channel numbers you want
//to read here, terminated by '255'.
switch (state) {
case 0:
//First select the channel required
set_adc_channel(chans[channel]);
state++;
break;
case 1:
//One 'tick' later, start the ADC
read_adc(ADC_START_ONLY);
state++;
break;
case 2:
//One tick later, read the adc
ad_readings[channel]=read_ADC(ADC_READ_ONLY);
state++;
break;
case 3:
//One tick later, tidy up
if (chans[++channel]==255) {
channel=0; //restart the channel scan
}
state=0;
break;
}
}
|
Each 'tick', is quite quick (slowest thing is the array accesses), doing just one job, on the selected channels. It works through a 'list' of ADC channel numbers (in the example, 0,1,4,6), first selecting the channel, then returnng to the main code, while the capacitor in the chip charges, then triggering sampling, and returning while this is done, then reading the channel and storing it in the array, and finally selecting the next required channel.
Beest Wishes |
|
|
Guest
|
|
Posted: Thu Sep 03, 2009 7:24 am |
|
|
Thank Ttelmah for the quick reply - and the code too! This is fantastic - just what I was hoping to be able to do and not able to get my mind around how to go about it.
It works great!
Thank you so much for this - made my day for sure. Maybe even week.
Madri |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Thu Sep 03, 2009 8:20 am |
|
|
i have been doing almost the identical thing for over a year with a four analog channel rotation, but using just 2 states :
state ZERO, TWO and THREE can be combined very efficiently
by the sequence:
STATE: 0
Start ADC CHAN
state =!state;
STATE:1
1- READ_ADC,
2- adjust chan number glo var ,
3- set next ADC_CHAN ( for acquisition time) -
state =!state;
then the reading is STARTED on the next tick,
the OTHER state of the machine )
there are only two time consuming things being done ( for good reading accuracy) and they are (1) settling time - which occurs after the chan is switched "set adc chan" and (2) conversion time "start adc conv"
this 2 state machine allocates ( equal enough ) time to each
the reason to go this way ?
Your acq rate is doubled over ttelmahs nice example and
the extra time lingering in the ISR is diddly , since other than ADC START and the ACQ time delay after chan set - all instrux are wholly deterministic.
this pretty evenly splits ACQ time and AD/CONVERT time between alternating 'ticks'
you do need to INIT the process vars of course before first call
in my implementation i actually use a rolling average integrator that sums 4 sets of readings on each of four channels and updates the running average every 16 tix - of 8.19ms , on an ~20 mhz system but that is another story |
|
|
Ttelmah Guest
|
|
Posted: Thu Sep 03, 2009 9:10 am |
|
|
Yes. In fact my normal one does the 'select next channel', immediately after reading the value, reducing the states by one. I have posted versions of this in the past.
However I normally keep one more state than the minimum, since I typically perform some averaging functionality, and prefer to perform the maths in an extra state. You can keep the arithmetic very fast, if you only perform integer addition, and integer division by a binary factor (2,4,8), using the >> function, avoiding maths re-entancy problems, and still keeping the ticks fast.
It is worth also realising, that if you have the tick at some nice division of a second, using perhaps timer2, you can also do a lot of your basic system timings, with something like:
Code: |
int16 time_to_wait;
//Then in the interrupt
static int8 ticker;
if (ticker) --ticker;
else {
ticker=TICKS_PER_SECOND;
if (time_to_wait) --time_to_wait;
}
|
Then in your main code, if you want to wait for 30 seconds, you just set 'time_to_wait' to 30, and can perform other jobs, till it gets to zero.
The 'nice' thing is that the work per tick is tiny (for 99% - on a 100Hz ticker), just a single byte test, and decrement. On the 100th loop, just a 16bit test, decrement and a load. Also, you cannot 'miss' the end, since one the [spam] reaches '0', it'll stay there till re-loaded.
Using a system 'tick', is a very useful general tool.
Best Wishes |
|
|
|
|
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
|