To follow up on yesterday’s post about WiringPi and how it was used to display an image on a colour LCD screen attached to the RPi via its GPIO pins.
A quick calculation indicated that the WiringPi library is able to toggle the GPIO pins millions of times per second. Now this might not seem so special if you are used to an ATmega328 on a JeeNode or other Arduino-like board, but actually it is…
Keep in mind that the RPi code is running under Linux, a multi-tasking 32-bit operating system which not only does all sorts of things at (nearly) the same time, but which also has very solid memory and application protection mechanisms in place:
- each application runs in its own address space
- the time each application runs is controlled by Linux, not the app itself
- applications can crash, but they cannot cause the entire system to crash
- dozens, hundreds, even thousands of applications can run next to each other
- when memory is low, Linux can swap some parts to disk (not recommended with SD’s)
So while the LCD code works well, and much faster than one might expect for all the GPIO toggling it’s doing, this isn’t such a trivial outcome. The simplest way to deal with the RPi’s GPIO pins, is to manipulate the files and directories under the /sys/class/gpio/
“device tree”. This is incredibly useful because you can even manipulate it via plain shell script, using nothing but the “echo”, “cat”, and “ls” commands. Part of the convenience is the fact that these manipulations can take place entirely in ASCII, e.g. writing the string “1” or “0” to set/reset GPIO pins.
But the convenience of the /sys/class/gpio/
virtual file system access comes at a price: it’s not very fast. There is too much involved to deal with individual GPIO pins as files!
WiringPi uses a different approach, called “memory mapped files”.
Is it fast? You bet. I timed the processing time of this snippet of C code:
int i; for (i = 0; i < 100000000; ++i) { digitalWrite(1, 1); digitalWrite(1, 0); } return 0;
Here’s the result:
real 0m19.592s
user 0m19.490s
sys 0m0.030s
That’s over 5 million pulses (two toggles) per second.
This magic is possible because the I/O address space (which is normally completely inaccessible to user programs) has been mapped into a section of the user program address space. This means that there is no operating system call overhead involved in toggling I/O bits. The mapping is probably virtualised, i.e. the kernel will kick in on each access, but this is an interrupt straight into protected kernel code, so overhead is minimal.
How minimal? Well, it takes less than 90 ns per call to digitalWrite()
, so even when running at the RPi’s maximum 1 GHz rate, that’s less than 90 machine cycles.
Note how the RPi can almost toggle an I/O pin as fast as an ATmega running at 16 MHz. But the devil is in the details: “can” is the keyword here. Being a multi-tasking operating system, there is no guarantee whatsoever that the GPIO pin will always toggle at this rate. You may see occasional hick-ups in the order of milliseconds, in fact.
Is the RPi fast? Definitely! Is it guaranteed to be fast at all times? Nope!
Some more about fast i/o: Turning the Raspberry Pi Into an FM Transmitter
With a square wave? That’s going to chuck out some interference!
Fantastic project to get this working. I’m ordering one of these from iTead! :-)
Sprite_tm did a project where he connected a small TFT to a Raspberry Pi. He even wrote a framebuffer kernel driver. You can find the project here: http://spritesmods.com/?art=rpi_arcade
PS: now if you extend his code to use some kind of DMA magic to make it even faster, that would be great :)
Is it necessary to use simple bit-banging to drive a LCD? The R-Pi does have a SPI output, doesn’t it? Which is (at least in theory) capable of clock speeds quite a bit higher than 10 MHz ?
To answer my own question, the R-Pi SPI has been demonstrated to work at 32 MHz, and in theory it goes to 125 MHz. http://elinux.org/index.php?title=RPi_SPI RPi forum user ‘notro’ reports 65 fps on a 128×160 1.8-inch sainsmart18fb TFT LCD using the 32 MHz clock. http://www.raspberrypi.org/phpBB3/viewtopic.php?f=44&t=43442&p=347073 http://www.raspberrypi.org/phpBB3/viewtopic.php?t=43285&p=368177
Nice! The TFT LCD unit I used uses an 8-bit wide data path and is fast enough for my purposes, but good to know.
Lallafa also hooked up a cheap, small tft display to the raspberry pi, even adding touch-support (also using a framebuffer driver)…
I read somewhere that the Advanced Microcontroller Bus Architecture (AMBA Bus – see Wikipedia) on the Broadcom part is a bottleneck with these ARM IP-based SOC’s. DMA may help speed things up.