← Índice del curso

Microcontroladores · Tema 39

Taller: motor de corriente continua, relé y paso a paso

Tres actuadores, tres maneras de moverse. El motor de corriente continua gira libre y se regula con PWM; el relé conecta y desconecta con un clic y no perdona los cambios frecuentes; el motor paso a paso avanza pasos exactos siguiendo una secuencia de bobinas. Los comandamos desde el puerto serie con un intérprete de texto, usando los periféricos del curso: temporizadores, PWM, UART e interrupciones.

01 · Punto de partida

Qué vamos a construir

Un pequeño banco de actuadores controlado desde la computadora. En el monitor serie se escriben comandos de una letra y un número:

Comandos del banco.
ComandoEfecto
M 200Motor de CC a +200 de 255 (hacia adelante); M -255 a fondo hacia atrás; M 0 lo suelta
R 1 / R 0Cierra o abre el relé (respetando un tiempo mínimo entre maniobras)
S 1024 / S -512Mueve el motor paso a paso esa cantidad de pasos, hacia adelante o hacia atrás
V 300Fija la velocidad del paso a paso en pasos por segundo
?Informa el estado de los tres

Aplica casi todo lo anterior: la PWM del tema 25, la base de tiempo del tema 24, el intérprete de comandos del tema 32 y la etapa de potencia del tema 38 (puente H, diodos de rueda libre, fuente separada). Las decisiones de este taller tienen que ver con cómo se comporta cada actuador: los tres tienen un detalle que un programa ingenuo ignora.

02 · Repartir los recursos

El diseño: un periférico por actuador

Asignación de pines y periféricos.
FunciónPinesPeriféricoPor qué
Velocidad del motor de CC (ENA)PD6 (OC0A, pin 6)Timer0, Fast PWM de 8 bits, 62,5 kHzEl hardware genera la PWM sin carga para la CPU; por encima de 20 kHz, el motor no silba
Sentido del motor (IN1, IN2)PD5 y PD4 (pines 5 y 4)GPIODos señales lentas
Relé (activo en bajo)PD7 (pin 7)GPIOCambia pocas veces
Paso a paso (IN1 a IN4)PB0 a PB3 (pines 8 a 11)Timer1 en CTCEl temporizador marca el ritmo de los pasos con exactitud, aunque el programa esté ocupado
Base de tiempo (1 ms)—Timer2 en CTCRampas y tiempos mínimos del motor y del relé
ComandosPD0 y PD1USART0, 9600 baudiosIntérprete de texto (tema 32)

Es la idea del tema 26: un temporizador por tarea. La PWM del motor y el ritmo del paso a paso no dependen del lazo principal: seguirán funcionando mientras el programa interpreta un comando. El lazo principal solo lee la UART y reacciona.

Los tres actuadores tienen un «comportamiento oculto» que cada paso resuelve:

  • El motor de CC consume entre 10 y 20 veces más al arrancar que al girar, y no arranca con PWM bajas (zona muerta). Se resuelve con una rampa y un valor mínimo de arranque.
  • El relé se desgasta con cada maniobra. Se resuelve con un tiempo mínimo entre cambios y, en un lazo de control, con histéresis.
  • El paso a paso pierde pasos si acelera bruscamente y se calienta si queda energizado. Se resuelve con una velocidad moderada (o una rampa) y soltando las bobinas al terminar.

03 · Paso 1

El cableado

Conexiones.
MóduloConexiónArduino o fuente
Puente H (TB6612)PWMA / ENAPin 6 (PD6)
Puente HAIN1 / IN1 y AIN2 / IN2Pin 5 (PD5) y pin 4 (PD4)
Puente HVCC lógico (STBY a VCC)5 V del Arduino
Puente HVM (potencia del motor)+ de la fuente de 6 V
Puente HGND− de la fuente y GND del Arduino
Motor de CCTerminalesSalidas A del puente H; 100 nF entre los terminales del motor
Módulo de reléIN, VCC, GNDPin 7 (PD7), 5 V, GND
ReléContactos COM y NOEn serie con la carga de 12 V y su fuente (nunca con la red)
Placa ULN2003IN1 a IN4Pines 8, 9, 10 y 11 (PB0 a PB3)
Placa ULN2003+ y −5 V de la fuente de potencia y su GND
Fuente de potenciaCerca del puente H470 µF entre + y −

Cuatro reglas del tema 38 aplican acá:

  • La masa de la fuente de potencia se une a la del Arduino en un solo punto. Sin ella, las señales de comando no tienen referencia.
  • Los motores no se alimentan del Arduino ni del USB. Un motor pequeño al arrancar pide más que lo que el USB entrega y el microcontrolador se reiniciaría.
  • El módulo del relé lleva transistor, diodo y optoacoplador; solo hay que verificar si es de activación en bajo (casi todos).
  • Los diodos de rueda libre ya vienen en la placa ULN2003 y en los puentes H integrados. Si armaras el puente con transistores sueltos, hay que agregarlos.

04 · Paso 2

El motor de corriente continua

La velocidad se fija con el ciclo de trabajo de la PWM y el sentido con los dos pines de dirección. Tres refinamientos hacen la diferencia entre un motor que funciona y uno que se porta bien:

  • Zona muerta. Por debajo de cierto ciclo de trabajo (10 a 20 % en un motor pequeño), la fuerza no vence la fricción y el motor solo zumba. Se resuelve «saltando» ese tramo: un valor de comando de 1 a 255 se mapea a PWM de PWM_MIN a 255.
  • Rampa. Un arranque a fondo consume la corriente de trabado (en el ejemplo, 2 A con un motor que gira libre con 100 mA). Cambiar el ciclo de trabajo de a poco reduce el pico de forma notable: con una rampa de 300 ms el máximo baja de 2 A a 0,43 A (simulación del programa de Python).
  • Inversión segura. Cambiar de sentido pasa siempre por 0: se baja la velocidad, se suelta el motor y recién después se invierte. Evita el golpe de corriente y, en un puente H discreto, el cortocircuito.
#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdint.h>

#define IN1_ALTO()  (PORTD |=  (1 << PD5))
#define IN1_BAJO()  (PORTD &= ~(1 << PD5))
#define IN2_ALTO()  (PORTD |=  (1 << PD4))
#define IN2_BAJO()  (PORTD &= ~(1 << PD4))
#define PWM_MIN     40            // ciclo de trabajo mínimo que hace girar al motor (se mide en el banco)
#define RAMPA       5             // cambio máximo del comando por tick de 10 ms: de 0 a 255 en 0,5 s

static int16_t motor_objetivo, motor_actual;           // −255 a +255

static void motor_init(void) {
    PORTD &= ~((1 << PD5) | (1 << PD4));               // primero el nivel...
    DDRD  |=  (1 << PD6) | (1 << PD5) | (1 << PD4);      // ...después la dirección
    TCCR0A = (1 << COM0A1) | (1 << WGM01) | (1 << WGM00);   // Fast PWM de 8 bits en OC0A
    TCCR0B = (1 << CS00);                            // sin divisor: 62,5 kHz
    OCR0A  = 0;
}

static void motor_hw(int16_t v) {                      // aplica un comando ya limitado
    if (v == 0) { OCR0A = 0; IN1_BAJO(); IN2_BAJO(); return; }     // giro libre
    uint8_t mag = (uint8_t)(v < 0 ? -v : v);
    OCR0A = (uint8_t)(PWM_MIN + ((uint16_t)mag * (255 - PWM_MIN)) / 255);   // salta la zona muerta
    if (v > 0) { IN2_BAJO(); IN1_ALTO(); } else { IN1_BAJO(); IN2_ALTO(); }
}

static void motor_tick(void) {                         // llamar cada 10 ms
    int16_t dest = motor_objetivo;
    if ((motor_actual > 0 && dest < 0) || (motor_actual < 0 && dest > 0)) dest = 0;   // pasa por cero
    if (dest > motor_actual + RAMPA)      dest = motor_actual + RAMPA;
    else if (dest < motor_actual - RAMPA) dest = motor_actual - RAMPA;
    if (dest != motor_actual) { motor_actual = dest; motor_hw(dest); }
}

El valor de PWM_MIN se averigua en el banco: se aumenta la PWM de a poco hasta que el eje empieza a girar con certeza. Depende del motor y de la tensión de la fuente. La primera herramienta del laboratorio da una estimación.

05 · Paso 3

El relé

El relé es el actuador más simple de programar y el más fácil de maltratar. Tres reglas:

  • El estado al encender. En el reinicio los pines son entradas en alta impedancia; en un módulo de activación en bajo, la entrada flotante puede disparar el relé. Se escribe primero el nivel «suelto» (alto) y recién después se configura el pin como salida (tema 38).
  • Un tiempo mínimo entre maniobras. Un relé de uso general soporta del orden de 100 000 maniobras con carga. Un programa que lo cambie 10 veces por segundo lo agota en horas. El programa impone un mínimo (por ejemplo 2 s).
  • Histéresis en el control. Si el relé gobierna una temperatura, no debe encender al bajar de 20,0 °C y apagar al subir de 20,0 °C: oscilaría con cualquier ruido. Se define una banda (encender bajo 19,5 °C y apagar sobre 20,5 °C): cuanto más ancha, menos maniobras (tercera herramienta).
#define RELE_ACTIVAR()  (PORTD &= ~(1 << PD7))     // módulo activo en bajo
#define RELE_SOLTAR()   (PORTD |=  (1 << PD7))
#define RELE_GAP_MS     2000UL                       // tiempo mínimo entre maniobras

static uint8_t  rele_estado;
static uint32_t rele_ultimo_ms;

static void rele_init(void) {
    PORTD |= (1 << PD7);                             // primero «suelto»...
    DDRD  |= (1 << PD7);                             // ...después como salida: no hay clic al encender
}

// Devuelve 1 si se aplicó el cambio, 0 si se ignoró por ser muy pronto
static uint8_t rele_pedir(uint8_t encender, uint32_t ahora_ms) {
    if (encender == rele_estado) return 1;
    if (ahora_ms - rele_ultimo_ms < RELE_GAP_MS) return 0;
    rele_estado = encender;
    rele_ultimo_ms = ahora_ms;
    if (encender) RELE_ACTIVAR(); else RELE_SOLTAR();
    return 1;
}

// Termostato con histéresis: se llama con la temperatura en centésimas de grado
static void termostato(int32_t temp_centi, int32_t consigna_centi, uint32_t ahora_ms) {
    const int32_t banda = 100;                         // ±0,5 °C
    if (temp_centi < consigna_centi - banda / 2)      rele_pedir(1, ahora_ms);
    else if (temp_centi > consigna_centi + banda / 2) rele_pedir(0, ahora_ms);
    // dentro de la banda no se hace nada
}

06 · Paso 4

El motor paso a paso

Un motor paso a paso mueve su eje en pasos discretos al energizar sus bobinas en secuencia. El 28BYJ-48 es unipolar de cuatro bobinas, con una caja reductora de unos 64:1; la placa ULN2003 (tema 38) maneja las bobinas. Tres secuencias:

Secuencias para las bobinas A, B, C y D (1 = energizada).
SecuenciaEstadosPasos por vuelta del eje de salida (28BYJ-48)Característica
Una bobina (onda)A, B, C, D2048Poco par, consume poco
Paso completo (dos bobinas)AB, BC, CD, DA2048Más par
Medio pasoA, AB, B, BC, C, CD, D, DA4096Doble resolución y movimiento más suave

En el programa, la secuencia de medio paso es un arreglo de ocho valores de 4 bits que se escriben en PB0 a PB3; avanzar es incrementar el índice (módulo 8) y retroceder, decrementarlo. Para que el ritmo sea exacto y no dependa del lazo principal, cada paso lo da la interrupción de comparación del Timer1 en modo CTC (tema 24).

static const uint8_t SEC[8] = { 0x01, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x09 };   // medio paso: A, AB, B, BC, C, CD, D, DA

static volatile uint8_t  paso_idx;                    // 0 a 7
static volatile int8_t   sentido = 1;                 // +1 o −1
static volatile uint32_t pendientes;                  // pasos que faltan
static volatile int32_t  posicion;                    // posición acumulada

static void stepper_init(void) {
    PORTB &= 0xF0;
    DDRB  |= 0x0F;                                    // PB0 a PB3 como salidas
}

ISR(TIMER1_COMPA_vect) {
    if (pendientes == 0) {                            // terminó: soltar bobinas y parar el temporizador
        TIMSK1 &= ~(1 << OCIE1A);
        PORTB &= 0xF0;                                // sin corriente: no se calienta (pero tampoco retiene)
        return;
    }
    paso_idx = (uint8_t)((paso_idx + sentido) & 7);
    PORTB = (PORTB & 0xF0) | SEC[paso_idx];
    posicion += sentido;
    pendientes--;
}

static uint8_t stepper_ocupado(void) { return (TIMSK1 & (1 << OCIE1A)) != 0; }

// Mueve 'pasos' (con signo) a 'hz' pasos por segundo (31 a 1000 con el divisor 8)
static void stepper_mover(int32_t pasos, uint16_t hz) {
    if (pasos == 0 || hz < 31 || stepper_ocupado()) return;
    sentido = pasos > 0 ? 1 : -1;
    pendientes = (uint32_t)(pasos > 0 ? pasos : -pasos);
    TCCR1A = 0;
    TCCR1B = (1 << WGM12) | (1 << CS11);             // CTC, divisor 8: cuenta de 0,5 µs
    OCR1A  = (uint16_t)(2000000UL / hz - 1);
    TCNT1  = 0;
    TIMSK1 = (1 << OCIE1A);
}

Con esa estructura el motor se mueve solo, mientras el programa atiende otras cosas. Algunas cuestiones prácticas:

  • Velocidad máxima. El 28BYJ-48 sin carga admite del orden de 500 a 1000 pasos por segundo; con un impulso inicial brusco pierde pasos. El valor de 300 pasos/s (unas 4,4 vueltas por minuto del eje) es seguro.
  • Rampa. Para velocidades mayores se parte de unos 100 pasos/s y se sube con aceleración constante (f = √(f₀² + 2·a·n)): para ir de 100 a 500 pasos/s con 2000 pasos/s² hacen falta 60 pasos y 0,2 s. En cada paso, la ISR recalcula OCR1A.
  • Posición sin sensor. El motor paso a paso trabaja en lazo abierto: se supone que siguió todos los pasos. Si se sobrecarga y pierde alguno, no hay forma de enterarse. Por eso se limita la carga y la aceleración.
  • Calentamiento. Mantener una bobina energizada consume corriente continua (retiene el eje pero calienta). Si no hace falta par de retención, se sueltan las bobinas al terminar, como en la ISR.
  • La relación real. La reducción del 28BYJ-48 es de unos 63,684:1, no exactamente 64:1: 4096 medios pasos mueven el eje un 0,5 % más que una vuelta. Para posicionar con precisión conviene medirla.

07 · Paso 5

El programa completo

La base de tiempo de 1 ms la da el Timer2 y las funciones de la UART salen del tema 32. El lazo principal lee la UART, ensambla líneas y ejecuta comandos; llama a motor_tick() cada 10 ms.

#include <stdlib.h>
#include <util/atomic.h>
/* uart_init, uart_texto, uart_leer, uart_decimal...: tema 32 */

static volatile uint32_t ms;
ISR(TIMER2_COMPA_vect) { ms++; }                       // base de tiempo de 1 ms

static uint32_t ahora_ms(void) {
    uint32_t t;
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { t = ms; }      // 32 bits: lectura protegida (tema 21)
    return t;
}

static uint16_t velocidad_pasos = 300;

static void ejecutar(const char *cmd) {
    long n = strtol(cmd + 1, NULL, 10);
    switch (cmd[0]) {
    case 'M':  motor_objetivo = (int16_t)(n > 255 ? 255 : n < -255 ? -255 : n);  uart_texto("OK\r\n");  break;
    case 'R':  uart_texto(rele_pedir(n != 0, ahora_ms()) ? "OK\r\n" : "ESPERAR\r\n"); break;
    case 'S':  if (stepper_ocupado()) uart_texto("OCUPADO\r\n"); else { stepper_mover(n, velocidad_pasos); uart_texto("OK\r\n"); } break;
    case 'V':  if (n >= 31 && n <= 600) { velocidad_pasos = (uint16_t)n; uart_texto("OK\r\n"); } else uart_texto("RANGO\r\n"); break;
    case '?':  /* imprimir motor_actual, rele_estado, posicion */ uart_texto("OK\r\n"); break;
    default:   uart_texto("ERROR\r\n");
    }
}

int main(void) {
    uart_init(); motor_init(); rele_init(); stepper_init();
    TCCR2A = (1 << WGM21);  TCCR2B = (1 << CS22);  OCR2A = 249;  TIMSK2 = (1 << OCIE2A);   // 1 ms
    sei();

    char    linea[16];
    uint8_t largo = 0;
    uint32_t proximo = ahora_ms() + 10;
    for (;;) {
        int16_t c;
        while ((c = uart_leer()) >= 0) {                // comandos de texto (tema 32)
            if (c == '\r') continue;
            if (c == '\n') { linea[largo] = '\0'; if (largo) ejecutar(linea); largo = 0; }
            else if (largo < sizeof linea - 1) linea[largo++] = (char)c;
        }
        if ((int32_t)(ahora_ms() - proximo) >= 0) {      // cada 10 ms
            proximo += 10;                               // suma el período: sin deriva (tema 26)
            motor_tick();
        }
    }
}

Observá que el lazo principal nunca espera: no hay _delay_ms. Los tres actuadores avanzan por sí solos (PWM del hardware, interrupción del paso a paso, marca de tiempo del motor y del relé), y el programa solo reacciona a eventos. Es la estructura cooperativa del tema 26 y de la que hablaremos en el tema 42.

08 · Paso 6

Verificar y medir

  1. Antes de conectar los motores, probá cada comando con un LED y un multímetro. Verificá que el relé no se active al encender o reiniciar la placa.
  2. La PWM. Con un osciloscopio o un analizador lógico (tema 35) en el pin 6, comprobá 62,5 kHz y el ciclo de trabajo de M 128 (casi 50 % más el desplazamiento de PWM_MIN).
  3. La zona muerta. Subí el comando de a uno (M 1, M 2...) y anotá desde dónde el eje gira con certeza. Con eso se ajusta PWM_MIN.
  4. La corriente. Con el multímetro en serie con la fuente, medí la corriente girando libre, y frená el eje con los dedos (un instante, nunca a fondo): la diferencia muestra por qué el driver se dimensiona para el trabado.
  5. El arranque. Observá la tensión de 5 V del Arduino mientras el motor arranca con M 255: con la rampa casi no cae; sin rampa, puede reiniciarse. Probá bajar RAMPA a 255 para ver la diferencia.
  6. Inversión. Pasá de M 200 a M -200: el motor debe frenar hasta cero y recién después invertir.
  7. El relé. Enviá R 1 y R 0 rápido: la segunda debe responder «ESPERAR».
  8. El paso a paso. Marcá el eje con una cinta, enviá S 4096 y contá las vueltas (debe dar una vuelta casi exacta, con un exceso de 0,5 % por la relación real). Enviá S -4096: debe volver a la marca. Probá V 600 y V 100 para ver cuándo pierde pasos.
  9. La temperatura. Tras un movimiento largo, las bobinas deben estar tibias, no calientes. Si lo están, revisá que se suelten al terminar.

09 · Analogía con programación

Simular motor, pasos y relé

Este programa calcula la constante del motor y el rendimiento a distintas cargas, simula el arranque con y sin rampa, recorre la secuencia de medio paso hacia adelante y hacia atrás, calcula una rampa de aceleración del motor paso a paso y simula un termostato con histéresis para contar las maniobras del relé.

Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.

import math

# 1) motor de corriente continua: modelo de primer orden (se ignora la inductancia)
V, RPM0, I0, R = 6.0, 10000.0, 0.100, 3.0
omega0 = RPM0 * 2 * math.pi / 60
ke = (V - I0 * R) / omega0
print(f"ke = {ke * 1000:.3f} mV*s/rad ; corriente de trabado a {V:.0f} V = {V / R:.2f} A ({V / R / I0:.0f} veces la de giro libre)")
for carga in (0.1, 0.3, 0.6, 1.0):
    omega = max(0.0, (V - carga * R) / ke)
    rpm = omega * 60 / (2 * math.pi)
    pm = ke * omega * (carga - I0)
    pe = V * carga
    print(f"  con {carga * 1000:4.0f} mA: {rpm:6.0f} rpm, rendimiento {pm / pe * 100:4.0f} %")

# arranque brusco frente a arranque con rampa de PWM
tau_m = 0.05                                   # constante de tiempo mecanica supuesta
J = tau_m * ke ** 2 / R
def arranque(rampa_s, dt=1e-4, T=1.0):
    omega, pico, t = 0.0, 0.0, 0.0
    while t < T:
        v = V if rampa_s == 0 else V * min(1.0, t / rampa_s)
        i = max(0.0, (v - ke * omega) / R)
        pico = max(pico, i)
        omega += ke * (i - I0) / J * dt if i > I0 else 0.0
        t += dt
    return pico
print()
for rampa in (0, 0.1, 0.3, 0.6):
    print(f"arranque con rampa de {rampa * 1000:3.0f} ms: corriente maxima {arranque(rampa):.2f} A")

# 2) motor paso a paso: secuencia de medio paso para la placa ULN2003 + 28BYJ-48
MEDIO_PASO = [0b0001, 0b0011, 0b0010, 0b0110, 0b0100, 0b1100, 0b1000, 0b1001]
indice, posicion = 0, 0
historial = []
for paso in range(4096):                         # una vuelta nominal en medios pasos
    indice = (indice + 1) % 8
    posicion += 1
    historial.append(MEDIO_PASO[indice])
print()
print("medio paso: primeros estados", [f"{b:04b}" for b in historial[:8]])
for _ in range(4096):
    indice = (indice - 1) % 8
    posicion -= 1
print(f"4096 pasos adelante y 4096 atras: indice final {indice}, posicion {posicion}")
print(f"pasos por vuelta nominales {32 * 64 * 2}; con la relacion real de 63,68395:1 son {64 * 63.68395:.2f} ({(4096 / (64 * 63.68395) - 1) * 100:+.2f} % de diferencia)")
print(f"giro de 90 grados a 5 rpm: {4096 / 4 / (5 * 4096 / 60):.2f} s, {5 * 4096 / 60:.0f} pasos por segundo")

# rampa de aceleracion constante: f(n) = sqrt(f0^2 + 2 a n)
f0, f1, a = 100.0, 500.0, 2000.0
n_rampa = int((f1 ** 2 - f0 ** 2) / (2 * a))
print()
print(f"rampa de {f0:.0f} a {f1:.0f} pasos/s con {a:.0f} pasos/s^2: {n_rampa} pasos y {(f1 - f0) / a:.2f} s")
for n in (0, 1, 10, 30, n_rampa):
    f = math.sqrt(f0 ** 2 + 2 * a * n)
    print(f"  paso {n:3d}: {f:6.1f} pasos/s  OCR1A (divisor 8) = {round(2e6 / f) - 1}")

# 3) termostato con histeresis: maniobras por hora del rele
def maniobras(banda, sube=0.2, baja=0.1, horas=6, dt=1.0):
    temp, encendido, cambios = 20.0, True, 0
    for _ in range(int(horas * 3600 / dt)):
        temp += (sube if encendido else -baja) * dt / 60
        if encendido and temp >= 20 + banda / 2:
            encendido, cambios = False, cambios + 1
        elif not encendido and temp <= 20 - banda / 2:
            encendido, cambios = True, cambios + 1
    return cambios / horas

print()
for banda in (0.1, 0.5, 1.0, 2.0):
    ops = maniobras(banda)
    formula = 2 * 3600 / (banda / 0.2 * 60 + banda / 0.1 * 60)
    print(f"banda de {banda:3.1f} C: {ops:5.1f} maniobras/h (formula {formula:5.1f}); {100000 / (ops * 24):6.0f} dias de vida de 100 000 maniobras")

Resultado: el motor de ejemplo (6 V, 3 Ω, 100 mA en vacío, 10 000 rpm) tiene una corriente de trabado de 2 A, veinte veces la de giro libre, y gira a 8947 rpm con 300 mA de carga con un rendimiento del 57 %. Arrancar a fondo pide 2 A de pico; con rampas de 100, 300 y 600 ms baja a 0,95, 0,43 y 0,27 A. La secuencia de medio paso vuelve al estado inicial tras 4096 pasos adelante y 4096 atrás. A 5 rpm el eje gira 90° en 3 s (341 pasos por segundo). Una rampa de 100 a 500 pasos/s con 2000 pasos/s² dura 60 pasos y 0,2 s. Y con una banda de 1 °C el termostato hace 8 maniobras por hora (521 días de vida de un relé de 100 000 maniobras); con 0,1 °C serían 77 por hora (54 días).

El modelo del motor ignora la inductancia y la fricción viscosa; el del termostato supone velocidades de calentamiento constantes. Sirven para entender las magnitudes, no para predecir un motor real.

10 · Exploración interactiva

Motor, pasos y relé

La primera herramienta modela un motor de CC: velocidad, corriente de trabado, rendimiento y zona muerta. La segunda calcula pasos, ritmo y el valor del temporizador de un motor paso a paso. La tercera estima la vida de un relé según la histéresis de un termostato.

Banco de actuadores

1. Motor de corriente continua

Constante del motor—
Velocidad—
Arranque y trabado—
Potencias—
Zona muerta—

2. Motor paso a paso

Pasos—
Ritmo—
Temporizador—
Duración—

3. Vida de un relé con histéresis

Ciclo—
Maniobras—
Vida del relé—

Recorrido guiado

  1. Con los valores del motor de ejemplo: la constante es 5,44 mV·s/rad, a plena marcha gira a 8947 rpm con 300 mA y rinde un 57 %. La corriente de trabado es 2 A, veinte veces la de vacío; la nota avisa que esa cifra dimensiona el driver.
  2. Bajá el ciclo de trabajo al 50 %: 3684 rpm. La velocidad baja más que proporcionalmente porque la carga sigue pidiendo corriente. Con 5 %, queda por debajo de la zona muerta de 7,5 %.
  3. Subí la carga a 2000 mA con el 100 %: el motor se detiene porque I·R iguala la tensión.
  4. En el paso a paso, 90° con 4096 pasos/vuelta son 1024 pasos. A 5 rpm: 341 pasos por segundo, OCR1A = 5858 con divisor 8 (un paso cada 2,93 ms) y 3 s. Subí a 10 rpm: 683 pasos/s supera los 500 que admite el motor y la nota advierte que perderá pasos.
  5. Con 1 rpm y 0,5 rpm el OCR1A sigue entrando en 16 bits con divisor 8 (29 296 y 58 593). Bajá a 0,2 rpm: pasa al divisor 64.
  6. En el relé, con 1 °C de banda hay 8 maniobras por hora y el relé dura 521 días. Bajá la banda a 0,1 °C: 80 maniobras por hora y 52 días.
Desafío: un robot con dos ruedas

Un robot con dos motores de CC y un puente H doble debe girar sobre su eje (una rueda adelante, la otra atrás). Con los comandos del banco, ¿qué valores se mandan? Un motor M 150 y el otro M -150. ¿Qué problema aparece al arrancar y cómo se resuelve? Ambos motores pidiendo corriente a la vez duplican el pico de arranque y la tensión de la batería cae: se escalonan las rampas (primero uno y luego el otro), se aumenta la capacidad del capacitor y se limita el cambio por tick. Y si las ruedas giran a velocidades un poco distintas con el mismo comando (cada motor es diferente), el robot se desvía: hace falta un sensor (encoder) y un lazo de control para compensar, algo que el comando solo no logra.

11 · Cierre del bloque 9

Lo aprendido en el bloque

  1. Medir bien es una cadena: acondicionar la señal, calibrar con referencias, filtrar según el tipo de ruido y validar antes de actuar; el presupuesto de errores dice qué mejorar.
  2. Un sensor digital da cuentas crudas que se convierten con los coeficientes de fábrica (BME280); un sensor de tiempo (HC-SR04) se lee con la captura de un temporizador.
  3. Un pin no es una fuente de potencia: el microcontrolador comanda transistores, MOSFET, relés o drivers, y la carga toma su energía de otra fuente con masa común.
  4. Toda bobina necesita un diodo de rueda libre, y un motor pide mucha más corriente al arrancar y al trabarse que al girar.
  5. Cada actuador tiene un comportamiento oculto: el motor de CC, la zona muerta y la rampa; el relé, el desgaste y la histéresis; el paso a paso, la pérdida de pasos y el calentamiento.
  6. Un temporizador por tarea mantiene los actuadores independientes del lazo principal, que solo reacciona a eventos sin bloquearse.

Con este taller termina el bloque 9. El último bloque trata de lo que hace que un sistema embebido sea utilizable de verdad: cuánta energía consume y cómo ahorrarla, cómo se estructura un programa que atiende varias cosas a la vez, y cómo se integran los conocimientos del curso en proyectos completos.

12 · Comprensión

Confusiones frecuentes

«El motor consume 100 mA, así que cualquier driver de 500 mA sirve»

Esa es la corriente de giro libre. Al arrancar o trabarse consume V/R, veinte veces más en el ejemplo. El driver y la fuente se eligen para el trabado, o se limita la corriente.

«Con 10 % de PWM, el motor gira al 10 % de la velocidad»

Por debajo de un umbral de ciclo de trabajo no gira (zona muerta), y por encima la velocidad no es proporcional: depende de la carga y de la tensión de la fuente.

«Un relé es solo un interruptor, puedo conmutarlo todo lo que quiera»

Tiene una vida limitada (del orden de 100 000 maniobras con carga) y cada cambio desgasta los contactos. Un lazo de control sin histéresis ni tiempo mínimo lo destruye en días.

«Un motor paso a paso siempre sabe en qué posición está»

Solo si no perdió pasos. En lazo abierto no hay forma de comprobarlo; una sobrecarga o una aceleración excesiva lo desfasan sin avisar.

«Dejo las bobinas energizadas para que no se mueva»

Retienen el eje, pero consumen corriente continua y calientan. Si no hace falta par de retención, se sueltan al terminar.

13 · Práctica guiada

Predecí antes de comprobar

1. La corriente de trabado

Un motor de 12 V tiene un bobinado de 8 Ω. ¿Qué corriente pide si se traba a plena tensión y con un ciclo de trabajo del 50 %?

Ver solución

A plena tensión: 12 / 8 = 1,5 A. Con PWM del 50 %, el promedio es 0,75 A, pero durante cada pulso circulan los 1,5 A (la inductancia los suaviza solo en parte): el driver debe soportar 1,5 A.

2. Pasos para un ángulo

Con el 28BYJ-48 en medio paso (4096 por vuelta), ¿cuántos pasos hacen falta para girar 270° y cuánto mueve cada paso?

Ver solución

270 / 360 × 4096 = 3072 pasos. Cada paso mueve 360 / 4096 = 0,0879°.

3. El temporizador

Para 300 pasos por segundo con el Timer1 en CTC y divisor 8 a 16 MHz, ¿cuánto vale OCR1A?

Ver solución

La cuenta es de 0,5 µs: 2 000 000 / 300 = 6667 cuentas por paso. OCR1A = 6667 − 1 = 6666. Un paso cada 3,33 ms.

4. Un termostato

Un termostato con banda de 0,5 °C, calentamiento de 0,2 °C/min y enfriamiento de 0,1 °C/min: ¿cuántas maniobras por hora hace y cuánto dura un relé de 100 000 maniobras?

Ver solución

Calienta 0,5 / 0,2 = 2,5 min y enfría 0,5 / 0,1 = 5 min: ciclo de 7,5 min, es decir 8 ciclos por hora y 16 maniobras por hora (384 por día). El relé dura 100 000 / 384 = 260 días. Con 2 °C de banda, el relé duraría casi tres años.

5. «Se reinicia al arrancar el motor»

Cada vez que se acciona el motor, el microcontrolador se reinicia. ¿Cuáles son las causas probables y las soluciones?

Ver solución

Causas: la corriente de arranque hace caer la tensión de la fuente compartida por debajo del umbral de brown-out (tema 13), o el pico de las escobillas se acopla al microcontrolador. Soluciones: fuente separada para el motor con masa común, capacitor de 470 µF en la fuente de potencia y de 100 nF en el motor, rampa de arranque y cables de potencia gruesos y cortos.

14 · Síntesis

Ideas para recordar

  • Motor de CC con puente H: la PWM del hardware fija la velocidad, dos pines el sentido; se salta la zona muerta, se arranca con rampa y se invierte pasando por cero.
  • La corriente de trabado (V/R) y de arranque, no la de giro libre, dimensiona el driver y la fuente.
  • Relé: iniciar el pin en el estado «suelto» antes de configurarlo como salida, imponer un tiempo mínimo entre maniobras y usar histéresis en los lazos de control.
  • Motor paso a paso: una secuencia de bobinas (medio paso: 8 estados) avanzada por la interrupción de un temporizador; la posición se supone, no se mide, así que se limitan velocidad y aceleración.
  • Un temporizador por tarea: el hardware sostiene la PWM y el ritmo de los pasos, y el lazo principal solo atiende comandos y marcas de tiempo, sin esperas.
  • Fuente de potencia separada con masa común, capacitores en la fuente y en el motor, y estados seguros al encender.

¿Listo para el bloque 10?

Antes de avanzar, calculá el OCR1A para un ritmo de pasos dado, explicá por qué se arranca un motor con rampa y qué hace la histéresis en un relé. Si podés justificar las tres respuestas, cerraste el bloque de sensores y actuadores.

En el próximo tema empieza el bloque de energía, estructura y proyectos con el consumo energético y los modos de bajo consumo: cuánta corriente gasta un microcontrolador, cómo dormirlo y despertarlo, y qué cambia en un dispositivo que funciona meses con una batería.