← Volver al inicio

Laboratorio 1: Gestión de los GPIO

Programación a bajo nivel de registros para el control de pines de entrada/salida en el STM32F103RB.

1. Habilitación del Reloj (RCC)

Antes de usar cualquier puerto GPIO, es imperativo suministrarle señal de reloj. Esto se realiza en el registro RCC_APB2ENR.

RCC->APB2ENR |= (1 << 2);   // Habilita Puerto A (IOPA EN)
RCC->APB2ENR |= (1 << 3);   // Habilita Puerto B (IOPB EN)
RCC->APB2ENR |= (1 << 4);   // Habilita Puerto C (IOPC EN)

Nota: Sin este paso, cualquier acceso a los registros del puerto será ignorado por el procesador.

2. Configuración: Registros CRL y CRH

Cada puerto tiene 16 pines, divididos en dos registros de configuración de 32 bits cada uno:

Cada pin se define mediante 4 bits: MODE (2 bits) y CNF (2 bits).

Modo Deseado CNF [1:0] MODE [1:0] Valor Hex
Salida Push-Pull (2MHz) 00 10 0x2
Salida Open-Drain (2MHz) 01 10 0x6
Entrada Flotante 01 00 0x4
Entrada Pull-Up/Down 10 00 0x8

Ejemplo: Configurar PB6 como salida a 2MHz:

GPIOB->CRL &= ~(0xF << 24); // Limpia bits 24-27
GPIOB->CRL |= (0x2 << 24);  // Modo Salida Push-Pull (CNF=00, MODE=10)
// Limpiar configuración del pin 6 usando máscaras CMSIS
GPIOB->CRL &= ~(GPIO_CRL_MODE6 | GPIO_CRL_CNF6);
// Configurar como salida 2MHz Push-Pull
GPIOB->CRL |= GPIO_CRL_MODE6_1;  // MODE6 = 10 (2MHz)

Configuración de Pull-Up / Pull-Down

A diferencia de otros modos, la selección entre Pull-Up y Pull-Down requiere un paso adicional usando el registro ODR después de configurar el pin como entrada (CNF=10, MODE=00):

Ejemplo: Configurar PA0 como entrada con Pull-Up:

GPIOA->CRL &= ~(0xF << 0);   // Limpia configuración PA0
GPIOA->CRL |= (0x8 << 0);    // Configura CNF=10, MODE=00
GPIOA->ODR |= (1 << 0);      // Activa resistencia de Pull-Up

3. Operaciones de E/S

Escritura (Salida)

Acción directa con ODR o atómica con BSRR.

// Usando BSRR
GPIOB->BSRR = (1 << 6);  // SET: PB6 a 1
GPIOB->BSRR = (1 << 22); // RESET: PB6 a 0 (6+16)
// Usando BSRR con CMSIS
GPIOB->BSRR = GPIO_BSRR_BS6;  // SET: PB6 a 1
GPIOB->BSRR = GPIO_BSRR_BR6;  // RESET: PB6 a 0

Lectura (Entrada)

Consulta del estado lógico mediante IDR.

// Ejemplo PB6 con Pull-Down
if (GPIOB->IDR & (1 << 6)) {
    // PB6 está en HIGH
} else {
    // PB6 está en LOW (Pull-Down activo)
}

Ejemplo: Blink en PB6 (Bare Metal)

#include "stm32f10x.h"

int main(void) {
    // 1. Reloj para el Puerto B
    RCC->APB2ENR |= (1 << 3);

    // 2. PB6 como salida 2MHz
    GPIOB->CRL &= ~(0xF << 24);
    GPIOB->CRL |= (0x2 << 24);

    while(1) {
        GPIOB->ODR ^= (1 << 6); // Alternar estado
        for(int i=0; i<500000; i++); // Retardo software
    }
}