CANopenNode / CanOpenSTM32

CANopenNode on STM32 microcontrollers.
Other
275 stars 110 forks source link

CanopenNode STM32 with FreeRtOS and CRC-Unit #19

Closed Pascal8749 closed 1 year ago

Pascal8749 commented 1 year ago

I have observed an interesting phenomenon. The following scenario: I ran CanopenNode_STM32 under FreeRtOS. There are three tasks in total. One task is the send and process routine for CanopenNode. One task is a controller. The third task is the receive routine for CanopenNode, so basically the function "canopen_app_interrupt()".

void start_CO_TI_Thread(void *argument)
{
  for(;;)
  {
    canopen_app_interrupt();
        osDelay(1);
  }
  osThreadTerminate(NULL);
}

Everything works fine until I enable CRC. In this case FreeRtOS ignores the delay function (osDelay(1)) and does not process any other task except the receive routine. Why the CRC unit does this is beyond me, but in this case it is because of the macros "CO_LOCK_OD" and "CO_UNLOCK_OD". After some research I rewrote the macro "CO_UNLOCK_OD" from

#define CO_UNLOCK_OD(CAN_MODULE)                __set_PRIMASK((CAN_MODULE)->primask_od

to


#define CO_UNLOCK_OD(CAN_MODULE)                do{__set_PRIMASK((CAN_MODULE)->primask_od);__enable_irq();}while(0)

And now my code seems to work even with CRC unit turned on. Tests are still pending. I am not a computer scientist, so please correct me if my approach was stupid.

MaJerle commented 1 year ago

Your unlock does not work correctly. If interrupts were locked before you locked them again, your unlock method will unlock them.

Actually __set_PRIMASK((CAN_MODULE)->primask_od) will unlock them only if they were previously unlocked.

Process is:


uint32_t primask = __get_PRIMASK(); // Get interrupt status
__disable_irq();    // Force interrupt disabled

// do the job

__set_PRIMASK(primask); //Re-enable interrupts only if these have been enabled before the disable call

Anything else is wrong and can sooner or later lead to issues.

MaJerle commented 1 year ago

Driver is done like explained above. Issue you are facing lays somewhere else, not in the interrupt management.

/* (un)lock critical section in CO_CANsend() */
// Why disabling the whole Interrupt
#define CO_LOCK_CAN_SEND(CAN_MODULE)                                                                                   \
    do {                                                                                                               \
        (CAN_MODULE)->primask_send = __get_PRIMASK();                                                                  \
        __disable_irq();                                                                                               \
    } while (0)
#define CO_UNLOCK_CAN_SEND(CAN_MODULE) __set_PRIMASK((CAN_MODULE)->primask_send)

/* (un)lock critical section in CO_errorReport() or CO_errorReset() */
#define CO_LOCK_EMCY(CAN_MODULE)                                                                                       \
    do {                                                                                                               \
        (CAN_MODULE)->primask_emcy = __get_PRIMASK();                                                                  \
        __disable_irq();                                                                                               \
    } while (0)
#define CO_UNLOCK_EMCY(CAN_MODULE) __set_PRIMASK((CAN_MODULE)->primask_emcy)

/* (un)lock critical section when accessing Object Dictionary */
#define CO_LOCK_OD(CAN_MODULE)                                                                                         \
    do {                                                                                                               \
        (CAN_MODULE)->primask_od = __get_PRIMASK();                                                                    \
        __disable_irq();                                                                                               \
    } while (0)
#define CO_UNLOCK_OD(CAN_MODULE) __set_PRIMASK((CAN_MODULE)->primask_od)
Pascal8749 commented 1 year ago

Okay thanks for the feedback. I don't understand exactly what the two macros do, but I believe you. I could not solve the problem, and so I ran the function canopen_app_interrupt() in a timer interrupt again, despite freertos. Just like it is solved in the example to freertos. This is a bit unsatisfying, but at least now everything runs as expected.