[AVR] is it worth using the ADC interrupt to multitask - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: June 26, 2018, 01:42:30 pm
content: I'm furthering my AVR libraries and although I have no plans now I wonder if it is worth having the capability to continue running a program while the ADC works and then come back for the result.So with a system clock of 8MHz and an ADC clock of 100KHz that means the CPU sits idle waiting for a conversion for 80 clocks or 20-80 instructions. Is it therefore worth it? I mean I'd not be surprised if it takes most of those cycles to manage the thing anyway?

author: MorgothCreator
author_rank: Contributor
timestamp: June 26, 2018, 01:47:44 pm
content: Depending how many conversions you need in a second, if you have 10K conversions you can use interrupts, or to start a conversion and come back for the result, but if you need 1 conversion or something like that you can make it to wait for the conversion.

author: Simon
author_rank: Unknown
timestamp: June 26, 2018, 02:10:26 pm
content: Yes but then i need to create a system where i "remember" which variable that result in the ADC register is so I need a mechanism to manage the routine updating of variables which makes me wonder if attempting such a thing is worthwhile.I know that ARM µC's do this automatically in hardware but then with significantly higher clocks and the additional hardware i guess it is easy.

author: JPortici
author_rank: Super Contributor
timestamp: June 26, 2018, 02:31:25 pm
content: if the accuracy of the sample rate is low enough i just poll the ADC interrupt flag or the DONE bit in the ADC registers...since i usually use the various analog inputs, instead of using one and multiplexing before the ADC pin, my "selection" variable is the channel select bits!!this pseudocode at least works on PICs, but i am sure you can adapt it to AVRCode:void ADC_Read() {//This is a "Task", called inside the main loop.if (!ADCIF) {return;}switch(ADCONbits.ADCHS) {case 0://AN0 has finished convertingdata0 = ADRES;ADCONbits.ADCHS = 1; //Next Step will be converting AN1break;case 1://AN1 has finished convertingdata1 = ADRES;ADCONbits.ADCHS = 2;break;// ...default://Shouldn't come here.ADCONbits.ADCHS = 0;break;}ADCIF = 0;ADCONbits.GO = 1;}newest PIC18 and i think also PIC16 with ADCC (ADC with Computation.. fancy name) will also have automatic sample/convert with channel scanso this is not required, when the interrupt flag is set just get the whole buffer of scans..

author: MasterT
author_rank: Frequent Contributor
timestamp: June 26, 2018, 03:50:21 pm
content: Quote from: Simon on June 26, 2018, 01:42:30 pmADC has it's own prescaler, in your example it's running 100 kHz. Also ADC requires 13-14 clock cycles to complete conversion, so we are talking about 14x80 = 1120 uCPU clock cycles. Yes, it's worth not to wait ADC conversion complete , and spend 20-50 clock to save registers / stack, and about same to restore after returning from ISR, instead of loosing 1120 polling ADC flag.

author: NorthGuy
author_rank: Super Contributor
timestamp: June 26, 2018, 04:09:01 pm
content: Quote from: Simon on June 26, 2018, 01:42:30 pmIf you have nothing else to do, why not idle the CPU while you're waiting?

author: Kleinstein
author_rank: Super Contributor
timestamp: June 26, 2018, 04:27:04 pm
content: If there is nothing to do, there is a special ADC power saving mode to bring the CPU to idle state while the ADC conversion is running. It's advertised to reduce ADC noise, thought I usually don't have a noise problem.It is definitely worth to do an ADC conversion in the background. Some 1000 cycles it quite a lot of time. Even with a 1 MHz clock and thus maybe 100 cycles it is sometimes worth it. However alternative to using an interrupt one can also use program control with separate ADC trigger and ADC reading.Another important case is using the ADC ISR together with auto-trigger / continuous conversion mode. Though I don't see much need for library support on this.

author: Simon
author_rank: Unknown
timestamp: June 26, 2018, 06:26:29 pm
content: Quote from: MasterT on June 26, 2018, 03:50:21 pmAh yes of course I thought i had worked it out to be more once before. So that is up to 300 instructions.The thing is I also do oversampling so this takes time and makes matters a little more complicated. It's not something I need to do but would be nice to have in my back pocket.

author: westfw
author_rank: Super Contributor
timestamp: June 27, 2018, 08:53:44 am
content: QuoteHuh? Most AVR instructions are single-cycle. 14.5 ADC clocks at 125000Hz is about 900 instructions. (The available prescalers will give you 125kHz or 62.5kHz At 62.5kHz, )With an ISR, you could maintain several pins worth of averaged/filtered ADC values in memory, and then access them "instantly" from your top-level code. Sometimes you want the ADC conversion to provide your timing as well, but other times, not.Code:ISR(ADC) { result = ADC; adjustReading(whichpin++, ADC); if( whichpin > MAXADCPINS) whichpin = 0;}

author: Scrts
author_rank: Frequent Contributor
timestamp: June 27, 2018, 01:20:47 pm
content: In general, it's a good practice to use interrupts. You just let your microcontroller idle and save energy. It is also a more neat in terms of code design most of the times.

author: Simon
author_rank: Unknown
timestamp: June 27, 2018, 02:44:02 pm
content: Yes the thing i have to look at is how I design a system that decides which channel it wants to sample and how many times when I am doing over sampling and where to put that data once acquired.So far I have only done slow responding things that have one ADC channel so even running the amount of conversions for oversampling has a response that is non lagging from the human time frame point of view.

author: Simon
author_rank: Unknown
timestamp: June 27, 2018, 03:01:02 pm
content: Oh and as for "most" instructions being single cycle setting a pin high or low takes 4 clocks

author: Simon
author_rank: Unknown
timestamp: June 27, 2018, 06:06:38 pm
content: So, If I want to "auto scan" a certain number of inputs I could set up in the finished conversion loop interrupt routine that takes the value from the ADC output register and puts it into the relevant variable and then sets a new channel and goes again.If i want to do oversampling I have to either add the numbers to the variable while counting and then do the average calculation or I set up an array variable for each channel and keep cycling through those so that the final value can be extracted by he main program that will have a slightly more up to date value.

author: Monkeh
author_rank: Super Contributor
timestamp: June 27, 2018, 06:39:04 pm
content: Quote from: Simon on June 27, 2018, 03:01:02 pmSo your most common instruction is toggling a pin?What is this, advanced blinky?

author: Kleinstein
author_rank: Super Contributor
timestamp: June 27, 2018, 06:53:47 pm
content: For oversampling / averaging, I would leave the division to the main program and let the ISR only add up the numbers, count samples and copy the result. To get the extra resolution from oversampling, one would divide by less than the number of samples anyway - so maybe average 256 samples and divide by 16.Changing the the ADC channel in the ISR with a continuous ADC mode can be tricky: it depends on the timing if the new set channel value effects the next conversion or the next conversion has already started sampling and would use the old value. It is usually OK if there are no other interrupts but difficult if there are other interrupts active that could add extra delay.

author: westfw
author_rank: Super Contributor
timestamp: June 27, 2018, 07:46:57 pm
content: Quote"Setting a pin high" is anoperation, not an instruction. How many instructions (and how many cycles) it will take depends on a lot of things - exactly which chip, exactly which pin, whether the arguments are constants or variables, which compiler at what optimization level...None of the variations I can think of will take 4 cycles:Code:out PORTB, r16 ; one cycle. Affects all bits PORTB. "medium" port rangesbi PORTB, bitno ; two cycles. requires constants. limited PORT range.in r16, PORTB ; three cycles. "medium" port rangeori r16, bitmaskout PORTB, r16lds r16, PORTL ; 5 cycles. ANY port.ori r16, bitmasksts PORTL, r16digitalWrite(pin, val); // About 80 cycles...(edit: corrected formatting of code)

author: obiwanjacobi
author_rank: Super Contributor
timestamp: June 28, 2018, 06:36:44 am
content: I view using the asynchronous hardware 'cores' of the IO peripherals the only way to get real parallelism out of small MCU.If you want to squeeze out everything out of an AVR then using its hardware to the fullest is the first thing you think about.It also depends on how critical your mainloop is: can you afford to wait for any peripheral (sync) until its finished - causing jitter in the main-loop timings?Of course using interrupts would also make the code more complex - the dev (user of your lib) has to think about this parallelism and mark data vars as volatile etc.For inspiration here is my take on the AVR UART, but its C++/templates (all files that start with 'Usart').