[En-Nut-Discussion] Nut/OS GPIO API Initial Design and Current Status

Ulrich Prinz ulrich.prinz at googlemail.com
Fri Oct 12 22:20:04 CEST 2012

Hi Harald,

I am still very deep in other things, far away from Nut/OS but I asked 
myself the same questions two years or so ago when starting to adapt a 
software to the AT91SAM7X and then a year later when porting STM32Fx.

So here are my thoughts again:

1) There is no reason to number ports out. No one ever needs to have 
ports in a row following a numbered scheme. With pins it is useful, but 
I never saw a solution that required ports to exist in a certain follow up.

2) To assign numbers to things that obviously are not related to any 
mathematical predictable rule always causes software overhead in sort of 
tables, switch cases, if then else...

3) From the users point of view, Who (the hell) cares what 
NUT_GPIO_PORTA really means? Everyone would expect that if I place this 
'thing' somewhere it addresses exactly the port that the manufacturer 
named A *). As a Nut/OS user I simply expect that 
NutGpioPinGet(NUT_GPIO_PORTA, 5) would give me the state of the pin 4 on 
port A.

4) If you programmed on dozens of litte chips, you learn that every chip 
has its special way to support a kind of fast GPIO access. But every 
chips does it in a different way. So to port a system to this chip, you 
need to take your time and take your revolutions.
(When I startet with STM32 the GPIO was kind of some us per access, the 
final driver needed just a single instruction.)

So step back, and take a second to add these facts together...

What must always work is the combination of names and functions to keep 
Nut/OS API users happy.
Independent of any chip or CPU, NutGetPin/Port(NUT_GPIO_PORTx, bit) must 
give the value of the bit at that port.

It does not matter if NUT_GPIO_PORTA is a thing hidden in a #define, a 
hex value for a hard coded address, a pointer to a virtual register or a 
simple number. As long as the API user can use it to set a bit or a 
port, it is a number in AVR, a struct in STM32 an offset in ARM.

Now we have a second problem:
As you mentioned the extra features are a problem as different chips do 
provide different optional features. I already proposed there something
but only Thiago aver tried to follow that thing. The idea was to make 
some some commonly used options as a standard and to allow integrators 
to add more.

If a value is not supported by the chip, it could be rejected with an 
error code by the setup function, or it could just be ignored.
In any case it works with abstraction too. So again, for AVR the code 
could take a lot of switch case instructions to configure the 4 possible 
options if supports, while STM32 uses a hard to understand 4-liner to 
setup 10 different configurations with a help of crude #defines.

And now, last not least, we have the ever existing CPU / demoboard 
compatibility problem...

I think at the times we started porting of STM32 or a bit before we 
thought about a board specific startup file. Before I already had added 
some stupid things like push button handler or LED driver with some 
blinking options.
So if you like to write more or less abstract demo code, you should add 
board specific startup files and initialize buttons, eeproms, LEDs and 
other things there. The demo application just uses the registered 
devices. And in addition to the startup.c you need a board specific .h 
file where you can adapt some things.
And if a board really doesn't support any kind of LED you could assign 
the LED pins to some unused GPIOs, so it doesn't hurt the demo with lots 
of #ifdef...#endif.

Thats just my small collection of ideas.


*) Only small CPUs have their ports numbered with letters, all bigger 
ARMs use numbered values like GPIO1_4 as port 1, pin 4

Am 12.10.2012 13:01, schrieb Harald Kipp:
> Hi,
> 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:
> http://lists.egnite.de/pipermail/en-nut-discussion/2012-October/014048.html
> 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.
> http://lists.egnite.de/pipermail/en-nut-discussion/2012-October/014053.html
> 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
> hours."
> 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.
> 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.
> Regards,
> Harald
> _______________________________________________
> http://lists.egnite.de/mailman/listinfo/en-nut-discussion

More information about the En-Nut-Discussion mailing list