OpenEdv-开源电子网

标题: 基于STM32的FreeRtos下开启DMA接收空闲中断系统卡死的问题 [打印本页]

作者: Jaken    时间: 2020-5-26 14:01
标题: 基于STM32的FreeRtos下开启DMA接收空闲中断系统卡死的问题
向大佬们求助!!在FreeRTOS系统上添加了三个任务,解析报文任务、打印函数任务、指示灯检测任务,每当串口来了个IDLE中断,其他任务都会先执行一次,但然后就卡住了。这是为啥呀?
//串口中断函数
void USART1_IRQHandler(void)                       
{
       
        uint32_t temp=0;
         if(__HAL_USART_GET_FLAG(&UART1_Handler, USART_FLAG_IDLE)!=RESET)
    {
        __HAL_UART_CLEAR_IDLEFLAG(&UART1_Handler);  
        HAL_UART_DMAStop(&UART1_Handler);  
        temp = UART1_Handler.hdmarx->Instance->NDTR;
        rx_len= USART_REC_LEN-temp;                       
        HAL_UART_Receive_DMA(&UART1_Handler,USART_RX_BUF,USART_REC_LEN);
        Comp_Flag=1;
         vTaskResume(Fetch_Da_task);
                          
    }


//解析报文任务
void Fetch_Da_task(void *pvParameters)
{
        const u8* qt = USART_RX_BUF;
        while(1)
        {
            if(Comp_Flag)
                  {
                          Buffer=My_ctoi(qt, Buffer);   //解析报文
                          Tak_Data();                         //提取报文
                                Comp_Flag=0;
                        }
                        else vTaskSuspend(Fetch_Da_task);
                       
                         vTaskDelay(500);
        }
}





作者: 霸王猫    时间: 2020-5-26 14:01
  提个小小的建议,在FREERTOS下编程你应该忘记前后台的编程方法。

   1、在串口中断服务程序中
          你设置  Comp_Flag=1;
          然后在解析任务中用   if (Comp_Flag ) 方法检测 是否有报文,是前后台编程的方法

  2、 FreeRTOS编程方法如下
        (1)、定义一个信号量
         (2)、在串口中断中检测到数据后,释放信号量
          (3)、定义一个高优先级的任务void Fetch_Da_task(void *pvParameters),目的是只要中断服务程序释放信号量就可以立即获得CPU的控制权
          (4)、在任务void Fetch_Da_task(void *pvParameters) 中读取信号量
                      如果读取到信号量,就解析该报文
      
      3、还发现一个问题
           你在中断服务程序中调用  vTaskResume(Fetch_Da_task);  问题很严重。
作者: 霸王猫    时间: 2020-5-26 21:14
本帖最后由 霸王猫 于 2020-5-27 13:12 编辑




作者: 正点原子    时间: 2020-5-27 01:29
帮顶
作者: fww123    时间: 2020-5-27 09:45
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
    {
        USART1->SR;
        USART1->DR; //清USART_IT_IDLE标志

        DMA_Cmd(DMA1_Channel5, DISABLE);

        USART1_RX_LEN = USART1_REC_LEN - DMA_GetCurrDataCounter(DMA1_Channel5);

        DMA_SetCurrDataCounter(DMA1_Channel5, USART1_REC_LEN);//设置传输数据长度

        xEventGroupSetBitsFromISR(usart1_t.eventhandle, UART_EVENT_RECIVE_STATUS, &xHigherPriorityTaskWoken);

        DMA_Cmd(DMA1_Channel5, ENABLE);//打开DMA
    }
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);


if(xEventGroupWaitBits(usart1_t.eventhandle, UART_EVENT_RECIVE_STATUS, pdTRUE, pdFALSE, timeout))
    {
        *(u32 *)buf = (u32)&USART1_RX_BUF;
        cnt = USART1_RX_LEN;
        USART1_RX_LEN = 0;

        return  cnt;
    }

你这个中断里不能让任务恢复,可以让任务一直跑着,长时间等待一个状态,可以用事件标志,或者信号量都可以实现。
作者: fww123    时间: 2020-5-27 09:46
我这个在stm32f105底下实现的代码,用了很长时间,没有问题的。
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
    {
        USART1->SR;
        USART1->DR; //清USART_IT_IDLE标志

        DMA_Cmd(DMA1_Channel5, DISABLE);

        USART1_RX_LEN = USART1_REC_LEN - DMA_GetCurrDataCounter(DMA1_Channel5);

        DMA_SetCurrDataCounter(DMA1_Channel5, USART1_REC_LEN);//设置传输数据长度

        xEventGroupSetBitsFromISR(usart1_t.eventhandle, UART_EVENT_RECIVE_STATUS, &xHigherPriorityTaskWoken);

        DMA_Cmd(DMA1_Channel5, ENABLE);//打开DMA
    }
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);


if(xEventGroupWaitBits(usart1_t.eventhandle, UART_EVENT_RECIVE_STATUS, pdTRUE, pdFALSE, timeout))
    {
        *(u32 *)buf = (u32)&USART1_RX_BUF;
        cnt = USART1_RX_LEN;
        USART1_RX_LEN = 0;

        return  cnt;
    }

你这个中断里不能让任务恢复,可以让任务一直跑着,长时间等待一个状态,可以用事件标志,或者信号量都可以实现
作者: 霸王猫    时间: 2020-5-27 13:06
本帖最后由 霸王猫 于 2020-5-27 13:15 编辑

楼主主要的问题如下:

   1、在中断服务程序中使用唤醒任务API---》vTaskResume(Fetch_Da_task);

   2、标准的做法如下:
        定义1个信号量,用于唤醒Fetch_Da_task任务。
       (1)、中断服务程序中调用释放信号量API函数,注意:一定要使用ISR的释放信号量API函数,例如:xSemaphoreGiveFromISR
        (2)、Fetch_Da_task任务中调用读取信号量API函数,例如:xSemaphoreTake
  3、你根本就没有必要让该任务挂起呀!
         当使用了信号量后,如果收到信号量该任务    Fetch_Da_task 会自动被唤醒,执行完后,自动会被操作系统挂起的呀!
   直到下次再次收到信号量。所以你根本没有必要调用
vTaskSuspend(Fetch_Da_task);挂起Fetch_Da_task呀!

4、感觉楼主还停留在前后台编程模式中,没有真正进入到FREERTOS编程模式。
        在FREERTOS编程模式下是不会使用(1)、在中断服务程序中置标识Comp_Flag=1
       (2)、在Fetch_Da_task任务中通过if(Comp_Flag) 来判断是否收到数据这种方法的。
      在FREERTOS编程模式下使用信号量即可,即:
              中断服务程序中调用释放信号量API函数,注意:一定要使用ISR的释放信号量API函数。
             Fetch_Da_task任务中调用读取信号量API函数

作者: 霸王猫    时间: 2020-5-28 16:30
在FREERTOS编程模式下,虽然使用:
     (1)、在中断服务程序中置标识Comp_Flag=1
      (2)、在Fetch_Da_task任务中通过if(Comp_Flag) 来判断是否收到数据这种方法
   也可以解析报文,但是【实时性不高】,你需要将Fetch_Da_task任务设计成周期性任务

     用信号量的方式实现,实时响应快(前提是你把Fetch_Da_task任务的优先级定义的高一些)。


作者: ultraelec    时间: 2020-5-28 17:31
- 在中断中恢复任务使用支持中断的API: vTaskResumeFromISR()函数,而不是vTaskResume()
- vTaskSuspend() 和 vTaskResumeFromISR() 的参数为TaskHandle_t类型,即任务句柄,而不是函数名(函数指针)。

作者: Jaken    时间: 2020-6-2 11:17
霸王猫 发表于 2020-5-26 14:01
提个小小的建议,在FREERTOS下编程你应该忘记前后台的编程方法。

   1、在串口中断服务程序中

你说的这些都是问题,还有个小问题是堆栈大小不够,一直被忽略了




欢迎光临 OpenEdv-开源电子网 (http://47.111.11.73/) Powered by Discuz! X3.4