【问题标题】:FreeRTOS Error with High Speed UART Interrupt in PIC24HPIC24H 中的高速 UART 中断导致 FreeRTOS 错误
【发布时间】:2013-11-11 03:32:42
【问题描述】:

我已经将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);  
}

任务的执行:

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

   }
}

为了让以上两个任务运行,我使用一个Timer 和一个UART 来给它们信号量:

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

我的定时器每 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      

UART1_RxISRHandler 是我的 ISR 实现。我对 TX、定时器中断也是如此。

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

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

谢谢

【问题讨论】:

  • 嗯,通过以 230400 波特为每个字符发送一个信号量来发起线程切换是一个相当大的负担。你能缓冲 rx 字符从而减少信号量吗?
  • 亲爱的詹姆斯知道这更好,但我认为对于这个演示案例,FreeRTOS 仍然应该正常工作。
  • 一个字符,一个调度运行,每 43us :( putchar1() 是做什么的?
  • 你能展示你的信号量和中断初始化代码吗?

标签: c multithreading stm32 mplab freertos


【解决方案1】:

我也有类似的问题。

你的 uart 中断的优先级是多少?

它不应高于 FreeRTOSConfig.h 中设置的 RTOS 内核中断优先级,该优先级的默认优先级为 1,而 PIC 中断的默认优先级为 3。

这似乎导致了非常偶然的崩溃。

对于 PIC,SAVE_CONTEXT() 和 RESTORE_CONTEXT() 不是必需的,因为编译器会处理这些问题,至少在您使用编译器函数声明并避免使用 _FASTISR 或遮蔽的情况下。

【讨论】:

    【解决方案2】:

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

    定义 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 ) );
    }
    

    【讨论】:

    • 我已成功将其移植到 ATMEL SAM3X 上,效果很好。
    【解决方案3】:

    可能是堆栈溢出。 您为每个任务分配了多少堆栈?

    由于 FreeRTOS 中断处理程序与 FreeRTOS 任务共享堆栈(AFAIK,没有系统堆栈),当任务和处理程序的堆栈使用量加起来时,很容易导致溢出。

    由于任何任务都可能在中断启动时运行,这会导致零星的行为,因为任务在中断点的堆栈使用情况不同。

    要检查它,您可以简单地增加所有任务的堆栈分配大小,包括隐式创建的空闲任务(和其他系统任务,如果配置为使用一个)。或者,您可以在代码中添加以下内容以捕获堆栈溢出:

    // in FreeRTOSConfig.h
    #define configCHECK_FOR_STACK_OVERFLOW 1
    
    === somewhere in your code ===
    void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
        // Embed assembler code to put your processor into debughalt state.
        // I'm not familiar with PIC24 asm, but
        //
        // - http://www.microchip.com/forums/tm.aspx?m=434136
        //
        // may help.
        MACRO_TO_DEBUG_HALT();
    }
    

    【讨论】:

      猜你喜欢
      • 2020-10-02
      • 1970-01-01
      • 2019-09-20
      • 2018-10-25
      • 2017-03-16
      • 2023-02-03
      • 1970-01-01
      • 1970-01-01
      • 2022-01-14
      相关资源
      最近更新 更多