PIC24H 中的 FreeRTOS 错误与高速 UART 中断

Ngu*_*Anh 5 c multithreading stm32 freertos mplab

我已经FreeRTOS在一些嵌入式项目中使用了一年时间,到目前为止它工作得非常完美。FreeRTOS目前我面临着一个与使用高速中断移植到相关的难题PIC24H,希望大家能帮助我解决这个问题。提前致谢

我创建了一个小型演示项目以方便测试:

两个任务:

// Task 1

if (xTaskCreate(RTOSTask_1, (signed char) "[T1]", configMINIMAL_STACK_SIZE2, 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_SIZE2, NULL, tskIDLE_PRIORITY + 2, &hTask2) == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY)
{
  LREP("\r\nCannot create Task 2."); 
  Display_Error(1000);  
}
Run Code Online (Sandbox Code Playgroud)

任务落实:

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');
    } 

   }
}
Run Code Online (Sandbox Code Playgroud)

为了让以上两个任务运行,我使用 one Timer& oneUART为它们提供信号量:

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

我的定时器每 100us 中断一次,UART 工作在 230400 bps 波特率速度。

运行几秒或几分钟后,程序崩溃并跳转到陷阱:

_AddressError或者

_StackError

我不知道这个问题怎么会发生。经过长时间的调查和测试,我认为当程序在中断服务例程(ISR)中运行和运行时,就会出现问题。看来我们需要几个SAVE_CONTEXT()&RESTORE_CONTEXT()函数。但在PIC24端口上没有这样的人。

请您针对这个问题给我一些建议

谢谢你们 !


我想我已经发现了我的问题。当PIC24H进入和退出中断服务程序时,就会出现问题,这里它们是UART RX、TX、定时器中断。

目前我不使用这样的 ISR:

void属性((中断, auto_psv))

相反,我自己用汇编代码创建了一个机制:

__U1RX中断:; 将CPU寄存器压入堆栈

    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      
Run Code Online (Sandbox Code Playgroud)

UART1_RxISRHandler 是我的 ISR 实现。我对 TX 进行同样的操作,定时器中断。

结果是我的程序运行更流畅,时间更长了1小时(程序崩溃前仅1-5分钟后)。但最后运行1-2小时后仍然崩溃。这意味着我的方法是正确的,但仍然有问题。可能是我错过了上面代码的一些内容。

如果大家对这种情况有什么理想,请告诉我。

谢谢

Har*_*ain 1

尝试使用队列。LPC1769 上的示例。您可以轻松地将其移植到您的 MCU 中。

定义 mainQUEUE_LENGTH ( 1 )

这将修复可以存储在队列中的最大字节数。根据您的要求修改它,因此将免受堆栈错误或地址错误的影响:

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

/* Priorities at which the tasks are created. */
#define mainQUEUE_RECEIVE_TASK_PRIORITY     ( tskIDLE_PRIORITY + 2 )
#define mainQUEUE_SEND_TASK_PRIORITY        ( tskIDLE_PRIORITY + 1 )

/* The bit of port 0 that the LPCXpresso LPC13xx LED is connected. */
#define mainLED_BIT                         ( 22 )

/* The rate at which data is sent to the queue, specified in milliseconds. */
#define mainQUEUE_SEND_FREQUENCY_MS         ( 500 / portTICK_RATE_MS )

/* The number of items the queue can hold.  This is 1 as the receive task
will remove items as they are added, meaning the send task should always find
the queue empty. */
#define mainQUEUE_LENGTH                    ( 1 )

/*
 * The tasks as described in the accompanying PDF application note.
 */
static void prvQueueReceiveTask( void *pvParameters );
static void prvQueueSendTask( void *pvParameters );

/*
 * Simple function to toggle the LED on the LPCXpresso LPC17xx board.
 */
static void prvToggleLED( void );

/* The queue used by both tasks. */
static xQueueHandle xQueue = NULL;

/*-----------------------------------------------------------*/

int main(void)
{
    /* Initialise P0_22 for the LED. */
    LPC_PINCON->PINSEL1 &= ( ~( 3 << 12 ) );
    LPC_GPIO0->FIODIR |= ( 1 << mainLED_BIT );

    /* Create the queue. */
    xQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( unsigned long ) );

    if( xQueue != NULL )
    {
        /* Start the two tasks as described in the accompanying application
        note. */
        xTaskCreate( prvQueueReceiveTask, ( signed char * ) "Rx", configMINIMAL_STACK_SIZE, NULL, mainQUEUE_RECEIVE_TASK_PRIORITY, NULL );
        xTaskCreate( prvQueueSendTask, ( signed char * ) "TX", configMINIMAL_STACK_SIZE, NULL, mainQUEUE_SEND_TASK_PRIORITY, NULL );

        /* Start the tasks running. */
        vTaskStartScheduler();
    }

    /* If all is well we will never reach here as the scheduler will now be
    running.  If we do reach here then it is likely that there was insufficient
    heap available for the idle task to be created. */
    for( ;; );
}
/*-----------------------------------------------------------*/

static void prvQueueSendTask( void *pvParameters )
{
portTickType xNextWakeTime;
const unsigned long ulValueToSend = 100UL;

    /* Initialise xNextWakeTime - this only needs to be done once. */
    xNextWakeTime = xTaskGetTickCount();

    for( ;; )
    {
        /* Place this task in the blocked state until it is time to run again.
        The block state is specified in ticks, the constant used converts ticks
        to ms.  While in the blocked state this task will not consume any CPU
        time. */
        vTaskDelayUntil( &xNextWakeTime, mainQUEUE_SEND_FREQUENCY_MS );

        /* Send to the queue - causing the queue receive task to flash its LED.
        0 is used as the block time so the sending operation will not block -
        it shouldn't need to block as the queue should always be empty at this
        point in the code. */
        xQueueSend( xQueue, &ulValueToSend, 0 );
    }
}
/*-----------------------------------------------------------*/

static void prvQueueReceiveTask( void *pvParameters )
{
unsigned long ulReceivedValue;

    for( ;; )
    {
        /* Wait until something arrives in the queue - this task will block
        indefinitely provided INCLUDE_vTaskSuspend is set to 1 in
        FreeRTOSConfig.h. */
        xQueueReceive( xQueue, &ulReceivedValue, portMAX_DELAY );

        /*  To get here something must have been received from the queue, but
        is it the expected value?  If it is, toggle the LED. */
        if( ulReceivedValue == 100UL )
        {
            prvToggleLED();
        }
    }
}
/*-----------------------------------------------------------*/

static void prvToggleLED( void )
{
unsigned long ulLEDState;

    /* Obtain the current P0 state. */
    ulLEDState = LPC_GPIO0->FIOPIN;

    /* Turn the LED off if it was on, and on if it was off. */
    LPC_GPIO0->FIOCLR = ulLEDState & ( 1 << mainLED_BIT );
    LPC_GPIO0->FIOSET = ( ( ~ulLEDState ) & ( 1 << mainLED_BIT ) );
}
Run Code Online (Sandbox Code Playgroud)