GPIO

GPIO stands for General Purpose Input/Output.

L476_CN10

Let’s choose a GPIO port pin, available on the Nucloe L476 – for example, PA6 on CN10/CN5, pin 13. Connect an LED to this pin and to GND, with a 220 ohms resistor (or a greater value).

del

Note

PA13, PA14, PA15 - PB3 and PB4 are normally used for debugging purposes

L476_CN10

GPIO configuration

Configuration of GPIO pins is achieved by configuring the GPIO registers (12 different registers):

del del

Every port, from port A to port I, has a distinct base address, eg “0x4800 0000” for GPIOA, “0x4800 0400” for GPIOB, etc.

To access in assembler a specific register of a specific GPIO module, use the base address and add an offset:

ldr  r1, =GPIOA_BASE           // 0x4800 0000
ldr  r2, [r1,#GPIO_BSSR]               // read at base address plus offset (0x18)

Clock Configuration

Most periphery clocks are disabled by default to save power.

In order to turn on the GPIOA module to which the desired pin PA6 belongs, the bit IOPAEN must be set to 1.

AHB2 peripheral clock enable register (RCC_AHB2ENR)

AHB2ENR

Since this is the first bit (number zero), a value of 0x0000 0001 must be written in the register.

The AHB2ENR address is at offset 0x4c from the RCC register address (4002 1000). To access RCC_AHB2ENR, we want to access the location pointed to by 0x40021000 + 0x4c.

We use RCC_AHB2ENR_GPIOAEN, with following definitions:

#define RCC_AHB2ENR_GPIOAEN_Pos    (0U)
#define RCC_AHB2ENR_GPIOAEN_Msk    (0x1UL << RCC_AHB2ENR_GPIOAEN_Pos)  // 0000 0001
#define RCC_AHB2ENR_GPIOAEN         RCC_AHB2ENR_GPIOAEN_Msk

One way to activate the GPIOA clock, without changing the others bits of the AHB2ENR RCC register, is the following:

ldr  r1, =RCC_BASE       //  4002 1000
ldr  r2, [r1,#AHB2ENR]   //  4002 104c
orr  r2, #RCC_AHB2ENR_GPIOAEN   // enable GPIO port A  #0x01
str  r2, [r1,#AHB2ENR]

There is no problem with r2 (RCC_AHB2ENR) containing 1s, since the reset has cleared all the RCC_AHB2ENR bits. If r2 contains several ones, the OR operator would not change anything.

RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;

Mode configuration

It is made through the MODE register

GPIO_mode

Address offset:0x00 Reset value: 0xABFF FFFF (for port A) Reset value: 0xFFFF FEBF (for port B)

On STM32L47x/L48x: Reset value: 0xFFFF FFFF (for ports C..G) Reset value: 0x0000 000F (for port H)

There are sixteen 2-bit MODE available, corresponding to I/O pin #0 to #15. For PA6, we need to use MODE6.

GPIO_MODER_MODE6_Pos   =      (12U)
GPIO_MODER_MODE6_Msk   =      (0x3UL << GPIO_MODER_MODE6_Pos)         /*!< 0x00003000 */
GPIO_MODER_MODE6       =       GPIO_MODER_MODE6_Msk
GPIO_MODER_MODE6_0     =      (0x1UL << GPIO_MODER_MODE6_Pos)         /*!< 0x00001000 */
GPIO_MODER_MODE6_1     =      (0x2UL << GPIO_MODER_MODE6_Pos)         /*!< 0x00002000 */

/* Legacy defines */
#define GPIO_MODER_MODER6                   GPIO_MODER_MODE6
#define GPIO_MODER_MODER6_0                 GPIO_MODER_MODE6_0
#define GPIO_MODER_MODER6_1                 GPIO_MODER_MODE6_1
00: Input mode
01: General purpose output mode                GPIO_MODER_MODEx_0
10: Alternate function mode            GPIO_MODER_MODEx_1
11: Analog mode (reset state)

With libopencm 3

#define GPIO_MODE_INPUT                 0x00    /* default */
#define GPIO_MODE_OUTPUT                0x01
#define GPIO_MODE_AF                    0x02
#define GPIO_MODE_ANALOG                0x03

Output mode configuration

GPIO_mode

The output mode offers two options:

  • Push-pull output mode

  • Open-drain output mode

The mode is activated through GPIO port pull-up / pull-down register (GPIOx_PUPDR).

GPIO port output type register (GPIOx_OTYPER)(x = A to I)

The control is done through the GPIO port output type register (GPIOx_OTYPER).

GPIO_OTYPE

0: Output push-pull (reset state) 1: Output open-drain

GPIO_SPEED

Address offset: 0x08 Reset value: 0x0C00 0000 (for port A) Reset value: 0x0000 0000 (for the other ports)

00: Low speed
01: Medium speed
10: High speed
11: Very high speed

GPIO port pull-up/pull-down register (GPIOx_PUPDR)(x = A to I).

GPIO_PUPD

Address offset: 0x0C Reset value: 0x6400 0000 (for port A) Reset value: 0x0000 0100 (for port B) Reset value: 0x0000 0000 (for other ports)

  • 00: No pull-up, pull-down

  • 01: Pull-up

  • 10: Pull-down

  • 11: Reserved

GPIO port output data register (GPIOx_ODR)(x = A to I).

Writing the related bit of the output register (GPIOx_ODR) to 0 forces the I/O pin to ground. Writing the related bit of the output register (GPIOx_ODR) to 1 forces the I/O pin to VDD.

GPIO_ODR

Address offset: 0x14 Reset value: 0x0000 0000

GPIO port bit set/reset register (GPIOx_BSRR) (x = A to I)</h3>

GPIO BSRR

Address offset: 0x18 Reset value: 0x0000 0000

Input mode configuration

GPIO_mode

5V tolerant inputs

“Up to 114 fast I/Os, most 5 V-tolerant, up to 14 I/Os with independent supply down to 1.08 V.”

When applying a voltage you should be aware to limit it for some GPIO, as shown below:

FT: 5 V tolerant I/O pin
TT: 3.6 V tolerant I/O pin

5 V tolerant I/O pin (FT):

PA0,PA1,PA2,PA4,PA6,PA7,PA8,PA9,PA10,PA11,PA12,,PA13,PA14,PA15
PB1,PB2,PB3,PB4,PB5,PB6,PB7,PB8,PB9,PB10,PB11,PB12,PB13,PB14,PB15
PC0,PC1,PC2,PC3,PC4,PC5,PC6,PC7,PC8,PC9,PC10,PC11,PC12,PC13,PC14,PC15
PD0,PD1,PD2,PD3,PD4,PD5,PD6,PD7,PD8,PD9,PD10,PD11,PD12,PD13,PD14,PD15
PE0,PE1,PE2,PE3,PE4,PE5,PE6,PE7,PE8,PE9,PE10,PE11,PE12,PE13,PE14,PE15
PF0,PF1,PF2,PF3,PF4,PF5,PF6,PF7,PF8,PF9,PF10,PF11,PF12,PF13,PF14,PF15
PG0,PG1,PG2,PG3,PG4,PG5,PG6,PG7,PG8,PG9,PG10,PG11,PG12,PG13,PG14,PG15
PH0,PH1

3.6 V tolerant I/O pin (TT):

PA3,PA4,PA5
PB0

source: datasheet table #16

Input options

The input mode offers three options:

* Input with internal pull-up
* Input with internal pull-down
* Floating input

Connect a button

In the Nucleo L476, there is a blue button connected to PC13.

blue button

When the button is pressed, the GPIO PC13, thet must be configured as input, becomes low.

// configure PC13 as input
ldr r1, =GPIOC_BASE
ldr r2,[r1,#GPIO_MODER]
bic r2, #GPIO_MODER_MODE13_0   // reset bit 0 mode 13 -> "x0"
bic r2, #GPIO_MODER_MODE13_1   // reset bit 1 mode 13 -> "0x"
str r2,[r1,#GPIO_MODER]
// configure PC13 as input
GPIOC->MODER &=  GPIO_MODER_MODE13_0;   // input mode "00"
GPIOC->MODER &= ~GPIO_MODER_MODE13_1;

Note

    //  check bit 13 of IDR register
if ((GPIOC->IDR & GPIO_IDR_ID13) == GPIO_IDR_ID13)
{
            // if button pressed, do something
}

If the button is connected to GPIO in this way

button

it is necessary to configure the GPIO input in pull-up.

When the button is pressed, the PA1 voltage will go to zero.

We need to modify the GPIOx_PUPDR Register for GPIO A1

//   input PA1 - button
GPIOC->MODER &=  GPIO_MODER_MODE1_0;   // input mode "00"
GPIOC->MODER &= ~GPIO_MODER_MODE1_1;

// pull-up output "01" because the button connects to ground
GPIOA->PUPDR |=  GPIO_PUPDR_PUPD1_0;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD1_1;

Read GPIO inputs

GPIO port input data register (GPIOx_IDR) (x = A to I)

  • Address offset: 0x10

  • Reset value: 0x0000 XXXX

IDR

The 16 bits of the register contain the input value of the corresponding I/O port.

(gdb) x 0x48000810
0x48000810: 0x00002000 // blue button not pressed

(gdb) x 0x48000810
0x48000810: 0x00000000 // blue button pressed
bit 13 set
15 14 13 12   11 10 9 8   7 6 5 4  3 2 1 0
0  0  1  0    0  0  0 0   0 0 0 0  0 0 0 0        -> hex 2000

Alternate functions

Several functions, such as timers, communication functions (SPI, UART, I2C, etc) are available.

For Port B, we have:

AF

Note

You can find tables for alternate functions AF0 to AF7 and AF8 to AF15 for ports A to H in the L476 datasheet.

We set the alternate mode of the GPIO pin x by setting the bit 1 and resetting the bit 0:

10: Alternate function mode
* reset bit 0 with bic
* set bit 1 with orr

For example, for PB9:

// ----------- set PB9 alternate function
ldr  r1, =GPIOB_BASE                //  4800 0400
ldr  r2, [r1,#GPIO_MODER]
bic r2, r2, #GPIO_MODER_MODE9_0     // clear mode9 bit "x0"
orr r2, r2, #GPIO_MODER_MODE9_1     //   set mode9 bit "1x"
str  r2, [r1,#GPIO_MODER]
For a given AFx, we also need to specify one of these two registers:
* GPIOx_AFRL for GPIO pin 0 to 7
* GPIOx_AFRH for GPIO pin 8 to 15
GPIO alternate function low register (GPIOx_AFRL)(x = A to I)
Address offset: 0x20
AFRL
GPIO alternate function high register (GPIOx_AFRH)(x = A to I)
Address offset: 0x24
AFRH
As an example, for assigning AF4 to GPIO pin 8, write in AFSEL[8] the binary 0100 (ie 4 as in AF4):
* reset bit 0 with bic
* reset bit 1 with bic
* set bit 2 with orr
* reset bit 3 with bic

Since the pin number is greater than 7, we use the GPIOx_AFRH register

// alternate function to I2C1   AFRH->AFSEL[8] =  AF4
ldr  r1, =GPIOB_BASE   //  4800 0400
ldr r2,[r1,#AFRH]
bic r2,r2, #GPIO_AFRH_AFSEL8_0
bic r2,r2, #GPIO_AFRH_AFSEL8_1
orr r2,r2, #GPIO_AFRH_AFSEL8_2
bic r2,r2, #GPIO_AFRH_AFSEL8_3
str r2,[r1,#AFRH]
// set open-drain
ldr  r1, =GPIOB_BASE
ldr  r2, [r1,#OTYPER]
bic  r2, #GPIO_OTYPER_OT8   // 1: output open drain
bic  r2, #GPIO_OTYPER_OT9   // 1: output open drain
str  r2, [r1,#OTYPER]
// set pin speed
ldr  r2, [r1,#OSPEEDR]
orr  r2,r2, #GPIO_OSPEEDR_OSPEED8_0
orr  r2,r2, #GPIO_OSPEEDR_OSPEED8_1
str  r2, [r1,#OSPEEDR]
//  00: No pull-up, pull-down   01: Pull-up   10: Pull-down
ldr  r2, [r1,#PUPDR]
bic  r2, #GPIO_PUPDR_PUPD6_1   // pull-up 0x
orr  r2, #GPIO_PUPDR_PUPD6_0   // pull-up x1
str  r2, [r1,#PUPDR]

Analog configuration

analog configuration

Some GPIO pins can be configured in analog mode which allows the use of ADC, DAC, OPAMP, and COMP internal peripherals.

To use a GPIO pin in analog mode, the following register are considered:

*GPIOx_MODER to select the mode (Input, Output, Alternate, Analog)
*GPIOx_ASCR to select the required function ADC, DAC, OPAMP, or COMP