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 编辑
//DMA中断方式
void USART1_IRQHandler(void)
{
uint16_t ch;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
{
USART_ClearITPendingBit(USART1 , USART_IT_IDLE); //必须先清除总线空闲中断标识,然后读一下数据寄存器,DMA接收才会正确(先读SR,然后读DR才能清除空闲中断标识)注意:这句必须要,否则不能够清除中断标志位。
ch = USART_ReceiveData(USART1);
DMA_Cmd(DMA1_Channel5 , DISABLE); //关闭DMA,防止处理其间有数据
DMA_ClearFlag(DMA1_FLAG_GL5 | DMA1_FLAG_TC5 | DMA1_FLAG_HT5 | DMA1_FLAG_TE5);
ch = USART1_DMA_RX_BUFFER_MAX_LENGTH - DMA_GetCurrDataCounter(DMA1_Channel5);
if (ch > 0) //表示收到数据
{
MB_USART1.receCount = ch;
memcpy(MB_USART1.mscomm_buffer , USART1_DMA_RX_Buffer , MB_USART1.receCount);
}
DMA_SetCurrDataCounter(DMA1_Channel5 , USART1_DMA_RX_BUFFER_MAX_LENGTH);
DMA_Cmd(DMA1_Channel5, ENABLE);
xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken);//释放信号量
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
void MODBUS_Task(void *pvParameters)
{
BaseType_t xResult;
BaseType_t xStatus;
const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
uint8_t Buffer[10];
uint8_t Over = 0x00;
USART1_Configuration();
USART1_DMA_Tx_Configuration();
USART1_DMA_Rx_Configuration();
while(1)
{
xResult = xSemaphoreTake(xSemaphore, portMAX_DELAY); //读取信号量
if(xResult == pdTRUE) //读取信号时成功
{
解析报文
}//if(xResult == pdTRUE)
}//while(1)
}
作者:
正点原子
时间:
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