0% encontró este documento útil (0 votos)
4 vistas48 páginas

Microcontroladores ARM: Características y Usos

Los microcontroladores ARM son dispositivos integrados que combinan un procesador basado en la arquitectura ARM con memoria y periféricos, siendo populares en sistemas embebidos por su rendimiento y eficiencia energética. La familia Cortex-M incluye diferentes modelos adaptados a diversas aplicaciones, desde simples hasta críticas en tiempo real. Además, ofrecen ventajas sobre otros microcontroladores en términos de rendimiento, consumo y soporte para sistemas operativos en tiempo real.

Cargado por

hector199d
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
4 vistas48 páginas

Microcontroladores ARM: Características y Usos

Los microcontroladores ARM son dispositivos integrados que combinan un procesador basado en la arquitectura ARM con memoria y periféricos, siendo populares en sistemas embebidos por su rendimiento y eficiencia energética. La familia Cortex-M incluye diferentes modelos adaptados a diversas aplicaciones, desde simples hasta críticas en tiempo real. Además, ofrecen ventajas sobre otros microcontroladores en términos de rendimiento, consumo y soporte para sistemas operativos en tiempo real.

Cargado por

hector199d
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd

MICROCONTROLADORES ARM

¿Que son los microcontroladores ARM?


Los microcontroladores ARM son dispositivos integrados que
incluyen un procesador basado en la arquitectura
ARM (Advanced RISC Machine), junto con memoria, periféricos y
otros componentes en un solo chip. Son ampliamente utilizados en
sistemas embebidos debido a su alto rendimiento, bajo
consumo de energía y escalabilidad.

Características principales de los microcontroladores


ARM

Arquitectura RISC

 Diseño simplificado para mayor eficiencia.

Familia Cortex-M (para microcontroladores)

 Cortex-M0/M0+: Bajo costo y consumo (aplicaciones simples).


 Cortex-M3/M4: Balance entre rendimiento y consumo (con DSP
y FPU opcional).
 Cortex-M7: Alto rendimiento (aplicaciones críticas en tiempo
real).

Aplicaciones comunes

 Electrónica embebida: Sensores, actuadores, wearables.


 IoT: Dispositivos conectados (Wi-Fi, Bluetooth, LoRa).
 Industria: Control de motores, automatización.
 Automoción: Sistemas de infoentretenimiento, control de
motor.
 Robótica: Procesamiento en tiempo real.

Ventajas frente a otros microcontroladores (como


AVR o PIC)
✅ Mayor rendimiento por MHz.
✅ Mejor relación potencia/consumo.
✅ Más opciones de fabricantes y familias.
✅ Soporte para sistemas operativos en tiempo real (FreeRTOS,
Zephyr).

Ejemplos populares

 STM32F4 (Cortex-M4 con FPU).


 RP2040 (Doble núcleo Cortex-M0+, usado en Raspberry Pi Pico).
 NXP LPC1768 (Cortex-M3 clásico).

En resumen, los microcontroladores ARM dominan el mercado de


sistemas embebidos gracias a su flexibilidad, eficiencia y amplio
ecosistema de herramientas de desarrollo (Keil, GCC, PlatformIO).

Caracteristicas del microcontrolador


STM32F103C8T6
1. Procesador y Arquitectura

 Núcleo: ARM Cortex-M3 a 72 MHz.


 Conjunto de instrucciones: Thumb-2 (eficiente en código y
rendimiento).
 Pipeline de 3 etapas para mayor velocidad.

2. Memoria

 Flash: 64 KB (aunque algunos chips tienen 128 KB "ocultos").


 SRAM: 20 KB.
 EEPROM: No incluida (se puede emular en Flash).

3. Periféricos Integrados
GPIO: 37 pines de E/S de propósito general (5V tolerant en
algunos).
ADC: 2 x ADC de 12 bits (hasta 16 canales, 1 µs de tiempo de
conversión).
Timers:

 3 x TIM2, TIM3, TIM4 (16-bit).


 1 x TIM1 (16-bit avanzado para PWM).
 1 x SysTick (para RTOS).

Comunicación serial:

 3 x USART (UART + sincronía opcional).


 2 x SPI.
 2 x I2C.
 1 x USB 2.0 Full-Speed (sin PHY integrado).
 1 x CAN 2.0B (para aplicaciones
industriales/automotrices).

Interfaces de depuración: SWD y JTAG (para programación y


debugging).

4. Voltaje y Consumo

Voltaje de operación: 2.0V a 3.6V (típico 3.3V).


Consumo:

 ~50 µA en modo Sleep.


 ~20 µA en modo Stop.
 ~2 µA en modo Standby.
o

5. Paquete y Pines

 Paquete: LQFP48 (48 pines).


 Pines accesibles: 37 GPIO + alimentación +
comunicación.

RESUMEN DE CONCEPTOS CLAVES


Comunicación Serial

Método para transmitir datos bit a bit (en serie) entre dispositivos.
Tipos comunes:

 UART: Asíncrona (2 cables: TX, RX). Ejemplo: Monitor


Serie en Arduino.
 SPI: Síncrona (4 cables: CLK, MOSI, MISO, SS). Alta
velocidad.
 I2C: Síncrona (2 cables: SDA, SCL). Conexión múltiples
dispositivos.

Memorias

 Flash: Almacena el código del programa (no volátil, se


borra por bloques). Ejemplo: 64 KB en STM32F103.
 SRAM: Memoria volátil (se pierde al apagar) para
variables y datos temporales. Ejemplo: 20 KB en
STM32F103.
 EEPROM: Memoria no volátil para guardar datos (ej:
configuraciones). En STM32 se emula en Flash.
Pines GPIO

(General Purpose Input/Output): Pines programables como:

 Entrada: Leer señales (ej: botones, sensores digitales).


 Salida: Controlar dispositivos (ej: LEDs, relés).
 Alternate Function: UART, SPI, etc.

ADC (Analog-to-Digital Converter)

Convierte señales analógicas (ej: 0-3.3V) a valores digitales (ej: 0-


4095 en 12 bits). Usos: Lectura de sensores (temperatura,
potenciómetros).

Timers: Circuitos para contar tiempo o generar señales:

 Base de tiempo: Delays, medición de pulsos.


 PWM: Control de motores, LEDs (señal cuadrada
ajustable).
 Captura/Compara: Medir frecuencia de señales.

Modos de bajo consumo

 Sleep: CPU detenida, periféricos activos. Despierta con


interrupciones.
 Stop: CPU y periféricos apagados (excepto SRAM).
Despierta con eventos externos.
 Standby: Todo apagado (incluida SRAM). Solo despierta
con reset o pin específico (ej: RTC).
Concept
Función Principal Ejemplo de Uso
o

Comunicación bit a
Serial Enviar datos a PC
bit

Flash Almacenar código Programa del micro

Variables
SRAM Datos en ejecución
temporales

GPIO Entrada/Salida Leer botón, encender LED

Leer señales
ADC Sensor de luz
analógicas

Contar/Generar
Timer PWM para motor
señales

Sleep/ Sensor que despierta cada


Ahorrar energía
Stop hora

CONCEPTOS BASICOS:
Diferencia entre byte y bit

 1 bit: Es la unidad más pequeña de datos en


computación. Puede tener dos valores: 0 o 1.

 1 byte: Es un grupo de 8 bits. Por ejemplo: 01101011 (8


dígitos binarios).01101011 (8 dígitos binarios).

Ejemplos prácticos

 Un archivo de texto de 1 KB (kilobyte) = 1024


bytes = 8192 bits.

 Un microcontrolador de 32 bits (como ARM Cortex-M)


procesa 4 bytes a la vez (32 ÷ 8 = 4).
Unida Equivalen
d cia

1 bit 0o1

1 byte 8 bits

1 KB 1024 bytes

1 MB 1024 KB

En redes (ej: Internet), la velocidad se mide en bits por


segundo (Mbps = megabits/s), mientras que el almacenamiento
(ej: disco duro) se mide en bytes (MB, GB).
¿Qué significa exactamente "32 bits"?

Que un microcontrolador (como los ARM Cortex-M) sea de 32


bits significa que su arquitectura interna está diseñada para
procesar datos y direcciones de memoria en bloques de 32 bits (4
bytes) a la vez. Esto tiene implicaciones clave en su rendimiento y
capacidades.

Ejemplo práctico

 Micro de 8 bits (AVR): Para sumar dos números de 32 bits,


necesita 4 operaciones (8 bits a la vez).
 Micro de 32 bits (ARM Cortex-M): Lo hace en 1 operación,
reduciendo tiempo y energía.

Resumen

Un microcontrolador de 32 bits (como el STM32) es como una


autopista de 4 carriles frente a una calle de 1 carril (8 bits):
mueve más datos en paralelo, acelerando operaciones y
permitiendo aplicaciones más complejas (IoT, robótica, audio
digital).

PROGRAMAS A UTILIZAR
¿Que es STM32CubeIDE?
STM32CubeIDE es el entorno de desarrollo integrado (IDE) oficial
de STMicroelectronics para programar y depurar
microcontroladores de la familia STM32 (basados en ARM Cortex-
M). Es una herramienta gratuita y todo en uno, diseñada para
simplificar el desarrollo de aplicaciones embebidas.

¿Qué es el ST-Link?

El ST-Link es un programador y depurador oficial de


STMicroelectronics diseñado específicamente para
microcontroladores STM8 y STM32. Permite:

 Programar el firmware en la memoria Flash del


microcontrolador.
 Depurar el código en tiempo real (breakpoints, paso a
paso, visualización de variables, etc.).
Versiones comunes

ST-Link/V2:

 La versión más usada (integrada en placas


como Nucleo y Discovery).
 Soporta STM32 y STM8.
 Conecta vía USB y usa interfaz SWD (Serial Wire Debug)
o JTAG.
ST-Link/V3:

 Más rápida y con mayor corriente para alimentar la placa


objetivo.
 Incluye interfaz VCP (Virtual COM Port) para
comunicación serial.
ST-Link integrado:

 Muchas placas de desarrollo (ej: STM32 Nucleo) lo traen


incluido.

¿Cómo se conecta?
Conexión típica (SWD):

 ST-Link → STM32

 SWDIO → PA13 (SWDIO)

 SWCLK → PA14 (SWCLK)

 GND → GND

 3.3V → 3.3V (opcional, para alimentar la placa objetivo).

En placas Nucleo/Discovery:

 Ya está integrado, solo conecta por USB.

PARTE 1: CREAR PROYECTO EN STM32CUBE IDE


 File....STM32Project....Buscar modelo del micro.....Poner
nombre pryecto....next...finish

 System Core:
RCC: En High speed clock poner: Cristal/Ceramic resonator

 Clock Configuration:
 Habilitar HSE....En PLL poner x9.....Seleccionar
PLLCLOCK....En APB1 Prescaler poner /2.
 Pinout and Configuration: Configurar pines

 Project Manager:
 Code generator....Habilitar Generate Peripheral
initation...

 Guardar todo
 Programar
 Conectar a ST-LINK y a la PC

 Cambiar posicion jumper y presionar boton de reset:

 En Run Last Tool....External Tools Configuration....Run

 En Run Last Tool poner 1 ST_LINK Programmer: (Carga


el código al micro)

 Una vez subido el programa cambiar posición de jumper y


presionar el botón de reset.
PARTE 2: CONTROL DE PUERTOS GPIO

Normalmente en los microcontroladores se les nombra como GPIO a las entradas y


salidas. GPIO significa entrada/salida de proposito general.

Este es el esquema de cada uno de los pines

PIN GPIO: Se puede configurar como entrada o salida


 Si es ENTRADA: Entra una señal de voltaje por el
pin pasa por Schmitt trigger, se almacena y el
micro lo lee.

 Si es SALIDA: Se escribe pasa por Multiplexador


y por etapa de transistores y envía 1 o 0 a la
salida.

¿Qué es un Trigger Schmitt?

El Trigger Schmitt (o Schmitt Trigger) es un circuito comparador


con histéresis, diseñado para convertir señales analógicas ruidosas
o lentas en señales digitales limpias (0 o 1). Es ampliamente usado
en microcontroladores (como en las entradas GPIO de los STM32)
para evitar falsos disparos causados por ruido eléctrico.

Funcionamiento básico NMOS y PMOS

¿Qué es un Multiplexador (MUX)?

Un multiplexador (o MUX) es un circuito digital que actúa como


un selector de canales, permitiendo elegir una de varias señales
de entrada y dirigirla a una única salida. Funciona como un
"interruptor controlado digitalmente" y es esencial en sistemas
electrónicos y microcontroladores (como los ARM Cortex-M).

Resistencias Pull-Up y Pull-Down: Usos y


Funcionamiento

Las resistencias pull-up y pull-down se utilizan en circuitos


digitales para definir un estado lógico estable en una línea de
señal cuando no está activamente impulsada (evitando estados
flotantes o indeterminados). Son esenciales en microcontroladores
(como STM32, Arduino) y sistemas digitales.

¿Qué Problema Resuelven?


Cuando un pin de entrada (como un GPIO) no está conectado a una
señal activa (ej: un botón sin pulsar), queda en estado
flotante (alta impedancia), lo que puede causar:

 Lecturas erráticas (ruido eléctrico interpretado


como 0 o 1 aleatorio).
 Consumo de energía innecesario.
 Fallos en la lógica del circuito.

Resistencia Pull-Up
Valor típico:

 1kΩ a 10kΩ (depende de la velocidad y consumo requerido).

Aplicaciones:

 Botones e interruptores.
 Comunicaciones I2C (los buses SDA y SCL requieren pull-ups).
 Entradas digitales en microcontroladores.

Resistencia Pull-Down
Valor típico:

 1kΩ a 10kΩ (similar a pull-up).

Aplicaciones:

 Detección de señales activas en HIGH.


 Circuitos donde el estado por defecto debe ser LOW.

Conclusión
 Pull-Up: Para estados por defecto HIGH (ej: botones, I2C).
 Pull-Down: Para estados por defecto LOW (ej: sensores activos
en HIGH).
 Internas vs Externas: Usa las internas para simplificar, y las
externas para mayor precisión.
Push-Pull vs Open-Drain: Diferencias y Usos

Ambos son modos de configuración para salidas digitales en


microcontroladores (como STM32, Arduino) y circuitos integrados,
pero tienen comportamientos eléctricos y aplicaciones distintas.

Salida Push-Pull

 Si ENTRADA = 1. Conecta VDD a la salida

 Si ENTRADA = 0. Conecta GND a la salida

Características

✅ Ventajas:

 Entrega corriente activamente en ambos estados


(HIGH y LOW).
 Señales más rápidas (ideal para alta frecuencia).
 No requiere resistencia externa.

❌ Desventajas:

 No permite conexión en bus (ej: múltiples salidas unidas).

Aplicaciones

 Señales digitales estándar (ej: GPIO, PWM, UART).


 Control de LEDs, motores, relés.

Salida Open-Drain (o Open-Collector)

Cómo funciona

 Solo usa un transistor NMOS conectado a GND.

 LOW: El NMOS conduce, conectando la salida a GND.


 HIGH: El transistor se apaga, dejando la
salida flotante (requiere una resistencia pull-up externa
para llevarla a VCC).

Características

✅ Ventajas:
 Permite conexión en bus (varios dispositivos comparten la
misma línea).
 Compatibilidad con diferentes voltajes (ej: 3.3V y 5V).

❌ Desventajas:

 Más lento que Push-Pull (depende de la resistencia pull-up).


 Requiere resistencia externa para estado HIGH.

Aplicaciones

 Comunicaciones I2C (el bus necesita open-drain).


 Interfaz de niveles mixtos (ej: 3.3V ↔ 5V).
 Control compartido (varios dispositivos en una línea).

Comparación Push-Pull vs Open-Drain


Caracterí
Push-Pull Open-Drain
stica

Estructur
NMOS + PMOS Solo NMOS
a

Estado Flotante (necesita pull-


Conecta a VCC
HIGH up)

Estado
Conecta a GND Conecta a GND
LOW

Velocida
Más rápida Más lenta
d

Uso en
No compatible Ideal (ej: I2C)
buses

Mayor en Menor (solo consume en


Consumo
transiciones LOW)
FUNCIONES PARA CONTROL DE ENTRADAS Y
SALIDAS

1. Función HAL_GPIO_WritePin()

Propósito
 Establecer un pin GPIO en estado HIGH (1) o LOW (0).

Sintaxis

HAL_GPIO_WritePin(GPIOx, GPIO_PIN_y, Estado);

 GPIOx: Puerto del GPIO (ej: GPIOA, GPIOB, etc.)


 GPIO_PIN_y: Número del pin (ej: GPIO_PIN_5).
 Estado:

 GPIO_PIN_SET → HIGH (3.3V).


 GPIO_PIN_RESET → LOW (0V).
Ejemplo:

// Encender un LED conectado a PB0 (HIGH)


HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);

// Apagar un LED conectado a PB0 (LOW)


HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);

2. Función HAL_GPIO_TogglePin()
Propósito
 Cambiar el estado actual de un pin (HIGH → LOW o LOW → HIGH). Útil para
parpadear LEDs sin necesidad de leer el estado previo.
Sintaxis

HAL_GPIO_TogglePin(GPIOx, GPIO_PIN_y);

Ejemplos:

// Cambiar el estado de PB0 (si estaba HIGH, pasa a LOW, y


viceversa)
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);

// Uso típico en un bucle para parpadear un LED


while (1) {
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
HAL_Delay(500); // Espera 500 ms
}

RETARDO EN MILISEGUNDOS Y
MICROSEGUNDOS
La función HAL_Delay() es una utilidad proporcionada por la capa de abstracción de
hardware (HAL) de STMicroelectronics para generar retardos temporizados en
milisegundos. Es ampliamente usada en proyectos con microcontroladores STM32
para pausar la ejecución del código sin bloquear otras operaciones críticas (como
interrupciones).

Libreri: #include “stm32_delay.h”

Int main(void) {

//inicializa las funciones para el manejo de los retardos


MX_Delay_Init();

 HAL_Delay_US(); Permite generar retardos en


microsegundos

 HAL_Delay_MS(); Permite generar retardos en


milisegundos

ANTIREBOTE EN PULSADORES

El código mostrado implementa un antirrebote (debounce) por


software para eliminar los falsos contactos mecánicos de pulsadores
o interruptores. Aquí te explico cómo funciona, tanto para
configuraciones Pull-Up como Pull-Down.

Explicación del Código

1. Configuración Pull-Up

if (HAL_GPIO_ReadPin(Port, Pin) == 0) { // Si se detecta LOW


(botón presionado)
HAL_Delay(5); // Espera 5 ms (filtra ruido inicial)
while (HAL_GPIO_ReadPin(Port, Pin) == 0); // Espera hasta que
se suelte el botón
HAL_Delay(5); // Espera 5 ms (filtra ruido al soltar)
// Acciones a ejecutar (ej: encender LED)
}

¿Cómo funciona?

 Pull-Up: El pin está en HIGH (1) cuando el botón no se


presiona (gracias a la resistencia pull-up).
 Al presionar el botón, el pin va a LOW (0).
 Los retardos (HAL_Delay(5)) ignoran los rebotes mecánicos
(señales fluctuantes al presionar/soltar).

2. Configuración Pull-Down

if (HAL_GPIO_ReadPin(Port, Pin) == 1) { // Si se detecta HIGH


(botón presionado)
HAL_Delay(5); // Espera 5 ms (filtra ruido inicial)
while (HAL_GPIO_ReadPin(Port, Pin) == 1); // Espera hasta que
se suelte el botón
HAL_Delay(5); // Espera 5 ms (filtra ruido al soltar)
// Acciones a ejecutar (ej: encender LED)
}
¿Cómo funciona?

 Pull-Down: El pin está en LOW (0) cuando el botón no se


presiona (resistencia pull-down).
 Al presionar el botón, el pin va a HIGH (1).
 Los retardos filtran los rebotes.

¿Por qué 5 ms?


 Los rebotes mecánicos suelen durar 1–50 ms.
 5 ms es un valor típico para asegurar que los rebotes han
terminado.
Explicación del ADC en el
STM32F103C8T6
El microcontrolador STM32F103C8T6 incluye 2 módulos ADC
(Analog-to-Digital Converter) con las siguientes características
clave:

Características Principales
1. Resolucion de 12 bits

 Convierte señales analógicas en valores digitales desde 0


hasta 4095 (porque = 4096 niveles)

 Ejemplo:

 0V → Valor digital 0.

 3.3V → Valor digital 4095.

2. 16 canales analógicos:

 Puede leer hasta 16 señales analógicas diferentes


(multiplexadas).

 Los pines ADC están etiquetados como PA0 a PA7, PB0, PB1,
PC0 a PC5, etc. (consulta el datasheet para el mapeo exacto).

3. Rango de voltaje de entrada:

 0V a 3.3V (voltaje de referencia del ADC).

 Precisión: Cada paso (bit) equivale a:

3.3V / 4095 ≈ 0.0008V = 0.8mV por bit.

¿Cómo funciona el ADC?


1. Muestreo:

 El ADC captura el voltaje analógico en un pin (ej: PA0).


2. Conversión:

 Transforma el voltaje en un valor digital de 12 bits (0–4095).

3. Escalado:

 Si mides un sensor de 0–5V, necesitas un divisor de voltaje


para adaptarlo a 0–3.3V.

¿Qué es el ADC?
Un ADC convierte una señal analógica (como el voltaje de un
potenciómetro o un sensor) en un valor digital que tu
microcontrolador puede entender.

Por ejemplo, un valor de voltaje entre 0V y 3.3V se convierte en un


número entre 0 y 4095 si el ADC es de 12 bits (porque =
4096 niveles posibles).
✅ Funciones mostradas en la imagen
1. HAL_ADC_Start();
 ¿Qué hace?
Inicia la conversión ADC. Es decir, le dice al ADC: "Empieza a
convertir la señal analógica en digital".

2. HAL_ADC_GetValue();
 ¿Qué hace?

Después de que la conversión ha terminado, esta función obtiene el


valor digital convertido del ADC.

 ¿Qué valor devuelve?

Devuelve un número entre 0 y 4095, dependiendo del voltaje leído.

EJEMPLO SENSILLO:

Pasos:
1. Configurar reloj
2. Click en System Core....Analog
 Aquí se encuentran los dos módulos del ADC del
microcontrolador
3. Utilizar ADC1...INO
 Por el momento solo usamos un canal
4. Unico parametro que vamos a modificar
 En configuration:
 ADC_Settings
 Continuous Conver: Enabled
5. Aparece un error en la configuración del reloj:
 Esto es porque el ADC no trabaja a esta frecuencia tan alta.
Trabaja a 12MHz
 En ADC Prescaler dividir entre 6

CODIGO
Código es para encender y apagar led según la lectura del valor del
potenciometro en stm32f10c8t6 usando la placa blue pill

while (1)
{

HAL_ADC_Start(&hadc1);
int valor_adc = HAL_ADC_GetValue(&hadc1);
float voltaje = (float)((valor_adc*3.3)/4095.0);

if(voltaje > 2.5)


{
HAL_GPIO_WritePin(GPIOB, Led_Pin, GPIO_PIN_SET);
}

else
{
HAL_GPIO_WritePin(GPIOB, Led_Pin, GPIO_PIN_RESET); }
}

Control de brillo de un led con potenciometro


Para leer un potenciómetro con una Blue Pill (STM32F103C8T6) y
controlar el brillo de un LED mediante PWM, usando STM32CubeIDE,
tenés que hacer lo siguiente:

✅ Resumen de lo que necesitás


 Leer el potenciómetro por ADC (conversión analógica a digital)

 Generar una señal PWM en un pin con Timer

 Usar el valor del ADC para variar el duty cycle del PWM

1. Conexiones
Conexión a Blue
Componente
Pill
Potenciómetro
GND
GND
Potenciómetro
3.3V (no usar 5V)
VCC
Potenciómetro
PA0 (canal ADC)
OUT
PA8 (PWM) con
LED ánodo (+)
resistencia
LED cátodo (-) GND

2. Configuración en STM32CubeIDE
Paso a paso:
A. Habilitar ADC
1. En la vista de configuración de pines, hacé clic en PA0.
2. Seleccioná “ADC1_IN0”
3. En la pestaña "Analog", activá ADC1
4. En “Configuration”, activá “Continuous Conversion Mode”

B. Habilitar PWM (Timer)


a. Hacé clic en PA8
b. Seleccioná “TIM1 Channel 1”
c. En la pestaña “Timers”, configurá:
d. Prescaler: 72 - 1 (para 1 MHz si estás usando HCLK de 72 MHz)
e. Counter Period: 999 (para obtener un PWM de 1 kHz)

uint32_t adc_val;

int main(void)
{
HAL_ADC_Start(&hadc1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);

while (1)
{
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
adc_val = HAL_ADC_GetValue(&hadc1);
uint32_t pwm_val = adc_val * 1000 / 4095;
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1,
pwm_val);
}
}

¿Qué hace este código?


1. Inicializa los periféricos: ADC y Timer.
2. Lee el valor del potenciómetro.
3. Convierte ese valor a un duty cycle.
4. Actualiza el PWM para controlar el brillo del LED.

 #include "main.h"
 #include "adc.h"
 #include "tim.h"

Estos incluyen los archivos necesarios para usar el ADC, el Timer y


funciones de la HAL (Hardware Abstraction Layer).
No tenés que declarar hadc1 ni htim1, ya están en esos headers.

 uint32_t adc_val;
Variable global que guarda el valor leído desde el potenciómetro (0
a 4095 en resolución de 12 bits).

 int main(void)
Función principal que se ejecuta al encender la placa.

 HAL_Init();
Inicializa la HAL, necesaria antes de usar cualquier periférico.

 SystemClock_Config();
Configura el reloj del sistema (esto lo genera CubeIDE, no lo tocás).
Asegura que todo funcione con la frecuencia correcta (ej: 72 MHz).

1. MX_GPIO_Init();
2. MX_ADC1_Init();
3. MX_TIM1_Init();
Inicializa los periféricos:

a) GPIO: entradas/salidas generales


b) ADC1: para leer el potenciómetro
c) TIM1: para generar la señal PWM

 HAL_ADC_Start(&hadc1);
Inicia el ADC. Esto permite comenzar a hacer conversiones
analógicas a digitales.
&hadc1: Significa que active el ADC1

 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
Inicia el Timer en modo PWM, en el canal 1 (PA8 por ejemplo).
 &htim1: Significa que active TIM1
 TIM_CHANNEL_1: Significa que en Channel1 puse PWM
Generation CH1
 NOTA: Hay 4 timers, TIM1, TIM2, TIM3 y TIM4. Cada timer
tiene 4 canales, CH1, CH2, CH3 y CH4.
 while (1)
Bucle infinito. Todo lo que esté dentro se repite para siempre.

 HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
Espera hasta que el ADC termine de convertir. HAL_MAX_DELAY
significa que esperará el tiempo que sea necesario.

 adc_val = HAL_ADC_GetValue(&hadc1);
Obtiene el valor del ADC. Este valor representa el voltaje del
potenciómetro convertido a digital (0 a 4095).

 uint32_t pwm_val = adc_val * 1000 / 4095;


Convierte el valor del potenciómetro en un valor para el PWM:

1. Si el ADC da 0, el PWM es 0 (LED apagado).


2. Si el ADC da 4095, el PWM es 1000 (LED al máximo brillo).

1000 es el valor máximo del período del timer (lo configuraste como
999 en CubeIDE).

 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1,
pwm_val);
Cambia el duty cycle del PWM. Esto controla el brillo del LED:

1. Bajo valor → poca energía → LED tenue.


2. Alto valor → más energía → LED brillante.

Resumen:
 hadc1 → Es el ADC que está leyendo el voltaje del
potenciómetro.
 htim1 → Es el Timer que está generando el PWM para el LED.
 TIM_CHANNEL_1 → Es el canal del Timer que estás usando para
el PWM.
 Cada timer tiene cuatro canales que pueden ser configurados en
modo PWM. Podemos controlar hasta 16 servomotores en forma
independiente utilizando este microcontrolador.
Ciclo de trabajo

Ejemplo:
 Querés un PWM de 1 kHz
 El reloj principal del microcontrolador (f_CPU) es de 72 MHz =
72,000,000 Hz. Esto es demasiado rápido para muchas
aplicaciones, como generar PWM para un LED (porque queremos
frecuencias de 1 kHz, no 72 millones).

Un Timer en STM32 cuenta números hacia arriba, y cuando llega a


un valor (el Counter Period), vuelve a 0 y empieza de nuevo.
Esto genera una señal periódica: el PWM.

Querés una frecuencia de timer de 1 MHz (más manejable)

 Prescaler = (72MHz / 1MHz) - 1 = 72 - 1 = 71

 Period = ( 1MHz / 1KHz ) - 1 = 1000 - 1 = 999

¿Qué pasa si cambiás estos valores?

 Si Prescaler es más alto, el PWM es más lento.


 Si Period es más alto, tenés más resolución para controlar el
duty (más suavidad en el brillo).
 Si hacés ambas cosas muy altas, la frecuencia baja demasiado y
el LED empieza a parpadear.

Control de Servomotor SG90 con


STM32F103C8T6 (Blue Pill) usando
STM32CubeIDE
A continuación te mostraré cómo controlar un servomotor SG90
para que se mueva de 0 a 180 grados usando el STM32F103C8T6
(Blue Pill) con STM32CubeIDE.

Pasos para implementar


1. Configuración en STM32CubeIDE

1. Configura un timer para generar PWM:


a) Ve a la pestaña "Timers"
b) Selecciona un timer (por ejemplo, TIM2 o TIM3)
c) Activa un canal (por ejemplo, CH1) en modo "PWM
Generation"
d) Configura el prescaler y el periodo para obtener una
frecuencia de 50Hz (20ms de periodo):
e) Clock del timer: 72MHz (si usas el reloj por defecto)
f) Prescaler: 71 (72MHz / (71+1) = 1MHz)
g) Counter Period (ARR): 19999 (1MHz / 20000 = 50Hz)
h) Pulse: Valor inicial para 0 grados (aproximadamente 500)

Codigo
#define MIN_PULSE 500 // 0 grados
#define MAX_PULSE 2500 // 180 grados
#define MID_PULSE 1500 // 90 grados

void setServoAngle(uint32_t angle) {


if(angle > 180) angle = 180;
uint32_t pulse = MIN_PULSE + (angle * (MAX_PULSE - MIN_PULSE) /
180);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pulse);
}
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
setServoAngle(0);
HAL_Delay(1000);
while (1) {
for(int angle = 0; angle <= 180; angle += 10) {
setServoAngle(angle);
HAL_Delay(100);
}
HAL_Delay(500);
for(int angle = 180; angle >= 0; angle -= 10) {
setServoAngle(angle);
HAL_Delay(100);
}

HAL_Delay(500);
}
}

Análisis línea por línea completo del


código
1. Includes y definiciones
 #include "main.h" // Configuraciones principales generadas
por STM32CubeIDE
 #include "tim.h" // Configuración del Timer (PWM)
 #include "gpio.h" // Configuración de pines GPIO

Propósito: Incluir archivos de cabecera generados por


STM32CubeIDE con configuraciones de hardware.

 #define MIN_PULSE 500 // Ancho de pulso para 0° (500µs)


 #define MAX_PULSE 2500 // Ancho de pulso para 180° (2500µs)
 #define MID_PULSE 1500 // Ancho de pulso para 90° (1500µs)

Valores clave:

 Los servos SG90 usan pulsos de 500µs a 2500µs para moverse


de 0° a 180°.

2. Función setServoAngle
void setServoAngle(uint32_t angle) {

if(angle > 180) angle = 180;

uint32_t pulse = MIN_PULSE + (angle * (MAX_PULSE - MIN_PULSE) /


180);

__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pulse);

 Esta función mueve el servo al ángulo especificado (de 0° a


180°).
 Si el ángulo es mayor a 180°, lo limita a 180.
 Calcula el valor del ciclo de trabajo (duty cycle)
correspondiente.
 Usa __HAL_TIM_SET_COMPARE para actualizar el PWM en el
canal 1 del temporizador 2.
¿De donde sale?:
uint32_t pulse = MIN_PULSE + (angle * (MAX_PULSE - MIN_PULSE) /
180);

Mapeo:
Es una conversión lineal que mapea ángulos (0° a 180°) a pulsos
PWM (500µs a 2500µs) que entiende el servomotor SG90. Aquí su
procedencia:

Rango del Servomotor


Los servos estándar como el SG90 responden a pulsos PWM con:

 500µs (0.5ms): Posición extrema a 0°

 1500µs (1.5ms): Posición central a 90°


 2500µs (2.5ms): Posición extrema a 180°

Por tanto:

 MIN_PULSE = 500 (para 0°)

 MAX_PULSE = 2500 (para 180°)

3. Función main()
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // Inicia el PWM en
el canal 1 de TIM2

setServoAngle(0); // Mueve el servo a 0°


HAL_Delay(1000); // Espera 1 segundo

4. Bucle infinito (while(1))

while (1)
{
for(int angle = 0; angle <= 180; angle += 10) {
setServoAngle(angle); // Incrementa de 10 en 10 hasta
180°
HAL_Delay(100); // Espera 100 ms entre cada
movimiento
}

HAL_Delay(500); // Espera 500 ms

for(int angle = 180; angle >= 0; angle -= 10) {


setServoAngle(angle); // Decrementa de 10 en 10 hasta

HAL_Delay(100);
}

HAL_Delay(500);
}
}

 En el while(1) se hace que el servo:

 Suba de 0° a 180° en pasos de 10°.

 Baje de 180° a 0° también en pasos de 10°.

 Entre cada paso hay una pausa de 100 ms.


 Al final de cada subida o bajada, se espera 500 ms.

Mover un servomotor sg90 con un


potenciometro con ChatGPT
1. Configuración en STM32CubeIDE
A. Configurar ADC (para el potenciómetro)
[Link] sugerido: PA0 (canal ADC1_IN0).
[Link] la pestaña "Pinout & Configuration", selecciona PA0 y
elige:
 ADC1_IN0
[Link] Configuration > ADC1:
 Modo: Single Conversion
 Scan: desactivado
 Continuous Mode: activado
 Data Alignment: Right
 Channel: IN0
 Sampling Time: 71.5 cycles (puede variar)
B. Configurar PWM (para el servo)
[Link] sugerido: PA8 (Timer1, Channel 1).
[Link] Pinout, selecciona PA8 y ponlo como:
 TIM1_CH1
[Link] Configuration > TIM1:
 Prescaler: 71
 Counter Period (ARR): 19999
(esto da un período de 20ms → 50Hz, frecuencia típica de
servos)
Cálculo:
 Timer clock = 72 MHz / (71 + 1) = 1 MHz
 1 MHz / 20000 = 50 Hz
4. PWM Mode: PWM Generation CH1
[Link] PWM en Channel 1

2. Conexiones
Componen Conexión a la
te Blue Pill
Potencióme VCC (3.3V), GND,
tro PA0
VCC (5V), GND,
Servo SG90
PA8

3.Código de ejemplo (main.c)


#include "main.h"
void SystemClock_Config(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
MX_TIM1_Init();

HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); // Inicia PWM


HAL_ADC_Start(&hadc1); // Inicia ADC
while (1)
{ // Lectura de pin del potenciometro
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
uint32_t adc_value = HAL_ADC_GetValue(&hadc1); // 0 - 4095
// Mapear a rango de servo: 500 - 2500 µs
uint32_t pulse = 500 + ((adc_value * 2000) / 4095);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, pulse);
HAL_Delay(10); // pequeño retardo
}
}
¿Qué significa uintXX_t?
Son tipos de tamaño fijo, definidos en C (en <stdint.h>) para que
siempre usen exactamente cierta cantidad de bits, sin importar el
compilador o arquitectura.
Formato general:
 u = unsigned (sin signo)
 int = entero
 XX = cantidad de bits: 8, 16, 32 o 64
 _t = "type" (solo convención de nombre)

Ejemplo: El ADC de la Blue Pill da un valor entre 0 y 4095 (12


bits):
uint16_t adc_val; // 16 bits, más que suficiente
adc_val = HAL_ADC_GetValue(&hadc1);

✅El ADC es de 12 bits, pero usamos uint16_t (16 bits)


 El ADC del STM32F103 da valores entre 0 y 4095 (eso es 2¹² - 1).
 El tipo más chico que puede almacenar hasta 4095 es:
uint8_t → 0 a 255 ❌ no sirve
uint16_t → 0 a 65.535 ✅ perfecto
Por eso usamos uint16_t o uint32_t aunque el ADC sea de
solo 12 bits.

¿Te conviene usar uint16_t o uint32_t?


Opció
Ventaja ¿Conviene?
n
uint32
100% compatible con HAL ✅ Sí
_t
uint16 Ahorra un poco de RAM (2 ✅ Si sos muy preciso con
_t bytes) recursos
ADC (Conversor Analógico Digital)
En la STM32F103C8T6 (Blue Pill):
 Tiene un ADC de 12 bits
 Eso significa: los valores de salida van de 0 a 4095
 Los tipos de datos adecuados para almacenarlo:

 uint16_t (suficiente)
 uint32_t (lo que devuelve HAL)

Timers: En la Blue Pill, los timers tienen diferente


cantidad de bits según cuál uses:

Tim
Bits Notas
er
Avanzado (PWM,
TIM1 16
break, etc)
TIM2 32 ✅ General-purpose
TIM3 16 General-purpose
TIM4 16 General-purpose

 TIM2 es de 32 bits
 TIM1, TIM3, TIM4 son de 16 bits

Protocolos de comunicación
Los protocolos de comunicación son conjuntos de reglas y
estándares que permiten que dos o más dispositivos electrónicos
intercambien información de manera ordenada y eficiente. Definen
cómo se transmiten, reciben, interpretan y gestionan los datos entre
sistemas (como microcontroladores, sensores, computadoras, etc.).

1. Tipos principales de protocolos de comunicación


Se clasifican en dos categorías según el tipo de señal:

A. Protocolos seriales
Transfieren datos bit a bit por un solo cable (o par de cables).
Ideales para distancias cortas o medias.

 I2C: 2 cables (SDA, SCL), multimaestro, bajo consumo. Ejemplo:


LCD1602, sensores.
 SPI: 4+ cables (MISO, MOSI, SCK, SS), alta velocidad. Ejemplo:
pantallas TFT, memorias SD.
 UART: 2 cables (TX, RX), asíncrono, punto a punto. Ejemplo:
comunicación Arduino-PC vía USB.
 1-Wire: 1 cable, bajo costo. Ejemplo: sensores de temperatura
DS18B20.

B. Protocolos paralelos
Transfieren múltiples bits simultáneamente (por varios cables).
Usados en sistemas de alta velocidad pero complejos.
 GPIO paralelo: Común en impresoras antiguas o pantallas LCD
de 8 bits.

2. Características clave de un protocolo


Velocidad: Bits por segundo (bps). Ejemplo: I2C (400 kbps), SPI (10+
Mbps).

 Sincronización:

 Síncrono: Usa un reloj (ej. I2C, SPI).


 Asíncrono: Sin reloj, con bits de inicio/parada (ej. UART).
 Topología:

 Punto a punto: UART.


 Bus compartido: I2C, CAN.

 Direccionamiento: Identifica dispositivos en la red (ej.


direcciones I2C).

3. Ejemplos de aplicación
Protoco
Uso típico
lo

I2C Sensores (BME280), memorias EEPROM.

SPI Pantallas OLED, módulos WiFi (ESP8266).

Comunicación PC-Arduino, módulos


UART
Bluetooth.

Sistemas automotrices (red de


CAN
sensores).

Conexión de periféricos (teclado,


USB
mouse).

4. ¿Cómo elegir un protocolo?


Velocidad requerida: SPI para alta velocidad, I2C para bajo
consumo.
 Número de dispositivos: I2C admite muchos esclavos; UART es 1
a 1.
 Complejidad: SPI requiere más pines que I2C.
 Distancia: Para largas distancias, usa RS-485 o CAN.

5. Comparativa rápida
Protoco
Ventajas Desventajas
lo

Simple, pocos cables, Lento, direcciones


I2C
multidispositivo. limitadas.

SPI Muy rápido, full-duplex. Requiere más pines.

UART Flexible, sin reloj. Solo punto a punto.

Tanto Arduino como STM32 pueden utilizar el protocolo I2C, pero


hay diferencias clave en su implementación y configuración. Aquí te
lo explico detalladamente:

1. Arduino con I2C


Hardware:
 Los modelos clásicos (Uno, Nano, Mega) tienen pines dedicados
para I2C:

 SDA: A4 (o SDA en placas modernas).

 SCL: A5 (o SCL en placas modernas).

 En Arduino Mega, los pines son 20 (SDA) y 21 (SCL).

2. STM32 con I2C


Hardware:
 Los STM32 tienen múltiples interfaces I2C (ej. I2C1, I2C2).

 Los pines varían según el modelo (consulta el datasheet o


usa STM32CubeMX para asignarlos).
 Ejemplo en STM32F103 (Blue Pill):

 I2C1: PB6 (SCL), PB7 (SDA).


 Software:

 Opción 1: Usar HAL Library (con STM32CubeIDE):

3. Diferencias clave
Aspect
Arduino STM32
o

Pines Fijos (A4/A5 o SDA/SCL Configurables (múltiples


I2C dedicados). opciones).

Velocid Hasta 1 MHz (en algunos


Hasta 400 kHz (Fast Mode).
ad modelos).

Librería HAL/LL (más compleja, pero


Wire.h (sencilla).
s potente).

Flexibili Alta (DMA, interrupciones,


Limitada.
dad etc.).

Ejemplo Sencillo para LCD 16x2 con I2C en STM32


Blue Pill
1. En el Pinout & Configuration:

 Click en el pin PB6 → elegir I2C1_SCL.

 Click en el pin PB7 → elegir I2C1_SDA.

2. En el panel izquierdo, ir a Connectivity → I2C1:

 Mode: I2C

 I2C Speed Mode: Standard Mode (100 kHz)

Conexiones físicas:
LCD I2C Blue Pill (STM32F103C8)
-------------------------------
GND GND
VCC Fuente lm317 a 4.5V y mover potenciometro modulo I2C.
SDA PB7
SCL PB6

Cuando di a guardar todo se genero este codigo


#include "main.h"
#include "i2c.h"
#include "gpio.h"
void SystemClock_Config(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
while (1)
{
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.[Link] = RCC_PLL_ON;
RCC_OscInitStruct.[Link] = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.[Link] = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
Codigo final
#include "main.h"
#include "i2c.h"
#include "gpio.h"
#include <string.h>

void SystemClock_Config(void);

// Configuración LCD
#define LCD_ADDR (0x27 << 1) // Dirección I2C (0x27 o 0x3F,
ajustar según módulo)
#define LCD_COLS 16
#define LCD_ROWS 2

// Prototipos de funciones LCD


void LCD_SendCmd(uint8_t cmd);
void LCD_SendData(uint8_t data);
void LCD_Init(void);
void LCD_SendString(char *str);
void LCD_SetCursor(uint8_t row, uint8_t col);
void LCD_Clear(void);

int main(void)
{

HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();

// Inicializar LCD
LCD_Init();

// Escribir mensajes
LCD_SendString("PICHULO");
LCD_SetCursor(1, 0);
LCD_SendString("LCD 16x2 I2C");

while (1)
{
// Puedes agregar más funcionalidad aquí
HAL_Delay(1000);
}

// Función para enviar comandos al LCD


void LCD_SendCmd(uint8_t cmd)
{
uint8_t data_u, data_l;
uint8_t data_t[4];

data_u = (cmd & 0xF0);


data_l = ((cmd << 4) & 0xF0);

data_t[0] = data_u | 0x0C; // en=1, rs=0


data_t[1] = data_u | 0x08; // en=0, rs=0
data_t[2] = data_l | 0x0C; // en=1, rs=0
data_t[3] = data_l | 0x08; // en=0, rs=0
HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDR, data_t, 4, 100);
HAL_Delay(1);
}

// Función para enviar datos al LCD


void LCD_SendData(uint8_t data)
{
uint8_t data_u, data_l;
uint8_t data_t[4];

data_u = (data & 0xF0);


data_l = ((data << 4) & 0xF0);

data_t[0] = data_u | 0x0D; // en=1, rs=1


data_t[1] = data_u | 0x09; // en=0, rs=1
data_t[2] = data_l | 0x0D; // en=1, rs=1
data_t[3] = data_l | 0x09; // en=0, rs=1

HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDR, data_t, 4, 100);


HAL_Delay(1);
}

// Inicialización del LCD


void LCD_Init(void)
{
HAL_Delay(50); // Espera inicial

// Secuencia de inicialización
LCD_SendCmd(0x03);
HAL_Delay(5);
LCD_SendCmd(0x03);
HAL_Delay(1);
LCD_SendCmd(0x03);
HAL_Delay(10);
LCD_SendCmd(0x02); // Modo 4 bits

// Configuración del display


LCD_SendCmd(0x28); // 2 líneas, fuente 5x8
LCD_SendCmd(0x0C); // Display on, cursor off
LCD_SendCmd(0x06); // Incremento automático del cursor
LCD_Clear(); // Limpiar display
}

// Enviar cadena de texto al LCD


void LCD_SendString(char *str)
{
while(*str) LCD_SendData(*str++);
}
// Posicionar cursor
void LCD_SetCursor(uint8_t row, uint8_t col)
{
uint8_t address;
col &= 0x0F; // Máximo 15 columnas

if(row == 0) address = 0x80 + col;


else address = 0xC0 + col;

LCD_SendCmd(address);
}

// Limpiar display
void LCD_Clear(void)
{
LCD_SendCmd(0x01);
HAL_Delay(2);
}

void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.[Link] = RCC_PLL_ON;
RCC_OscInitStruct.[Link] = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.[Link] = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|
RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|
RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) !
= HAL_OK)
{
Error_Handler();
}
}

void Error_Handler(void)
{

__disable_irq();
while (1)
{
}

#ifdef USE_FULL_ASSERT

void assert_failed(uint8_t *file, uint32_t line)


{

}
#endif

Codigo explicado
1. Estructura General
El código controla un LCD 16x2 con interfaz I2C desde una placa
STM32 Blue Pill (STM32F103C8). Usa el hardware I2C de la STM32 y
el protocolo HD44780 (estándar para LCDs alfanuméricos).

2. Headers e Inicialización

#include "main.h" // Configuración generada por


STM32CubeIDE
#include "i2c.h" // Driver I2C de HAL
#include "gpio.h" // Configuración de GPIO
#include <string.h> // Para manejo de cadenas
main.h: Contiene configuraciones de hardware generadas por
STM32CubeIDE.
i2c.h/gpio.h: Drivers para I2C y GPIO.

3. Configuración del LCD

#define LCD_ADDR (0x27 << 1) // Dirección I2C (típica para


PCF8574)
#define LCD_COLS 16 // Columnas del LCD
#define LCD_ROWS 2 // Filas del LCD
LCD_ADDR: La dirección I2C del módulo (normalmente 0x27 o
0x3F). Se desplaza << 1 porque la HAL de STM32 maneja
direcciones de 7 bits.
LCD_COLS/ROWS: Dimensiones del display.

4. Función Principal (main())

int main(void) {
HAL_Init(); // Inicializa HAL (Hardware Abstraction Layer)
SystemClock_Config(); // Configura el reloj a 72 MHz (típico en
Blue Pill)
MX_GPIO_Init(); // Inicializa GPIOs
MX_I2C1_Init(); // Inicializa I2C1 (PB6=SCL, PB7=SDA)
LCD_Init(); // Inicializa el LCD
LCD_SendString("PICHULO"); // Escribe en la primera línea
LCD_SetCursor(1, 0); // Mueve cursor a la segunda línea
LCD_SendString("LCD 16x2 I2C"); // Escribe en la segunda línea

while (1) {
HAL_Delay(1000); // Bucle infinito
}
}
Secuencia de inicio: Inicializa hardware, configura el LCD, y muestra
un mensaje.

while(1): Mantiene el programa activo.

5. Funciones Clave del LCD


a) LCD_SendCmd(uint8_t cmd)
Envía un comando al LCD (ej: limpiar pantalla, mover cursor).

void LCD_SendCmd(uint8_t cmd) {


uint8_t data_u, data_l;
uint8_t data_t[4];

data_u = (cmd & 0xF0); // Parte alta del nibble (4 bits)


data_l = ((cmd << 4) & 0xF0); // Parte baja del nibble

// Secuencia de enable (EN) para enviar el comando en 4 bits:


data_t[0] = data_u | 0x0C; // EN=1, RS=0 (RS=0 indica
comando)
data_t[1] = data_u | 0x08; // EN=0, RS=0
data_t[2] = data_l | 0x0C; // EN=1, RS=0
data_t[3] = data_l | 0x08; // EN=0, RS=0

HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDR, data_t, 4, 100);


HAL_Delay(1);
}
Protocolo de 4 bits: El LCD HD44780 usa 2 nibbles (4 bits) para
enviar 1 byte.

RS=0: Indica que es un comando (no datos).

b) LCD_SendData(uint8_t data)
Envía datos (caracteres) al LCD.

void LCD_SendData(uint8_t data) {


// Similar a LCD_SendCmd, pero con RS=1 (para datos)
data_t[0] = data_u | 0x0D; // EN=1, RS=1 (RS=1 indica dato)
data_t[1] = data_u | 0x09; // EN=0, RS=1
// ... (resto igual que LCD_SendCmd)
}
RS=1: Indica que es un dato (carácter).

c) LCD_Init()
Inicializa el LCD en modo 4 bits.

void LCD_Init(void) {
HAL_Delay(50); // Espera estabilización

// Secuencia de inicialización (obligatoria para modo 4 bits)


LCD_SendCmd(0x03); HAL_Delay(5);
LCD_SendCmd(0x03); HAL_Delay(1);
LCD_SendCmd(0x03); HAL_Delay(10);
LCD_SendCmd(0x02); // Modo 4 bits

// Configuración final:
LCD_SendCmd(0x28); // 2 líneas, matriz 5x8
LCD_SendCmd(0x0C); // Display ON, cursor OFF
LCD_SendCmd(0x06); // Auto-incremento del cursor
LCD_Clear(); // Limpia pantalla
}
Secuencia 0x03 repetida: Obligatoria para sincronizar el LCD en
modo 4 bits.

0x28: Configura el LCD para 2 líneas y caracteres 5x8 píxeles.

0x0C: Enciende el display sin cursor.


d) Otras Funciones
LCD_SendString(char *str): Envía una cadena de caracteres.

LCD_SetCursor(row, col): Mueve el cursor a una posición específica.

LCD_Clear(): Limpia la pantalla (0x01).

6. Configuración del Reloj (SystemClock_Config)

void SystemClock_Config(void) {
// Configura el HSE (oscilador externo) y PLL para 72 MHz
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.[Link] = RCC_PLL_MUL9; // 8 MHz * 9 =
72 MHz
// ...
}
HSE: Cristal externo de 8 MHz (típico en Blue Pill).

PLL: Multiplica la frecuencia para lograr 72 MHz (máximo para


STM32F103).

Aquí te detallo qué partes corresponden a


librerías/frameworks y qué partes son código
personalizado:
 Partes que dependen de librerías/frameworks:

[Link]ías HAL de STM32CubeIDE (manejo de hardware):

#include "main.h" // Configuraciones generadas por CubeIDE


#include "i2c.h" // Driver I2C de STM32 HAL
#include "gpio.h" // Driver GPIO de STM32 HAL
Estas son proporcionadas por STM32CubeIDE y manejan el
hardware a bajo nivel.

[Link]ía estándar de C:

#include <string.h> // Para funciones de manejo de cadenas


(aunque no se usa directamente)

 Partes de código personalizado (implementación específica para


el LCD):
Todas estas funciones fueron escritas manualmente para controlar
el LCD:
[Link]ón del LCD:

#define LCD_ADDR (0x27 << 1) // Dirección I2C


#define LCD_COLS 16 // Columnas
#define LCD_ROWS 2 // Filas

[Link] principales del LCD:

void LCD_SendCmd(uint8_t cmd); // Envía comandos al LCD


void LCD_SendData(uint8_t data); // Envía datos (caracteres)
void LCD_Init(void); // Inicialización
void LCD_SendString(char *str); // Escribe una cadena
void LCD_SetCursor(uint8_t row, uint8_t col); // Posiciona
cursor
void LCD_Clear(void); // Limpia la pantalla

[Link] de inicialización (en LCD_Init()):

LCD_SendCmd(0x03); HAL_Delay(5); // Reset en modo 8 bits


LCD_SendCmd(0x03); HAL_Delay(1);
LCD_SendCmd(0x03); HAL_Delay(10);
LCD_SendCmd(0x02); // Cambia a modo 4 bits

 Partes mixtas (HAL + lógica personalizada):

[Link]ón I2C (usa HAL pero con lógica personalizada):

HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDR, data_t, 4,


100);
La función es de HAL, pero la implementación de data_t y los
nibbles es personalizada.

2.SystemClock_Config():

Generada automáticamente por STM32CubeIDE, pero ajustada para


el reloj de 72 MHz.

También podría gustarte