基于GD32F1x0手动编程实现简易freertos之任务阻塞延时

上篇博客讲述了简易FreeRtos的任务创建、堆栈分配、任务启动、任务切换及任务抢占的实现过程,本篇将讲述freertos中任务阻塞延时的实现过程。在FreeRtos中优先级划分制度非常严格,跟封建社会的等级划分差不多,不同的任务根据优先级的不同被划分为三六九等,当高优先级的任务在执行时,低优先级的任务永远不可能获得CPU的控制权。那这很明显是霸权主义么,如果没有好的解决方法安抚优先级低的任务,那优先级低的任务肯定会聚众闹事,增加整个Rtos系统的不稳定性。所以呢?那肯定是有相关安抚民心的政策了,高优先级的任务大口吃肉,总得允许低优先级的喝点肉汤吧(开个小玩笑)。所以低优先级的任务需要运行获得CPU的控制权,那就是让高优先级的任务进入阻塞延时,什么意思呢?就是将高优先级的任务暂时从就绪列表移除,将其放入一个延时列表,等一段时间后再将其从延时列表中移除,再次加入就绪列表。这样在就绪列表移除高优先级任务的那段时间,低优先级的任务(也就是说高优先级任务吃完肉了,低优先级可以进去喝汤啦!)不就可以执行啦!
1、任务计数器函数实现
这个函数主要是在systick中断实现,判断是否有任务阻塞时间到,需要加入就绪列表,没有任务要解除阻塞时,则执行同优先级的下一个任务,根据返回值是否为1来确定是否触发PendSv任务切换中断,代码实现如下:

/*!
    \brief      SysTick Handler
    \param[in]  none 
		 \arg       none
    \param[out] none
    \retval     none
*/
void SysTick_Handler(void)
{
  if(xSchedulerRunning!=0){
		if(TaskIncrementTick()){
	    /* Pend a context switch. */
	    *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET;/* 触发一个PendSv中断 */
		}
	}
}
/*!
    \brief      task increment count
    \param[in]  none 
		 \arg       none
    \param[out] none
    \retval     task switch flag
*/
uint32_t TaskIncrementTick(void)
{
	Task* pxTCB=NULL;
	uint32_t xItemValue=0,xSwitchRequired =0;
	/*如果任务调度器没有被挂起*/
	if(uxSchedulerSuspended ==0){
		xTickCount+=1;//计数器值加1,这是一个全局变量
		if(xTickCount>=xNextTaskUnblockTime){//如果计数器的值大于等于下一个任务的阻塞时间时,说明有任务需要解除阻塞延时
			if(xDelayedTaskList.list_number==0){//此时阻塞延时列表的元素为空,说明没有任务需要解除阻塞
				 xNextTaskUnblockTime=0xffffffff;//那么就将下一个任务的阻塞时间设置为最大值。
			}else{
			    /*如果阻塞延时列表的元素不为空,则将阻塞时间到的任务解除阻塞*/
				 pxTCB=xDelayedTaskList.xListEnd.pxNext->pvOwner;//获取下一个要解除阻塞延时的任务
				 xItemValue=pxTCB->xStateListItem.ItemValue;//获取它要阻塞的时间
				 if(xTickCount < xItemValue){/*如果计数器的时间小于该任务的阻塞时间,说明该任务还未到解除阻塞*/
					 xNextTaskUnblockTime=xItemValue;//将下一次要解除阻塞的时间值更新为该任务的阻塞时间值
				 }else{
				   uxListRemove(&pxTCB->xStateListItem);//将该任务从延时列表移除
					 AddNewTaskToReadyList(pxTCB);//将该任务重新加入就绪列表
				 }
				 if((&pxTCB->xEventListItem)!=NULL){/*如果事件列表不为空,说明该任务在等待信号量 */
					 uxListRemove(&pxTCB->xEventListItem);//也将该任务事件列表从阻塞列表中移除,说明等待信号量超时
				 }
				 if(pxTCB->uxPriority >= pxCurrentTCB->uxPriority){
					 xSwitchRequired=1;//如果解除阻塞的任务优先级大于当前任务,那就需要执行一次任务切换了
				 }
			}
		}
		if(pxReadyTasksLists[pxCurrentTCB->uxPriority].list_number >= 1){
			xSwitchRequired=1;//同优先级的其他任务按时间片轮流执行,也需要进行一次任务切换
		}
	}else{
		++xPendedTicks;//当调度器挂起时,任务计数器增加
	}
	return xSwitchRequired;//返回任务切换标志位
}

2、任务阻塞延时函数实现
加入延时列表的任务按阻塞延时的时间从小到大排列,则阻塞时间到,依次解除阻塞。阻塞延时函数代码如下:

/*!
    \brief      task blocking delay
    \param[in]  xTicksToDelay 
		 \arg       delay value
    \param[out] none
    \retval     none
*/
void vTaskDelay(const uint32_t xTicksToDelay)
{
	uint32_t  xAlreadyYielded =0;
	if(xTicksToDelay > 0){
		vTaskSuspendAll();//将任务调度器挂起
		prvAddCurrentTaskToDelayedList( xTicksToDelay,0);//将任务加入阻塞延时列表,这另外一个参数是是否允许加入挂起列表
		xAlreadyYielded=xTaskResumeAll();//恢复任务调度器
	}
	if(xAlreadyYielded==0){//恢复任务调度器后,需要执行一次上下文切换
		*( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET;/*trigger a PendSv interrupt */
	}
}

/*!
    \brief      task increment count
    \param[in]  none 
		 \arg       none
    \param[out] none
    \retval     task switch flag
*/
void prvAddCurrentTaskToDelayedList( uint32_t xTicksToWait, const uint32_t xCanBlockIndefinitely)
{
	uint32_t xTimeToWake;
	const uint32_t xConstTickCount = xTickCount;//获取任务计数器
	uxListRemove( &( pxCurrentTCB->xStateListItem ) );//将当前任务从就绪列表移除
	if( ( xTicksToWait == 0xffffffff ) && ( xCanBlockIndefinitely !=0 ) ){//如果任务阻塞的时间为最大值,且允许将任务加入挂起列表
		 vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );//则将任务加入挂起列表
	}else{
		 xTimeToWake = xConstTickCount + xTicksToWait;//计算任务阻塞延时,当前的计数值加上任务需要阻塞的时间,也就是相对延时
		 pxCurrentTCB->xStateListItem.ItemValue= xTimeToWake;//该任务状态列表的Value值为需要阻塞的时间。
		 if(xTimeToWake < xConstTickCount){/*任务计数器值溢出 */
			 vListInsert(&pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );//则将阻塞任务加入溢出延时列表
		 }else{
			 vListInsert( &xDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );//阻塞任务加入延时列表。
			 if(xTimeToWake < xNextTaskUnblockTime){
				 xNextTaskUnblockTime = xTimeToWake;//如果该阻塞任务的阻塞时间小于下一个要解除阻塞的任务时间,则更新下一个解除阻塞时间
			 }
		 }
	}	
}

3、实验验证
该实验是创建了TaskA、TaskB及TaskC,任务A和B的优先级为2,任务C的优先级为3,systick中断时间为10ms,当任务C运行500次后,将任务C进入阻塞时间3秒,这个时候任务A和任务B轮流执行。实验现象为C执行一段时间后,A和B轮流执行,之后C又执行,A和B又轮流执行…,这样高优先级的任务和低优先级的任务不就都可以执行了么。代码如下:

/*!
    \file  main.c
    \brief led spark with systick 
*/



#include "gd32f1x0.h"
#include <stdio.h>
#include "main.h"
#include "gd32f1x0_eval.h"
#include "task.h"

__align(4) static char Task_Stack_A[512]={0};
__align(4) static char Task_Stack_B[512]={0};
__align(4) static char Task_Stack_C[512]={0};
void Task_A(void);
void Task_B(void);
void Task_C(void);
__align(4) Task TCB_A,TCB_B,TCB_C;
/*!
    \brief      main function
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{
   gd_eval_com_init(EVAL_COM1); 
   Creat_Task(Task_A,128,2,&TCB_A,Task_Stack_A);
	 Creat_Task(Task_B,128,2,&TCB_B,Task_Stack_B);
	 Creat_Task(Task_C,128,2,&TCB_C,Task_Stack_C);
	 Task_Start();
   //while (1);
}

void Task_A(void)
{
  while(1){
		
	  printf("taskA is running!!!\n");
	}
}

void Task_B(void)
{
	while(1){
	  printf("taskB is running!!!\n");
	}
}

void Task_C(void)
{
	uint16_t num=0;
  while(1){
	  printf("taskC is running!!!\n");
#if 1
		num++;
		if(num==500){
			 num=0;
		   vTaskDelay(300);
		}
#endif
	}
}
/* retarget the C library printf function to the USART */
int fputc(int ch, FILE *f)
{
    usart_data_transmit(EVAL_COM1, (uint8_t)ch);
    while(RESET == usart_flag_get(EVAL_COM1, USART_FLAG_TBE));
    return ch;
}

代码我已上传到https://download.csdn.net/download/qq_31446727/85163156地址,大家可下载测试,如有不解或者不对的地方,欢迎大家在博文下留言,大家互相交流,共同学习进步!!!

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
GD32F303R微控制器上,您可以使用FreeRTOS实现任务调度。以下是一个简单的示例,演示如何在GD32F303R上使用FreeRTOS创建和调度任务。 首先,确保您已经在GD32F303R上安装了FreeRTOS库,并将其包含在您的项目中。接下来,创建一个任务,并在任务中编写要执行的代码。 ```c #include "FreeRTOS.h" #include "task.h" void task1(void *pvParameters) { // 任务1的代码 while(1) { // 执行任务1的操作 } } void task2(void *pvParameters) { // 任务2的代码 while(1) { // 执行任务2的操作 } } int main(void) { // 初始化系统 // 创建任务 xTaskCreate(task1, "Task 1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL); xTaskCreate(task2, "Task 2", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL); // 启动任务调度器 vTaskStartScheduler(); // 如果一切正常,永远不会到达这里 while(1); return 0; } ``` 在上面的示例中,我们创建了两个任务`task1`和`task2`,并使用`xTaskCreate`函数在操作系统中创建这些任务。函数参数依次是任务函数指针、任务名称、堆栈大小、任务参数、任务优先级和任务句柄。 然后,我们使用`vTaskStartScheduler`函数启动任务调度器,它会开始调度已创建的任务任务调度器会根据任务的优先级和调度算法决定哪个任务应该运行。 注意,在任务函数内部,应该使用适当的延时函数(如`vTaskDelay`)或阻塞函数(如`vTaskSuspend`)来控制任务执行的时间和顺序。 以上是一个简单的示例,演示了如何在GD32F303R上使用FreeRTOS实现任务调度。根据您的具体需求,您可以创建更多的任务,并在任务之间共享资源。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值