【FreeRTOS】内核实现:task.c

之前写博客说过:

一直跟着野火的教程学习,从STM32基础、初级、高级,这部分学完就是下一部分的FreeRTOS。FreeRTOS的学习分两个阶段:1.从0到1写出FreeRTOS的内核,2.移植FreeRTOS到开发板上并逐步添加外设功能。

 

这章就是手把手写task.c的内容:

FreeRTOS学习记录:

-------01.07----------------------
今日完成:(1)将书第七章《任务的定义》,任务切换,所有代码手敲一遍,尽量作注释,理解FreeRTOS中任务调度
		  (2)调试代码  ——————


------------------------------------
以下笔记:
------------------------------------
第一部分:几个头文件的作用,功能,内容
	//头文件portmacro.h :关于数据类型、函数返回值、宏定义的一些说明
		#ifndef PORTMACRO_H
		#define PORTMACRO_H

		#include "stdint.h"
		#include "stddef.h"


		//数据类型的重定义 :FreeRTOS中
		#define portCHAR	  	char    //
		#define portFLOAT		  float   //
		#define portDOUBLE		double  //
		#define portLONG		  long    //
		#define portSHORT		  short   //
		#define portSTACK_TYPE	uint32_t  //栈类型:
		#define portBASE_TYPE	long   // 基类型:根据处理器的结构决定多少位,用于函数返回值 / bool类型

		//自定义的数据类型 ,很多都是 xxx_t
		typedef portSTACK_TYPE StackType_t;
		typedef long           BaseType_t;        
		typedef unsigned long  UBaseType_t;

		#if( configUSE_16_BIT_TICKS == 1 )
			typedef uint16_t TickType_t;   //定义时基计数器16位
			#define portMAX_DELAY ( TickType_t ) 0xffff
		#else
			typedef uint32_t TickType_t; //定义时基计数器32位,根据configUSE_16_BIT_TICKS宏来定义计数器的类型
			#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
		#endif  //configUSE_16_BIT_TICKS==1


		//以下是FreeRTOS中的编程风格,弄明白这个看代码会清晰点
		/*
		变量名前缀(箭头后面的内容):
			char -> c
			short -> s
			long -> l
			portBaseType -> x  (portXxxxType -> x)
			指针 -> p  
			char*  -> pc
			long*  -> pl
		*/

		/*
		函数名:包含函数返回值、文件名、功能
			vTaskPrioritySet():返回值为 void 型,在 task.c 这个文件中定义
			xQueueReceive():返回值为 portBASE_TYPE 型,在 queue.c 这个文件中定义
			vSemaphoreCreateBinary():返回值为 void 型,在 semphr.h 这个文件中定义
		*/

		/*
		宏:大写字母为宏,前缀小写字母表示那个文件定义
		前缀:                                 宏定义的文件:
			port (举例, portMAX_DELAY)                    portable.h
			task (举例, taskENTER_CRITICAL())             task.h
			pd (举例, pdTRUE)                             projdefs.h
			config(举例, configUSE_PREEMPTION)            FreeRTOSConfig.h
			err (举例, errQUEUE_FULL)                     projdefs.h
			
		几个通用宏:
			pdTRUE            1
			pdFALSE           0
			pdPASS            1
			pdFAIL            0
		*/

		#endif /* PORTMACRO_H */
			
	//头文件portable.h : 一个作用:include portmacro.h

	//头文件FreeRTOS.h:在这里定义任务控制块
		#ifndef INC_FREERTOS_H
		#define INC_FREERTOS_H

		#include "FreeRTOSConfig.h"
		#include "portable.h"
		#include "projdefs.h"
		#include "list.h"

		//该声明放在FreeRTOS.h中为了tskTCB在其他文件使用
		//任务控制块(结构体tskTCB) :任务的身份证(概念上有点类似操作系统中进程控制块)
		typedef struct tskTaskControlBlock
		{
			volatile StackType_t*           pxTopOfStack; //栈顶指针 ,StackType_t在portmacro.h中定义
			ListItem_t                      xStateListItem;//任务结点:链表结点;可将任务控制块挂在链表中
			StackType_t*                    pxStack;   //任务栈的起始地址
			char                            pcTaskName[configMAX_TASK_NAME_LEN];//任务名称:字符串 (configMAX_TASK_NAME_LEN在FreeRTOSConfig中)
		}tskTCB;

		typedef tskTCB TCB_t; //将任务控制块重定义为:TCB_t

		#endif /* INC_FREERTOS_H */	
			
	//头文件FreeRTOSConfig.h:宏定义的一些系统配置:FreeRTOS细节上的配置
		#ifndef FREERTOS_CONFIG_H
		#define FREERTOS_CONFIG_H

		//配置TickType_t :1-> TickType_t为16位  ,0->32位
		#define configUSE_16_BIT_TICKS	               	0

		//配置任务控制块:任务名称长度为16
		#define configMAX_TASK_NAME_LEN		            ( 16 )

		//配置静态任务创建方式:1->静态创建
		#define configSUPPORT_STATIC_ALLOCATION          1
		
		//最大任务优先级的宏
		#define configMAX_PRIORITIES                    5

		#endif //FREERTOS_CONFIG_H		
			
	//头文件projdefs.h:这里定义了任务的函数名称
		#ifndef PROJDEFS_H
	#define PROJDEFS_H

	//任务入口:任务的函数名称
	typedef void (*TaskFunction_t)( void * );

	#define pdFALSE			( ( BaseType_t ) 0 )
	#define pdTRUE			( ( BaseType_t ) 1 )

	#define pdPASS			( pdTRUE )
	#define pdFAIL			( pdFALSE )

	#endif /* PROJDEFS_H */		
		
		
第二部分:重点:任务的定义和函数实现 task.h task.c		
		
	//头文件task.h中函数声明:创建任务、列表初始化、调度器...
	#ifndef __TASK_H
	#define __TASK_H

	#include "FreeRTOS.h"

	//任务句柄
	typedef void* TaskHandle_t; //空指针

	//任务创建函数(静态创建):该函数需要调用prvInitialiseNewTask()
	#if( configSUPPORT_STATIC_ALLOCATION == 1 )                //如果静态创建宏定义命中
	TaskHandle_t xTaskCreateStatic(	TaskFunction_t pxTaskCode, //任务入口:任务的函数名称
									const char * const pcName,           //任务名称:字符串
									const uint32_t ulStackDepth,         //任务栈大小:单位Byte
									void * const pvParameters,           //任务形参
									StackType_t * const puxStackBuffer,  //任务栈起始地址
									TCB_t * const pxTaskBuffer );        //任务控制块指针
	#endif /* configSUPPORT_STATIC_ALLOCATION */

												
	//初始化任务就绪列表:表示任务已经就绪,系统随时可以调度										
	void prvInitialiseTaskLists( void );  

	//开启调度器										
	void vTaskStartScheduler( void );
												
	//(批注补充)
	void vTaskSwitchContext( void );

	#endif //__TASK_H	
		
	//task.c文件
		#include "task.h"
		#include "FreeRTOS.h"

		//用于指向当前正在运行或者即将要运行的任务的任务控制块
		TCB_t * volatile pxCurrentTCB = NULL;


		//创建新任务,该函数调用pxPortInitialiseStack 定义在port.c中
		static void prvInitialiseNewTask(TaskFunction_t        pxTaskCode,   //任务入口:任务的函数名称
																			const char * const   pcName,       //任务名称:字符串
																			const uint32_t       ulStackDepth, //任务栈大小:单位Byte
																			void * const          pvParameters,//任务形参
																			TaskHandle_t * const pxHandle,        //任务句柄
																			TCB_t*               pxNewTCB)             //任务控制块
		{
			StackType_t * pxTopOfStack;
			UBaseType_t   x;
			
			//获取栈顶地址
			pxTopOfStack = pxNewTCB->pxStack + (ulStackDepth - (uint32_t)1);	
			//向下做8字节对齐
			pxTopOfStack = ( StackType_t * ) ( ( ( uint32_t ) pxTopOfStack ) & ( ~( ( uint32_t ) 0x0007 ) ) );
			
			//将任务名称存起TCB中:字符串的拷贝
			for(x = ( UBaseType_t )0;x < (UBaseType_t)configMAX_TASK_NAME_LEN;x++)
			{
				pxNewTCB->pcTaskName[x] = pcName[x];
				if(pcName[x] == 0x00) break;
			}
			
			//任务名称的长度不超过configMAX_TASK_NAME_LEN
			pxNewTCB->pcTaskName[configMAX_TASK_NAME_LEN-1] = '\0';
			
			// 初始化 TCB 中的 xStateListItem 节点 */
		  vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
			
		  // 设置 xStateListItem 节点的拥有者 
		  listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
			
			
			//初始化任务栈
			pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
			
			//让任务句柄指向任务控制块
			if ( ( void * ) pxHandle != NULL )
		  {
				*pxHandle = ( TaskHandle_t ) pxNewTCB;
		  }
		}


		//任务创建函数(静态创建):该函数需要调用prvInitialiseNewTask()
		TaskHandle_t xTaskCreateStatic(	TaskFunction_t pxTaskCode, //任务入口:任务的函数名称
										const char * const pcName,           //任务名称:字符串
										const uint32_t ulStackDepth,         //任务栈大小:单位Byte
										void * const pvParameters,           //任务形参
										StackType_t * const puxStackBuffer,            //任务栈起始地址
										TCB_t * const pxTaskBuffer )                   //任务控制块指针
		{
			TCB_t* pxNewTCB;
			TaskHandle_t xReturn;//任务句柄,用于指向任务的TCB
			
			if((pxTaskBuffer != NULL) && (puxStackBuffer != NULL) )
			{
				pxNewTCB = (TCB_t*) pxTaskBuffer;
				pxNewTCB->pxStack = (StackType_t *)puxStackBuffer;

				//创建新任务
				prvInitialiseNewTask(pxTaskCode,pcName,ulStackDepth,pvParameters,xReturn,pxNewTCB);
			}
			else
			{
				xReturn = NULL;
			}
			//返回任务句柄,如果任务创建成功,此时 xReturn 应该指向任务控制块
			return xReturn;
		}

		//定义就绪列表 configMAX_PRIORITIES宏表示最大优先级 ,结构是结点的数组
		 List_t pxReadyTasksLists[ configMAX_PRIORITIES ];

		//初始化任务就绪列表:表示任务已经就绪,系统随时可以调度										
		void prvInitialiseTaskLists( void )
		{
			UBaseType_t uxPriority;
			
			for(uxPriority = (UBaseType_t)0u;uxPriority < ( UBaseType_t ) configMAX_PRIORITIES;uxPriority++)
			{
				//调用list.c中的根节点初始化函数
				vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
			}
		}

		extern TCB_t Task1TCB;
		extern TCB_t Task2TCB;
		//开启调度器:实现任务切换,从就绪列表中找优先级最高的然后执行									
		void vTaskStartScheduler( void )
		{
			//手动指定第一个任务运行
			pxCurrentTCB = &Task1TCB;
			
			//启动调度器  xPortStartScheduler() != pdFALSE定义在port.c
			if( xPortStartScheduler() != pdFALSE)
			{
				
			}
		}


		void vTaskSwitchContext( void )
		{    
			/*两任务切换 */
			if( pxCurrentTCB == &Task1TCB )
			{
				pxCurrentTCB = &Task2TCB;
			}
			else
			{
				pxCurrentTCB = &Task1TCB;
			}
		}
		
		
第三部分:port.c 和 main函数	
	//还有个port.c文件 里面最后实现的功能用汇编完成	
		
--------------------------------------------------------------		

		

任务和任务切换: 任务怎么创建?任务怎么切换?  这是FreeRTOS的基础中的基础,非常非常重要!

    每个任务独立不干扰,所以每个任务需要独立栈空间(函数调用、中断、局部变量)
    
    程序思路:
        首先,定义一个任务栈,就是个全局数组
        然后搞一个任务控制块的结构体(包含栈顶,任务结点,任务名称)
        然后,编写创建任务函数(分动态静态,能不能自动管理内存)
        再然后,搞个任务栈的初始化函数

        有任务了,系统需要调度了。所以搞个就绪列表,
            就序列表的初始化函数,任务插入列表函数
            
        实现一个调度器,实现任务的切换(从列表中找到优先级最高的)
            启动调度器函数、任务切换函数(利用中断)

思路就是大概这样一个思路,实现的代码还有很多细节需要深究的地方...感觉不是100%看懂,但也理解个大概了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值