El hardware CAN STM32F3 no ingresa al modo de inicio

Estoy tratando de escribir un controlador completo para el STM32F302 en una placa Nucleo-64. Tengo problemas para superar los primeros pasos y hacer que el hardware configure el Reconocimiento de inicialización. Este controlador se está escribiendo para ejecutarse junto con el marco del sistema operativo mbed si ese detalle es importante, tuve problemas por los cuales el firmware entraría en un estado de error si llamaba al constructor de CAN que proporcionan (es cierto que la placa no es técnicamente compatible bajo mbed OS 5, pero un ingeniero de ST me dijo que esto se debe a la cantidad de RAM estática).

Aquí están los fragmentos de código que son relevantes:

void can_initialize(unsigned int frequency)
{
    // Setup the CAN registers
    RCC->APB1ENR &= ~RCC_APB1ENR_USBEN;
    RCC->APB1ENR |= RCC_APB1ENR_CANEN;

    MY_CAN_PORT->MCR |= CAN_MCR_RESET;

    // Set to init mode
    can_setMode(CAN_MODE::INIT);

    // Enable transmit and receive
    MY_CAN_PORT->MSR |= (CAN_MSR_RXM | CAN_MSR_TXM);

    // Set the frequency
    can_setFrequency(frequency);
}

void can_setMode(CAN_MODE new_mode)
{
    switch (new_mode)
    {
        case CAN_MODE::SLEEP:
            MY_CAN_PORT->MCR |= CAN_MCR_SLEEP;
            // Wait for acknowledgement from hardware
            // SLAK = 1, INAK = 0
            while (~(MY_CAN_PORT->MSR & CAN_MSR_SLAK) 
                   || (MY_CAN_PORT->MSR & CAN_MSR_INAK));
            break;
        case CAN_MODE::INIT:
            MY_CAN_PORT->MCR |= CAN_MCR_INRQ;
            if (my_mode == CAN_MODE::SLEEP)
            {
                MY_CAN_PORT->MCR &= ~CAN_MCR_SLEEP;
            }
            // Wait for the acknowledgement from hardware
            // INAK = 1 indicates initialization mode
            while (~(MY_CAN_PORT->MSR & CAN_MSR_INAK));
            break;
        case CAN_MODE::NORMAL:
            // first make sure we aren't in loopback mode
            if (MY_CAN_PORT->BTR & CAN_BTR_LBKM)
            {
                // go to init so we can modify loopback mode
                can_setMode(CAN_MODE::INIT);
                // clear loopback mode
                MY_CAN_PORT->BTR &= ~CAN_BTR_LBKM;
                can_setMode(CAN_MODE::NORMAL);
            }
            MY_CAN_PORT->MCR &= ~(CAN_MCR_INRQ);
            // Wait for acknowledgement from hardware
            // SLAK = 0, INAK = 0
            while ((MY_CAN_PORT->MSR & CAN_MSR_SLAK) 
                    || (MY_CAN_PORT->MSR & CAN_MSR_INAK));
            break;
        case CAN_MODE::LOOPBACK:
            // go to init so we can modify loopback mode
            can_setMode(CAN_MODE::INIT);
            // set loopback mode
            MY_CAN_PORT->BTR |= CAN_BTR_LBKM;
            can_setMode(CAN_MODE::NORMAL);
            break;
    }

    my_mode = new_mode;
}

Planeo usar el dispositivo en modo Loopback para realizar pruebas, por lo que actualmente no tengo nada conectado externamente. ¿Necesito un bus CAN de dos nodos solo para poner el periférico en modo Init?

Noté que hay una publicación extremadamente similar y probé algunas de las soluciones propuestas aquí: Error de tiempo de espera de inicialización de CAN en STM32F4

Cosas que he probado en HW:

  • Intenté conectar TX y RX directamente, aunque como no está enviando nada (creo) que no debería ser necesario.
  • También intenté vincular CANRX a 3V3 para emular que el bus estaba en un estado recesivo para los 11 bits, pero esto tampoco resolvió el problema.
  • Probado en una segunda placa idéntica.

Aparte de conectar un segundo nodo, no estoy seguro de qué hacer desde aquí. Si hay algún truco para estos tableros que no estoy pensando o si se requieren más detalles para diagnosticar el problema, ¡házmelo saber!

El lenguaje es C++, no lo etiqueté con C.
No importa, logré confundirme con las etiquetas. He corregido la etiqueta correcta ahora. En este caso, el idioma no afecta la respuesta publicada.

Respuestas (1)

Este código está mal:

while(~(MY_CAN_PORT->MSR & CAN_MSR_INAK));

~no es el operador lógico NOT, sino el operador de complemento bit a bit.

Por lo tanto, esto no significa "mientras que el reconocimiento de inicio no está configurado", sino que significa "mientras 1". Cuando se configura INAK (máscara binaria 0x00000001), se obtiene 0xFFFFFFFEy cuando no se configura, se obtiene 0xFFFFFFFF. Ambos se evalúan como verdadero/1.

¿Seguramente su depurador debería colgar aquí?

Arréglalo usando comparaciones lógicas en su lugar:

while((MY_CAN_PORT->MSR & CAN_MSR_INAK)==0)
  ;

Tenga en cuenta que la colocación del punto y coma en una línea propia es una buena práctica, de modo que el lector pueda darse cuenta de que se trata de un ciclo vacío intencionalmente y que no se descargó el punto y coma al final de la línea por hábito/accidente. .

Mejor aún: while (cond) { }
Sí, eso fue exactamente. Buen punto en la colocación del punto y coma.