This is what I use:
// 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:
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*/;