FreeRTOS Problem with High Speed UART Interrupt in PIC24H

hoanganhtdh1 wrote on Thursday, October 31, 2013:

Dear My Bro !

I already used FreeRTOS for some embedded projects for some year time and It worked really perfectly until now. Currently i’m facing a difficult problem related to using High Speed Interrupt in FreeRTOS porting to PIC24H, hope you all can help me to though this problem. Thanks in advance

I created a small demo project for easy testing:

Two task:

// Task 1
if (xTaskCreate(RTOSTask_1, (signed char*) “[T1]”, configMINIMAL_STACK_SIZE*2, NULL, tskIDLE_PRIORITY + 1, &hTask1) == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY)
{
LREP("\r\nCannot create Task 1.");
Display_Error(1000);
}

// Task 2
if (xTaskCreate(RTOSTask_2, (signed char*) “[T2]”, configMINIMAL_STACK_SIZE*2, NULL, tskIDLE_PRIORITY + 2, &hTask2) == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY)
{
LREP("\r\nCannot create Task 2.");
Display_Error(1000);
}


Implement of tasks:

void RTOSTask_1(void* pvParameter)
{
while(1)
{
if (xSemaphoreTake(hTask1Semaphore, portMAX_DELAY) == pdTRUE)
{
putchar1(‘1’);
}
}
}

void RTOSTask_2(void* pvParameter)
{
while(1)
{
if (xSemaphoreTake(hTask2Semaphore, portMAX_DELAY) == pdTRUE)
{
putchar1(‘2’);
}
}
}

To let above two tasks running i use one Timer & one UART to give them Semaphores:

void attribute((interrupt, auto_psv)) _T2Interrupt (void)
{
_T2IF = 0;

static signed portBASE_TYPE xTaskWoken = pdFALSE;

xSemaphoreGiveFromISR(hTask1Semaphore, &xTaskWoken );	

if( xTaskWoken != pdFALSE )
{
taskYIELD();
}

}

void attribute((interrupt, auto_psv)) _U1TXInterrupt()
{
_U1TXIF = 0;

putchar1('E');	

}

void attribute((interrupt, auto_psv)) _U1RXInterrupt()
{
_U1RXIF = 0;

if(U1STAbits.URXDA == 1)
{
uint8 u8Recv = U1RXREG;
}	


static signed portBASE_TYPE xTaskWoken;

xTaskWoken = pdFALSE;

xSemaphoreGiveFromISR(hTask2Semaphore, &xTaskWoken);	

if( xTaskWoken != pdFALSE )
{
taskYIELD();
}

}


My Timer interrupts in every 100us, UART working at 230400 bps baudrate speed.


After running some second or minutes the program is crashed and the program jump to Traps:

_AddressError or
_StackError


I don’t know how this problem could happen. After a long investigating & testing i thought that the problem happen when the program running in & running out of the Interrupt service routine (ISR). It seems we need a couple of SAVE_CONTEXT() & RESTORE_CONTEXT() functions. but on PIC24 port there is no one like that.

Please you kindly give me some advises for this problem

Thank you all !

davedoors wrote on Thursday, October 31, 2013:

You are calling putchar1() from two tasks and an interrupt. Is the function thread safe? Can it be called from an interrupt safely? Can it even output the character at the speed you are asking it to?

Is your processor even physically capable of running the code you have in the time you are asking it to? Giving a semaphore each time a character is received is normally not a good idea even when slow baud rates are used. Demo code does this to show how the functions can be used and test the rtos port, but application code would normally buffer characters in RAM and give a semaphore when the buffer was full or the buffer contained a complete message.

rtel wrote on Thursday, October 31, 2013:

There may also be a slight error in your _T2Interrupt() code, although probably not relevant to your issue.

xTaskWoken is static and initialised to false. Once it has been set to true by your semaphore give function it will always be true from that point on. It needs to be set to false each time you enter the function, just as you are doing in _U1RXInterrupt().

Regards.

hoanganhtdh1 wrote on Thursday, October 31, 2013:

Thanks for your kind reply

To Dave, My putchar1() is a thread safe function so the problem cannot be introduced by that one i think. For the buffer character issue that you mentioned. i must receive byte by byte because i dont know the length of the incoming frame, i just receive bytes, push in to the FIFO then handle them in a RTOS task.

To Richard, That is really my false, i already fixed it according to your suggestion but the problem still occurred.

Please give me more advises if any

Thanks all

hoanganhtdh1 wrote on Monday, November 04, 2013:

I already find out my problem, i think. The problem is introduced when the PIC24H gets in & gets out Interrupt Service Routines, here they are UART RX, TX, Timer Interrupts.

Currently i don’t use the ISR like this:

void attribute((interrupt, auto_psv))

instead of it i created a mechanism myself with Assembly code:

__U1RXInterrupt:
; Push CPU registers in to Stack

    PUSH	SR			
PUSH	W0
PUSH	W1			
PUSH.D	W2
PUSH.D	W4
PUSH.D	W6
PUSH.D 	W8
PUSH.D 	W10
PUSH.D	W12
PUSH	W14
PUSH	RCOUNT
PUSH	TBLPAG
PUSH	CORCON
PUSH	PSVPAG

    ; Call my ISR
    call _UART1_RxISRHandler        
    
    ; Pop out CPU registers
POP	PSVPAG
POP	CORCON
POP	TBLPAG
POP	RCOUNT					
POP	W14
POP.D	W12
POP.D	W10
POP.D	W8
POP.D	W6
POP.D	W4
POP.D	W2
POP.D	W0
POP	SR


retfie      

UART1_RxISRHandler is my implement of ISR. I do the same with TX, Timer interrupts.

The result is that my program run more smoothly, longer 1 hour (before the program crash after 1-5 minutes only). But at the end it still crash after running 1-2 hours. That means my approach is correct but still there is something wrong. May be i miss something with above code.

If you all have any ideal for this situation, please let me know.

Thanks

richard_damon wrote on Monday, November 04, 2013:

I have a simple question, WHY? Why are you not writing your ISR the way that the compiler and FreeRTOS document it. Is there anything else in your program that isn’t by the documented requirements? One source of “rare” crashes is not doing something the way you are supposed to, and some subtle interaction that happens only in some very specific order of execution.

One thing I can see with your new entry code is you are wasting stack space by saving stuff twice, between your assembly code and the generated code in the real ISR.

hoanganhtdh1 wrote on Tuesday, November 05, 2013:

Dear Mr Richard

Following the ISR implement way that the compiler and FreeRTOS do always is my first & the best choise but in that way i got the problem as indicated. My application is really clear and simple but the problem still happen, that’s why i must try to do differently to find out what the problem is.

Thanks for your comment

richard_damon wrote on Tuesday, November 05, 2013:

The problem is that you have added significantly more issues that might cause problems. This is a bit like asking the repair shop to make your horn louder, because your brakes aren’t working well.

Go back to the original program presented.

Have you tried with stack checking enabled?
Have you verified that interrupt priorities are set correctly?
What does putchar1() do? Are you SURE it is thread/interrupt safe? This has the smell of a problem to me. Because it is called from both task and interrupt levels, it can’t do much, and probably can’t make any calls to FreeRTOS, directly or indirectly.

Your data rates are VERY high, both a 10,000 Hz tick rate and a character interrupt rate of over 20k char/sec may very well overtax the processor, especially if you are using queue to send single characters. This itself should cause a crash, but some part of your program doesn’t. You really need to evaluate if this really is needed. I never find the need for the main tick to be that fast, and serial ports that fast really do need to be made block oriented.

Note that on the PIC24 family, the ISRs don’t need to have special FreeRTOS context saves, as they run in the frame of the current task, and the taskYield call will create the needed frame. When the task resumes, it starts at the end of the ISR and that returns back to the tasks. The task switching code restores the interrupt state to allow the ISR to be called before this happens (which is why the taskYield must be the VERY LAST piece of the ISR, nothing after it, as it won’t get run when you might think it should).