freeRTOS---空闲任务与阻塞延时(2)

上一篇博客我写到了如何创建博客,这篇文章我们来实现阻塞延时。发布这篇博客的同时,也上传了对应的资源。欢迎下载学习!

空闲任务是不允许阻塞的。

如何实现阻塞延时?

1.在freeRTOS.h的typedef struct tskTaskControlBlock()中添加xTicksToDelay函数。如下:

typedef struct tskTaskControlBlock
{
    volatile StackType_t *pxTopOfStack;  // 栈顶
	  ListITEM_t           xStateListItem; // 任务节点
    StackType_t          *pxStack;       // 任务栈起始地址	
    char                 pcTaskName[configMAX_TASK_NAME_LEN];	// 任务名称,字符串形式

    TickType_t           xTicksToDelay;  // 用于阻塞延时的变量,单位为SysTick的一个周期 
	
}tskTCB;

2.修改void vTaskSwitchContext(void)()(这是任务选择函数)函数。因为没有空闲任务时,只有任务一,任务二,只能在两个任务中来回切换,但现在有了空闲任务,所以也要把空闲任务计算在内。在task.c中声明(因为在main.c中已经定义了)extern TCB_t xTicksToDelay;,再修改上面的那个函数。

void vTaskSwitchContext(void)
{
    // 如果当前线程是空闲线程,那么就去尝试执行线程1或线程2,
	  // 看看他们的延时时间是否结束,如果线程的延时时间均没有到期,
	  // 那么就返回继续执行空闲任务。
	  if(pxCurrentTCB == &IdleTaskTCB)
		{
		    if(Task1TCB.xTicksToDelay == 0)
				{
				    pxCurrentTCB = &Task1TCB;
				}
				else if(Task2TCB.xTicksToDelay == 0)
				{
				    pxCurrentTCB = &Task2TCB;
				}
				else
				{
				    return;               // 线程延时均没有到期返回,继续执行空闲线程
				}
		}
		else
		{
		    // 如果当前线程是线程1或线程2的话,检查下另外一个线程,如果该线程不在延时中,就切换到该线程,
			  // 否则,判断下当前线程是否应该进入延时状态,如果是的话,就切换到空闲线程,否则就不进行任何切换。
			  if(pxCurrentTCB == &Task1TCB)
				{
				    if(Task2TCB.xTicksToDelay == 0)
						{
						    pxCurrentTCB = &Task2TCB;
						}
						else if(pxCurrentTCB -> xTicksToDelay != 0)
						{
						    pxCurrentTCB = &IdleTaskTCB;
						}
						else
            {
						    return;     // 返回,不进行切换,因为两个线程都在延时中。
						}
				}
				else if(pxCurrentTCB == &Task2TCB)
				{
				    if(Task1TCB.xTicksToDelay == 0)
						{
						    pxCurrentTCB = &Task1TCB;
						}
						else if(pxCurrentTCB -> xTicksToDelay != 0)
						{
						    pxCurrentTCB = &IdleTaskTCB;
						}
						else
            {
						    return;     // 返回,不进行切换,因为两个线程都在延时中。
						}
				}
		}			
}

在task.c中实现延时函数。
xTicksToDelay变为零时,表示延时结束。而它是以Systick中断为周期。(操作系统最小的时间周期就是Systick的时间周期。)

// 任务延时函数
void vTaskDelay( const TickType_t xTicksToDelay )
{
    TCB_t *pxTCB = NULL;
	   
	  // 获取当前任务的TCB
	  pxTCB = pxCurrentTCB;
	  
	  // 设置延时时间
	  pxTCB -> xTicksToDelay = xTicksToDelay;
	
	  // 任务切换
	  taskYIELD();
}

这些都是实现了任务的切换,但是如何实现任务的定时计数呢?

3.实现SysTick中断服务函数。
在port.c中实现。

static portFORCE_INLINE void vPortClearBASEPRIFromISR( void )
{
    __asm
	{
		    msr basepri, #0
	}
}

// Systick中断服务函数
void xPortSysTickHandler( void )
{
    // 关中断
	  vPortRaiseBASEPRI();
	  // 更新中断系统时基
	  xTaskIncrementTick();
	  // 开中断
	  vPortClearBASEPRIFromISR();
}

再在port.c中实现xTaskIncrementTick()函数。
首先定义:

static volatile TickType_t xTickCount  =   (TickType_t)0U;

在task.h中声明void xTaskIncrementTick();
在port.c中写函数xTaskIncrementTick()

void xTaskIncrementTick(void)
{
    TCB_t *pxTCB = NULL;
	  BaseType_t i = 0;
	
	  // 更新系统时基计数器xTickCount,xTickCount是一个在port.c中定义的全局变量
	  const TickType_t xConstTickCount = xTickCount + 1;
    xTickCount = xConstTickCount;

    // 扫描就绪列表中所有线程的xTicksToDelay,如果不为0,就减1
    for(i = 0;i < configMAX_PRIORITIES;i++)
    {
		    pxTCB = (TCB_t *)listGET_OWNER_OF_HEAD_ENTRY( ( &pxReadyTasksLists[i] ) );
			  if(pxTCB -> xTicksToDelay > 0)
				{
				    pxTCB -> xTicksToDelay--;
				}
		}  
		//任务切换
		portYIELD();
}

在freeRTOSConfig.h中添加:

#define configCPU_CLOCK_HZ                   ((unsigned long) 25000000)    // 系统时钟大小
#define configTICK_RATE_HZ                   ((TickType_t) 100)         // SysTick每秒中断多少次    这里对应的是10ms

在port.c中添加:

// SysTick配置寄存器
#define portNVIC_SYSTICK_CTRL_REG   (*((volatile uint32_t *) 0xe000e010))
#define portNVIC_SYSTICK_LOAD_REG   (*((volatile uint32_t *) 0xe000e014))

在port.c中的调度器函数中初始化: vPortSetupTimerInterrupt()
调度器函数中添加:

// 初始化SysTick
vPortSetupTimerInterrupt();

记得声明下面的函数呀!

void vPortSetupTimerInterrupt(void)
{
    // 设置重装载寄存器的值
	  portNVIC_SYSTICK_LOAD_REG = (configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ) - 1UL;
	
	  // 设置系统定时器的时钟等于内核时钟
	  // 使能 SysTick 定时器中断
    // 使能 SysTick 定时器
    portNVIC_SYSTICK_CTRL_REG = (portNVIC_SYSTICK_CLK_BIT|
	                               portNVIC_SYSTICK_INT_BIT|
	                               portNVIC_SYSTICK_ENABLE_BIT); 	
}
#define portNVIC_SYSTICK_INT_BIT      (1UL << 1UL)
#define portNVIC_SYSTICK_ENABLE_BIT   (1UL << 0UL)

main.c中的软件延时函数也要换为系统延时函数:

// 定义一个任务函数
void Task1_Entry(void *p_arg)
{
    for(;;)
	  {
		    flag1 = 1;
			  vTaskDelay(2);
			  flag1 = 0;
			  vTaskDelay(2);
		}
}

// 定义一个任务函数
void Task2_Entry(void *p_arg)
{
    for(;;)
	  {
		    flag2 = 1;
			  vTaskDelay(2);
			  flag2 = 0;
			  vTaskDelay(2);
		}
}
 
int main(void)
{
	  
	  prvInitialiseTaskLists();
	
	  // 创建两个任务
	  Task1_Handle = xTaskCreateStatic(Task1_Entry,
																		"Task1_Entry",
																		TASK1_STACK_SIZE,
																		NULL,
																		Task1Stack,
																		&Task1TCB	);
	   /* 将任务添加到就绪列表 */                                 
    vListInsertEnd( &( pxReadyTasksLists[1] ), &( ((TCB_t *)(&Task1TCB))->xStateListItem ) );
	
	  Task2_Handle = xTaskCreateStatic(Task2_Entry,
																		"Task2_Entry",
																		TASK2_STACK_SIZE,
																		NULL,
																		Task2Stack,
																		&Task2TCB	);
	  /* 将任务添加到就绪列表 */                                 
    vListInsertEnd( &( pxReadyTasksLists[2] ), &( ((TCB_t *)(&Task2TCB))->xStateListItem ) );  
	
// 核心函数是vListInsert,将任务控制块的列表项插入到列表中	
//    vListInsert(&pxReadyTasksLists[2],&Task1TCB.xStateListItem);
    
	  // 手动切换一个任务,启动一个调度器
		vTaskStartScheduler();
	
	  for(;;)
		{
			   	
		}
}

// 定义一个任务函数
void Task1_Entry(void *p_arg)
{
    for( ;   ; )   
	  {
		    flag1 = 1;
			  vTaskDelay(2);
			  flag1 = 0;
			  vTaskDelay(2);
		}
}

// 定义一个任务函数
void Task2_Entry(void *p_arg)
{
    for( ;   ; )
	  {
		    flag2 = 1;
			  vTaskDelay(2);
			  flag2 = 0;
			  vTaskDelay(2);        // 延时20ms
		}
}

// 获取空闲任务的内存
StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];
TCB_t IdleTaskTCB;
void vApplicationGetIdleTaskMemory(TCB_t **ppxIdleTaskTCBBuffer,  
	                                StackType_t **ppxIdleTaskStackBuffer, 
	                                uint32_t *pulIdleTaskStackSize)  
{
    *ppxIdleTaskTCBBuffer = &IdleTaskTCB;
	  *ppxIdleTaskStackBuffer = IdleTaskStack;
	  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}

如果有错误,欢迎指明!
欢迎交流学习!欢迎批评指正!

完!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值