Índice

Protocolo Bus CAN y CAN-FD en Sistemas Embebidos: Capa Física, Arbitraje e Implementación en C


Introducción al Bus CAN y CAN-FD en Sistemas Embebidos

En la ingeniería mecatrónica moderna, el bus CAN (Controller Area Network) es el estándar de facto para la comunicación robusta y en tiempo real entre múltiples microcontroladores y Unidades de Control Electrónico (ECU). Diseñado originalmente por Robert Bosch GmbH en 1983, el protocolo destaca por su topología de bus lineal, alta inmunidad al ruido electromagnético y un mecanismo de arbitraje basado en prioridades sin colisiones destructivas.

Con la evolución de la mecatrónica del automóvil, la robótica móvil y la automatización industrial, el bus CAN tradicional (CAN 2.0B) ha comenzado a sufrir limitaciones de ancho de banda. Como respuesta, surgió CAN-FD (CAN with Flexible Data-Rate), permitiendo cargas útiles mayores y velocidades de transmisión significativamente más altas en la fase de datos.


Capa Física: Transceptores, Tensiones Diferenciales y Estados del Bus

La capa física del bus CAN (definida bajo el estándar ISO 11898-2) utiliza transmisión de señales diferenciales sobre un par trenzado no apantallado o apantallado (CAN_HCAN\_H y CAN_LCAN\_L). Esto confiere al bus una altísima inmunidad al ruido de modo común.

Estados Lógicos y Voltajes Diferenciales

A diferencia de los protocolos serie tradicionales (como UART o SPI) donde los niveles lógicos corresponden a voltajes respecto a masa (GNDGND), CAN define sus bits como estados Dominantes (0 lógico) y Recesivos (1 lógico):

  1. Estado Recesivo (1 lógico):

    • Los transceptores mantienen las líneas CAN_HCAN\_H y CAN_LCAN\_L a un nivel de voltaje flotante o sesgado cerca de 2.5 V2.5\text{ V}.
    • Tensión diferencial: VDIFF=VCAN_H−VCAN_L≈0 VV_{DIFF} = V_{CAN\_H} - V_{CAN\_L} \approx 0\text{ V}.
    • Si ningún nodo transmite o todos transmiten un bit recesivo, el bus permanece en este estado.
  2. Estado Dominante (0 lógico):

    • El transceptor conmuta activamente CAN_HCAN\_H a aproximadamente 3.5 V3.5\text{ V} y CAN_LCAN\_L a 1.5 V1.5\text{ V}.
    • Tensión diferencial: VDIFF=VCAN_H−VCAN_L≈2.0 VV_{DIFF} = V_{CAN\_H} - V_{CAN\_L} \approx 2.0\text{ V}.
    • El estado dominante prevalece sobre el recesivo: si un nodo transmite un ‘0’ y otro transmite un ‘1’, la tensión diferencial en el bus físico será dominante (0).

Resistencias de Terminación

Para evitar reflexiones de señal en los extremos de la línea de transmisión, el bus debe terminarse en sus dos extremos físicos con resistencias de 120 Ω120\ \Omega, lo que otorga al bus una impedancia equivalente de 60 Ω60\ \Omega:

REQ=120 Ω×120 Ω120 Ω+120 Ω=60 ΩR_{EQ} = \frac{120\ \Omega \times 120\ \Omega}{120\ \Omega + 120\ \Omega} = 60\ \Omega

El Transceptor Físico

El controlador CAN (periférico interno del microcontrolador) maneja señales de nivel de lógica digital (p. ej. RXD y TXD a 3.3V/5V). El transceptor físico (como el TJA1050, MCP2551 o SN65HVD230) convierte estas señales digitales a niveles diferenciales CAN_HCAN\_H / CAN_LCAN\_L y viceversa.


Arbitraje de Tramas Bit a Bit No Destructivo

El bus CAN no utiliza direccionamiento de nodos (máster/slave); utiliza un esquema CSMA/CR (Carrier Sense Multiple Access with Collision Resolution) basado en el Identificador (ID) de la Trama.

Mecanismo de Arbitraje

  1. Cuando el bus está inactivo, cualquier nodo puede iniciar una transmisión.
  2. Cada nodo transmite el Identificador de su trama bit a bit, comenzando por el MSB (bit más significativo).
  3. Al mismo tiempo que un nodo conmuta la línea TX, lee el estado real del bus a través de su línea RX.
  4. Si un nodo transmite un bit Recesivo (1) pero lee un bit Dominante (0) en el bus, detecta que otro nodo con mayor prioridad está transmitiendo.
  5. El nodo pierde el arbitraje, desconecta inmediatamente su etapa de salida y pasa a modo receptor sin interrumpir la transmisión del nodo ganador.

Debido a que el bit ‘0’ es dominante, los identificadores numéricamente menores tienen mayor prioridad (por ejemplo, el ID 0x010 gana a 0x020).


Evolución a CAN-FD (Flexible Data-Rate)

CAN-FD (ISO 11898-1:2015) soluciona dos grandes limitaciones de CAN 2.0B:

  • Carga útil máxima: Pasa de 8 bytes a 64 bytes por trama.
  • Tasa de transferencia (Bit Rate): Introduce la conmutación de velocidad (BRS - Bit Rate Switch).

Comparativa entre CAN Clásico y CAN-FD

ParámetroCAN 2.0B ClásicoCAN-FD
Carga Útil (Payload)0 a 8 Bytes0, 8, 12, 16, 20, 24, 32, 48, 64 Bytes
Velocidad Nominal (Arbitraje)Hasta 1 MbpsHasta 1 Mbps
Velocidad de Datos (Payload)Máx 1 MbpsHasta 5 - 8 Mbps
Tamaño de CRC15 bits17 bits (<=16 B) o 21 bits (>16 B)

Implementación Práctica en C: Estructura y Manejo de Tramas CAN-FD

A continuación se presenta una implementación de firmware en C orientada a la creación, codificación y procesamiento de tramas CAN 2.0B y CAN-FD.

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

// Banderas para caracterizar la trama CAN / CAN-FD
#define CAN_FLAGS_EXTENDED_ID  (1U << 0)
#define CAN_FLAGS_RTR          (1U << 1)
#define CAN_FLAGS_FD_BRS       (1U << 2) // Bit Rate Switch (solo CAN-FD)

// Estructura de trama CAN-FD universal
typedef struct {
    uint32_t id;             // Identificador de 11 o 29 bits
    uint8_t  dlc;            // Data Length Code (0-15)
    uint8_t  data_length;    // Longitud real en bytes (0-64)
    uint8_t  flags;          // Máscara de banderas
    uint8_t  payload[64];    // Carga útil de hasta 64 bytes
} can_fd_frame_t;

// Conversión de DLC a longitud de datos real en bytes segun norma CAN-FD
static uint8_t dlc_to_bytes(uint8_t dlc) {
    static const uint8_t dlc_table[] = {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64
    };
    if (dlc > 15) return 0;
    return dlc_table[dlc];
}

// Constructor de trama CAN-FD
bool can_fd_create_frame(can_fd_frame_t *frame, uint32_t id, const uint8_t *data, uint8_t len, bool is_fd, bool brs) {
    if (!frame || len > 64) {
        return false;
    }

    frame->id = id;
    frame->flags = 0;

    if (id > 0x7FF) {
        frame->flags |= CAN_FLAGS_EXTENDED_ID;
    }

    if (is_fd && brs) {
        frame->flags |= CAN_FLAGS_FD_BRS;
    }

    // Mapear longitud real al DLC correspondiente
    uint8_t dlc = 0;
    while (dlc < 15 && dlc_to_bytes(dlc) < len) {
        dlc++;
    }
    
    frame->dlc = dlc;
    frame->data_length = dlc_to_bytes(dlc);
    memset(frame->payload, 0, sizeof(frame->payload));
    memcpy(frame->payload, data, len);

    return true;
}

// Procesamiento de trama recibida en el driver
void can_process_rx_frame(const can_fd_frame_t *frame) {
    printf("--- Trama CAN Recibida ---\n");
    printf("ID: 0x%08X [%s]\n", frame->id, 
           (frame->flags & CAN_FLAGS_EXTENDED_ID) ? "Ext (29-bit)" : "Std (11-bit)");
    printf("Tipo: %s\n", (frame->flags & CAN_FLAGS_FD_BRS) ? "CAN-FD (BRS Activo)" : "CAN 2.0B / CAN-FD Clásico");
    printf("Longitud de Datos: %u Bytes (DLC: %u)\n", frame->data_length, frame->dlc);
    
    printf("Payload: ");
    for (uint8_t i = 0; i < frame->data_length; i++) {
        printf("%02X ", frame->payload[i]);
    }
    printf("\n---------------------------\n");
}

int main(void) {
    can_fd_frame_t tx_frame;
    uint8_t sensor_data[12] = {0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04, 0xAA, 0xBB, 0xCC, 0xDD};

    // Crear una trama CAN-FD con 12 bytes y conmutación de velocidad activa
    if (can_fd_create_frame(&tx_frame, 0x123, sensor_data, sizeof(sensor_data), true, true)) {
        can_process_rx_frame(&tx_frame);
    }

    return 0;
}

Conclusiones y Consideraciones de Diseño

  1. Topología de red: Se debe mantener una topología estrictamente en bus lineal. Las derivaciones (stubs) deben ser lo más cortas posible (idealmente <0.3 m<0.3\text{ m} a 1 Mbps1\text{ Mbps}) para evitar distorsiones del punto de muestreo (sample point).
  2. Uso Mecatrónico: El bus CAN debe reservarse para comandos de control crítico (p. ej. consignas de torque, estados de seguridad e interrupciones) gracias a su arbitraje determinista, mientras que protocolos como CAN-FD permiten transmitir bloques de datos más pesados como estados completos de robots o telemetría multi-eje.