# How to configure an ARM GPIO port (STR9) to an alternate function?

Because I am new to programming on microcontrollers coming from the C++ world, I have been studying on getting code running on a microcontroller board to work with many inputs and outputs.

For the first project I am working on an ARM9 chip (STR912FAW44) with Rowley Crossworks for ARM to program my board. I understand that I have to setup port 7.3 to output mode to drive a LED. I also understand that the GPIO port has multiple modes on this pin, defaulting to GP Input. I also know the alternate output 1 configuration is GP Output.

Now the manual for the chip says:

There are up to 80 GPIO pins available on 10 I/O ports for 128-pin and 144-ball devices, and up to 40 GPIO pins on 5 I/O ports for 80-pin devices. Each and every GPIO pin by default (during and just after a reset condition) is in high-impedance input mode, and some GPIO pins are additionally routed to certain peripheral function inputs. CPU firmware may initialize GPIO pins to have alternate input or output functions as listed in Table 8.

This sounds wonderful. The question is how can I get the port to accept a different function?

I have sample code for a similar, but different board where the LED (easiest to test) is on port 9, but this does not work when adjusted to port 7 as I expected.

Here is the sample:

void
ctl_board_init(void)
{
// leds are connected to GPIO9.0-GPIO9.3
SCU_PCGR1 |= SCU_PCGR1_GPIO9_MASK; // turn on GPIO9
GPIO9_DIR = 0x01; // select output direction
}

void
ctl_board_on_button_pressed(CTL_ISR_FN_t buttonFn)
{
}

void
ctl_board_set_leds(unsigned v)
{
*((&GPIO9_DATA)+(0x01<<2)) = v ? 0x01 : 0;
}


The macros come from header files, but can anyone help me find reference to which bits I should set to drive GPIO port 7.3 as output?

The STR91x family of microcontrollers has very flexible peripherals. Unfortunately, this flexibility makes them somewhat complicated to set up. My suggestion is to start by downloading the STR91xFA Firmware Library from ST.com. Once you have the library linked into your project, you need to do something similar to the following:

void InitGPIO7( void )
{
GPIO_InitTypeDef GPIO_InitStructure;

/* Enable the GPIO7 clock */
SCU_APBPeriphClockConfig(__GPIO7, ENABLE);

/* Initialize the GPIO port */
GPIO_DeInit(GPIO7);

/* Configure pin GPIO7.3 */
GPIO_InitStructure.GPIO_Direction = GPIO_PinOutput;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Type = GPIO_Type_PushPull;
GPIO_InitStructure.GPIO_IPConnected = GPIO_IPConnected_Disable;
GPIO_InitStructure.GPIO_Alternate = GPIO_OutputAlt1;
GPIO_Init (GPIO7, &GPIO_InitStructure);
}


Note that this is untested but it should be pretty close.

• Rowley includes a header file which should mostly work for my device, but the problem is that there is little to no documentation about the different ports. This library you linked to provides me with a lot more documentation. I am going to try to use it to get my chip working and will post back here if your answer fixes my problem. Thanks for the help. – Wouter Simons Jun 30 '10 at 7:15
• This is tested and works fine. I used the default project generated startup options to configure the basics of the chip. This function called from main as a hardware init function. To enable and disable the port all I have to do is use GPIO_WriteBit from the library code referenced. - Thanks again for your answer. – Wouter Simons Jun 30 '10 at 9:27
• Oh a PS, didn't realize until now. The text: GPIO_IPConnected needs to be GPIO_IPInputConnected in both instances. – Wouter Simons Jul 1 '10 at 13:59

From looking at the datasheet (13742.pdf), it looks like for GPIO pins 0-7 you need to set the appropriate bit in the SCU_GPIOTYPE registers and possibly others.

System control unit GPIO registers GPIO pins on P0 thru P7 have multiple input and output alternate functions. You select these using the System Control Unit (SCU) registers. SCU registers are also used to select open collector or Push-Pull operation and to configure Port 4 pins for use as analog inputs. GPIO pins on P8 thru P9 are only multiplexed with EMI and have no SCU output or input control registers. All ports have SCU_GPIOTYPE registers for selecting Open Collector or Push/Pull configuration.

• @Joby, Unfortunately, it is much more complicated than simply writing to the SCU_GPIOTYPE register. There is also the SCU_GPIOOUT, SCU_GPIOIN, GPIO_DATA, GPIO_DIR, GPIO_SEL, SCU_PCGR1, and SCU_PRR1 registers. There may even be more. This is why I suggested using the firmware library from ST. – semaj Jun 29 '10 at 16:26
• This is the first approach I tried, so I could stick with the simple header included in a Rowley project. Figuring out exactly what to write where was overwhelming. Perhaps this solution is more efficient if you really need high code density, but you would probably have to find a reference telling you exactly what to write to each register. Perhaps reverse engineer the library from ST? I am accepting the library solution as the answer to this question, but of course the approach of writing registers directly is possible as well. – Wouter Simons Jun 30 '10 at 9:30

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