2

I want to implement something in an ARM Cortex-M3 processor (with NVIC). I have limited knowledge about embedded systems, but I know, that an ISR routine should be as simple as possible.

Now I have the following problem: I have an interrupt routine which invokes when a CAN message is received. In one case I have to calculate some time-consuming task with the CAN message which task cannot be interrupted by another CAN message, but other interrupts can be served during this operation. My idea is the following:

  1. CAN message received, ISR started, do some simple jobs (e.g. setting flags)
  2. Disable CAN interrupts in the CAN ISR routine. Also save the CAN message into a global variable so it can be reached in the main loop.
  3. In the main loop, do the time consuming task.
  4. After this task, enable the CAN interrupt handling again.

Is it a good (or not totally bad) idea or should I do this in another way?

artless noise
  • 21,212
  • 6
  • 68
  • 105
Zoltán Várnagy
  • 137
  • 2
  • 11
  • Dunno - what do have in the way of a tasking system? Threads and semaphores? – Martin James Jan 25 '16 at 12:01
  • 2
    In general, leaving interrupts masked off for long periods is something you should avoid if a better scheme for flow-control can be used. – Martin James Jan 25 '16 at 12:04
  • 2
    Perhaps use a ring buffer large enough to hold 2 or more messages (or a pair of buffers that you alternate). Then you don't have to copy data around, and the next CAN message can be received while you process the first. – Weather Vane Jan 25 '16 at 12:09
  • @WeatherVane He will have to copy the data from the CAN hardware buffer into RAM, that's unavoidable. So there's no point in having more buffers than one. CAN hardware typically have multiple buffers internally so you don't need to worry about which buffer that is getting filled up, which is done etc. All that is taken care of at the point when the CAN ISR fire. – Lundin Jan 25 '16 at 12:25
  • If the input from the bus is 'bursty' as it often is when you are restricted to only 8 bytes per transfer, then a pool of buffers, ie. more than one, is not unreasonable. – Martin James Jan 25 '16 at 12:36

2 Answers2

4

It is generally not a good idea to disable all (CAN) interrupts. It seems that what you want to protect yourself against is the same message arriving a second time, before you are done serving the first.

In that case you should take advantage of the ISR itself being non-interruptable. You can create a simple semaphore with a bool variable, and since the interrupt that sets it is non-interruptable, you don't even have to worry about atomic access to that boolean. C-like pseudo-code for the can handler:

typedef struct
{
  bool busy;
  can_msg_t msg;
} special_msg_t;

// must be volatile, to prevent optimizer-related bugs:
volatile static special_msg_t special = { false, {0} };

interrupt void can_isr (void)
{
  // non-interruptable ISR, no other interrupt can fire

  if(id==SPECIAL && !special.busy)
  {
    special.busy = true;
    // right here you can open up for more interrupts if you want
    memcpy(&special.msg, &new_msg, size);
  }
}

result_t do_things_with_special (void) // called from main loop
{
  if(!special.busy) 
    return error; // no new message received, no can do

  // do things with the message

  special.busy = false; // flag that processing is done
  return ok;
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Note that this code assumes that there's no other buffer than the CAN hardware buffer. Otherwise you can speed up the ISR a bit by swapping pointers instead of doing a hard copy of the buffer. – Lundin Jan 25 '16 at 12:22
  • Wouldnt it be safer to open up for interrupts after data from new_msg is copied? – AlphaGoku Feb 22 '18 at 03:27
0

an ISR routine should be as simple as possible.

Exactly.

There is a Linux kernel concept called bottom half. i.e Keeping ISR simple as much as possible. Rest of interrupt handling operations are deferred to later time.

There are many ways to implement bottom half like task-let, work-queues,etc

Suggest to read the following links
http://www.makelinux.net/ldd3/chp-10-sect-4
http://www.ibm.com/developerworks/library/l-tasklets/

Interrupt disabled for longer time could lead to interrupt missing(Obviously some data loss). After interrupt flag is set, create a deferred work and come out of interrupt context.

Jeyaram
  • 9,158
  • 7
  • 41
  • 63
  • Can you really squeeze the Linux kernel into a Cortex-m3... and even if you can, is that such a good idea? I'm assuming this is a bare metal MCU. – Lundin Jan 25 '16 at 12:18
  • @Lundin, I assumed it as embedded system where Linux is running. – Jeyaram Jan 25 '16 at 12:27
  • 2
    Yeah, I forgot to mention, that this is a simple microcontroller with no OS on it, my mistake. – Zoltán Várnagy Jan 25 '16 at 12:31
  • 1
    Linux will run on a Cortex-m3 and it is only ~300k-1M when minimal with a scheduler and memory management. File systems, networking, etc take a lot of code space. That said, if you only use such a minimal set, there maybe better solutions available depending on your exact needs. See: [Minimum Linux kernel size](http://stackoverflow.com/questions/27941775/what-is-the-minimum-amount-of-ram-required-to-run-linux-kernel-on-an-embedded-de) – artless noise Jan 25 '16 at 20:20