FreeRtos 学习笔记

一、动态静态任务的创建

void Task1Function( void *param )
{
	while(1)
	{
		printf("1");
	}				
}

void Task2Function( void *param )
{
	while(1)
	{
		printf("2");
	}				
}

void Task3Function( void *param )
{
	while(1)
	{
		printf("3");
	}				
}
/*静态任务的TCB结构体和栈缓冲区放在main外面*/
StaticTask_t xIdleTaskTCB;
StackType_t xIdleTaskStack[100];//堆栈缓冲区

StackType_t xStackBuffer[100];//创建静态任务需要的类型
StaticTask_t xTask3TCB;


void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
                                    StackType_t ** ppxIdleTaskStackBuffer,
                                    uint32_t * pulIdleTaskStackSize )
{
	* ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
	* ppxIdleTaskStackBuffer = xIdleTaskStack;
	* pulIdleTaskStackSize  = 100;
}

int main( void )
{
  TaskHandle_t xHandleTask1;//Task1的句柄,可以定义为全局变量
  

#ifdef DEBUG
  debug();
#endif

	prvSetupHardware();//RCC和外设的初始化
 
	printf("Hello, world!\r\n");
	
	xTaskCreate(Task1Function,"Task1",100,NULL,1,&xHandleTask1);
	xTaskCreate(Task2Function,"Task2",100,NULL,1,NULL);
	xTaskCreateStatic(Task3Function,"Task3",100,NULL,1,xStackBuffer,&xTask3TCB);
	/* Start the scheduler. */
	vTaskStartScheduler();//开启调度程序

	/* Will only get here if there was not enough heap space to create the
	idle task. */
	return 0;
}

静态任务的函数体和数据类型 

  普通任务的函数体和数据类型

 二、进一步实验

 现象 。 注意:更高优先级的、或者后面创建的任务先运行。

 void *)4,(void *)5  类型匹配

 //任务4和5的栈是不一样的,所以用同一个函数可以执行不同的效果

 结束任务实验

void Task2Function(void * param)
{
	int i = 0;
	
	while (1)
	{
		task1flagrun = 0;
		task2flagrun = 1;
		task3flagrun = 0;
		printf("2");

		if (i++ == 100)
		{
			vTaskDelete(xHandleTask1);//杀死任务1
		}

		if (i == 200)
		{
			vTaskDelete(NULL);//自杀
		}

	}
}

 

 

程序崩溃 

 

三、任务状态

Tack相当于中断,1ms切换一次任务 

 

 

 四、两个Delay函数

 

 在vTaskDelay退出来之前会更新&tStart    然后下次运行时就会用更新的tStart + 20

 

 五、空闲任务及其钩子函数

  对任务的清理工作是放在空闲任务里面的。如果自己自杀,那自己就不能清理内存。会导致创建失败,要让空闲任务杀死。下面自己自杀,导致内存不足,无法创建任务。

 或者再创建一个空闲任务,再自己自杀也是可以的。这个时候可以创建钩子函数:

 

 需要创建configUSE_IDLE_HOOK  为 1。创建空闲任务,调用钩子函数。清除内存。

 

 六、任务调度算法

 阻塞状态的任务,等待的事件分为两类:

 1.时间相关的事件:两个Delay函数

 2.同步事件:同步事件就是,某个任务在等待某些信息,别的任务或者中断服务程序会给它发送                          信息。

    发送信息的方法: 任务通知(Task notification)

                                  队列(Queue)

                                  事件组(event group)

                                  信号量(semaphoe)

                                  互斥量(mutex)

这些方法用来发送同步信息比如表示某个外设得到了数据。

这节课分为三部分的理解:

1.是否抢占?   (配置项: configUSE_PREEMPTION)

 2.允许抢占时,是否允许时间片轮转(是否轮流执行)(配置项:configUSE_TIME_SLICING)

3.允许抢占,允许时间片轮转,空闲任务是否让步?(配置项:configIDLE_SHOULD_YIELD)

七、同步与互斥

什么叫同步?就是:哎哎哎,我正在用厕所,你等会。(存在依赖关系)
什么叫互斥?就是:哎哎哎,我正在用厕所,你不能进来。(存在排斥关系)

 

 通过flagCalcEnd标志来通知任务2实现同步:

          缺点是浪费cpu资源。最好让等待的任务进入阻塞状态或者进入休眠状态。需要用的时候再唤醒。这样就不用一直轮询任务2了,大大节省资源。

 下面举一个串口互斥的例子

 

这样就可以实现独立打印一句话的功能了。但是也会出错。

    我们知道,任务在没有执行完成时,只要1个Tick到了就会切换另一个任务 。

那么就可能发生下面的情况:

 为了解决这些问题引入新的概念。细节后面再讲

八、队列

   把数据写到队列头部不会覆盖原来的数据,队列中的数据使用环形缓冲区管理数据,把数据放到头部时,会先移动头部位置,并不会覆盖原来数据。

 

     来学习队列的创建 

  然后往队列里发送数据

如果xTicksToWait不是零,那么他就会把这个调用这个函数的任务
放到 List_t xTasksWaitingToSend ; 里面等待发送

 读队列

 如果xTicksToWait不是零,那么他就在List中等待

 用队列实现同步:

       首先定义Handle,创建队列

       编写任务实现同步,两秒后打印val的值

 用队列实现互斥:

 InitUARTLock();在main函数中初始化一下。

同样的如果不Delay,就绪态的任务3,抢不过运行中的任务4。用taskYIELD()会主动发起一次任务切换,不用阻塞。

   如果有很多不同的任务发送不同的数据,那么接收的时候如何区分?   

    当传输大数据时就可以传输他的地址:

九、队列集

      邮箱也是队列,只不过长度为1.

从多个队列中得到数据(队列集)

    在实际应用中,我们需要从多个队列中得到数据:

 队列集放的都是队列。队列集实质上也是队列

任务1创建队列  任务2创建队列   任务3读QueueSet  

十、信号量

 无法通过信号量传输数据,只能表示数据的数量,核心是计数值。

        信号量最小为0  

 前面我们用队列实现同步,现在我们用信号量实现同步:

     使用信号量时不需要传递数据,不需要复制数据,节省空间和提高效率。

    可以计数很多次,这里只计数一次

这个程序只要删除掉 

 打印出来的数就不是一千万。也就是无法保证数据的完整性。怎么解决这个问题? 

 阻塞一下或者用taskYIELD主动发起任务切换,让任务2运行

     说明一下,任务2先运行,然后它被一直阻塞,直到有信号量可以获取,只要任务1give

任务二就可以运行。

 用二进制信号量实现互斥

main函数创建 

实验结果可以互斥访问串口。 

十一、互斥量

互斥量用来保护临界资源。有了二进制信号量为什么还要有互斥量? 

        二进制信号量不能做到谁上锁就由谁解锁。这会导致A上锁 ,B开锁,C和A抢占资源。要解决这个问题就要做到:上锁解锁的代码成对出现,在临界代码中不要解锁。

 互斥量可以提高优先(优先级继承)    

优先级反转的解决方法是优先级继承 

 递归上锁

怎么解决

递归锁是互斥量的另一种形式 

互斥量有两种,一种是普通的互斥量它具有优先级继承的功能,另一种是递归锁,他除了有优先级继承的功能外他还有递归的功能。

一种

 另一种

 做实验  常规使用

Handle和信号量一样,Take   Give也是一样 。

不需要手动Give。互斥量的初始值就是1 

实验结果互斥访问

 实验优先级反转

 

 实验递归锁,谁上锁谁释放

 十二、事件组

事件组可以应用于   某个事件   若干个事件中的某个事件     若干个事件中的所有事件  但是不能用于若干个事件中的几个事件。

这些位,值为 1 表示事件发生了,值为 0 表示事件没发生 .等待就是等待他们为1

简单实现一下事件组的使用

,保留之前的队列 

  

事件组不能传递数据,只是起到通知作用,等不到事件就会阻塞,数据保护还需要自己来做。比如使用队列。 

同步点

 

 xEventGroupSync的函数定义

十三、 任务通知

   使用队列、信号量、事件组时,我们都要先创建对应得结构体,双方通过中间的结构体通信

 队列的结构体

 通知状态有三种取值

 任务通知实现轻量级信号量

 main函数里面只需要创建任务就可以了

 

 任务通知实现轻量级队列

任务通知总是不能等待指定的事件,不能等待若干个事件中的任意一个,一旦有事件就会唤醒任务。

它指定的是在入口时要不要清除某些位,在退出的时候要不要清除某些位。  

任务通知实现轻量级事件组

十四、定时器 

定时器三要素:超时时间、函数、单次触发还是周期触发。

定时器的常规使用

 定时器消抖

任务1,任务2不做事。

最后的效果是按好几次才捕获到一次按键输入。

十五、中断管理

 

使用 xTimerReset(xMyTimerHandle, 0);时不能等待

 十六、资源管理

 

 如果这个中断没有这么多,那么他会有转换

  • 10
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值