Error de tiempo de espera de inicialización CAN en STM32F4

Estoy usando una placa Nucleo STM32F446ZET6U (programada con CubeMx y Keil uVision 5) y trato de usar algunos de los ejemplos de ST para aprender sobre sus periféricos. Estoy atascado con el periférico CAN. El problema es que cuando el código entra en el

HAL_StatusTypeDef HAL_CAN_Init(CAN_HandleTypeDef* hcan)

devuelve un HAL_TIMEOUT por lo que no se puede inicializar el CAN. Quiero usar CAN en el modo LOOPBACK (sin un transceptor externo conectado) para probar las funciones. Mientras rastreaba el error del programa con el depurador KEIL, el código parece estar atascado en el

/* Wait the acknowledge */
while((hcan->Instance->MSR & CAN_MSR_INAK) == CAN_MSR_INAK)
{
 if((HAL_GetTick() - tickstart ) > CAN_TIMEOUT_VALUE)
  {
    hcan->State= HAL_CAN_STATE_TIMEOUT;
    /* Process unlocked */
    __HAL_UNLOCK(hcan);
    return HAL_TIMEOUT;
  }
}

Que está en el archivo stm32f4xx_hal_can.c.

Así que busqué en la hoja de datos y encontré esto

30.4.1 Modo de inicialización La inicialización del software se puede realizar mientras el hardware está en modo de inicialización. Para ingresar a este modo, el software configura el bit INRQ en el registro CAN_MCR y espera hasta que el hardware haya confirmado la solicitud configurando el bit INAK en el registro CAN_MSR. Para salir del modo de inicialización, el software borra el bit INQR. bxCAN abandonó el modo de inicialización una vez que el hardware borró el bit INAK.

y también porque es el bit 0 del registro CAN_MSR también indica

Bit 0 INRQ: Solicitud de inicialización El software borra este bit para cambiar el hardware al modo normal. Una vez que se han monitoreado 11 bits recesivos consecutivos en la señal Rx, el hardware CAN está sincronizado y listo para transmisión y recepción. El hardware señala este evento borrando el bit INAK en el registro CAN_MSR. El software establece este bit para solicitar al hardware CAN que ingrese al modo de inicialización. Una vez que el software ha establecido el bit INRQ, el hardware CAN espera hasta que se complete la actividad CAN actual (transmisión o recepción) antes de ingresar al modo de inicialización. El hardware señala este evento configurando el bit INAK en el registro CAN_MSR.

Entonces, después de esto, puedo entender que el problema es que CAN_RX no recibe los 11 bits recesivos, por lo que el hardware no borra el bit INAK en el registro CAN_MSR, por lo que CAN no se puede inicializar. Siento que el problema podría ser el hecho de que no uso un transceptor, por lo que de alguna manera el CAN_RX no puede recibir los 11 bits recesivos que necesita, pero si no es así, no sé cómo solucionarlo. (Ya he pedido algunos transceptores y los probaré cuando lleguen). Esta es también la implementación del código:

El reloj está configurado en el HSI interno a 16 MHz y el Prescaler APB1 en 1, por lo que el CAN obtiene un reloj de 16 MHz.

void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;

    /**Configure the main internal regulator output voltage 
    */
  __HAL_RCC_PWR_CLK_ENABLE();

  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = 16;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure the Systick interrupt time 
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

Este es el procedimiento de inicialización de CAN. Con un reloj de 16 MHz y un preescalador de 2, SJW = 1, BS1 = 11, BS2 = 4 le brinda una velocidad de transmisión de 500 kbps con el punto de muestra en 75%.

static void MX_CAN1_Init(void)
{
    CAN_FilterConfTypeDef  sFilterConfig;
  static CanTxMsgTypeDef        TxMessage;
  static CanRxMsgTypeDef        RxMessage;

  hcan1.Instance = CAN1;
    hcan1.pTxMsg = &TxMessage;
  hcan1.pRxMsg = &RxMessage;

  hcan1.Init.Prescaler = 2;
  hcan1.Init.Mode = CAN_MODE_LOOPBACK;
  hcan1.Init.SJW = CAN_SJW_1TQ;
  hcan1.Init.BS1 = CAN_BS1_11TQ;
  hcan1.Init.BS2 = CAN_BS2_4TQ;
  hcan1.Init.TTCM = DISABLE;
  hcan1.Init.ABOM = DISABLE;
  hcan1.Init.AWUM = DISABLE;
  hcan1.Init.NART = DISABLE;
  hcan1.Init.RFLM = DISABLE;
  hcan1.Init.TXFP = DISABLE;
  if (HAL_CAN_Init(&hcan1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

     /*##-2- Configure the CAN Filter ###########################################*/
  sFilterConfig.FilterNumber = 0;
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
  sFilterConfig.FilterIdHigh = 0x0000;
  sFilterConfig.FilterIdLow = 0x0000;
  sFilterConfig.FilterMaskIdHigh = 0x0000;
  sFilterConfig.FilterMaskIdLow = 0x0000;
  sFilterConfig.FilterFIFOAssignment = 0;
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.BankNumber = 14;

  if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
  {
    /* Filter configuration Error */
    Error_Handler();
  }

}

Entonces, ¿el problema puede ser el hecho de que no tengo un transceptor externo o es otra cosa?

Funcionó...?
Preferiblemente, debe colocar el punto de muestra después del estándar CANopen, en lugar de crear un "estándar de garaje" casero. ¿Fuiste tú quien eligió este o el controlador ST? DS301 enumera 85-90% como rango válido para la ubicación del punto de muestra a 500 kbps, siendo 87,5% la práctica recomendada. La ventaja de seguir el estándar es que todas las recomendaciones de longitud de cable, etc., se basan en él.
¡Era el conductor ST, no yo! Sí, también noté que el punto de muestra está por debajo del 50% y realmente no entiendo por qué.

Respuestas (4)

No pude probar su solución porque no tenía otros dispositivos CAN, ¡pero intenté usar un puente entre los pines y realmente funcionó! ¡No puedo entender por qué porque la hoja de datos indica que el pin TX está conectado al RX internamente!

Suena como si el controlador CAN no estuviera en modo de bucle invertido.
Mi solución fue tirar de RX alto , sin involucrar a otros dispositivos CAN. ¿Puedes probar eso también?
Sí, funcionó de nuevo así. Aunque no puedo entender por qué.

Si realmente usa el pin CAN1_RX (no está en modo de bucle invertido), entonces el pin CAN1_RX debe estar alto, ya sea interna o externamente. Recesivo es lógica alta en el lado del microcontrolador y dominante es lógica baja. En el bus CAN es más o menos lo contrario, una diferencia de alto voltaje es dominante y ninguna diferencia de voltaje es recesiva.

Realmente no necesita los transceptores hasta que tenga más de un dispositivo CAN. Tampoco duelen, pero luego las resistencias de terminación en el lado del bus CAN deben conectarse entre CAN_H y CAN_L (actúa como una resistencia de unión para obtener un nivel de voltaje recesivo (diferencial) bien definido en el lado del bus CAN, aproximadamente 0 V).

Tuve el mismo problema cuando usé el modo de bucle invertido silencioso. La solución fue levantar el pin RX para que pudiera recibir 11 bits recesivos mientras cambiaba un modo de "Modo de inicialización" a "Modo normal".

Editar: - También tengo el mismo problema que durante la inicialización, CAN estaba esperando el ACK. pero descubrí que configuré el pin Rx como modo GPIO_NOPULL y eso crea un problema porque, en general, el bus CAN permanece ALTO cuando no hay comunicación, pero baja cuando el mensaje se envía a través del bus. La mejor solución es configurar el pin Rx y Tx como modo GPIO_PULLUP.

¿Quiere configurar el pullup durante la inicialización o mediante una resistencia externa?

Tuve un problema similar. Durante mis pruebas, ocasionalmente, tengo el error de tiempo de espera en HAL_CAN_Start. El problema parece desaparecer después de haber aumentado la tasa de bits del bus CAN, originalmente probé solo en 10kb/s. Espero que ayude a alguien.