There's a detailed description in the STR91xF datasheet - it's complicated, but not as complicated as the ST libraries make it seem. Given the #defines and structs listed at
the end, to turn on e.g. GPIO4:
SCU->PCGR1 |= __GPIO4; // switch the port on.
SCU->PCGRO &=~ (u32)__GPIO4; // reset the device
SCU->PRR1 |= __GPIO4; // peripheral not held in Reset
Then, y'can set up the port using a combination of
GPIO4->DDR
SCU->GPIOOUT[4]
SCU->GPIOIN[4]
SCU->GPIOTYPE[4]
(and GPIO4->DR[n] to set and read the pins)
GPIOn->DDR is 8 bits, bits 7:0 corresponding to pins 7:0. any set bit makes its
corresponding pin an output, default is zero, all input.
The SCU registers are also bit masks, as follows:
GPIOTYPE[port_number] is 8 bits, with bit 7:0 corresponding to pin 7:0. Any set bits
make their corresponding output pins open-collector. Default is all
zero (push-pull)
GPIOIN[port number] is 8 bits. Any set bit has the input routed to some or other
internal peripheral input function, as detailed in a big table in
the data sheet, depends on device. NOTE: Even when connected to
an internal peripheral, you can still also read it as a general input.
Defaul is all zero, plain general purpose input
GPIOOUT[port number] is 16 bits, each pair of bits corresponding to a pin, so bits
14 and 15 control pin 7, bits 0 and 1 control pin 0. Zero means
ordinary GPIO input, 1 is normal GPIO output, 2 and 3 are alternative
outputs from internal peripherals. (See big table in STR9xx data
sheet)
So to set up GPIO4 so that pins 0 and 1 are outputs, with 0 being open collector, and
the remaining pins are inputs, do:
GPIO4->DDR = 0x03; // 0 0 0 0 0 0 1 1 pins 0 and 1 are output
SCU->GPIOOUT[4] = 0x0005; // 00 00 00 00 00 00 01 01 pins 0 and 1 are Alt1 output
SCU->GPIOTYPE[4] = 0x01; // 0 0 0 0 0 0 0 1 pin 0 open collector
SCU->GPIOIN[4] = 0x00; // no alternate inputs connected
To actually set/read port pins, STR9 has a memory mapped system where you mask pins
by both address and value, so that you can write to just the pins you want without having
to do too much ORing of arguments. E.g.
GPIO4->DR[GPIO_Pin_1 << 2] = GPIO_Pin_1; //sets pin1 output to high.
x = GPIO4->DR[GPIO_Pin_3 << 2]; //reads pin3
y = GPIO4->DR[(GPIO_Pin_3 | GPIO_Pin_4) << 2]; // reads pins 3 and 4
and so on.
The symbols used above boil down to:
#define __GPIO0 0x4000
#define __GPIO1 0x8000
#define __GPIO2 0x10000
#define __GPIO3 0x20000
#define __GPIO4 0x40000
#define __GPIO5 0x80000
#define __GPIO6 0x100000
#define __GPIO7 0x200000
#define __GPIO8 0x400000
#define __GPIO9 0x800000
typedef struct
{
vu32 CLKCNTR;
vu32 PLLCONF;
vu32 SYSSTATUS;
vu32 PWRMNG;
vu32 ITCMSK;
vu32 PCGRO;
vu32 PCGR1;
vu32 PRR0;
vu32 PRR1;
vu32 MGR0;
vu32 MGR1;
vu32 PECGR0;
vu32 PECGR1;
vu32 SCR0;
vu32 SCR1;
vu32 SCR2;
u32 EMPTY1;
vu32 GPIOOUT[8];
vu32 GPIOIN[8];
vu32 GPIOTYPE[10];
vu32 GPIOEMI;
vu32 WKUPSEL;
u32 EMPTY2[2];
vu32 GPIOANA;
} SCU_TypeDef;
typedef struct
{
vu8 DR[1021]; /* Data Register */
vu32 DDR; /* Data Direction Register */
} GPIO_TypeDef;
// #define AHB_BASE (0x58000000) // unbuffered
#define AHB_BASE (0x48000000) // buffered
// #define __APB1_BASE 0x5C000000 // unbuffered
#define __APB1_BASE 0x4C000000 // buffered
#define __SCU_BASE (__APB1_BASE + 0x2000)
#define SCU ((SCU_TypeDef *)__SCU_BASE)
#define GPIO0_OFFSET (0x00006000)
#define GPIO0_OFFSET (0x00007000)
// ...
#define GPIO4_OFFSET (0x0000A000)
#define GPIO4 (AHB_BASE + GPIO4_OFFSET)
#define GPIO_Pin_None 0x00
#define GPIO_Pin_0 0x01
#define GPIO_Pin_1 0x02
#define GPIO_Pin_2 0x04
#define GPIO_Pin_3 0x08
#define GPIO_Pin_4 0x10
#define GPIO_Pin_5 0x20
#define GPIO_Pin_6 0x40
#define GPIO_Pin_7 0x80
#define GPIO_Pin_All 0xFF