[En-Nut-Discussion] Nut/OS GPIO API Initial Design and Current Status
harald.kipp at egnite.de
Fri Oct 12 13:01:07 CEST 2012
Sorry, this will become a bit lengthy now. If, while reading this, you
get the impression that I'm insulting Nut/OS developers as a bunch of
ignorants and drunks, then keep in mind, that I'm including myself.
I'm intentionally putting this in a new thread, because many previous
discussions appeared under misleading topics. IMHO, it would be
confusing, to put this into the thread, that Ole recently opened:
Ole's topic is about how to simplify GPIO configuration, while this
thread is about the API implementation. We will later see, how fatally
both topic are related. As all this is not trivial, please do not mix
them up. I recommend, to discuss the API here and the configuration in
the thread that Ole initiated.
I know, I'm blamable for some confusion. Unfortunately, a statement I
made in another thread about GPIO banks and PIO IDs was wrong.
In fact, banks are not assigned to IDs, they are simply numbered
upwards. Sorry for the confusion. Hopefully I'll get it right now. :-)
GpioPinSetLow() and friends had been intentionally implemented as simple
functions, which can be easily ported to any platform. The initial idea
was: As soon as this is done for any new target, many (bit-banging and
polling) drivers should be available without any further effort.
This means: Implement a few simple functions and you'll get low level
I2C, SPI, MMC and higher level parts like file systems, calender chips,
serial flash and much more goodies for free. Motto: "Porting Nut/OS in 2
GpioPinSetLow() expects 2 arguments, a bank number (port identifier) and
the bit number. The bank number has been 1 for PORTA, 2 for PORTB and so
on for Atmel targets. As a special exception, early Atmel targets do
only provide as single PORT (without a letter), which has been assigned
to bank 0. Still simple, isn't it?
After the first version had been created, it looked as a good idea to
use this API in other places. For example, Ethernet drivers for memory
mapped Ethernet controller chips do not really depend on the target they
are running on, except a few GPIO functions, which are used to control
the controller's reset line or to monitor the controller's IRQ line. As
a portable interrupt API existed already, it was simple to replace any
outr() with GpioPinXXX() to get a portable driver. Other examples are
the MMA745X velocity driver or the VS10XX audio codec drivers.
So far so good. Are you still with me? :-)
Having an embedded system without samples that demonstrate GPIO is
somehow incomplete. Such samples existed, but were contaminated with
#ifdefs to handle all those different platforms. It looked attractive to
use the new GPIO API to create portable applications. And, in my view,
this is where all the trouble starts.
Suddenly users demand to implement all these special functions, that
their target chip provides. Enabling pull-ups or switching from
push/pull to open drain are the most harmless variants (which btw. would
have been required by an I2C driver anyway). Other, less harmless
extensions were debouncing, repeater mode and other exotic stuff.
Instead of one simple dev/gpio.h and its related implementations
arch/arm/at91_gpio.c, arch/avr/gpio.c etc. we now have several headers,
and a growing number of features. This makes it harder and harder for
newbies to implement GPIO on a new platform. The initial idea was spoiled.
What happed to the intended bit-banging GPIO API based drivers? Almost
nothing! Well, there is one SPI bus driver and a MultiMediaCard driver,
but no one is using it. They are quite bulky and slow, not very
attractive for board supporters. Just for the completeness: Recently a
1-wire bus driver had been added.
Scanning the current trunk shows, that GpioPinConfigSet() appears in 63
files! Who is using this then?
Now the fun really begins: The GPIO API is mainly used by target
specific drivers. Why, the hell, do target specific drivers prefer the
bulky and slow GPIO API, instead of using native port access? My
assumption is, that this was motivated by the configuration feature. The
Nut/OS Configurator offers to define banks in a user friendly way,
depending on the current target as PORTA on Atmel chips or PORT0 on NXP
chips. So using GpioPinXX() allows to (ab-)use this feature.
Of course, developer's recognized, that GpioPinXX() was bulky and slow.
To overcome this limitation, target specific inline versions had been
created to replace the original versions. IMHO, that's the final stab.
GPIO API, REST IN PIECE. ;-)
Now I'm really coming to an end. Just a few seconds...
A few target specific drivers use a different approach to avoid the GPIO
API and still provide configuration capabilities. They are based on
macros and include/cfg/arch/porttran.h. And they are very tricky to
implement, difficult to use and result in ugly, hard to follow code.
Beside that, they use PIO IDs, not port banks.
More information about the En-Nut-Discussion