Interrupts Not Required

Go To Last Post
58 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've been tidying my filing cabinet and came across an article describing an approach to co-operative multitasking which makes an interesting statement about the approach...

 

Quote:

No interrupts - all I/O is polled. If the processor doesn't have enough time to poll for I/O then it doesn't have enough time to handle the worst-case arrival rate of interrupts.

 

 

I can't decide if I agree or not. It feels wrong to someone who has used interrupts for more years than I care to remember but the argument also feels right. If, over a long period of time, you don't have enough processing to deal with the amount of 'traffic' your system sees then it will fail. I think the approach fails in that interrupts can even out the processing required. Things like FIFO buffers on incoming UART data will keep up with incoming data faster than you can process it BUT you will then need a period of time when the UART is quiet to allow you to process the data. And if you adopt the no-interrupts approach then you can gain back processing time as you won't have the overhead of ISRs or buffer manipulation.

 

The approach is basically a timer tick which starts a while() loop that calls each task in turn. Each task is short and if needed is based around a state machine. So the total run time of all your tasks is less than the tick time. All tasks run on every tick.

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

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

I think some "High Integrity" regimes forbid interrupts, because their non-determinate non-deterministic nature makes it impossible to prove correctness ... ?

 

ISTR that the VIPER had no interrupts for this reason?

 

https://en.wikipedia.org/wiki/VIPER_microprocessor

 

But I think it's probably one of those cases where interrupts may not be necessary - but they can certainly be very convenient.

 

 

EDIT

 

I think "deterministic" was the word I was looking for.

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Mon. Mar 26, 2018 - 11:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Brian Fairchild wrote:
It feels wrong
Surely it's a question of sequencing? If you poll things then, unless you are very good at "plate spinning" you may be in the loop waiting for action from one device when another needs immediate servicing. Interrupts allow for "instant service". But there is always the question of "overhead" to get in and out of interrupts. Most posters here believe, for example, that if you want to keep an SPI pipe "fed" you are better doing it in a polling loop than contemplating interrupts.

Last Edited: Mon. Mar 26, 2018 - 11:04 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

... you may be in the loop waiting for action from one device when another needs immediate servicing...

 

The technique used overcomes this by having each task built around a state machine. On every invocation a task will see if it's peripheral needs servicing and returns if not. Task that need a lot of time are broken up into steps which run across multiple loop times. It talks of a 'tick' time as low as 100us. It also supports inter-task communication although thats a bit of a kludge as it involves one task altering the state of another (it's written in assembler).

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

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

Brian Fairchild wrote:

... Task that need a lot of time are broken up into steps which run across multiple loop times. It talks of a 'tick' time as low as 100us. It also supports inter-task communication although thats a bit of a kludge as it involves one task altering the state of another ...

 

 Does not look like less overhead compared to ISR.

 And you can't sleep if pollig all the time, can you?

 Nevertheless, it may fly if kept simple. It could be a half-duplex interface converter or similar.

 


Qoitech AB, The Home of Otii Arc, an SMU for every developer

Check out Otii solution at www.qoitech.com

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

Task that need a lot of time are broken up into steps which run across multiple loop times.

To some extent I agree with the approach of breaking things up.  And IMO co-operative multitasking scheme on an AVR8 is much more appropriate than preemptive.  But "no ISRs" as a "rule"?  I'd have to disagree.

 

1.  Some things are hard to break up into tiny chunks.  E.g. multi-byte EEPROM could, I guess, be broken up.  But...

2.  The overhead and structure to keep track of all these tiny task will end up decreasing the system throughput, and not increasing it.

 

IMO/IME.

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

Yabusame wrote:
And you can't sleep if pollig all the time, can you?

AVR8's don't awaken without interrupts enabled and a catching ISR, do they?

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

I actually quite like this way of programming.

The timer tick interrupt is of course always ticking.

Most of the hard work is done in several state machines.

C++ is very nice for state machines. You can wrap a class around a state machine together with a function pointer.

Execute the function pointer for a state, states may change the pointer at will.

This avoids the switch( state) overhead, and it also chops the usually very big switch into small state() functions.

 

Because of the flat structure there is much less need for using volatile vars and protection around sempaphores or other complications such as ATOMIC_BLOCK{}.

The is no worry whatsoever if a 4 byte long is shared between 2 state machines in such a cooperative system.

 

But I do not completely try to avoid interrupts. I just tend to make them as small as feasible.

My interrupts rarely exceed 100 cpu cycles, most are < 50, which keeps the interrupt latency for single interrupt level processors such as AVR's low.

 

This quote is plain wrong / oversimplified:

Brian Fairchild wrote:
If the processor doesn't have enough time to poll for I/O then it doesn't have enough time to handle the worst-case arrival rate of interrupts.

A simple example: If you manage to pull all I/O and run the statemachines in 100us, then you would be unable to send/receive a long string through a uart @115k2baud.

115k2baud needs attention on average every 87us (8 bits +start+stop) A Uart receive interrupt can read UDR and put it in a circular buffer in a handfull of CPU cycles. I've done reliable 115k2baud interrupts controlled uart comm's with a F_CPU of 1.842MHz. If because of other interrupts the latency for the UART increases a bit that's not such a big deal, as long as it manages to milk the UDR register before it under or overflows.

 

Some time ago I had a special case. I had a 4096ppr quadrature encoder connected to a motor shaft, which I wanted to run at upto 3000rpm.

Because quadrature uses 4 states for each "pulse" cycle that is a total of:

4096*4*3000/60  =  819200 interrupts/s.

It was doable, but the result was that I could only use the 2 interrupt for the A and B quadrature channels. Adding a uart interrupt was out of the question because it would wreack havoc on the encoder interrupt latency.

This would mean a rewrite of existing uart and other libraries and too much debug time to make the polled stuff work reliably.

Because of that I abandoned that idea and it was one of the reasons I bought some STM32F103C8T6 "blue pills" to play with. They are about the same price as an AVR from China.

These 72MHz arm Cortex 3 monsters have hardware support for quadrature encoders.

Also bought some "Maple Mini's" with twice the Flash, but haven't taken those out of their antistatic bags yet...

The AVR Xmega's (and some new Tiny's) have similar capabilities.

 

"Co routines" might be applicable here. They have been a part of the "Boost" library collection for some time, and they seem to be in the process of becoming an oficial part of the next C++ standard.

https://en.wikipedia.org/wiki/Coroutine

https://isocpp.org/search/google?q=coroutines

 

Everything put together...

Put some diversity in your toolbox so you can be flexible in your approach to a project.

Sitting down with a piece of paper and a pen for a few hours to make strategic decisions on how to tackle a particular problems can avoid a lot of hacks and headackes later on in product development.

 

Edit:

Added "baud" to 115k2 to avoid confusion with 115k2 interrupts/s. The uart interrupt only triggers at 10% of that (11.520 interrupts/s).

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Last Edited: Mon. Mar 26, 2018 - 07:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 I've done reliable 115k2 interrupts with a F_CPU of 1.842MHz

Perhaps in ASM but not in C!

 

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

Paulvdh wrote:

The timer tick interrupt is of course always ticking.

 

I've just knocked out a simple 9 task application. I don't even use the timer tick interrupt; I just poll TOV0 at the top of my main loop which happens every 128us.

 

Paulvdh wrote:

Most of the hard work is done in several state machines.

 

In my test case, 9 of them. Each one of 8 turns one LED on and off with different on and off periods. They have 5 states: idle, turn on led, delay, turn off led, delay. The 9th starts each of the other task in turn with a visible delay between them.

 

 

 

Paulvdh wrote:

Because of the flat structure there is much less need for using volatile vars and protection around sempaphores or other complications such as ATOMIC_BLOCK{}.

The is no worry whatsoever if a 4 byte long is shared between 2 state machines in such a cooperative system.

 

That's one of the other advantages mentioned. Because you absolutely guarantee the order things happen you can absolutely guarantee access to messages and buffers etc. 

 

Paulvdh wrote:

This quote is plain wrong / oversimplified:

Brian Fairchild wrote:
If the processor doesn't have enough time to poll for I/O then it doesn't have enough time to handle the worst-case arrival rate of interrupts.

 

I don't think it is. If you have, say, data arriving every 10us then you absolutely must have, ON AVERAGE, 10us available to process that data. It might be that after a burst of data things go quiet and you can catch up but you still need that processing time.

 

Let's take another example....

 

An AVR is expected to process a continuous stream of data arriving on a 100kbaud serial link. So that's a byte every 100us (assuming 8-N-1).

 

That same AVR could process data at 1Mbaud (a byte every 10us) as long as the duty cycle was 10% so that, on average, the data rate was still a byte every 100us.

 

 

I'm not particularly proposing this as something new or exciting. That statement I quoted though did make me stop and go 'Huh?' and then think about it.

 

I am a great fan of cooperative multitasking frameworks. They don't have to be complicated and rarely run to more than a few 100 bytes. Once you move beyond linking up an ultrasonic distance sensor to a couple of 7-segment display, an X-Bee module, and a USB connection to a PC, all written in the Arduino IDE and ported to Studio 7 to run on an obsolete chip with too few pins is a certain clarity in design. Sorry, did I mention the D word....?

 

 

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

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

Brian Fairchild wrote:

Paulvdh wrote:

The timer tick interrupt is of course always ticking.

 

I've just knocked out a simple 9 task application. I don't even use the timer tick interrupt; I just poll TOV0 at the top of my main loop which happens every 128us.

 

Paulvdh wrote:

Most of the hard work is done in several state machines.

 

In my test case, 9 of them. Each one of 8 turns one LED on and off with different on and off periods. They have 5 states: idle, turn on led, delay, turn off led, delay. The 9th starts each of the other task in turn with a visible delay between them.

 

 

 

Paulvdh wrote:

Because of the flat structure there is much less need for using volatile vars and protection around sempaphores or other complications such as ATOMIC_BLOCK{}.

The is no worry whatsoever if a 4 byte long is shared between 2 state machines in such a cooperative system.

 

That's one of the other advantages mentioned. Because you absolutely guarantee the order things happen you can absolutely guarantee access to messages and buffers etc. 

 

Paulvdh wrote:

This quote is plain wrong / oversimplified:

Brian Fairchild wrote:
If the processor doesn't have enough time to poll for I/O then it doesn't have enough time to handle the worst-case arrival rate of interrupts.

 

I don't think it is. If you have, say, data arriving every 10us then you absolutely must have, ON AVERAGE, 10us available to process that data. It might be that after a burst of data things go quiet and you can catch up but you still need that processing time.

 

Let's take another example....

 

An AVR is expected to process a continuous stream of data arriving on a 100kbaud serial link. So that's a byte every 100us (assuming 8-N-1). Let's say it takes 98us to process the data.

 

That same AVR could process data at 1Mbaud (a byte every 10us) as long as the duty cycle was 10% so that, on average, the data rate was still a byte every 98.1us.

 

 

I'm not particularly proposing this as something new or exciting. That statement I quoted though did make me stop and go 'Huh?' and then think about it.

 

I am a great fan of cooperative multitasking frameworks. They don't have to be complicated and rarely run to more than a few 100 bytes. Once you move beyond linking up an ultrasonic distance sensor to a couple of 7-segment display, an X-Bee module, and a USB connection to a PC, all written in the Arduino IDE and ported to Studio 7 to run on an obsolete chip with too few pins is a certain clarity in design. Sorry, did I mention the D word....?

 

 

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

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

Paulvdh wrote:
A simple example: If you manage to pull all I/O and run the statemachines in 100us, then you would be unable to send/receive a long string through a uart @115k2.
Bad example, because your statement is wrong for these specific values. UDR is a two byte queue, so you can handle 115k2 in 100µs intervals. You simply have to "empty" UDR in each interval instead of reading only one byte from it.

Stefan Ernst

Last Edited: Mon. Mar 26, 2018 - 02:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

115k2 with an 1.842MHz crystal is 158 cpu cycles, which is enough to put some data into a buffer even in C.

But I must admit, it was not a continous stream of data. Data came in bursts of around 20 bytes.

This was also on an old m8 and the extra buffered UDR was new back then and only worked for incoming data.

The extra buffer did not work properly for me anyway because I was continuously switching between 9-bit and 8-bit data.

Later I simplified the packet transfer a bit by always using 9-bit data, at the cost of 10% lower effective bandwidth.

I haven't paid much attention to newer variants of the USART after that code was fully debugged. It just keeps on working...

 

@ Brian:

For continous data streams you are correct, but lots of data tends to be short bursts.

If you are polling all tasks round robbin continuously then you must make sure everything is polled once within your time frame.

With interrupt controlled data transfers you can have a 98% CPU load for a single interrupt during a data burst, which will slow other (less real time sensitive) tasks down a bit temporarily, but that can be easily acceptable in a lot of applications.

 

But stuff like this all requires carefull planning, testing and debugging.

Most of the (hobby) projects I do have an average CPU load of ... probably in the single digit ppm

(Just to be extra safe I specified the crystal frequency to a mimimum of 3.6864MHz for the 115k2 baud network nodes and I know the cpu load is <10% during those bursts).

 

Nowaday's the trend is more in the direction of DMA and multiple level interrupt controllers, but I have not had an urgent need to use those.

Price difference between an <20MHz 8-bitter and a  > 100MHz 32-bit uC is also negligible for low quantity products and hardly worth the effort if it needs a few days extra of debugging work to get the timing just right for the 8-bitter.

 

I like the cooperative / round robin servicing mostly because it is a handy and fairly transparent framework to keep different tasks separate from each other.

I find it particularly sad that there are a tremendous amount of new programmers which start with a led and a blocking software loop to blink it in the "arduino" contraption environment.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

Paulvdh wrote:

I find it particularly sad that there are a tremendous amount of new programmers which start with a led and a blocking software loop to blink it in the "arduino" contraption environment.

 

And then find a website that tells them that what they need is an RTOS and so go and (try to) implement FreeRTOS on an 8-bit CPU.

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

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

Brian Fairchild wrote:
And then find a website that tells them that what they need is an RTOS and so go and (try to) implement FreeRTOS on an 8-bit CPU.
Adding insult to injury smiley With a few relatively small changes the "arduino" framework could have been a quite nice environment to get projects started fast. But I'm also an oldtimer and I can not seem to find main() in those arduino thingies and have no idea how to start with it.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

Brian Fairchild wrote:
And then find a website that tells them that what they need is an RTOS ...
or an active object framework.

Embedded Gurus – Experts on Embedded Software

State Space

Modern Embedded Programming: Beyond the RTOS

by Miro Samek

April 27, 2016

https://embeddedgurus.com/state-space/2016/04/beyond-the-rtos-a-better-way-to-design-real-time-embedded-software/

via

Barr Group

Firmware Update v18.02

by Michael Barr

2018-02-08

https://barrgroup.com/resources/firmware-update/v1802

(mid-page, search for RTOS)

 

(copyrighted)

 

Edit: (copyrighted)

 

"Dare to be naïve." - Buckminster Fuller

Last Edited: Tue. Mar 27, 2018 - 01:51 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

115k2 with an 1.842MHz crystal is 158 cpu cycles

You wrote 115k2 interrupts that is 16 clk pr ISR  

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

Brian Fairchild wrote:
... and so go and (try to) implement FreeRTOS on an 8-bit CPU.

http://start.atmel.com/#examples/rtos

...

ATmega4809 FreeRTOS Example

...

FreeRtos for the ATXMEGA128A1
https://www.avrfreaks.net/forum/freertos-atxmega128a1

https://github.com/yuriykulikov/FreeRTOS-on-XMEGA/wiki

 

"Dare to be naïve." - Buckminster Fuller

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

The article by do-while jones was quite compelling. Ganssle has a few comments.
http://www.ganssle.com/articles/acodeisr.htm

Like most here, its about the right technique and the right application thereof. There is no magic ‘one size fits all’ solution. Having said that, most of my commercial AVR apps were cooperative basic on the little scheduler i outlined in my tutorial ‘multi-tasking part 1’

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

Don't you need to use at least timer interrupts if you are doing anything that requires precise timing?

 

 

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

Not necessarily. You can poll the timer interrupt flags and not need interrupts.
The early PIC chips had no interrupts .

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

Kartman wrote:
Not necessarily. You can poll the timer interrupt flags and not need interrupts. The early PIC chips had no interrupts .

 

If you are polling the interrupt flags, doesn't that mean there are interrupts?  Or do you mean you use interrupts but not ISRs?

 

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

No interrupts, just poll on, for example, TOV0.

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

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

If you have a free running timer you can also just read the TCNT value and use that for calculating delays.

 

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

Paulvdh wrote:

If you have a free running timer you can also just read the TCNT value and use that for calculating delays.

 

Isn't that much less precise?  Since you can't guarantee that a wait cycle will always poll at the right moment?  Doesn't matter for most things, but if you are doing things like DDS or measuring frequencies, or anything else like that? 

 

At least that's what I got from my prior questions here and the Bruce Land videos.  I'm new at this.

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

jgalak wrote:
Isn't that much less precise?
Depends...

An interrupt may be delayed a couple of cycles by long instructions, or by another interrupt.

And then there is the overhead of saving state within an ISR before even getting to the actual code of the ISR.

Plus the state restoration at the end of the ISR.

 

Edit: added quote

David (aka frog_jr)

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

jgalak wrote:
Isn't that much less precise?
It depends a bit on what order of precision you need. you are not likely to get single cpu cycle accuracy this way, but you can get to withing 10 cpu cycles or so if your uC has nothing else to do but to poll that single register in a tight loop. Toying a bit with low level stuff like this can be fun if you're having a class to make a little contest who can reach the best accuracy.

Edit: Huh, I needed > 10 minutes to type those 3 sentences?

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Last Edited: Mon. Mar 26, 2018 - 09:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

If the processor doesn't have enough time to poll for I/O then it doesn't have enough time to handle the worst-case arrival rate of interrupts.

It's "correct" in the sense that an interrupts have higher overhead than polling.

It's "incorrect" because the worst-case arrival rate of interrupts is determined by external events, not by the speed of code, and "enough" is a "cliff" value - either you have enough cycles, or you don't.  There are some "edge" cases where PERHAPS implementing polled IO will save the few extra cycles you need between "working" and "not working", but "engineering wisdom" says that you should try to have your firmware operating far away from that edge...

 

(Another way of putting it: you choose whether "waste cycles" polling more often than you need to, or "waste cycles" in interrupt overhead.  The first can cause your "foreground processing" to not have enough cycles, even when events are "seldom"; the latter can lose events, when they occur too often.  In most cases, if you don't have those cycles to waste, you should find a faster processor or smarter peripherals.)

 

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

Paulvdh wrote:
"Co routines" might be applicable here. They have been a part of the "Boost" library collection for some time, and they seem to be in the process of becoming an oficial part of the next C++ standard.
co-routines are also in C11 and C99.

https://en.wikibooks.org/wiki/C_Programming/Coroutines

 

Edit: and

 

"Dare to be naïve." - Buckminster Fuller

Last Edited: Tue. Mar 27, 2018 - 03:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

From #1 

Each task is short and if needed is based around a state machine. So the total run time of all your tasks is less than the tick time.

If you write in C you have the problem that you don't know how long time each piece (state) of code take and often the compiler generate code that is fast, but worst case isn't.   

 

So this is great for ASM but be careful when using C.

  

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

jgalak wrote:

Paulvdh wrote:

If you have a free running timer you can also just read the TCNT value and use that for calculating delays.

Isn't that much less precise?  Since you can't guarantee that a wait cycle will always poll at the right moment?  Doesn't matter for most things, but if you are doing things like DDS or measuring frequencies, or anything else like that? 

 

As others have said, an interrupt will always allow the currently running instruction to finish before jumping to the ISR. So on an AVR that could be up to 5 cycles of delay before the ISR fires. And that delay will vary between 0 and those 5 cycles in a random fashion so you get jitter on your ISR timing. The ISR itself will have a  fixed overhead delay on entry to save registers etc and then it gets to your code. As long as the timing stuff is done at the top of the ISR, before any variable length code ('while' loops, 'if' tests etc) happens, then your critical bit will see a delay of between (fixed delay) and (fixed delay + 5 cycles).

 

Polling a bit in a status register is usually done with an SBIS (1-2 cycles) and an RJMP (2 cycles). Again, where the bit gets set is random so again you see a jitter on your timing but no fixed hidden delay.

 

In my test setup sitting next to me I can verify this...

 

    while (1) {
        while (!(TIFR0 & (1<<TOV0))) {
            ;
        }

        TIFR0 = 0x01;
        DEBUG = 1;
        //startup task
        task0();
        //LED tasks
        task1();
        task2();
        task3();
        task4();
        task5();
        task6();
        task7();
        task8();
        //switch tasks
        task9();
        task10();
        task11();
        task12();
        task13();
        task14();
        task15();
        task16();
        //USART tasks
        task17();
        task18();

        DEBUG = 0;
    }

 

DEBUG is a port pin that I can see on my scope and, with a 16MHz clock, I can see 250ns peak to peak of jitter (=4 clocks) which ties in with the SBIS and RJMP timings.

 

DEBUG also gives me a direct measure of how much processor time I am using. In the above code task0 runs once and starts all the other tasks one at a time, task1-task8 flash an LED with different on/off times, task9-task16 poll an IO pin connected to a switch, debounce it and individually turn task1-task8 off, task17 reads a serial port and task18 sends to a serial port.

 

Timer0 is free-running with a /8 prescaler so I get a (16MHz/8/256) 128us loop 'tick'. I can see from my DEBUG pin that I am using 38us +/-1us of my processor's time.

 

 

jgalak wrote:

At least that's what I got from my prior questions here and the Bruce Land videos.  I'm new at this.

 

Don't apologise. Questions like yours are the way to learn. Keep them coming.

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

Last Edited: Tue. Mar 27, 2018 - 07:06 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:

If you write in C you have the problem that you don't know how long time each piece (state) of code take and often the compiler generate code that is fast, but worst case isn't.   

 

So this is great for ASM but be careful when using C.

 

But cycle counting is so dull cheeky. Better to stick a scope on it and see (see above post).

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

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

Better to stick a scope on it and see.

And that is the safe way to get into problems in the long run. (you can't understand why it fails once every month)

when you do the lab tests it don't, but with real life signals (like #31 all keys run debounce sw , and at the same time all LED's change). Then perhaps it takes more than 128 us  ! (a bad example but it gives the idea).

 

 

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

I would not relish doing software serial, especially something like LANC, without interrupts. Lamp multiplexing also seems like a good use. No doubt both could be done by polling...

Having said that, most of the stuff I write does revolve around timer ticks, with the exception of an interrupt for the tick itself, and generally one for serial in.

I also make frequent use of the:

if((systemtime - lastthingtime) > someperiod)

   {

    lasthingtime += someperiod;

    dothing();

    }

 

idiom

 

 

 

Quebracho seems to be the hardest wood.

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

Sorry I can't resist ;)

 

Let's hope that systemtime never have a overrun !

 

I assume that you show a simplified version. 

 

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

Ok, you could have a problem if you don't poll for longer than systemtime max, but with a typical tick of 0.5 mS, and a 32 bit count, that's unlikely in most stuff I do.

The method works fine with simple overflow, though, as long as long as you don't use signed variables.

So no, I was not showing a simplified version.

 

Quebracho seems to be the hardest wood.

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

As I remember (C police please help), that is not guaranteed to work in C (it is in C++).  

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

Interesting.

It's always worked for me, in GCC and MS (although the MS is a long time aqo).

 

Quebracho seems to be the hardest wood.

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

sparrow2 wrote:

As I remember (C police please help), that is not guaranteed to work in C (it is in C++).  

All the research I've done suggests that you are wrong, and that unsigned integer addition and subtraction are performed as moulo operations. But I'm happy to see what the C Police have to say.

 

Quebracho seems to be the hardest wood.

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

sparrow2 wrote:

 I've done reliable 115k2 interrupts with a F_CPU of 1.842MHz

Perhaps in ASM but not in C!

 

C'mon, don't get me started yet again.  What magic AVR op code incantations do you have available in ASM when writing an ISR that are not available to the C compiler?

 

Yes, if your algorithm makes use of constructs foreign to C to great advantage, such as rotate, then there are situations.  But the mention is for handling of the USART peripheral, isn't it?

 

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

Even with CV I think it's pushing it with a 16 clk ISR , for UART read and update a ringbuffer!

 

Even in ASM the 2 clk saved by having code at ISR start, and place the buffer aligned will be at the limit (I think no time now to look at it)

 

But it seemed to be a typing error, it was 158 clk (don't know why not 160) between ISR

 

Add something like this ISR:

IN   R2,UDR
ST   X+,R2
CPSE XL,R3 ;buffer end+1
IRET
MOV  XL,R4  ;buffer start
IRET

Can read and put in the buffer, but "main" should not read the buffer at the same time! (or perhaps a read using Y+ if YL!=XL would work, but for sure it would easy overrun, but for a burst it's ok (I think for sure not tested))

Last Edited: Tue. Mar 27, 2018 - 02:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
Even with CV I think it's pushing it with a 16 clk ISR , for UART read and update a ringbuffer!

Well, I'd have to agree with that.  Even in your ASM solution if there is a 12-cycle overhead for each ISR...

 

However, I thought your claim had to do with a 115.2k UART bit rate.  That is abo0ut a character every 100us.  About 200 cycles.

 

=================

Indeed, if one really wanted to get the fastest ring buffer then CV's code generation model would not lend itself, as one cannot reserve an index register pair.  But I have done a pointer in "global register variable" pair, buffer on a power-of-2 boundary, and manipulate and mask just the low byte.  An extra MOVW, an extra mask step. 

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: 1

I usually don't use interrupts except for chip features like waking up from sleep.

 

If you have 2 kinds of tasks, a slow one that takes 10s of ms, and a fast one that has to be answered in us, then use an interrupt to deal with the fast task interrupting the slow.

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

However, I thought your claim had to do with a 115.2k UART bit rate.

then reread #8 #9 #13 and #17 then you know where the confusion came from.

But since this i about NOT using ISR I guess we should stop this dead end ;) 

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

Paulvdh wrote:
I've done reliable 115k2baud interrupts controlled uart comm's with a F_CPU of 1.842MHz.
I've already corrected this some time ago in post #8

Tnx to sparrow2 for pointing out the other typo. 1.8432MHz/(115k2*10bits) = a nice round 160 CPU cycles between interrupts. On most days my brain is a bit foggy.

160 CPU Cycles is plenty to do some work in an interrupt.

theusch wrote:
What magic AVR op code incantations do you have available in ASM when
GCC tends to push (and pop) more a lot of registers. This is the first time I thought a bit more about that. I think the reason is: The AVR architecture has quite a lot of registers (32) and GCC tries to use themefficiently. This is a good approach for "normal" programs, because the compiler has a lot less of data to re-load from RAM. But this is a bad approach for ISR's because every register used also has to be pushed and popped.

Brian Fairchild wrote:
But cycle counting is so dul
A technique for cycle counting is to put your uC to sleep just before an event happens and let the event take it out of sleep. Then compensate for a fixed wake-up delay and you can have single cycle accuracy. But stuff like this is usally better done in hardware or a CPU which is 10x faster (for almost the same price nowaday's).

 

sparrow2 wrote:
Let's hope that systemtime never have a overrun !
There are 2 different overflows here. One is the time in which the timer counts from 0 to Max_Count, and that is of course the maximum delay you can use without extending the timer with another var in an interrupt or otherwise.

The other is of course when you start timing if the timer is near Max_Count (assume up counting) and the timer overflows within the time period to measure.

You can compensate for this by using signed integers and a substraction and then use the difference in further calculations.

I gobbled up a little PC program to demonstrate this:

#include <stdio.h>
#include <inttypes.h>

int main( void) {
	int8_t start, delay, TCNT;
	int8_t bingo = 0;
	
	delay = 12;	// Initialisation of the timing delay.
	TCNT = 250;	// Simulation of "hardware" timer.
	start = TCNT;	// Simulation of start of our timing period.
	
	for ( ; TCNT != 10; ++TCNT) {
		printf( "\n %3d - %3d = %3d", TCNT, start, TCNT - start);
		if(( TCNT - start) == delay) {
			printf( " bingo");
		}
	}
}

This is the output of that program:

  -6 -  -6 =   0
  -5 -  -6 =   1
  -4 -  -6 =   2
  -3 -  -6 =   3
  -2 -  -6 =   4
  -1 -  -6 =   5
   0 -  -6 =   6
   1 -  -6 =   7
   2 -  -6 =   8
   3 -  -6 =   9
   4 -  -6 =  10
   5 -  -6 =  11
   6 -  -6 =  12 bingo
   7 -  -6 =  13
   8 -  -6 =  14
   9 -  -6 =  15

If you interpret the unsigned 250 hardware counter value as a signed integer, you get: -6

In the last column you can see that the difference just keeps on increasing even if the timer overflows to 0.

You might get into trouble here if your wanted delay is > half of the size of your hardware counter. I haven't looked into that.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

But if all values a unsigned and the compiler work as expected 2's complement (for sure c++ does and GCC does), then the overflow don't matter.

You will get the correct value. (C ignore the Carry)

 

Add:

70-50 =20

  4-240 =20

Last Edited: Tue. Mar 27, 2018 - 03:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This:

 

 

However, this text doesn't take into account explicitly specified handling of unsigned integers, as described later:

 

 

 

and:

 

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

gchapman wrote:
co-routines are also in C11 and C99. https://en.wikibooks.org/wiki/C_...
Tnx for the link. "Co routines" are for me in the part of "I'll have to look into that sometime" but I never really put the required effort into it. That wikibook uses "setjmp()" and "longjmp()", and I've always considered those to be in the "weird" part of C and assumed they were non-standard. after looking at the manpage of setjmp, a simple "man setjmp" in about any linux terminal window, it has a quite extensive explanation and also says:

CONFORMING TO
       setjmp(), longjmp(): POSIX.1-2001, POSIX.1-2008, C89, C99.

C89 is now 29 years old.

At this moment It is not clear to me yet how setjmp() and longjmp() fit into the "co routine" libary and if that is also as common & standarized as <stdlib.h>.

---------------

Frankly, I'm not even sure if it is worth putting any effort in. I have sort of standarized on putting state machines in a class and use a function pointer to switch between the states of that statemachine. In C++ the shared variables are set to private in the class. By declaring multiple instances of that class you can even easily run multiple state machines. setjmp() and longjmp() need a buffer for context switching, and that implies more overhead. The man page also has some warnings about this technique becoming a bit fuzzy and difficult to maintain if you're not very carefull.  A C++ class implementation is very clear and transparent and easy to maintain and I haven't seen any clear advantage for setjmp()/longjmp() yet.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Last Edited: Tue. Mar 27, 2018 - 04:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:

But if all values a unsigned and the compiler work as expected 2's complement (for sure c++ does and GCC does), then the overflow don't matter.

You will get the correct value. (C ignore the Carry)

 

Add:

70-50 =20

  4-240 =20

Thanks. That's exactly what I thought.

 

Quebracho seems to be the hardest wood.

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

Paulvdh wrote:
GCC tends to push (and pop) more a lot of registers. ... But this is a bad approach for ISR's because every register used also has to be pushed and popped.

Free Software Foundation (FSF)

GNU Project

GCC 8 Release Series — Changes, New Features, and Fixes

https://gcc.gnu.org/gcc-8/changes.html

...

The compiler now generates efficient interrupt service routine (ISR) prologues and epilogues. This is achieved by using the new AVR pseudo instruction__gcc_isr which is supported and resolved by the GNU assembler.

...

...

"Dare to be naïve." - Buckminster Fuller

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

http://start.atmel.com/#examples/rtos

...

ATmega4809 FreeRTOS Example

...

Process Scheduling on an 8-bit Microcontroller

(page 8)

4. Benefits of Using FreeRTOS™ on AVR® Microcontrollers

[mega4809 example in Atmel START (ASF4)]

[memory usage for a one task instance of above : 2400B flash, 51B RAM, heap (tasks' stacks are in the heap), 22B flash per task]

(last two paragraphs)

FreeRTOS offers debugging functions like stack painting and stack overflow hooks. These capabilities are easily enabled in the FreeRTOSConfig.h file by setting configCHECK_FOR_STACK_OVERFLOW to either "1", or "2". With a setting of "1" to enable the stack overflow hook, FreeRTOS will then call the function, void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ); defined by the user, if the task's stack pointer goes outside the allocated area. A setting of "2" will fill the stack with a known value at the start. These tools together with the extensive debugging features offered by the AVR microcontrollers make it easy to fine-tune the tasks' memory consumption, e.g., by reading out the content of SRAM during run-time debugging. That way, it is easy to pinpoint stack overflow issues in the application that would otherwise be difficult to detect and debug.

via AN2751 - Process Scheduling on an 8-bit Microcontroller

 

"Dare to be naïve." - Buckminster Fuller

Pages