ADC_ISR firing to0 fast to nearly stop main() from executing

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi again All,

I have a project and running @ 16Mhz with Atmega324p
this means that using the maximum prescaller of 128 my ADC speed will be 16000000 / 128 which is 125kHz Thus therefore each ADC conversion will be lasting about 13 / 125kHz which is about 104 us.

The MCU is normaly be sleeping and i will need to
periodically wake it every 500 ms and run some codes (ADC ISR)

The problem was that i was always wanted to use ADC interrupts but i didn't take into account the effects of its speed ':shock:'
I have calculated the possible runtime of my entire project and seems to be in about 165 ms. that is every 165 ms the system starts again from task_0 on... what does this have to with the ADC ISR?
well... it's clear that although a periodic 104 us ADC interrupt. will be a non blocking for the rest of tasks in my project, but will surely make the entire system
extremely slow! and this becomes really very poor and totally undesirable!
so my question was:

Is there anything wrong if i can eliminate the ADC ISR and place its ISR code into WDT's ISR insteady? since i decided to eliminate the ADC ISR due to its erroneous anyway?
the plan is that i must read what was in the ADC ISR every 500 ms. thus I am thinking that with this approach i will be waking the mcu up every 500 ms and do the ADC and checking of some bits stuff but again in WDT's ISR. i think it sounds funny to do ADC conversion within some other peripherals' ISR afterall ADC itself having its own ISR but which in this case fail to fit in due to the enormous speed which will almost be stopping the main() routine to execute!

The ISR code with the ADC is actually initializing an ADC and do some conversion for
2 channels and store their values into some other global volatile values. and if during this case these ADC values happen to exceed some threshold values I stop the mcu from sleeping mode
and main() routine is executed which in turn main() resumes the mcu back to sleep mode when it finishes doing the operation that's been caused due to the exceeding of the threshold by the ADC value and this carries on again& again

I know i wrote a lot and you still probably don't understand what my question was
but briefly i wanted to know if there would be anything wrong with doing ADC initialisation and other few quick bits (which don't have any delays) within the ISR WDT_vect. which will reasonably doing this every 500 ms.
I personally think that this isn't probably wiser (and surely someone will give me some better alternative approaches) But since it's just like any other ISR which is always meant to executes their code when fires it should be fine as long as all main() shared variables within it have declared volatile. ':o'

This is probably the lack of knowing how embedded system designer behave
in such conditions.
Please give me your thoughts and ideas or even better improvement
I will very much appreciated!

Thank you all

Last Edited: Wed. Apr 18, 2012 - 03:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The WDT isn't very accurate as a timing source, and it RESETS the entire micro, almost as if you had just turned it on. Probably not what you want.

One would typically set up a Timer / Counter to fire an ISR every 0.5 Sec.

Inside that ISR you read the LAST interrupt's ADC reading, and store that value. You then Trigger the ADC to take the next reading, and exit the ISR.

The ADC module takes the reading without slowing down the rest of the micro. So only a few clock cycles are needed to do the above, and it won't block the Main program.

If you need to take the ADC readings from several different inputs then another approach is needed. As you noted, you don't want to be waiting for the ADC to finish the first reading in a polled wait loop, before you can take the second reading.

If you don't need the readngs at the "same" time, and it is OK to sample them 1/4 Sec apart, then one option would be to set up the Timer/Counter for fire an interrupt every 1/4 Sec.

In that ISR you read the last interrupt's ADC value, (Input A). Store that value. Switch the ADC input MUX, (or whatever is needed to change channel, based upon that partucular micro), and trigger the next ADC reading, and exit the ISR. This is only a few more clock cycles than the above, one-channel, method.

The result would be the micro wakes up every 1/4 Sec and takes a Reading, first Ch A, then Ch B. So every 1/2 Sec you would still have two new readings, and both are obtained via interrupts, with very little impact on the overall system throughput.

Another Option is to keep track of which reading you are taking. The Timer/Counter fires every 1/2 Sec. In the ISR you Read Ch A, from the last interrupt), set up for Ch B, and trigger the ADC read of Ch B.

When the ADC done ISR fires you Read Ch B, set up for Ch A, trigger the start of A. Now sleep until the every 1/2 Sec interrupt fires again.

The Ch A readings are triggered by the Timer/Counter every 1/2 Sec interrupt. The Ch B readings are triggered by the ADC Done interrupt.

Note that your readings are always 1/2 Sec, or so, behind real time. If you need a faster response then yet another approach is needed.

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

it's clear that although a periodic 104 us ADC interrupt. will be a non blocking for the rest of tasks in my project, but will surely make the entire system
extremely slow!

Why is that so "clear"?

I have many apps that do continuous conversions on several/many ADC channels. At a 57kHz ADC clock, that is about 225us between ADC interrupts.

If my ADC ISR takes 10us, then I'm using 5% of my processor to do continuous ADC conversions. Hardly noticed. Certainly not "extremely slow".

What do you >>do<< with the results of all those conversions? About 1000 values during your 100ms "task". If you don't need the results of the conversions, then don't do the conversions!

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

104µs sounds like a lot of time to me.
The shortest ISR I've ever used is 5µs.
And the MCU still update an LCD properly and run some other tasks.
Just for fun I tried to run the ISR (Timer Ovf) at 2µs but then things were beginning to crumble.

But I agree with Lee, what on earth are going to do with all those samples?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@ theusch

What i wanted to say was that being interrupted let's say maybe every 120 us (incl. ADC ISR code) is indesirable since my main() needs to run its code in every 165 ms.
So to clarify why this really is undesirable, while ADC is firing every 120 us the main routine () won't even be having enough time to process and do something with that ADC value while waiting for the next one! do you get my point?
(I know i am not good in explaining and i most have trouble explaining exactly what i mean)

The point was that ADC ISR should be firing > = than main() 's total runtime
So that for every channel's ADC in progress... main() has enough time to do something with the previous ADC channel result (during the time in which ADC is doing yet another conversion) and then ADC interrupt main () when finishes again main do something with that ADC's latest result etc...
Do you see why I don't want ADC to interrupt main() in less or equal than every 165 ms ?

I think i should have also mention that I am using a cooperative multitasking with the scheduler of 5ms. thus in every so often, the scheduler checks if there is any due tasks, if the current one isn't due, it then checks the next one, if it's due it then process it, and it repeats again and and again. if the scheduler happen to find every task due after another
it will use the total time of 165 ms. (Tho not all tasks are using 5 ms, some spend more time)

So I don't worry about any other interrupt like external since hey are not periodic, but I think that I surely don't want any periodic interrupt (like ADC ISR) that might have to interrupt every so often than main() will be checking & finish throughout all its tasks!

You can give me your ideas and thought about this,

@ DocJC

Thank you for your idea of using a timer ISR insteady, I have also been thinking of timer too.
but yet WDT was nothing more than simple counter that gives a pulse when it counts up. probably not as accurate as the actual timer like you said.
But I will def consider your idea too. Thank you.

@ Lennart

hey Thank you for your ideas too. though every 104 us isn't the time the ADC ISR takes
my apologies if it wasn't clear. (like i said i sometimes have trouble explaining)
but that 104 us is the time it takes between each conversion to another thus (13/125kHz)
and remember that ADIF won't be set until conversion is finishes the the associated ADC ISR can be run (this ISR itself) isn't more than probably 8 us

I was mostly concerned about how often, since all my tasks have a definite runtime ( Tho, it doesn't mean i can't interrupt them at anytime which is the case of some Ext. ISRs) but again i am worrying about interrupting them every often! so often that almost only ADC's ISR is executed everytime the MCU isn't sleeping.
I don't know if my point is a bit clear now?

Thank you all for your feedback.

Kase.

Last Edited: Wed. Apr 18, 2012 - 05:04 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually I think i am going to make it easier and forget about all ADC ISR insteady i will implement the Read_ADC f(x) which I will place in one of timer ISR of every 500 ms like DocJC suggested which in turn will be doing the same thing every 500 ms.

Probably the lesson to learn here is that using an ADC ISR in some Real Time Operating System isn't ideal.
That's what it seems to me AFAIC but someone else who might have more experience with RTOS can surely make it even clear.

Thank you all

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

insteady i will implement the Read_ADC f(x) which I will place in one of timer ISR of every 500 ms like DocJC suggested which in turn will be doing the same thing every 500 ms.

Quote:

Probably the lesson to learn here is that using an ADC ISR in some Real Time Operating System isn't ideal.

Just the opposite. Doing the read function will cause a bloated ISR, and find you sitting in one spot wasting many microseconds.

Why don't you trigger off the timer event as suggested? The result will then be waiting for you at some point.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.