Author Topic: STM32 macro for port and pin read and write  (Read 5086 times)

0 Members and 1 Guest are viewing this topic.

Offline rvalente

  • Frequent Contributor
  • **
  • Posts: 520
  • Country: br
STM32 macro for port and pin read and write
« on: December 07, 2019, 03:26:01 pm »
Hello Mates,

i'm starting my first stm32 serious project, CUBE32MX IDE, CUBEmx libraries with hal and etc..

What I find very annoying is the ARM read and write ports, always need to declare GPIO port and PIN, very boring.

Do you guys use any macro to make this less painfull?

Code: [Select]
RA0 = 0; //Just like pics do
Code: [Select]
digitalWrite(13, 1); //Just like arduinos
BANG! DONE!

On STM32:

Code: [Select]
HAL_GPIO_WritePin(DO0_GPIO_Port, DO0_Pin, 1); //ohhh, c'mon, boring, makes passing pins to functions as arguments twice painful
 
The following users thanked this post: grantb5

Offline JohnnyBerg

  • Frequent Contributor
  • **
  • Posts: 474
  • Country: de
Re: STM32 macro for port and pin read and write
« Reply #1 on: December 07, 2019, 04:14:22 pm »
Not a macro, but a simple C++ solution:

Code: [Select]
class Pin
{
private:
    GPIO_TypeDef *port;
    uint16_t pin;

public:
    Pin::Pin(GPIO_TypeDef *_port, uint16_t _pin) : port(_port), pin(_pin){};
    void Pin::write(bool value) { value ? LL_GPIO_SetOutputPin(port, pin) : LL_GPIO_ResetOutputPin(port, pin); }
};

 

Offline rvalente

  • Frequent Contributor
  • **
  • Posts: 520
  • Country: br
Re: STM32 macro for port and pin read and write
« Reply #2 on: December 07, 2019, 04:17:08 pm »
Not a macro, but a simple C++ solution:

Code: [Select]
class Pin
{
private:
    GPIO_TypeDef *port;
    uint16_t pin;

public:
    Pin::Pin(GPIO_TypeDef *_port, uint16_t _pin) : port(_port), pin(_pin){};
    void Pin::write(bool value) { value ? LL_GPIO_SetOutputPin(port, pin) : LL_GPIO_ResetOutputPin(port, pin); }
};



Thats nice but, i'm using C not C++

This hole trouble is due to me converting the sevseg library from arduino to stm32
 

Offline emece67

  • Frequent Contributor
  • **
  • Posts: 346
  • Country: es
Re: STM32 macro for port and pin read and write
« Reply #3 on: December 07, 2019, 04:42:21 pm »
First:
Code: [Select]
#ifndef PINOUT_BLUE_PILL_H
#define PINOUT_BLUE_PILL_H

#include "GPIO_STM32f10x.h"

typedef struct {
  GPIO_TypeDef * const  port;
  uint8_t               pos;
} pin_t;

#define SIZE_OF_PIN                 (sizeof(pin_t))
                                   
#define PIN_BLUE_PILL_PA0           ((pin_t const){GPIOA, 0U})
#define PIN_BLUE_PILL_PA1           ((pin_t const){GPIOA, 1U})
                                   
...                                 

#define PIN_BLUE_PILL_WKUP                (PIN_BLUE_PILL_PA0)
#define PIN_BLUE_PILL_USART2_CTS          (PIN_BLUE_PILL_PA0)
#define PIN_BLUE_PILL_ADC12_IN0           (PIN_BLUE_PILL_PA0)

#define PIN_BLUE_PILL_TIM2_CH1_ETR        (PIN_BLUE_PILL_PA1)
#define PIN_BLUE_PILL_USART2_RTS          (PIN_BLUE_PILL_PA1)
#define PIN_BLUE_PILL_TIM2_CH2            (PIN_BLUE_PILL_PA1)

...

#endif  // PINOUT_BLUE_PILL_H

Then:
Code: [Select]
#define PIN_WRITE(pin, lvl)         (GPIO_PinWrite((pin).port, (pin).pos, (lvl)))
#define PIN_READ(pin)               (GPIO_PinRead((pin).port, (pin).pos))

Then, if you like:
Code: [Select]
#define LED                         (PIN_BLUE_PILL_PA0)
#define SWITCH                      (PIN_BLUE_PILL_PA1)

And finally:
Code: [Select]
PIN_WRITE(LED, 1);
a = PIN_READ(SWITCH);

Note that I'm not using the STM HAL, but cmsis, but that's the idea.

Hope this helps, regards.
« Last Edit: December 07, 2019, 04:45:26 pm by emece67 »
Information must flow.
 
The following users thanked this post: thm_w

Offline rvalente

  • Frequent Contributor
  • **
  • Posts: 520
  • Country: br
Re: STM32 macro for port and pin read and write
« Reply #4 on: December 07, 2019, 04:46:40 pm »
First:
Code: [Select]
#ifndef PINOUT_BLUE_PILL_H
#define PINOUT_BLUE_PILL_H

#include "GPIO_STM32f10x.h"

typedef struct {
  GPIO_TypeDef * const  port;
  uint8_t               pos;
} pin_t;

#define SIZE_OF_PIN                 (sizeof(pin_t))
                                   
#define PIN_BLUE_PILL_PA0           ((pin_t const){GPIOA, 0U})
#define PIN_BLUE_PILL_PA1           ((pin_t const){GPIOA, 1U})
                                   
...                                 

#define PIN_BLUE_PILL_WKUP                (PIN_BLUE_PILL_PA0)
#define PIN_BLUE_PILL_USART2_CTS          (PIN_BLUE_PILL_PA0)
#define PIN_BLUE_PILL_ADC12_IN0           (PIN_BLUE_PILL_PA0)

#define PIN_BLUE_PILL_TIM2_CH1_ETR        (PIN_BLUE_PILL_PA1)
#define PIN_BLUE_PILL_USART2_RTS          (PIN_BLUE_PILL_PA1)
#define PIN_BLUE_PILL_TIM2_CH2            (PIN_BLUE_PILL_PA1)

...

#endif  // PINOUT_BLUE_PILL_H

Then:
Code: [Select]
#define PIN_WRITE(pin, lvl)         (GPIO_PinWrite((pin).port, (pin).pos, (lvl)))
#define PIN_READ(pin)               (GPIO_PinRead((pin).port, (pin).pos))

Then, if you like:
Code: [Select]
#define LED                         (PIN_BLUE_PILL_PA0)
#define SWITCH                      (PIN_BLUE_PILL_PA1)

And finally:
Code: [Select]
PIN_WRITE(LED, 1);
a = PIN_READ(SWITCH);

Note that I'm not using the STM HAL, but cmsis, but that's the idea.

Hope this helps, regards.


Will try it, tks!
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 3737
  • Country: fi
Re: STM32 macro for port and pin read and write
« Reply #5 on: December 07, 2019, 04:57:30 pm »
This is what I use:

Code: [Select]

// Outputs:
#define HI(port, idx) do{(port)->BSRR = 1UL<<(idx);}while(0)
#define LO(port, idx) do{(port)->BSRR = 1UL<<(16+idx);}while(0)

// Inputs:
#define IN(port, idx) ((port)->IDR & (1UL<<(idx)))
// Always 0 or 1:
#define IN_LOGICAL(port, idx) (!!((port)->IDR & (1UL<<(idx))))


// Configuration:

#define IO_TO_GPI(port, idx) do{ uint32_t _tmp_ = (port)->MODER; _tmp_ &= ~(0b11UL<<((idx)*2)); (port)->MODER = _tmp_; }while(0)
#define IO_TO_GPO(port, idx) do{ uint32_t _tmp_ = (port)->MODER; _tmp_ &= ~(1UL<<((idx)*2+1)); _tmp_ |= 1UL<<((idx)*2); (port)->MODER = _tmp_; }while(0)
#define IO_TO_ALTFUNC(port, idx) do{ uint32_t _tmp_ = (port)->MODER; _tmp_ &= ~(1UL<<((idx)*2)); _tmp_ |= 1UL<<((idx)*2+1); (port)->MODER = _tmp_; }while(0)
#define IO_TO_ANALOG(port, idx) do{ uint32_t _tmp_ = (port)->MODER; _tmp_ |= 0b11<<((idx)*2); (port)->MODER = _tmp_; }while(0)
#define IO_PULLUP_ON(port, idx) do{ uint32_t _tmp_ = (port)->PUPDR; _tmp_ &= ~(1UL<<((idx)*2+1)); _tmp_ |= 1UL<<((idx)*2); (port)->PUPDR = _tmp_; }while(0)
#define IO_PULLDOWN_ON(port, idx) do{ uint32_t _tmp_ = (port)->PUPDR; _tmp_ &= ~(1UL<<((idx)*2)); _tmp_ |= 1UL<<((idx)*2+1); (port)->PUPDR = _tmp_; }while(0)
#define IO_PULLUPDOWN_OFF(port, idx) do{ uint32_t _tmp_ = (port)->PUPDR; _tmp_ &= ~(0b11UL<<((idx)*2)); (port)->PUPDR = _tmp_; }while(0)

#define IO_PUSHPULL(port, idx)  do{(port)->OTYPER &= ~(1UL<<(idx));}while(0)
#define IO_OPENDRAIN(port, idx) do{(port)->OTYPER |=  (1UL<<(idx));}while(0)

#define IO_SPEED(port, idx, speed) do{uint32_t _tmp_ = (port)->OSPEEDR; _tmp_ &= ~(0b11UL<<((idx)*2)); _tmp_ |= (speed)<<((idx)*2); (port)->OSPEEDR = _tmp_;  }while(0)

#define IO_SET_ALTFUNC(port, pin, af) do{ _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wshift-count-negative\"")  _Pragma("GCC diagnostic ignored \"-Wshift-count-overflow\"") \
if((pin)<8) {uint32_t _tmp_ = (port)->AFR[0]; _tmp_ &= ~(0b1111UL<<((pin)*4));     _tmp_ |= af<<((pin)*4);     (port)->AFR[0] = _tmp_;} \
        else {uint32_t _tmp_ = (port)->AFR[1]; _tmp_ &= ~(0b1111UL<<(((pin)-8)*4)); _tmp_ |= af<<(((pin)-8)*4); (port)->AFR[1] = _tmp_;} _Pragma("GCC diagnostic pop") }while(0)


#define IO_ALTFUNC(port, pin, af) do{ IO_TO_ALTFUNC((port),(pin)); IO_SET_ALTFUNC((port),(pin),(af));}while(0)



Usage:
Code: [Select]
IO_TO_GPI(PORTA, 5);
IO_TO_GPO(PORTB, 6);

if(IN(PORTA,5))
{
    HI(PORTB, 6);
}


// Full uart configuration example for STM32H743:

RCC->APB2ENR |= 1UL<<4;
IO_ALTFUNC(GPIOB, 14, 4);
IO_ALTFUNC(GPIOB, 15, 4);
USART1->BRR = 100000000/115200;
USART1->CR1 = 0UL<<5 /*RX interrupt*/ | 1UL<<3 /*TX ena*/ | 1UL<<2 /*RX ena*/ | 1UL /*USART ENA*/;

« Last Edit: December 07, 2019, 05:00:36 pm by Siwastaja »
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 7583
  • Country: us
    • Personal site
Re: STM32 macro for port and pin read and write
« Reply #6 on: December 07, 2019, 05:23:51 pm »
https://github.com/ataradov/mcu-starter-projects/blob/master/stm32g0/hal_gpio.h Use:
Code: [Select]
HAL_GPIO_PIN(LED,      D, 0)
HAL_GPIO_PIN(BUTTON,   B, 0)

int main(void)
{
  HAL_GPIO_LED_out();
  HAL_GPIO_LED_set();

  HAL_GPIO_BUTTON_in();
  HAL_GPIO_BUTTON_pullup();

  if (HAL_GPIO_BUTTON_read())
  {
    // ...
  }
}
Alex
 

Offline jhpadjustable

  • Frequent Contributor
  • **
  • Posts: 295
  • Country: us
  • Salt 'n' pepper beard
Re: STM32 macro for port and pin read and write
« Reply #7 on: December 07, 2019, 07:29:34 pm »
STM32 libraries are well-known for bloat and verbosity. One of Cube's main goals is to make product design more efficient, by making the software and hardware more independent, so that software can more easily adapt to changes in a larger system of hardware. For example, if a signal moves to another pin or a different connector, just relabel in Cube and recompile, without needing to edit files by hand. Or, you can build a .c file for setting up some circuit block which you use a lot, and copy that file and the circuit into a new design, setting pin labels to suit your new configuration. Or, you can easily move your code from a development board to a right-sized device for production just by changing the device/package and relabeling pins to match.

If your design will only live on a dev board forever, you won't see those gains and don't need to do things the HAL way. You could just as well:
Code: [Select]
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_13, 1);
or use the C preprocessor to compact two parameters into one for less typing:
Code: [Select]
#define D13_pin GPIOA, GPIO_PIN_13
...
HAL_GPIO_WritePin(D13_pin, 1);
HAL_GPIO_TogglePin(D13_pin);

If I need to pass around one and only one parameter, such as when dynamically configuring ports at run-time, I do something like this:
Code: [Select]
/* ... in the header ... */
static const GPIO_TypeDef * const gpioports[] = { GPIOA, GPIOB, GPIOC, GPIOD };
#define BLUEPILL_PIN_4 ((1 << 4) + 15)  /* B15 = GPIOB (1) << 4, + bit 15 */
#define BLUEPILL_PIN_5 ((0 << 4) + 8)  /* A8 = GPIOA (0) << 4, + bit 8 */
/* ... */
static inline void dwrite(int pin, bool val)
{
  assert(pin < (16 * sizeof(gpioports)/sizeof(GPIO_TypeDef *)));
  HAL_GPIO_WritePin(gpioports[pin >> 4], 1 << (pin & 0xf), val);
}
/* ... in the program ... */
dwrite(BLUEPILL_PIN_5, 1);

There are many ways to sugar-coat the reality of a sophisticated MCU, according to your own taste and habits. But you must be willing to confront C and its preprocessor and the hardware, on their own terms, which aren't necessarily beautiful or fun. If you need entertainment and beauty and fun, why not stick with a HAL that promises those qualities.
"There are more things in heaven and earth, Arduino, than are dreamt of in your philosophy."
 

Offline mark03

  • Frequent Contributor
  • **
  • Posts: 688
  • Country: us
Re: STM32 macro for port and pin read and write
« Reply #8 on: December 08, 2019, 12:25:00 am »
This is one (of many) examples where the simplicity of direct register access really shines.

GPIOB->BSRR = 1 << 4;            // set PB4 high
GPIOB->BSRR = 1 << (4+16);  // set PB4 low

Hard to see how it could get any simpler.  Although @Siwastaja's shorthand is kind of nice too.

 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 7583
  • Country: us
    • Personal site
Re: STM32 macro for port and pin read and write
« Reply #9 on: December 08, 2019, 12:28:53 am »
Well, my macros translate to a similar minimal code, except that you operate with actual application specific names. This makes is very easy to read the code and change the pin assignments.
Alex
 

Offline emece67

  • Frequent Contributor
  • **
  • Posts: 346
  • Country: es
Re: STM32 macro for port and pin read and write
« Reply #10 on: December 08, 2019, 01:36:52 pm »
Then:
Code: [Select]
#define PIN_WRITE(pin, lvl)         (GPIO_PinWrite((pin).port, (pin).pos, (lvl)))
#define PIN_READ(pin)               (GPIO_PinRead((pin).port, (pin).pos))

In case you are worried about the possibly ill side effects of these macros evaluating their first argument twice, you can use instead:
Code: [Select]
static inline void pin_write(pin_t pin, uint32_t lvl) {
  GPIO_PinWrite(pin.port, pin.pos, lvl);
}

static inline uint32_t pin_read(pin_t pin) {
  return GPIO_PinRead(pin.port, pin.pos);
}

After the compiler optimization there's no overhead at all.

This is one (of many) examples where the simplicity of direct register access really shines.

GPIOB->BSRR = 1 << 4;            // set PB4 high
GPIOB->BSRR = 1 << (4+16);  // set PB4 low

Hard to see how it could get any simpler.  Although @Siwastaja's shorthand is kind of nice too.

I think that all code posted here up to now, after compiler optimization, gives more or less the same. In fact, in the code I extracted it from, the pin_write() & pin_read() inline functions where called 3 times inside a thread. Each function "call" gave exactly one single STR/LDR assembly instruction directly accessing the port, plus other 3 assembly instructions at thread entry to preload some registers, that's all.
« Last Edit: December 08, 2019, 01:55:54 pm by emece67 »
Information must flow.
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 6353
  • Country: fr
Re: STM32 macro for port and pin read and write
« Reply #11 on: December 08, 2019, 05:36:18 pm »
https://github.com/ataradov/mcu-starter-projects/blob/master/stm32g0/hal_gpio.h

Not bad. That covers a lot of use cases and is simple enough.

I think there are still cases when using the port (pointer) and pin number is useful, and when I use it: for instance writing "drivers" for some external peripherals - I usually prefer passing the required pins as parameters, so that the same functions can be reused with several instances, rather than defining them as macros in the driver code itself. When the driver is a bit heavy, including it as all inline functions would be too heavy. But for simpler stuff, it certainly works.
 

Online lucazader

  • Regular Contributor
  • *
  • Posts: 176
  • Country: nz
Re: STM32 macro for port and pin read and write
« Reply #12 on: December 08, 2019, 10:37:27 pm »
I would tend to avoid register access macros like this, and then use an inline function as emece67 suggests.
If you dont want this to then call a HAL function for speed reasons, then you could place some of the suggested direct register access expression in the inlined functions.

This way you get all the benefits of the c type safety system with optimized code.
Inline functions are usually a bit easier to debug than macros, as well as they cant affect the control flow of software accidentally like some macros can (if you dont use the do-while(0) trick with them).
And with a decent optimizing compiler with the optimization level set to anything but off, these inline function should be optimized to the direct register read and writes anyway!

Personally I will only ever use a macro in C to define the size of an array.
However this is a matter of choice for me, as I have been bitten by liberal use of macros in the past, and now avoid them like the plague.
 

Offline emece67

  • Frequent Contributor
  • **
  • Posts: 346
  • Country: es
Re: STM32 macro for port and pin read and write
« Reply #13 on: December 08, 2019, 11:51:25 pm »
This is what I use:
Code: [Select]
// Outputs:
#define HI(port, idx) do{(port)->BSRR = 1UL<<(idx);}while(0)
#define LO(port, idx) do{(port)->BSRR = 1UL<<(16+idx);}while(0)

What's the utility of the do-while(0)? I understand its use in macros containing various statements, but here?
Information must flow.
 

Offline donotdespisethesnake

  • Super Contributor
  • ***
  • Posts: 1091
  • Country: gb
  • Embedded stuff
Re: STM32 macro for port and pin read and write
« Reply #14 on: December 09, 2019, 09:53:55 pm »
This is one (of many) examples where the simplicity of direct register access really shines.

GPIOB->BSRR = 1 << 4;            // set PB4 high
GPIOB->BSRR = 1 << (4+16);  // set PB4 low

Hard to see how it could get any simpler.  Although @Siwastaja's shorthand is kind of nice too.

That's terrible code though, a nightmare to debug and maintain. In general, if you need a comment to explain the code like that, the code is inadequate. What should you aim to be writing is lucid code like:
Code: [Select]
  set_high (PB4);
  set_low (PB4);
which can be easily and efficiently achieved using macros or inline functions (preferred).
Comments not required.
Bob
"All you said is just a bunch of opinions."
 
The following users thanked this post: thm_w, rvalente, lucazader

Offline pigrew

  • Frequent Contributor
  • **
  • Posts: 615
  • Country: us
Re: STM32 macro for port and pin read and write
« Reply #15 on: December 09, 2019, 11:07:50 pm »
I've never found a perfect GPIO interface. Some will let you set multiple bits at once, others don't. Sometimes you want to read the ODR, other times you want to read the IDR.

One way in C99 to achieve close to the PIC syntax is to use bit slices, something like:


struct bitAccessStruct {
    unsigned int p0 : 1;
    unsigned int p1 : 1;
    unsigned int p2 : 1;
    unsigned int p3 : 1;
    unsigned int p4 : 1;
    unsigned int p5 : 1;
/* more bits here */
} __attribute__((packed));

#define PA0_OUT ((struct bitAccessStruct*)(&(GPIOA->ODR)))->p0

#define ODR(port,pin) (((struct bitAccessStruct*)(&((GPIO##port)->ODR)))->p##pin)
#define IDR(port,pin) (((struct bitAccessStruct*)(&((GPIO##port)->IDR)))->p##pin)

void f() {
     ODR(A,3) = IDR(A,2);
}


The advantage of this syntax is that it is fast (everything is inlined), and can be used as both a rvalue and a lvalue.

I don't think it's possible in C for it to automatically select the ODR when used as a lvalue and the IDR as a rvalue.... so probably READ and WRITE macros are the best solution, as has been already mentioned.

PS: Personally, I just use the HAL functions....
 

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1199
  • Country: fi
Re: STM32 macro for port and pin read and write
« Reply #16 on: December 09, 2019, 11:44:53 pm »
If you want to set a pin to a non-constant value, you can do
Code: [Select]
GPIOn->BSRR = (1 << 16+pin) | (val << pin);wrapped in your macro/function/idiom of choice. Ie. if you set and clear a pin at the same time, the set operation takes precedence.

Offline westfw

  • Super Contributor
  • ***
  • Posts: 3419
  • Country: us
Re: STM32 macro for port and pin read and write
« Reply #17 on: December 10, 2019, 03:32:34 am »
Quote
One way in C99 to achieve close to the PIC syntax is to use bit slices, something like:


struct bitAccessStruct {
    unsigned int p0 : 1;
    unsigned int p1 : 1;
    unsigned int p2 : 1;
    unsigned int p3 : 1;
    unsigned int p4 : 1;
    unsigned int p5 : 1;
/* more bits here */
} __attribute__((packed));
Don't do this on SET or CLEAR registers, though.  Behind the scenes, a line like:
Code: [Select]
   bitclears.p5 = 1;Will result in a read/modify/write of the whole register.  If it reads "the current bits" of the port, the "write" part will clear ALL the bits that were set.   I've seen this be responsible for "weird" operation of ISRs where interrupt causes were reset by writing ones...
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 3737
  • Country: fi
Re: STM32 macro for port and pin read and write
« Reply #18 on: December 10, 2019, 08:40:02 am »
This is what I use:
Code: [Select]
// Outputs:
#define HI(port, idx) do{(port)->BSRR = 1UL<<(idx);}while(0)
#define LO(port, idx) do{(port)->BSRR = 1UL<<(16+idx);}while(0)

What's the utility of the do-while(0)? I understand its use in macros containing various statements, but here?

It's a stupid C quirk required in macros so that they can be used function-like, including cases like

if(something)
    function_like_macro();

Not required in single statements if you ditch {} as well, but I wanted to write all the macros similarly in that file.

static inline functions tend to work mostly properly now, but there is a reason I didn't use them, and still don't fully trust them. At the very least, use -Winline with gcc so it reports back when it fails to do the trivial inlining. Attribute always_inline also suggested.
« Last Edit: December 10, 2019, 08:47:31 am by Siwastaja »
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 6353
  • Country: fr
Re: STM32 macro for port and pin read and write
« Reply #19 on: December 10, 2019, 03:38:07 pm »
static inline functions tend to work mostly properly now, but there is a reason I didn't use them, and still don't fully trust them. At the very least, use -Winline with gcc so it reports back when it fails to do the trivial inlining. Attribute always_inline also suggested.

True. And it's also a pretty clear statement from the C standard: the 'inline' keyword is merely an hint to the compiler, which can do whatever it wants with it.
GCC will often inline static functions automatically (even without the 'inline' qualifier) at the higher opt. levels (don't remember which, but it sure does at -O3), but there's no guarantee about it (it depends on a lot of factors including the resulting code size), and the "inline" qualifier is often nicely ignored. The '-Winline' parameter to get warnings about it is cool (purely GCC though?), but if GCC ever emits warnings about some of your inline-qualified functions, you'll be glad to know. But basically stuck, or finding yourself trying to tweak stuff until GCC gets to inline all of them. Yuck.

Macros are fine when you know what you're doing. Just avoid calling them with parameters that have side-effects (or make sure you only use all macro parameters ONCE in the macro expansion, which is not always possible).

 
The following users thanked this post: Siwastaja

Offline JohnnyBerg

  • Frequent Contributor
  • **
  • Posts: 474
  • Country: de
Re: STM32 macro for port and pin read and write
« Reply #20 on: December 17, 2019, 07:51:04 am »
Not a macro but the power of C++, giving abstraction, maintainability, readability, namespaces, strong typing, domain driven design and unit testing. Also very easy to port to another board.
(example based on cmsis headers from ST, board is a STM32F103 blue pill)


Code: [Select]
int main()
{
    GPIO::Pin<GPIOC_BASE, 13> led;
    while(1)
    {
        led.set();
        led.clear();
    }
}

No explanation needed whats going on :)

The class:

Code: [Select]
namespace GPIO
{
template<uint32_t P, uint8_t p>
class Pin
{
    public:
    void set()
    {
        memory<uint32_t>(P + offsetof(GPIO_TypeDef, BSRR)) = BIT(p);
    }
    void clear()
    {
        memory<uint32_t>(P + offsetof(GPIO_TypeDef, BSRR)) = BIT(p) << 16;
    }
};
}

Two helper functions:

Code: [Select]
template<typename T>
volatile T &memory(const size_t loc)noexcept{
    return *reinterpret_cast<T*>(loc);
}

constexpr auto BIT(const unsigned long i)noexcept{
    return 1 << i;
}

When compiled (-Os) gives the following assembler:
Code: [Select]
main:
        mov     r1, #8192
        mov     r2, #536870912
        ldr     r3, .L3
.L2:
        str     r1, [r3, #16]
        str     r2, [r3, #16]
        b       .L2
.L3:
        .word   1073811456

AFAIK is this the most optimal result, that cannot be beaten by any other code.. or?
« Last Edit: December 17, 2019, 08:27:06 am by JohnnyBerg »
 
The following users thanked this post: thm_w, mark03

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 579
Re: STM32 macro for port and pin read and write
« Reply #21 on: December 17, 2019, 01:38:13 pm »
Most optimal if you don't want the led to blink. Maybe your model is different, but the one I have needs the mode set and the port clock enabled.

I received an stm32g031k8 nucleo32 and had a compiler explorer page I was playing around with while deciding whether I wanted to install another ide-
https://godbolt.org/z/g62eza
fired up stm32 whatever ide (eclipse variation) on a spare pc, created a few files from the above, and had a blinky light on the first try. I'm not fond of eclipse for some reason, and its 4 pixel wide scroll bars are not helping, but getting a blinky going with just the datasheet and some c++ is a good thing. Maybe it goes downhill from here.

I have variations of the same thing for a pic32mm, avr0/1, and a samd10.

Not that anyone cares, but a variation where the pin alternate functions are grouped into the pin info-
https://godbolt.org/z/2_nhJR
so you can have each pin in the PINS namespace store information about each pin (in a struct), such as what the alternate function names/values are, default properties, etc. You are then unable to use an alternate function name for something that does not apply to the pin as the alternate function is an enum as well as the altFunc function argument type. Since these are constant expressions, there is no extra cost in using them.

« Last Edit: December 18, 2019, 06:42:46 am by cv007 »
 
The following users thanked this post: JohnnyBerg

Offline JohnnyBerg

  • Frequent Contributor
  • **
  • Posts: 474
  • Country: de
Re: STM32 macro for port and pin read and write
« Reply #22 on: December 17, 2019, 02:17:38 pm »
Most optimal if you don't want the led to blink. Maybe your model is different, but the one I have needs the mode set and the port clock enabled..

I left setting up port clock and pin mode out for clarity.

I tried to use provided header files, which is far from ideal. On the other hand, defining the structure is also a lot of work and error prone.

Looking at svdconv, there is a possibility to generate a header in various styles from a svd file. I wonder if anyone has tried to generate some sensible c++ out of a svd file?
« Last Edit: December 17, 2019, 02:29:55 pm by JohnnyBerg »
 

Offline JohnnyBerg

  • Frequent Contributor
  • **
  • Posts: 474
  • Country: de
Re: STM32 macro for port and pin read and write
« Reply #23 on: December 19, 2019, 11:13:55 am »
Not that anyone cares, but a variation where the pin alternate functions are grouped into the pin info-
https://godbolt.org/z/2_nhJR


Hmm .. embedded is mostly the domain of hardware guys, writing firmware using -O0 as a compiler option.
Do not expect applause here ;)
 

Offline ogden

  • Super Contributor
  • ***
  • Posts: 3476
  • Country: lv
Re: STM32 macro for port and pin read and write
« Reply #24 on: December 19, 2019, 11:31:44 am »
One of Cube's main goals is to make product design more efficient
Nah. Cube's main goal is to make "Hello world" design more efficient ;) Most vendor-supplied (not only ST) free libraries are in "use at your own risk" category, I would not use such for professional work and rather go in-house or 3rd party solution. DIY "products" obviously are fine with such.
 
The following users thanked this post: jhpadjustable


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf