STM32第二十四课(Freertos ,HAL ,cubemx, Tasksuspend)

osDelay方式,将任务挂起,是基于time base的,当时间到了之后,任务会取消挂起,

但是,有些情况下,并不知道何时需要将任务挂起,又何时再将任务恢复,这时候,就需要使用事件挂起方式,当事件到来时,将任务挂起,当另外的事件到来时,再将任务恢复。
所有有两种场景,一种是ISR内挂起任务,一种是任务内挂起任务。

tasksuspend和taskresume是成对使用。
suspend的任务,只能由resume函数解放。

如果是在ISR中使用,那么需要调用fromisr版本的API。并检查返回值。

+++++++++++++++++++++++++++++++++++++++++++
来看看key_task。

void key_task(void const * argument)
{
	u8 key;
	
	for(;;)
	{
		key=KEY_Scan(0);
		switch(key)
		{
			case WKUP_PRES:
				vTaskSuspend(Task1Task_Handler);//挂起任务1
				printf("挂起任务1的运行!\r\n");
				break;
			case KEY1_PRES:
				vTaskResume(Task1Task_Handler);	//恢复任务1
				printf("恢复任务1的运行!\r\n");
				break;
			case KEY2_PRES:
				vTaskSuspend(Task2Task_Handler);//挂起任务2
				printf("挂起任务2的运行!\r\n");
				break;
		}
		
		vTaskDelay(10);			//延时10ms 
	}
}

这个任务中,负责扫描按键,并根据扫描到的按键值,相对应的挂起某个任务或者恢复某个任务。
这是一个负责和输入进行交互的任务,
从前后台的角度考虑,它类似于前台功能,所以我们称为前台任务。更细化的角色分类,我们认为,它是一个输入任务。
对应的,不需要和输入进行交互的任务,我们称为后台任务。更细化的角色分类,我们认为,它是一个操作产出任务。

++++++++++++++++++++++++++++++++++
来看看task1_task。

void task1_task(void const * argument)
{
	u8 task1_num=0;	
	POINT_COLOR = BLACK;

	LCD_DrawRectangle(5,110,115,314); 	//画一个矩形	
	LCD_DrawLine(5,130,115,130);		//画线
	POINT_COLOR = BLUE;
	LCD_ShowString(6,111,110,16,16,"Task1 Run:000");
	
	for(;;)
	{
		task1_num++;	//任务执1行次数加1 注意task1_num1加到255的时候会清零!!
		LED0=!LED0;
		printf("任务1已经执行:%d次\r\n",task1_num);
		LCD_Fill(6,131,114,313,lcd_discolor[task1_num%14]); //填充区域
		LCD_ShowxNum(86,111,task1_num,3,16,0x80);	//显示任务执行次数
		
        vTaskDelay(1000);                           //延时1s,也就是1000个时钟节拍	
	}
}

这个任务,是一个后台任务,它有可能被前台任务挂起或者恢复。

+++++++++++++++++++++++++++++++++++++++
来看看task2_task。

void task2_task(void *pvParameters)
{
	u8 task2_num=0;
	POINT_COLOR = BLACK;

	LCD_DrawRectangle(125,110,234,314); //画一个矩形	
	LCD_DrawLine(125,130,234,130);		//画线
	POINT_COLOR = BLUE;
	LCD_ShowString(126,111,110,16,16,"Task2 Run:000");
	
	for(;;)
	{
		task2_num++;	//任务2执行次数加1 注意task1_num2加到255的时候会清零!!
        LED1=!LED1;
		printf("任务2已经执行:%d次\r\n",task2_num);
		LCD_ShowxNum(206,111,task2_num,3,16,0x80);  //显示任务执行次数
		LCD_Fill(126,131,233,313,lcd_discolor[13-task2_num%14]); //填充区域
		
        vTaskDelay(1000);                           //延时1s,也就是1000个时钟节拍	
	}
}

++++++++++++++++++++++++++++++++++++
来看看ISR的处理。

//任务句柄
extern TaskHandle_t Task2Task_Handler;

//外部中断4服务程序
void EXTI4_IRQHandler(void)
{
	BaseType_t YieldRequired;
	
	delay_ms(20);	//消抖
	if(KEY0==0)	 
	{				 
		YieldRequired=xTaskResumeFromISR(Task2Task_Handler);//恢复任务2
		printf("恢复任务2的运行!\r\n");
		if(YieldRequired==pdTRUE)
		{
			portYIELD_FROM_ISR(YieldRequired);
		}
	}		 
	 EXTI_ClearITPendingBit(EXTI_Line4);//清除LINE4上的中断标志位  
}

这里需要注意的是,当使用fromisr版本的API时,需要进行返回值判断。

当调用了API的时候,实际上是触发了SWI。
所以,当从SWI中返回的时候,又回到了ISR中。
这里,我们调用API,恢复了一个任务,在这个API中,OS进行了任务调度,下次执行时,会调度优先级最高的任务去执行,被恢复的任务,可能优先级比之前被ISR打断的任务的优先级更高,所以,有可能会直接执行这个刚刚被恢复的高优先级任务。
这就是任务嵌套。
除非高优先级任务让出CPU,否则,低优先级的任务,将无法获得重新调度。
如果我们贸然的直接从ISR中返回,那么就会造成断点丢失。当前任务的现场并没有得到保存,断点没有被合理设置。

OS的API在返回时,会返回一个抢占码,
我们需要检查API的返回值,即抢占码,
如果发现抢占码为pdTRUE,那么,调用portYIELD_FROM_ISR这个API,将当前任务的断点更新,
如果返现抢占码不是pdTRUE,那么,直接从ISR中返回,最新被恢复的任务,不会扰乱OS的调度顺序,自然也就不需要让OS对当前任务进行断点更新。

+++++++++++++++++++++++++++++++++++
ISR,通常是名为IRQ_Handler的函数。
在这个函数中,首先是进行中断源判断,中断屏蔽,等等前期工作,之后,会调用对应的callback,来执行实际需要执行的操作。
在退出ISR的最后阶段,是进行中断清标,开启中断等扫尾工作。这些工作,是由IRQHandler来完成的。

顺序为,enter ISR --> pre-pocess --> callback --> post-process --> return to break point。
所以,我们编写ISR,工作就是集中在编写callback上。

+++++++++++++++++++++++++++++++++++++

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用CubeMX配置FreeRTOS和DMA串口接收的示例代码: 1. 配置FreeRTOSCubeMX中,打开Project Manager视图,选择FreeRTOS,并启用RTOS。 2. 配置USART串口 在Pinout & Configuration视图中,选择需要使用的USART串口,并配置为接收模式。例如,将USART2配置为接收模式,波特率为115200。 3. 配置DMA 在Pinout & Configuration视图中,选择需要使用的DMA通道,并配置为USART2的接收通道。例如,将DMA1_Stream5配置为USART2的接收通道。 4. 生成代码并添加接收代码 在CubeMX中,点击"Generate Code"按钮,生成代码。然后,在main.c文件中添加以下代码,以接收USART2的数据: ``` #include "main.h" #include "FreeRTOS.h" #include "task.h" #include "usart.h" /* Buffer used for reception */ uint8_t uartRxBuffer[10]; /* Task handle */ TaskHandle_t xTaskHandle; void vTaskFunction(void *pvParameters) { while (1) { /* Wait for data to be received */ xSemaphoreTake(xSemaphore, portMAX_DELAY); /* Process received data */ printf("Received data: %s\r\n", uartRxBuffer); } } /* DMA USART2 interrupt callback */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* Give semaphore to unblock the task */ xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken); /* Clear the interrupt flag */ __HAL_DMA_DISABLE_IT(huart->hdmarx, DMA_IT_TC); __HAL_UART_CLEAR_OREFLAG(huart); /* Switch context if necessary */ portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } int main(void) { /* Initialize FreeRTOS */ xSemaphore = xSemaphoreCreateBinary(); xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, &xTaskHandle); /* Enable DMA interrupt */ __HAL_DMA_ENABLE_IT(&hdma_usart2_rx, DMA_IT_TC); /* Start USART reception */ HAL_UART_Receive_DMA(&huart2, uartRxBuffer, sizeof(uartRxBuffer)); /* Start FreeRTOS scheduler */ vTaskStartScheduler(); while (1) { } } ``` 在上述代码中,我们创建了一个FreeRTOS任务,在任务中等待接收到数据。当数据被接收时,DMA USART2中断会触发回调函数HAL_UART_RxCpltCallback(),它将释放一个信号量,以便任务可以继续处理接收到的数据。请注意,我们使用了xSemaphoreTake()和xSemaphoreGiveFromISR()函数来在任务和中断之间进行同步。 这就是使用CubeMX配置FreeRTOS和DMA串口接收的示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值