PIC24EP DMA interrupts not working after context switch

My application is using a PIC24EP256GU810 for CAN bus communication. I’ve been able to get a simple non FreeRTOS example running by setting up a DMA channel (required for this micro to use CAN) and I can detect message reception by connecting to the DMA interrupt etc.

However, when a create even the simplest FreeRTOS task to toggle a pin every so often (consuming very few cycles). The DMA interrupt will no longer fire. My interrupt handler currently makes no FreeRTOS API calls - just clears the interrupt flag and the CAN buffer full flag and toggles a pin so I can see the message was received.

All that changed is that I created a task and started the task scheduler.

I’ve set the DMA interrupt priority above the kernel tick priority. I’ve tried by enabling and disabling interrupt nesting on the micro (In another project I determined that interrupt nesting had to be disable for this port as even trying options to define portYIELDFROMISR() didn’t seem to work well.).

Another thing I tried was to not use the DMA interrupt (disable it) and just test for the can buffer full flag (or the DMA interrupt flag) in a FreeRTOS task periodically. The task never sees the flag bits change, unless I make the task continually check the bit and never go to sleep. However, if I add a higher priority task that forces a context switch - then the first task will miss the bit changes. Sometimes, if I step through with the debugger, I can see the flag in the SFR change so I suspect there is something conflicting at the chip level. This only seems to be happening with DMA interrupt and peripherals like CAN that dependent on DMA channels. I was able to do something very similar with just a Timer2 interrupt flag and there were no issues detecting them in the tasks.

Further investigation shows that this may be specific to the CAN module. I set up a similar test with an Input Capture (Interrupt every rising edge) attached to button input. Set up a DMA channel in two ways 1 word with register indirect without post increment (DMA interrupt every input capture event) and 4 word transfers using register indirect w/post increment. (Input capture has 4 deep FIFO). Connected DMA interrupt handler to see validate dma was working. In both cases the my DMA handler executed as expected: either every button press or every 4th button press. I had the same FreeRTOS task running in the background as with the CAN test, so at least register indirect modes seem to work, but I need to test with peripheral indirect mode with the ADC module as that is the only other peripheral that can make use of this DMA mode.

SOLVED:
After experiments with the ADC + DMA I noticed that the RAM buffer in the application note was declared as:

__eds__ unsigned int ecanMsgBuf[NUM_OF_ECAN_BUFFERS][8] \__attribute__((eds, space(dma)));

before I was only aligning the buffer as

unsigned int ecanMsgBuf[NUM_OF_ECAN_BUFFERS][8]
__attribute__((aligned(NUM_OF_ECAN_BUFFERS * 16)));

The eds/dma attributes did the trick and now the DMA interrupt is reliably firing with each received CAN message.

The __eds__ is not required if you are not putting the buffer into extended data space and using it causes issues with using library functions like memcpy - so instead declaring the buffer as:

unsigned int ecanMsgBuf[NUM_OF_ECAN_BUFFERS][8]__attribute__((eds, space(dma)));

Works -it is a bit confusing because the attribute still has “eds” but according to the compiler manual this eds means something different.

Thank you for sharing your solution.