1

I need to verify myself about what I am doing.

In my app., there is data stream between my device and vehicle CAN bus which contains like speed, gearbox etc. My device gets these data and by interpreting them, it gives an output to the vehicle. Now. I am handling all these receive transactions in RxCallback and in order to not cause circular dependency, I create a User_RxIndication function.

In this function, I am checking id, dlc, frame, format etc. If all validation passed, then data is passing from specific parser and it is saved. All these transactions are handling in User_RxInditication and this fuction is passed from application layer to the lower layers.

Here is the code sample.

static CanMsg_t RxMsg;
static vehicle_t MyBus;
void VDCU_CanReceiveIndication(CanHandle_t Dummy)
{
    if(ComM_CanRead(&RxMsg)== E_OK)
    {
        switch(RxMsg.id)
        {
        case SPEED_CAN_ID:
            if(     VALIDATE_DATA_FORMAT(RxMsg.id, SPEED_ID_FORMAT) &&
                    VALIDATE_DATA_FRAME(RxMsg.frame, SPEED_FRAME)   &&
                    VALIDATE_DATA_LENGTH(RxMsg.len, SPEED_LENGTH ))
            {
                /*TODO: Apply Parser, if required ?*/
                VehicleIf_SetVehicleSpeed(MyBus, RxMsg.data[0]);
                
            }
            else
                 /*TODO: Send Dia*/
            break;
        case STEERINGWHEEL_CAN_ID:
            if(     VALIDATE_DATA_FORMAT(RxMsg.id, STEERINGWHEEL_ID_FORMAT) &&
                    VALIDATE_DATA_FRAME(RxMsg.frame, STEERINGWHEEL_FRAME)   &&
                    VALIDATE_DATA_LENGTH(RxMsg.len, STEERINGWHEEL_LENGTH ))
            {
                /*TODO: Apply Parser, if required ?*/
                VehicleIf_SetVehicleWheelAngle(MyBus, RxMsg.data[1]);
            }
            else
                /*TODO: Send Dia*/
            break;
        default:
            break;
        }
     }
 }

These cases may extend up to 9-10 btw. I did not design parser but, I will do.

Now, This approach is valid ? Whenever read something about Interrupt mechanism. It is said "keep interrupts as short as possible. Set a flag and do whatever you want in main."

Should I apply this switch case and guard conditions in main or here is okey ?

In case of handling in main , do I miss data while interpreting them in main depending on other task time allocation in app ?

What is the right approach. Thanks.

Lundin
  • 195,001
  • 40
  • 254
  • 396

1 Answers1

2

No, all of that is not sensible to do from inside an ISR, unless your real-time requirements are very tough. In the rare case where you are counting microseconds and you must deal with a package immediately upon arrival, then you can deal with the data directly from the ISR - as long as you are aware that this stalls everything else in the MCU.

(Or in case the data doesn't affect other interrupts, you could use the dirty old school trick of dropping the global interrupt mask first thing you do in the ISR, but keep the CAN rx flag set until the end of the ISR... But these are rather desperate tricks for extreme real-time requirements.)

In normal programs, you would rather just copy data out of the CAN controller hardware buffers and into RAM asap, then worry about decoding it later. Depending on hardware, you basically have to use one of these options:

  • Use a modern CAN controller with a 'mailbox' feature (> year 2010 technology). In that case, data arrives in its mailbox and you can grab the latest one whenever you want. In many CAN systems you can even allow overruns of the mailbox in case you are only interested in the latest data. At any rate you don't need to worry about emptying a rx FIFO before it overflows, so timing becomes less critical.

  • Use DMA or similar to have incoming data copied from the CAN buffers to RAM, then decode it from the regular background program. Modern MCUs like Microchip SAMC have support for this.

  • In case you are using old, outdated technology like ST bxCAN, then if you are using interrupts you might be forced to hard copy the buffers into RAM from the ISR, then deal with them later. Or indeed set a flag, but in that case you might probably just as well use use polling.

    Because for these older controllers, you must service the CAN controller rx FIFO fast enough so that you never risk a FIFO overrun. How easy it is to achieve that depends on the higher layer CAN protocol. On such older controllers we often had to fiddle around with acceptance filters and masking, to reduce the amount of data. Always messy.

    And interrupts stink in general because the majority of all embedded systems programmers out there are sadly too incompetent to be aware of race conditions. Or the old "missing volatile" bug, for that matter. Avoid interrupts when you can.

Overall, if you have a fairly complex higher layer protocol and your MCU needs to listen to a lot of different messages (CANopen, J1939 or similar) then I would strongly recommend to pick a modern CAN controller with mailboxes, since these make real-time design so much easier. This is something to consider when picking MCU.

Since most STM32 can't even be purchased because ST can't manage to manufacture their own products, you might want to seriously consider porting to another MCU family for that reason alone.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Unfortunately, my app runs on STM32F405xx and using bxCAN. Althought, I abstracted HW from SW, a uC revision is big change now. If I just a flag in RxCallback and then apply polling to copy from FIFO for decoding, what happens what if RxCallback is triggered again while decoding preceding data ? Should I do disable Irq Handler while decoding ? My mind is getting confused. – EmbeddedMaker Mar 31 '21 at 19:36
  • @EmbeddedMaker Fortunately, that specific STM32F part appears to be available still, unlike most of them. But their CAN controller is very primitive, we are talking about something from the mid-1990s here. You might have to do an old school ring buffer routine where the ISR does nothing but shovel data into the ring buffer. Just like you do when dealing with primitive UART peripherals that only have 1 rx buffer. – Lundin Apr 01 '21 at 07:08
  • ST recommends disable Irq in Cb then decode it . I will apply this. If it does not work, I may try using FIFO or apply parser in Cb directly, because my previous version which I process them in Cb was working good. There is another thing in my mind. When I deep into HAL codes, If I am not wrong, HAL_IrqHandler calls callback pointer after clears specific Irq Flag. Now, does clearing flag in interrupt means that specific event is ended ? And also does it means you can call whatever you want because of flag was cleared ? – EmbeddedMaker Apr 01 '21 at 07:44
  • @EmbeddedMaker There's a global flag for "maskable" interrupts (as opposed to non-maskable that can't be disabled), usually called "the interrupt mask" and represented by an `i` bit in some condition code register. On ARM this one is called "PRIMASK" and toggled with `CPSIE i` (enable) and `CPSID i`. If I remember correctly "Change Processor State Interrupt Enable/Disable". When an interrupt is called, the CPU sets this automatically, and clears it automatically when you leave the ISR. So if you clear it manually from inside the ISR, you allow other interrupts to occur. – Lundin Apr 01 '21 at 08:28
  • @EmbeddedMaker Not to be confused with the interrupt enable flag for the specific interrupt (in this case CAN rx). When interrupts are enabled you have a corresponding peripheral-specific flag register that is set when an event occurs (the same flag can also generally be used for polling). This flag describes the interrupt reason, and generally you have to clear it from inside the ISR to signal that you have dealt with the event. This allows other interrupts of the same kind of occur again. – Lundin Apr 01 '21 at 08:32
  • So if you would clear the interrupt mask and the specific flag at once, you can receive the same kind of interrupt as you just got and this can happen from inside the ISR. Which is pretty dangerous since it could lead to infinite recursion or at least increased stack use. Hence "desperate tricks for extreme real-time requirements". – Lundin Apr 01 '21 at 08:33
  • Okey, when an rx interrupt occured, setting a flag, disabling irq then decoding and then enabling irq seems best way. – EmbeddedMaker Apr 01 '21 at 09:31
  • @EmbeddedMaker Do you have _extreme real-time requirements_ though? Why can't you use a common ring buffer software FIFO? – Lundin Apr 01 '21 at 09:55
  • App shall give an output with respect to speed data comes from CAN. The precision of vehicle speed content is 0.1 and speed varies according to acceleration and My app shall respond to this acceleration and never miss the data. My app' operating area are up to 70km/h. Unfortunately, I could not imagine how extreme it is. So far, I've never work on Vehicle CAN. Does Setting a FIFO quarantee that app no longer miss data? How should adjust FIFO capacity then ? Last one, I will enqueue messages into FIFO in Cb, then dequeue them later for decoding, am I right ? – EmbeddedMaker Apr 01 '21 at 11:52
  • @EmbeddedMaker 0.1 what? And what has the speed of the car got to do with anything? All that matters is your real-time requirements: how fast do you need to respond from the point when the CAN message is sent on the bus. Normally this is milliseconds rather than microseconds. Apart from CAN there's the usual automotive considerations: do you need automotive qualified parts? Do you need MISRA-C? ASIL? The higher ASIL classifications will ban interrupts like this - functional safety standards only allow cyclic interrupts. For very good reasons too. – Lundin Apr 01 '21 at 13:02
  • As I say 0.1, I meant precise of speed is like 15.1 km/h ,15.2 km/h, 15.3 km/h . Okey, app emits virtual engine sound which I sampled according to speed data coming from bus. There is no performance criteria about respond time to changings in speed at this level. Sound will stream via DMA. I aimed emiting sound till 70 km/h. No AUTOSAR, no ISO 26262 and no any other for now. There is no any request on market and standards that requires these. To ensure these qualifications is a good thing but this also requires know-how a lot and reduces agility in my case. – EmbeddedMaker Apr 01 '21 at 18:01