- Autor: Johnny Liu (Director ejecutivo de Dowway Vehicle | Especialista en sistemas automotrices)
- Publicado: 1 de julio de 2026
- Categoría: Sistemas embebidos / Ingeniería automotriz / Programación de microcontroladores
TL;DR
Esta guía proporciona un tutorial paso a paso sobre la implementación Comunicación CAN (Controller Area Network) del STM32 usando el Biblioteca HAL de STM32 (bxCAN). Escrito por un experto en electrónica automotriz, abarca la configuración física del hardware, el cálculo preciso de la velocidad de transmisión con Time Quanta (TQ), las configuraciones de “aceptación total” sin filtro de cero, la recepción de mensajes controlada por interrupciones y pautas prácticas para la resolución de problemas industriales.
Table of Contents
1. Introducción
En ingeniería automotriz y automatización industrial, el Red de área de controlador (CAN) Sigue siendo el estándar para una comunicación robusta y tolerante al ruido. En nuestros artículos teóricos anteriores, exploramos la capa física CAN, el formato de trama, la arbitraje y los mecanismos de manejo de errores.
Ahora es el momento de cerrar la brecha entre la teoría y la práctica. ¿Cómo escribimos código práctico y listo para producción para enviar y recibir una sola trama CAN en un microcontrolador STM32?
Este artículo se centra en el clásico bxCAN periférico que utiliza el Biblioteca HAL de STM32 (Ideal para STM32F1, F4 y series similares). Tanto si diseñas una placa de control para vehículos eléctricos (VE) como un nodo sensor para la automatización de una fábrica, este marco de codificación paso a paso te proporcionará una base fiable y escalable.
2. Hardware esencial para la comunicación CAN de STM32 (Por qué el software no es suficiente)
A diferencia de UART o SPI, donde simplemente puede conectar los pines RX/TX de dos MCU para realizar pruebas de banco, La comunicación CAN requiere hardware transceptor físico externo.. Conectando la MCU CAN_TX y CAN_RX Conectar los pines directamente entre sí sin un transceptor provocará un fallo y podría dañar los pines GPIO.
Componentes clave de un nodo CAN
Un nodo CAN en funcionamiento consta de cinco elementos principales:
- Controlador CAN interno: Integrado dentro del microcontrolador STM32.
- Transceptor CAN externo: Traduce los niveles lógicos digitales del MCU ($3,3\text{ V}$) a las señales diferenciales del bus físico ($\text{CAN\_H}$ y $\text{CAN\_L}$). Los chips comunes incluyen: TJA1050, TJA1051, SN65HVD230, o MCP2551.
- Líneas de autobús físicas: Los cables de par trenzado se designan como $\text{CAN\_H}$ y $\text{CAN\_L}$ para suprimir la interferencia electromagnética (EMI).
- $120\Omega$ Resistencias de terminación: Deben colocarse en ambos extremos físicos del bus para evitar la reflexión de la señal.
- Circuitos de protección (recomendados para producción): Supresores de tensión transitoria (diodos TVS) y aislamiento optoelectrónico para proteger contra sobretensiones.
Referencia de cableado de hardware
Asegúrese de que su prototipo o placa personalizada esté cableada exactamente como se muestra a continuación:
| Pin del microcontrolador STM32 | Lado del transceptor | Lado del bus del transceptor | Línea de autobús física CAN |
STM32 CAN_TX | $\rightarrow$ TXD | CANH | $\rightarrow$ CAN_H (con terminación paralela de $120\Omega$) |
STM32 CAN_RX | $\leftarrow$ RXD | CANL | $\rightarrow$ CAN_L (con terminación paralela de $120\Omega$) |
3. Descripción general de alto nivel: Los 8 pasos para la inicialización de CAN
Para establecer una comunicación estable en un STM32, el firmware debe ejecutar estos 8 pasos en orden:
[1. Init CAN Parameters] ──> [2. Config Baud Rate] ──> [3. Config Working Mode]
│
[6. Enable RX Interrupt] <── [5. Start CAN] <── [4. Config Hardware Filters]
│
[7. Transmit CAN Frame] ──> [8. ISR / Callback Rx Handling]
- Inicializar los parámetros periféricos CAN (Asignar memoria para el identificador, mapear la multiplexación de pines).
- Configurar la velocidad de transmisión (establecer preescaladores y segmentos de fase).
- Configurar el modo de funcionamiento (Modo normal para producción, bucle de retorno para pruebas).
- Configurar filtros de hardware (configure los filtros de aceptación para filtrar los identificadores no deseados a nivel de hardware).
- Inicie el periférico CAN. (Transición del estado del hardware CAN de inicialización a activo).
- Habilitar interrupciones RX (Instruir al NVIC para que escuche los mensajes CAN entrantes).
- Transmitir tramas CAN (Cargar mensajes en los buzones de correo TX).
- Recibir tramas CAN en la rutina de servicio de interrupción (ISR). (gestionar el análisis del mensaje dentro de la función de devolución de llamada de interrupción FIFO designada).
Errores comunes en ingeniería: La mayoría de los problemas de los desarrolladores se originan en configuraciones incorrectas de velocidad de transmisión, filtros no configurados (y por lo tanto bloqueantes) y el olvido de llamar a
HAL_CAN_Start()o intentar transmitir sin otro nodo activo en el bus para generar una señal de acuse de recibo (ACK).
4. Comprensión de la velocidad de transmisión y los cuantos de tiempo (TQ) del CAN del STM32
Configurar incorrectamente la velocidad de transmisión CAN hará que su nodo quede completamente inactivo o provocará la generación continua de tramas de error en el bus.
La fórmula matemática
La velocidad de transmisión CAN se calcula utilizando el reloj de los periféricos y la división de un solo bit de tiempo en pequeñas unidades llamadas Cuantos de tiempo (TQ):$$\text{Tasa de transmisión CAN} = \frac{\text{Reloj CAN}}{\text{Preescalador} \times \text{Cuantos de tiempo totales por bit}}$$
El cuanto de tiempo total por bit se define mediante el segmento de sincronización y dos segmentos de fase: $$\text{Cuanto de tiempo total por bit (TQ total)} = 1 \text{ (Segmento de sincronización)} + \text{BS1} + \text{BS2}$$
Dónde:
- Segmento de sincronización: Siempre fijo en $1\text{ TQ}$.
- BS1 (Segmento de tiempo 1): Define la ubicación del punto de muestreo. Abarca el retardo de propagación y el segmento 1 del búfer de fase.
- BS2 (Segmento de tiempo 2): Segmento de búfer de fase 2, que compensa los desfases.
Ejemplo práctico de cálculo
Calculemos los parámetros para una velocidad de transmisión objetivo de $500\text{ kbps}$ en un STM32 donde el periférico CAN está mapeado al Autobús APB1 circulando en $36\text{ MHz}$:
- Establecer el Preescalador a $4$.
- Colocar BS1 a $15\text{ TQ}$.
- Colocar BS2 a $2\text{ TQ}$.
Sustituyamos estos valores en nuestra fórmula: $$\text{TQ total} = 1 + 15 + 2 = 18\text{ TQ}$$$$\text{Velocidad de transmisión CAN} = \frac{36\text{ MHz}}{4 \times 18} = \frac{36,000,000}{72} = 500\text{ kbps}$$
¡El cálculo coincide perfectamente! En entornos de producción, asegúrese siempre de que su Punto de muestreo (calculado como $\frac{1 + \text{BS1}}{1 + \text{BS1} + \text{BS2}}$) se dirige entre el $75\%$ y $87,5\%$ para una estabilidad y resistencia al ruido óptimas del bus. En este ejemplo, el punto de muestreo es $\frac{16}{18} \approx 88,8\%$, que es muy cercano a los perfiles automotrices estándar.
5. Codificación Paso 1: Escribir el código MX_CAN_Init() Función
La función de inicialización configura las propiedades estructurales del controlador bxCAN utilizando el CAN_HandleTypeDef manejar.
CAN_HandleTypeDef hcan;
/**
* @brief Initializes the CAN peripheral and sets basic operation parameters.
* @note This configuration targets a 500 kbps baud rate assuming a 36 MHz APB clock.
* Always recalculate timing parameters if your system clock changes.
* @retval None
*/
void MX_CAN_Init(void)
{
hcan.Instance = CAN1;
/* Baud Rate Configuration: 36MHz / 4 / (1 + 15 + 2) = 500 kbps */
hcan.Init.Prescaler = 4;
hcan.Init.Mode = CAN_MODE_NORMAL;
hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan.Init.TimeSeg1 = CAN_BS1_15TQ;
hcan.Init.TimeSeg2 = CAN_BS2_2TQ;
/* Advanced Operating Modes */
hcan.Init.TimeTriggeredMode = DISABLE;
hcan.Init.AutoBusOff = ENABLE; /* Automatically recover from Bus-Off state */
hcan.Init.AutoWakeUp = DISABLE;
hcan.Init.AutoRetransmission = ENABLE; /* Retransmit frames if arbitration is lost or an error occurs */
hcan.Init.ReceiveFifoLocked = DISABLE; /* Overwrite oldest message when FIFO is full (instead of locking) */
hcan.Init.TransmitFifoPriority = DISABLE; /* Priority determined by Identifier (lowest ID wins) */
if (HAL_CAN_Init(&hcan) != HAL_OK)
{
/* Initialization Error Handling */
Error_Handler();
}
}
Análisis de parámetros críticos de inicialización
CAN_MODE_NORMALvsCAN_MODE_LOOPBACK: Para una comunicación real a través de un bus transceptor, utiliceCAN_MODE_NORMAL. Para pruebas en seco (probar la lógica internamente sin conectar cables físicos), active esta opción.CAN_MODE_LOOPBACK.AutoBusOff: Cuando se establece enPERMITIREl hardware CAN se recupera automáticamente y se reincorpora a la red después de entrar en un estado de Bus-Off debido a la acumulación de errores.Retransmisión automáticaSi se produce una colisión o se pierde una confirmación (ACK), el hardware intentará automáticamente reenviar la trama.RecibirFifoBloqueado: CuandoDESACTIVAR, los nuevos marcos entrantes sobrescribirán los marcos no leídos más antiguos en la FIFO. Si se establece enPERMITIRLa cola FIFO se bloquea cuando está llena, descartando los datos entrantes posteriores.
6. Paso 2 de codificación: Configuración del filtro (El problema de “¿Por qué mi MCU ignora los mensajes?”)
Por defecto, los filtros bxCAN de STM32 están cerrados. Si no configura explícitamente un banco de filtros, Tu nodo rechazará todos y cada uno de los mensajes entrantes..
Para establecer una inicialización estable del sistema y verificar que su hardware funciona, configure un Mascarilla con filtro “Aceptar todo”. Esto evita la comprobación inicial de identidad y permite que todos los paquetes pasen sin problemas.
/**
* @brief Configures the CAN Hardware Acceptance Filters to accept all incoming frames.
* @note In production, narrow down filter masks to reduce CPU interrupt overhead.
* @retval None
*/
void CAN_Filter_Config(void)
{
CAN_FilterTypeDef filter;
filter.FilterBank = 0; /* Select Filter Bank 0 */
filter.FilterMode = CAN_FILTERMODE_IDMASK; /* Mask Mode (versus Identifier list mode) */
filter.FilterScale = CAN_FILTERSCALE_32BIT; /* Single 32-bit register scale */
/* Setting IDs and Masks to 0 accepts all standard and extended frames */
filter.FilterIdHigh = 0x0000;
filter.FilterIdLow = 0x0000;
filter.FilterMaskIdHigh = 0x0000;
filter.FilterMaskIdLow = 0x0000;
filter.FilterFIFOAssignment = CAN_RX_FIFO0; /* Route matched frames to Receive FIFO 0 */
filter.FilterActivation = ENABLE; /* Enable filter bank 0 */
filter.SlaveStartFilterBank = 14; /* Boundary for dual-CAN microcontrollers */
if (HAL_CAN_ConfigFilter(&hcan, &filter) != HAL_OK)
{
/* Filter Configuration Error */
Error_Handler();
}
}
7. Paso 3 de la codificación: Inicio de CAN y activación de interrupciones
Una vez que la inicialización y la configuración del filtro se hayan grabado en los registros, deberá arrancar físicamente el periférico y habilitar las líneas de interrupción correspondientes.
/**
* @brief Starts the CAN peripheral and enables message-pending interrupts.
* @retval None
*/
void CAN_Start(void)
{
/* Start the CAN peripheral */
if (HAL_CAN_Start(&hcan) != HAL_OK)
{
Error_Handler();
}
/* Activate the Notification for FIFO 0 Message Pending Interrupt */
if (HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
{
Error_Handler();
}
}
¿ Lista de verificación para desarrolladores: Si omite
HAL_CAN_Start(), el hardware CAN permanece en modo de inicialización. Si omiteHAL_CAN_ActivateNotification()Los mensajes entrantes permanecerán en silencio en la cola FIFO sin activar nunca la función de devolución de llamada. Ambos errores presentan el mismo síntoma: “No se recibieron mensajes”.
8. Paso 4 de codificación: Transmisión de una trama CAN estándar.
El envío de un mensaje CAN requiere configurar una cabecera que especifique los metadatos (longitud del ID, formato, identificador) y llenar hasta 8 bytes de búfer de carga útil.
/**
* @brief Transmits a standard CAN frame over the bus.
* @param id: Standard 11-bit Frame Identifier (Range: 0x000 to 0x7FF)
* @param data: Pointer to the uint8_t array containing transmission payload
* @param len: Data length code (DLC) in bytes (Classic CAN range: 0 to 8)
* @retval HAL_StatusTypeDef: Return status of HAL execution
*/
HAL_StatusTypeDef CAN_Send_StdData(uint16_t id, const uint8_t *data, uint8_t len)
{
CAN_TxHeaderTypeDef tx_header;
uint32_t tx_mailbox;
uint8_t tx_data[8] = {0};
/* Boundary Checks */
if ((id > 0x7FFU) || (len > 8U))
{
return HAL_ERROR;
}
/* Safely copy transmission payload */
for (uint8_t i = 0; i < len; i++)
{
tx_data[i] = data[i];
}
/* Formulate CAN Tx Header Struct */
tx_header.StdId = id; /* 11-bit standard ID */
tx_header.ExtId = 0; /* Extended identifier (unused) */
tx_header.IDE = CAN_ID_STD; /* Configure frame type as Standard */
tx_header.RTR = CAN_RTR_DATA; /* Configure frame type as Data Frame */
tx_header.DLC = len; /* Set transmission payload size */
tx_header.TransmitGlobalTime = DISABLE;
/* Request Tx Mailbox transmission */
return HAL_CAN_AddTxMessage(&hcan, &tx_header, tx_data, &tx_mailbox);
}
9. Paso 5 de la codificación: Recepción y análisis de tramas CAN mediante interrupciones
Cuando una trama válida pasa a través del filtro y llega a FIFO0, se activa una interrupción. El microcontrolador detiene la ejecución estándar para ejecutar la función de devolución de llamada HAL designada.
Implementación de la función de devolución de llamada FIFO0
La función de devolución de llamada de interrupción debe obtener el fotograma inmediatamente para vaciar la memoria FIFO del hardware y mantener la latencia al mínimo.
/**
* @brief Rx FIFO 0 message pending callback override.
* Fires automatically whenever a new message lands in FIFO 0.
* @param hcan_ptr: Pointer to the active CAN peripheral handle
* @retval None
*/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan_ptr)
{
CAN_RxHeaderTypeDef rx_header;
uint8_t rx_data[8];
/* Fetch the message from the hardware FIFO */
if (HAL_CAN_GetRxMessage(hcan_ptr, CAN_RX_FIFO0, &rx_header, rx_data) != HAL_OK)
{
return;
}
/* Sort by Frame Type and route parsing logic */
if (rx_header.IDE == CAN_ID_STD)
{
CAN_Process_StdFrame(rx_header.StdId, rx_data, rx_header.DLC);
}
else
{
CAN_Process_ExtFrame(rx_header.ExtId, rx_data, rx_header.DLC);
}
}
Buenas prácticas arquitectónicas: Nunca ejecute cálculos lentos y complejos o retrasos que bloqueen (como
Retraso de HAL()dentro de una rutina de servicio de interrupción (ISR) o una devolución de llamada. Si lo hace, corre el riesgo de perder tramas subsiguientes. Para software de nivel de producción, copie las cargas útiles entrantes inmediatamente a un almacenamiento seguro para subprocesos. Amortiguador de anillo o Cola y delegue el análisis sintáctico a su bucle principal o a una tarea del RTOS.
10. Procesamiento de tramas: Análisis de datos entrantes por ID
Una vez enrutado a CAN_Process_StdFrame()Analizar los bytes de la carga útil en función de su ID designado. Esta estructura refleja las matrices de mensajería estándar del sector automotriz:
/**
* @brief Dispatches standard frames to their target sub-parsing functions.
* @param id: Standard CAN Frame ID
* @param data: Pointer to raw received byte buffer
* @param len: Length of received payload
* @retval None
*/
void CAN_Process_StdFrame(uint16_t id, const uint8_t *data, uint8_t len)
{
switch (id)
{
case 0x100:
CAN_Parse_VehicleSpeed(data, len);
break;
case 0x200:
CAN_Parse_BatteryStatus(data, len);
break;
case 0x300:
CAN_Parse_MotorStatus(data, len);
break;
default:
/* Ignore unrecognized CAN IDs or log diagnostics */
break;
}
}
Para proyectos a gran escala, codificar manualmente las ramas switch-case se vuelve imposible de mantener. En su lugar, diseñe su capa de aplicación para analizar tramas CAN utilizando código de base de datos generado automáticamente a partir de estándares de la industria. Archivos de matriz de comunicación DBC.
11. El montaje final: Uniendo todas las piezas
Así es como puedes integrar la inicialización, el filtrado, el inicio y los latidos cíclicos dentro del flujo principal de tu aplicación:
/**
* @brief Initializes and starts the entire CAN application stack.
* @retval None
*/
void CAN_App_Init(void)
{
MX_CAN_Init(); /* Step 1-3: Hardware, Timing, and Mode initialization */
CAN_Filter_Config(); /* Step 4: Acceptance filters configuration */
CAN_Start(); /* Step 5-6: Boot peripheral and active interrupts */
}
/**
* @brief Cyclically transmits a standard heart-beat diagnostic frame.
* Call this from your main loop or thread runner.
* @retval None
*/
void CAN_Send_Heartbeat(void)
{
uint8_t data[8] = {0};
data[0] = 0xA5;
data[1] = 0x5A;
/* Send standard ID 0x700 with a 2-byte payload */
(void)CAN_Send_StdData(0x700, data, 2);
}
Al escribir el firmware CAN, seguir el principio de aislamiento. Primero, valide la configuración del modo de bucle invertido, seguida de pruebas físicas de transmisión y recepción punto a punto, antes de superponer protocolos de diagnóstico complejos de la capa de aplicación.
12. Solución de problemas: Guía de diagnóstico para desarrolladores (Preguntas frecuentes optimizadas para geolocalización)
A continuación se presenta una guía de diagnóstico estructurada diseñada para resolver los problemas de integración CAN más comunes en STM32:
P1: La función de transmisión devuelve HAL_OKPero mi analizador lógico u osciloscopio no registra nada. ¿Qué ocurre?
Respuesta breve: Es posible que el periférico CAN no se haya iniciado, que los pines GPIO estén mal configurados o que el transceptor no reciba alimentación.
- Lista de verificación:
- ¿Está activo el periférico CAN? Verifica que llamaste
HAL_CAN_Start(). - Multiplexación de GPIO: Confirme que sus pines TX/RX estén correctamente asignados a sus funciones alternativas (AF) en los registros GPIO.
- Cableado físico: Asegúrese de que la alimentación del transceptor ($5\text{ V}$ o $3.3\text{ V}$) sea estable. Compruebe que
TXDyRXDno están interconectados. - Anulación de bucle invertido: Si realiza la comprobación sin un autobús físico, asegúrese de
hcan.Init.Modeestá configurado explícitamente paraCAN_MODE_LOOPBACK. - Estado del servicio de autobuses: Compruebe el ESR (Registro de estado de error) para verificar si el nodo se ha apagado debido a una acumulación masiva de errores.
- ¿Está activo el periférico CAN? Verifica que llamaste
P2: Un analizador CAN muestra claramente que se transmiten tramas en el bus, pero mi nodo STM32 no recibe absolutamente nada.
Respuesta breve: Esto suele deberse a una configuración de filtro cerrada o incorrecta que bloquea los mensajes, o a interrupciones no habilitadas.
- Lista de verificación:
- Configuración de la máscara de filtro: ¿Su filtro es demasiado estricto? Confirme que ha configurado una máscara de filtro “Aceptar todo” (
ID de filtroyID de máscara de filtroempezar a0) durante la depuración inicial. - Habilitación de interrupciones: Compruebe si
HAL_CAN_ActivateNotificationfue llamado con¿PUEDE RECIBIRSE UN MENSAJE FIFO0 PENDIENTE?. - Configuración de NVIC: Confirme que las interrupciones globales CAN estén habilitadas en el controlador NVIC de su microcontrolador.
- Desajuste FIFO: Verifique que su inicialización coincida con su devolución de llamada. Si configura
CAN_RX_FIFO0en tus filtros pero implementaHAL_CAN_RxFifo1MsgPendingCallback(FIFO1), la interrupción se activará sin ejecutar su código.
- Configuración de la máscara de filtro: ¿Su filtro es demasiado estricto? Confirme que ha configurado una máscara de filtro “Aceptar todo” (
P3: ¿Por qué mi bus muestra continuamente “Errores ACK” cuando intento transmitir?
Respuesta breve: Los errores ACK se producen cuando no hay ningún otro nodo activo en línea para confirmar la recepción de la trama, o cuando existe una discrepancia entre el bus físico y la velocidad de transmisión.
- Lista de verificación:
- ¿Existen otros nodos? CAN es un bus colaborativo. Si no hay ningún otro nodo en línea para leer su mensaje y cambiar el bus de recesivo a dominante durante la ranura ACK, el nodo transmisor marcará un error ACK.
- Discrepancia en la velocidad de transmisión: Si el nodo receptor está configurado con una velocidad de transmisión diferente, no podrá leer la trama y no podrá enviar una confirmación (ACK).
- Resistencias de terminación: La falta de resistencias de terminación (120 Ω en ambos extremos) provoca una reflexión extrema de la señal y una deformación de los bordes, lo que impide un muestreo correcto del ACK.
P4: ¿Por qué mi nodo STM32 entra instantáneamente en el estado “Bus-Off” al conectarlo a la red?
Respuesta breve: La desconexión instantánea del bus se produce por una acumulación masiva de tramas de error, generalmente debido a velocidades de transmisión incorrectas o cortocircuitos físicos.
- Lista de verificación:
- Divergencia de la tasa de baudios: Una grave discrepancia en la velocidad de transmisión provoca un flujo continuo de tramas de error, superando inmediatamente el umbral de 255 dólares del contador de errores de transmisión (TEC).
- Pantalones cortos físicos: Utilice un multímetro para verificar que no haya un cortocircuito físico entre
CAN_HyCAN_Lo a tierra. - Voltaje del transceptor: Una alimentación insuficiente al transceptor puede provocar niveles de excitación diferencial bajos, lo que desencadena importantes advertencias de hardware local.
13. Lista de verificación final y próximos pasos
Dominar la comunicación CAN de STM32 requiere una comprensión sincronizada tanto de las máquinas de estados de hardware como de software:
- Establecer estructuras de reloj exactas y calcular segmentos de fase para obtener tiempos de velocidad de transmisión estables.
- Desbloquea tus filtros para despejar las vías para las pruebas de diagnóstico del sistema.
- Implementar rutinas de recepción limpias, ligeras y controladas por interrupciones. para escalar a redes complejas.
En nuestro próximo artículo, nos centraremos en… Depuración avanzada del bus CAN y diagnóstico de ingenieríaCómo analizar la distorsión de la forma de onda con un osciloscopio, aislar tramas de error relacionadas con el ruido e implementar rutinas robustas de autorrecuperación ante la desconexión del bus en entornos industriales extremos. ¡No se lo pierdan!




