How fast does an Arduino UNO execute a loop? - Page 1
Forum section: electronics / general

author: Lucky-Luka
author_rank: Unknown
timestamp: February 29, 2020, 10:24:23 am
content: Hi allI have tried to understand how fast my Arduino can execute a loop.In the attached pics the results and the code.I've tried two codes: one with a 10ms delay function and the other one without it.I have connected the scope probe to the Arduino pin 4.In the delayed loop I got 50Hz. I'm a beginner in this field so I thought it should have been something like 100Hz..In the loop without the delay I got 129kHz... Where does this value comes from? Isn't this value pretty far from 16MHz clock?It should be 16 millions instructions per second, so do I have to conclude that more than 100 instructions are executed under the hood with my simple loop?Cheers

author: greenpossum
author_rank: Frequent Contributor
timestamp: February 29, 2020, 10:35:57 am
content: Look, each 10 ms you flip the bit. So a half cycle is 10 ms, and a full cycle is 20 ms. What frequency is that? 50 Hz. Ta da!As for the no delay loop, you are writing in a high level language, C, where each statement could translate into several machine instructions. And also 16 MHz is the raw clock rate. You have to look to see how many clock cycles the µC takes to execute an instruction.

author: hans
author_rank: Super Contributor
timestamp: February 29, 2020, 10:44:09 am
content: With a pin toggle you always get half the loop frequency, as you need 2 level toggles per measured period.Yes, the 100 clock ticks or so sounds about right for a single pinWrite() on Arduino. Those functions are meant as a creature comfort, not high performance. If you need faster I/O access, you could lookup the individual I/O registers of the pins you want to address and manually program them.16MHz is the instruction clock of the chip.. But even a small while(1) { /* toggle pin */ } loop may output 6-8x lower frequency than the MCU clock.

author: Lucky-Luka
author_rank: Unknown
timestamp: February 29, 2020, 10:45:32 am
content: Thanks

author: T3sl4co1l
author_rank: Super Contributor
timestamp: February 29, 2020, 10:48:06 am
content: Delay per toggle, so the frequency is half, innit?That sounds about right for Arduino routines. Don't forget the function overhead for your loop() function itself, that can be a few dozen cycles. digitalWrite itself is decoding, at run time, the parameters, constructing the required bitmask to access the port, then finally writing to it. It's not a very optimal process, but it is extraordinarily flexible. This is a normal tradeoff, and part of the reason why high level languages are notorious for running very slowly (even when driven by an experienced programmer).Compare to the manual method,Code:while (1) {PORTD ^= 1 << PIN4;}or whichever exact pin it is you're toggling with digitalWrite, you'll find the above compiles to, probably about a dozen cycles, so should run a bit under 1MHz. Obviously, this is less flexible: if you change which pin you're twiddling, you need to change the PIN macro; and if it's on another port entirely, you have to change PORTD as well.You are welcome to inspect the output -- you can view the *.lss output file (if it's generated; check the project folder, and if not, there may be project settings or compiler flags you can enable it with) and see how many instructions the CPU is going through on those calls. You'll need at least a passing familiarity with the AVR instruction set, of course; this is cryptic at first, but you will get used to it in time, and it's well worth learning at some point.Tim

author: HwAoRrDk
author_rank: Super Contributor
timestamp: February 29, 2020, 10:59:50 am
content: Also, the Arduino framework adds quite a bit of overhead. The digitalWrite function is quite slow for toggling pins, and the loop function does other stuff behind the scenes in between invocations.You'll see faster with some 'raw' code like this:Code:void setup() {DDRD |= _BV(DDD4); // Make pin 4 output.while(1) {PIND |= _BV(PIND4); // Toggle pin 4.}}void loop() {// Nothing happens here.}Here we directly manipulate the port registers and use the trick of writing to the input register to toggle the pin state without needing to know what its current state is. Also we do the loop within setup() to avoid the overhead of loop().See this for more information:https://www.arduino.cc/en/Reference/PortManipulation(Edit: T3sl4co1l beats me to it writing a post saying much the same thing. It's hard writing code on a mobile keyboard.)

author: brucehoult
author_rank: Super Contributor
timestamp: February 29, 2020, 11:12:08 am
content: Quote from: greenpossum on February 29, 2020, 10:35:57 amNo, it's not that. The loop itself is very fast. The problem is digitalWrite() is very slow as it does things such as mapping the virtual Arduino pin number to a physical port and bit. It's probably at least 20 or 25 instructions vs just a single instruction doing "PIND = _BV(pinNum)" or doing bit set or bit clear to PORTD using variables or constants you set up once at the start of the program, rather than discovering every time through the loop.

author: brucehoult
author_rank: Super Contributor
timestamp: February 29, 2020, 11:22:21 am
content: Quote from: HwAoRrDk on February 29, 2020, 10:59:50 amThat should be PIND = _BV(PIND4), not |=. You use |= and &= with PORTD.Code:void setup() {DDRD |= _BV(DDD4); // Make pin 4 output.while(1) {PORTD |= _BV(PIND4); // Set pin 4. Should generate a single bitset instruction.PORTD &= ~_BV(PIND4); // Clear pin 4. Should generate a single bitclear instuction.}}That not going to generate a symmetical wave -- but it you just wanted a square pulse train with no modulation and no logic you'd use the PWM anyway :-)

author: HwAoRrDk
author_rank: Super Contributor
timestamp: February 29, 2020, 11:31:48 am
content: Quote from: brucehoult on February 29, 2020, 11:22:21 amNo, it shouldn't. What I wrote is fine.Section 13.2.2 of the ATmega328P datasheet:QuoteQuote from: brucehoult on February 29, 2020, 11:22:21 amIndeed, because there will be 2 cycles for the bit set, but 4 cycles for the clear and jump. But what I wrote should be pretty symmetric.

author: greenpossum
author_rank: Frequent Contributor
timestamp: February 29, 2020, 11:53:03 am
content: Quote from: brucehoult on February 29, 2020, 11:12:08 amYeah, yeah, i know all that. I was just simplifying the argument. In the best case, a C statement might correspond to one machine instruction. For example an invert can sometimes turn to just an XOR. But in other cases, the assembly generated can be complex, including subroutine calls which take up many more cycles.Also a C statement doesn't always correspond to a contiguous set of machine instructions. A C looping statement contains a body, so it actually generates code before and after the body.

author: cv007
author_rank: Frequent Contributor
timestamp: February 29, 2020, 01:13:27 pm
content: >That should be PIND = _BV(PIND4), not |=.But you (can) use |= in this case to generate the better version for this instruction set-https://godbolt.org/z/yM2fr7More than 1 bit and it becomes trouble, so its probably better wrapped up in a macro or something to prevent yourself from attempting to do more than one bit.

author: Lucky-Luka
author_rank: Unknown
timestamp: February 29, 2020, 01:24:08 pm
content: I have noticed something strange happening in my simple circuit: a pushbutton with a 10kohm pull down resistor that turns on an LED when it's pressed.The Arduino loop cycles at 100Hz (as we have previously said) .Yellow probe connected through the yellow cable to the 10 kOhm pulldown resistor.Green probe connected through the green cable to the dgt output pin 4. this output signal has a freq of 50Hz.10X probesTrigger on the yellow signal.The 50Hz yellow signal disappears when I disconnect the green probe or I cancel the last two instructions in the code loop.I can't understand what's going on.https://youtu.be/Adc8EPPdDCgCheers

author: brucehoult
author_rank: Super Contributor
timestamp: February 29, 2020, 09:30:49 pm
content: Quote from: cv007 on February 29, 2020, 01:13:27 pmOK, fair enough, but see my addition to your codehttps://godbolt.org/z/pviPUg

author: Fire Doger
author_rank: Regular Contributor
timestamp: February 29, 2020, 10:17:39 pm
content: Quote from: Lucky-Luka on February 29, 2020, 01:24:08 pmThere is nothing strange, it's just crosstalk, you pickup the noise generated during rise of the pwm signal.Rise time harmonics are a lot faster than 50hz and your cables becomes 2 nice transmit and receive antennas.Also keep in mind that your main loop won't always "run at 100hz" because anything done inside interrupts (like keeping track of time, serial comunications, etc...)will take cpu usage and your main will hang till other functionalities finish.

author: Mortymore
author_rank: Frequent Contributor
timestamp: February 29, 2020, 11:49:33 pm
content: It happens the same to me. As Fire Doger explained, don't worry.Both test probes connected. CH1 on pushButton, and CH2 on digitalPin 4Probe disconnected on CH2 for digitalPin 4. Hick-ups goes away on CH1PS: Yes I wrote Lucky-Luka code on IDE and tested it on Arduino. Just4fun

author: ozcar
author_rank: Frequent Contributor
timestamp: February 29, 2020, 11:50:53 pm
content: Quote from: brucehoult on February 29, 2020, 11:12:08 amYes, the normal digitalWrite() is notoriously slow, and that is partly because it allows the pin number to change at execution time. It also caters for the possibility that you might have previously set PWM going on the pin, so it does additional messing around to turn PWM off.

author: Lucky-Luka
author_rank: Unknown
timestamp: March 01, 2020, 05:49:10 pm
content: QuoteIs there a simple way to get rid of it?It's just a curiosity.Cheers

author: westfw
author_rank: Super Contributor
timestamp: March 02, 2020, 02:01:29 am
content: There is extensive discussion on the whole "maximum pin toggle speed" here:https://forum.arduino.cc/index.php?topic=4324.0(ok, it might be a little aged by now. In particular, it doesn't talk at all about the whole "we need to emulate the ATmega8 pull-up enable feature and any new chip that would normally do it differently!" (done on m8 by writing a 1 to the output register while the pin is in input mode. Almost always a separate configuration in newer chips (SAM, Even ATmega4809.) digitalWrite() keeps getting slower, cycles-wise.)