[PATCH] Proposal for new GPIO API and example implementation for STM32F4 BSP

Duc Doan dtbpkmte at gmail.com
Mon Jun 27 10:39:14 UTC 2022


Hello Christian and Karel,

On Mon, 2022-06-27 at 08:29 +0200, Christian MAUDERER wrote:
> Please think about whether you want to start at 0 or at 1!
> 

I want it to start at 0.

> 
> Be really careful with that syntax. If you use increasing numbers
> like 
> you suggested, every controller would have to know it's own offest.
> On 
> the other hand, if there is a unique number to each pin, you don't 
> necessarily need the "ctrl" pointer at all. You can just use
> something like
> 
>     rtems_gpio_write(60, RTEMS_GPIO_PIN_SET);
> 
> That's an advantage from the usage perspective because you don't have
> to 
> fetch the GPIO controller and can work with a single pin number.
> 
> Disadvantage is a overhead in the API: You have to create some global
> table with GPIO controllers and their first and last pins. That table
> has to be searched for every operation. In the worst case it adds a
> loop 
> over the table with one comparison for each entry. Most systems
> should 
> only have a hand full of GPIO controller so it's not too much but
> it's a 
> lot more than just one pointer de-referencing. There could be methods
> to 
> optimize the search so the loop might isn't the best solution. But
> every 
> search is slower than if you already have the pointer.
> 
> It's more or less a trade-off. There are good arguments for both 
> directions. I think I have seen both directions implemented in
> different 
> situations.
> 

You are right; having both ctrl and virtual pin number is abundant.
Karel's email gave me some inspiration to (hopefully) improve this:

On Mon, 2022-06-27 at 10:25 +0200, Karel Gardas wrote:
> In fact I guess ctrl structure would contain some BSP specific data
> and 
> since bsp_gpio_get_ctrl is BSP specific function for f4 that means in
> ctrl you will save your GPIOx and pin number -- e.g. hardware
> specific 
> pin representation.
> 
> This means on write/read you do not need to map virtual pin ->
> physical 
> pin again, but in fact use physical pin from ctrl and be as fast as 
> possible.
> 

Actually as of now, my ctrl structure only has pointers to handlers and
GPIOx (F4 only). Pin number was not in there, but I am thinking
including pin number inside that structure might work. The changes I
want to make would be:

- ctrl structure will now include both pointers to handlers and BSP-
specific physical port/pin

struct rtems_gpio_ctrl {
    rtems_gpio_handlers_t *handlers;
}

struct stm32f4_gpio_ctrl {
    rtems_gpio_ctrl base;
    GPIO_TypeDef *GPIOx;
    uint32_t pin; //physical
}

- I will create 2 internal tables (based on Christian's suggestion) to
store the last pins of each controller and pointers to the get_ctrl()
functions. For example, if STM32 has 16 pins per port and 4 ports in
total, my table would be:

uint32_t table[MAX_CONTROLLERS] = {16, 32, 48, 64};

These numbers and the controller pointers are added to the tables by
calling a register() function for each controller. BSPs need to
implement an initialize() function. The prototype of initialize() is
provided by the GPIO API. initialize() needs to be called before any
GPIO operation in the application.

If GPIO expanders exist, their drivers need to have similar initialize
functions that call the register().

Currently I can only think of binary search as a faster method than a
for loop. rtems_gpio_get_ctrl(virtual_pin) will search for the correct
controller, call the BSP/driver-specific get_ctrl(), and return the
result.

- Because pin number is now in ctrl, functions only need to provide
pointer to ctrl object. 

Below is an example program with a fake STM32 with 4 GPIOs/16 pins
each. Two GPIO expanders (exA, exB) are used.

/********** API header *******/
void register(void (*get_ctrl) (uint32_t pin), uint32_t num_pins); //
shared, already implemented
void initialize(void); // to be implemented by BSP

/********** stm32f4 gpio.c **********/
void rtems_gpio_initialize(void) {
    register(stm32f4_get_ctrl, 16); // port A
    register(stm32f4_get_ctrl, 16); // port B
    register(stm32f4_get_ctrl, 16); // port C
    register(stm32f4_get_ctrl, 16); // port D 
}

/********* application blink.c *******/

// initialization
rtems_gpio_initialize();
exA_initialize(); // registers pins to the table
exB_initialize(); // registers pins to the table

uint32_t led_pin = 60; // just a random number
rtems_gpio_ctrl_t *led_ctrl = rtems_gpio_get_ctrl(60);

rtems_gpio_write(led_ctrl, RTEMS_GPIO_PIN_SET);

/********* END OF EXAMPLE **************/

The only place where the search must be done is now in get_ctrl(), so
read/write operations are still fast. One drawback of this approach
might be the support for pin groups, but currently I have quite little
clue about that.

What do you think about these changes?

Thank you,

Duc Doan

> > 
> > This, however, only returns integrated GPIO from a BSP. In order to
> > use
> > a GPIO expansion, a separate function must be used. Each GPIO
> > expander
> > driver will have its own get_ctrl function. For example, when using
> > 2
> > different expanders exA and exB:
> > 
> > rtems_gpio_ctrl_t *exA_ctrl = exA_get_ctrl(pin);
> > rtems_gpio_ctrl_t *exB_ctrl = exB_get_ctrl(pin);
> 
> What would be the pin numbers in that case? You mentioned that the 
> STM32F4 has 16 pins per GPIO. Let's assume it's 4 GPIOs so the last 
> internal GPIO would be 16*4-1=63. Would the first registered expander
> have pin numbers starting at 64 or again at 0?
> 
> I would strongly suggest to use the same method for internal and 
> external GPIOs. If you start with 0 for an expander, start with 0 for
> each internal GPIO controller too (in the STM example: You should
> have 4 
> internal pins with the number 0). If you plan to have an increasing 
> number, use that for the external GPIO controllers too.
> 
> Best regards
> 
> Christian
> 
> > 
> > I think this method will assure that it compiles and works on all
> > BSPs
> > but needs an additional function to get the controller of an
> > expander.
> > A drawback might be added computation because of translating
> > abstract
> > pin number to physical pin/port.
> > 
> > What do you think about this?
> > 
> > Best,
> > 
> > Duc Doan
> > 
> > On Sun, 2022-06-26 at 20:48 +0200, Karel Gardas wrote:
> > > On 6/26/22 10:49, Duc Doan wrote:
> > > > > > #define rtems_gpio_get_ctrl(_driver, _arg, _out) \
> > > > > >        _driver##_gpio_get_ctrl( _arg , _out )
> > > > > > 
> > > > > > In the application code:
> > > > > > 
> > > > > > rtems_gpio_get_ctrl(stm32f4, GPIOD, &led_ctrl);
> > > > > > rtems_gpio_get_ctrl(stm32f4, GPIOA, &button_ctrl);
> > > > > 
> > > > > It's only a different method of writing the same. It won't
> > > > > solve
> > > > > Karels
> > > > > problem because it still wouldn't compile on another BSP.
> > > > > 
> > > > 
> > > > Do you mean this application code should compile on other BSPs
> > > > without
> > > > changing the source?
> > > 
> > > Yes, that's exactly what portability means and that's exactly
> > > what is
> > > desired outcome of the API here -- if I'm not mistaken in your
> > > project
> > > outcome specification. :-)
> > > 
> > > I'm not expert here, so please bear with me, but as I see it, you
> > > will
> > > need to come with some abstraction for groups and pins and write
> > > API
> > > around it. Then in BSP you will perform/provide a mapping between
> > > your
> > > abstracted group/pins construct and between actual hardware. This
> > > way,
> > > if I take example from stm32f4 and try to compile on rpi4, it
> > > will
> > > compile well -- but it will not run well (probably!) of course.
> > > But
> > > API
> > > wise, it will compile. Now, to make it run, I'll need to connect
> > > LED
> > > example following BSP specific mapping and for that I need to
> > > consult
> > > BSP docs.
> > > 
> > > > I am a bit confused about that because I thought
> > > > at least we still need to specify the pin/port. And if we have
> > > > multiple
> > > > GPIO controllers, we still need to select one right?
> > > 
> > > Yes, and this needs to be done in abstract manner mapped down
> > > into
> > > actual BSP implementation code. Abstract mapping here ensure
> > > portability
> > > between BSPs/boards.
> > > 
> > > E.g. for stm32f4 you do not select GPIOA group and pin1, but you
> > > select
> > > group 0 and pin 1 and in f4 BSP this group 0 is mapped to GPIOA
> > > and
> > > pin
> > > 1 is mapped to its pin 1. -- something like that.
> > > 
> > > Karel
> > 
> > _______________________________________________
> > devel mailing list
> > devel at rtems.org
> > http://lists.rtems.org/mailman/listinfo/devel
> 



More information about the devel mailing list