Picture of Arduino is Slow - and how to fix it!
Arduino is slow? What? This instructable will show just how slow a part of Arduino is, and how to fix it.
It’s true – more specifically, Arduino’s digitalWrite command takes a considerable amount of time. If you are just switching on a LED once or something, you won’t be able to notice it. However, I realized how slow it was while I was trying to use a TLC5947 PWM driver. That requires the microcontroller to shift in 288 bytes each time! Each byte required about 12 digitalWrites, for a total of 3456 digitalWrites each time I wanted to shift in new data to the TLC5947.
How long did that take? 30 seconds of just digitalWrite!
But there is a solution – using “true c” style commands, or what the AVR GCC (GNU C Compiler) uses. The brains behind Arduinos are ATMega168s or ATMega328s. The AVR community typically uses “true c” commands to program these chips, using AVR Studio 4. The advantage of using these “true c” commands is that it does exactly what you tell it to do.
But before we get in to these commands, we must get familiar with port and pin definitions in the next step!

(If you predict you will like this instructable, feel free to vote for the Arduino contest!)
Remove these adsRemove these ads by Signing Up

Step 1: The Truth about Pins

Picture of The Truth about Pins
Arduino users know that the pins are labeled as digital 0-13 and analog 0-5. The makers behind Arduino used this for simplicity. The actual ATMega chip’s pins are labeled differently, however. Each pin has an assigned letter and number. The numbers range from 0-7. For example, pins can be A0-A8, B0-B8, and so on. All of the AVR 8-bit pins are labeled this way.
To help you clarify which digital/analog pin corresponds to which AVR pin, see the chart below.
My Seeeduino has a LED built in on digial pin 13. So, looking at the chart, its real pin would be B5.
Next I will show you what the actual C command is.

"0b10000000 is an 8-bit binary number, you can convert it to hex for a cleaner look. Doing it manually is a pain"

That's not true, every four bits can be directly translated to hex: 1000 is binary for 8 and 0000 is a 0, so the output will be 0x80. 0b11110101: 1111 is decimal 15 and hexadecimal F, 0101 is binary for 5 (dec and hex). Putting these numbers together gives 0xF5. If you get the hang of it, it is ways faster than looking up every single number.

Thanks for sharing that !
If you count down rather than up in the loop it will be even faster! On my Arduino Mega changing the loop to count down for the True C commands reduced the time taken from 288microseconds to 192 microseconds, big difference if you need it to be as fast as possible!
rcarvalho43 years ago
It's better change just the part after the logic operator:

PORTB |= 0b00100000;
PORTB &= ~0b00000000;

If you don't use the logic operators, but just the equal "=" sign, all the pins are going to be set like the byte you sent, not just pin B5. In the case where you set B5 high, you would set all ohter B pins to low.
n8hfi4 years ago
Part of the reason this works is that the DigitalWrite() and DigitalRead() do some error checking before it sets the register bit--in particular, it checks and turns off PWM if it's enabled for that pin.  Directly accessing the bit yourself is fine, but you're also assuming responsibility for skipping the check.

This is pretty safe if your sketch is simple and doesn't do much multitasking or multiplexing of pins, but can get perilous if you've got a complex one.
vilts5 years ago
 I wonder why I get this error:

In function void loop():
Error: 'PB5' was not declared in this scope.

I use 0018 software.

I used form PORTB = B.....;

and it took 3808 vs 284 microseconds.

I'm trying to get TLC5945 communicating with arduino, no luck with digitalWrites, hopefully this version works.
eewithmac vilts5 years ago
 I have the same problem. The example code is obviously missing something. Where and how should PB5 be declared? 

Me, I'm trying to PWM on all the pins of an Arduino Duemilanove to dim some LEDs. 
RazorConcepts (author)  eewithmac5 years ago
 Hmm, compiles fine for me on 0015, it may be something with the new software.

If anyone has problems, replace
" PORTB |= _BV(PB5);
  PORTB &= ~_BV(PB5);"

"PORTB = 0b00100000;
PORTB = 0b00000000;"

Which should toggle PB5 like the original code. PB5 shouldnt have to be declared, as winAVR should recognize it as a pin, but arduino 0018 may be doing something funky with that.
Thanks this helped a lot becaus its the only way to programm the 16 extra pins on the Seeduino Mega
zuixro5 years ago
Thanks for this instructable. Not only does this save a lot of time, it also knocks the program size down quite a bit.  You can also you the registers DDRx an PINx (where x is the letter of the port) to change a port's input/output status, and to check what the input value is.
ghaon5 years ago
 So do you then use the AVR Studio 4? 
Or how do you program an Arduino in this way?
Can you use the Arudino's usb ftdi connection?

Please elaborate. Thanks!
RazorConcepts (author)  ghaon5 years ago
I use Arduino with the "true c" style commands.

You can use AVR Studio 4, however it is quite complicated and you cannot use the usb connection, you will need a ISP programmer to burn the programs.
funlw655 years ago
Maybe using this "Arduino" ?

wholder5 years ago
Actually, there is a very nice writeup on "Direct Port Manipulation" (which is the Arduino folk's name for what you are doing) on the Arduino site.  Here's the link:

The description at the top of the page says "Port registers allow for lower-level and faster manipulation of the i/o pins of the microcontroller on an Arduino board."

WyoJustin5 years ago
Nice tutorial.  You should enter it into the Arduino contest.
gmoon5 years ago
Some explanation of C "bitwise" operators might be helpful, too... And maybe break it down without using compound assignment ops first.

Here's my fav bitwise link: AVR bit tutorial

Other than that, it's a good 'ible...
RazorConcepts (author)  gmoon5 years ago
Thanks! That may be a bit complicated, but I added a final step with that link. 
RazorConcepts (author)  RazorConcepts5 years ago
horrible pun was unintended
Nerdz5 years ago
How come you didnt you Hardware SPI instead of Bit Banging the commands to the PWM chip? I checked briefly and it seems it use SPI commands. Looking at the Atmega168 Data sheet, It Does have support for hardware SPI, and if you used hardware, it would much Much faster than bit Banging SPI Commands since you can usually get up to 20Mhz Data Transfer depending on your internal clock (or external, but usually it requires you to go with the max clock rate for the chip) since it would Free up the CPU to do other things.
richms Nerdz5 years ago
SPI uses the USART doesnt it? And thats pre-wired on the arduino to support the bootloader
RazorConcepts (author)  richms5 years ago
True, actually there is a TLC5940 library for Arduino that uses SPI, however I wanted to see what was actually going on so I just did it the "manual" way by bit banging.

SPI doesn't use the USART, it uses SS/MOSI/MISO/SCK, the latter 3 are used for ISP programming also. 
Dantex5 years ago
does this work for arduino mega? (chip is atmega1280
RazorConcepts (author)  Dantex5 years ago
 Yes it does, however I cannot find a chart that shows which arduino pin goes to which pin... you may have to take a peek in to the schematic of the Arduino Mega.
mtbf05 years ago
reading and writing pwm pins will also be somewhat slower than writing non-pwm pins.

hmmm, i notice that there's no longer any range checking on pin numbers.  although this speeds things up i wonder what havoc a pinMode or digitalWrite to some out of range value might wreak.
flof5 years ago
Good to know that there is such an overhead!
How does the ShiftOut() command compare to this? Looking at the arduino reference it seems to be dedicated for this task (if the SPI pins are available..)
Would be nice if someone posts some numbers, since i do not own an arduino to test with..

westfw5 years ago
There's a pretty extensive discussion on pin-toggling on the arduino forums here: