当前位置: 首页 > news >正文

28-FreeRTOS内核控制-延时-临界区

一、FreeRTOS的内核控制接口分析

1.1 函数taskYIELD

        此函数用于进行任务切换,此函数本质上是一个宏。它允许当前任务主动放弃CPU使用权,将控制权转移给调度器,以便调度器可以选择另一个就绪任务运行。taskYIELD通常用于协作式多任务系统中,任务在没有更多工作可做或需要等待某些事件时调用此函数。通过调用taskYIELD,任务可以显式地让出CPU资源,从而提高系统的响应性和效率。此外,taskYIELD的实现通常依赖于特定的硬件平台和操作系统,以确保任务切换的原子性和正确性。

1.2 函数taskENTER_CRITICAL

        进入临界区(即共享资源区),用于任务函数中,此函数本质上是一个宏,它通过禁用中断来确保任务在临界区内执行时不会被其他任务或中断打断,从而保护共享资源的完整性和一致性。在进入临界区后,任务可以安全地访问和修改共享数据,而不会受到并发访问的干扰。完成临界区操作后,应调用taskEXIT_CRITICAL函数来恢复中断并退出临界区,以允许其他任务和中断继续执行。这种机制在多任务系统中尤为重要,尤其是在实时操作系统中,用于确保任务之间的同步和资源的正确管理。

1.3 函数taskEXIT_CRITICAL

        退出临界区,用于任务函数中,此函数本质上是一个宏,用于在任务执行过程中安全地退出临界区,确保在退出临界区时不会发生任务切换或中断,从而保护共享资源的完整性。该宏通常会taskENTER_CRITICAL配对使用,以确保在进入和退出临界区时的一致性。taskEXIT_CRITICAL的实现通常依赖于特定的操作系统或实时内核的底层机制,以提供高效的临界区管理。

1.4函数taskENTER_CRITICAL_FROM_ISR

进入临界区,用于中断服务函数中,此函数本质上是一个宏。

1.5函数taskEXIT_CRITICAL_FROM_ISR

退出临界区,用于中断服务函数中,此函数本质上是一个宏。

1.6函数taskDISABLE_INTERRUPTS

关闭可屏蔽的中断,此函数本质上是一个宏。

1.7函数taskENABLE_INTERRUPTS

打开可屏蔽的中断,此函数本质上是一个宏。

1.8函数vTaskStartScheduler

启动任务调度器,且会创建空闲任务。

1.9函数vTaskEndScheduler

关闭任务调度器。

1.10函数vTaskSuspendAll

挂起任务调度器,调用此函数不需要关闭可屏蔽中断即可挂起任务调度器。

1.11 函数xTaskResumeAll

此函数用于将任务调度器从挂起状态恢复。

临界区指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待(例如:bounded waiting 等待法),有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用

二、睡眠延时函数

1.1 vTaskDelay(常用)

        在UCOSIII 中延时函数OSTimeDly()可以设置为三种模式:相对模式、周期模式和绝对模式。在FreeRTOS中延时函数只有相对模式和绝对模式,在FreeRTOS中不同的模式用的函数不同,其中函数 vTaskDelay()是相对模式(相对延时函数),函数 vTaskDelayUntil()是绝对模式(绝对延时函数)。函数vTaskDelay()在文件 tasks.c中有定义,要使用此函数的话宏INCLUDE_vTaskDelay必须为1,函数代码如下:

//xTicksToDelay:要延时的时间节拍数,该数值须大于0。void vTaskDelay( const TickType_t xTicksToDelay )

注意延时的时间为:节拍数*系统节拍时间

系统默认时间节拍:1ms(正常情况1ms请求一次任务切换)

系统已经使用配置Systick 1ms中断一次,用于系统节拍时间,任务是基于系统节拍时间进行任务调度。

SysTick->LOAD,下图是如何配置自己的时间节拍

系统节拍修改宏为configTICK_RATE_HZ, 系统节拍时间为:1/configTICK_RATE_HZ。系统节拍时间最好在1~10ms之间。过短,就是出现不是在切换任务就在切换任务的路上,过长,系统的实时性差。

1.2 vTaskDelayUntil(绝对延时)-了解

        函数 vTaskDelayUntil()会阻塞任务,阻塞时间是一个绝对时间,那些需要按照一定的频率运行的任务可以使用函数vTaskDelayUntil(),即使任务中执行的代码逐渐添加,也能让任务按照固定的频率运行(要求:任务总的执行时间要小于延时时间)。


void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime,const TickType_t xTimeIncrement )

代码示例:

static void app_task1(void* pvParameters)
{for(;;){printf("app_task1 is running ...,tick count = %u\r\n",xTaskGetTickCount());/* 相对延时:任务延时2000个节拍,每个节拍为1ms,所以延时2000ms */vTaskDelay(2000);}
}   static void app_task2(void* pvParameters)
{uint32_t i=0,j=1;TickType_t xLastWakeTime;/* 初始化上次唤醒时间为当前时间 */xLastWakeTime = xTaskGetTickCount();//一次循环时间为2Sfor(;;){for(i=0; i<j*10000; i++);j+=10;printf("app_task2 is running ...,tick count = %u\r\n",xTaskGetTickCount());/* 绝对延时:任务延时2000个节拍,每个节拍为1ms,所以延时2000ms */		vTaskDelayUntil(&xLastWakeTime, 2000);}
} 

实验效果:可知,每次延时2s(误差可以省略)

app_task2 is running ...,tick count = 0app_task1 is running ...,tick count = 47app_task2 is running ...,tick count = 2002app_task1 is running ...,tick count = 2096app_task2 is running ...,tick count = 4005app_task1 is running ...,tick count = 4150

总结:

任务2使用绝对延时能够给按照逼近2000个节拍频率固定运行(当前计数值:0-2002-4005-6007-8009-10012),任务1使用相对延时每次运行相隔时间不保证固定(当前计数值:47-2096-4150-6206-8264-10326)。

1.3 相对延时与绝对延时对比

相对延时:只会延时其任务的相对时间

绝对延时:会把其他突发事件的时间也计算进来

三、自定义延时函数

2.1 微秒延时

void delay_us(uint32_t nus)
{		uint32_t ticks;uint32_t told,tnow,tcnt=0;uint32_t reload=SysTick->LOAD;	//系统定时器的重载值	    	 ticks=nus*(SystemCoreClock/1000000);//需要的节拍数 told=SysTick->VAL;        	//刚进入时的计数器值/* 挂起调度器[可选,会导致高优先级任务无法抢占当前任务,但能够提高当前任务时间的精确性] */vTaskSuspendAll();	while(1){tnow=SysTick->VAL;if(tnow!=told){	 /* SYSTICK是一个递减的计数器 */if(tnow<told)tcnt+=told-tnow;		else tcnt+=reload-tnow+told+1;	  told=tnow;/* 时间超过/等于要延迟的时间,则退出。*/if(tcnt>=ticks)break;			}  }/* 恢复调度器[可选] */xTaskResumeAll();
}  

使用us级延时,不要超1000us,过长时间的微秒延时会导致系统实时性。

2.2 毫秒延时

void delay_ms(uint32_t nms)
{vTaskDelay(nms);
}

延时整个项目的代码示例:

https://download.csdn.net/download/m0_63622771/90887340

四、FreeRTOS共享资源的基本概述

1.1 基本概念

        共享资源典型的共享资源有:全局变量、I/O设备中的寄存器、多个任务访问的函数、缓冲区等。共享资源的可靠访问,任务必须对数据具有独享权变得极其重要,否则将可能导致任务间的竞争与数据损坏,进而引发系统崩溃、数据不一致、死锁、资源泄露等问题。

        为确保共享资源的安全访问,通常需要采用互斥锁、信号量、条件变量、原子操作等同步机制,以及设计合理的资源管理策略,如优先级继承、资源分配协议等,以避免任务间的冲突并保证系统的稳定性和可靠性。

1.2 种类说明

最常用的独占共享资源的方法有以下几种:
  • 关中断(就可以不会发送中断了。所以独占资源)
  • 禁止任务调度(给调度器上锁,调度器停摆)
  • 使用信号量(计数型的型号量、二值信号量)
  • 使用互斥型信号量(互斥锁)

       获得独占共享资源的方法,取决于代码访问共享资源的速度,即占用共享资源的时间(重点),具体使用说明如下表:

选择题

1.以下请问使用( A )方式保护最为合适。

void T1(void *parg)
{while(1){ ...........喂狗    ...........        }
}A. 关中断、开中断    B.给调度器上锁、解锁    C.信号量    D.互斥锁

2.以下请问使用( D )方式保护最为合适。

uint32 g=0;
void T1(void *parg)
{while(1){ ...........g++;	...........		}
}void T2(void *parg)
{uint32_t a=0;while(1){ ...........a = g;	...........		}
}A. 关中断、开中断    B.给调度器上锁、解锁    C.信号量     D.互斥锁

3.以下请问使用( A )方式保护最为合适。

uint32 g=0;void T1(void *parg)
{while(1){ ...........g++;	...........		}
}void T2(void *parg)
{uint32_t a=0;while(1){ ...........a = g;	...........		}
}void USART1_IRQHandler(void)
{...........g = USART_ReceiveData(USART1);	...........  }A. 关中断、开中断    B.给调度器上锁、解锁    C.信号量    D.互斥锁

4.以下请问使用( D )方式保护最为合适。

void T1(void *parg)
{while(1){ ...........printf("hello teacher.chen\r\n");	...........		}
}void T2(void *parg)
{while(1){ ...........printf("good bye teacher.chen\r\n");	...........		}
}A. 关中断、开中断    B.给调度器上锁、解锁    C.信号量    D.互斥锁

5.以下请问使用( D )方式保护最为合适。

void T1(void *parg)
{while(1){ ...........printf("hello teacher.chen\r\n");	...........		}
}void T2(void *parg)
{while(1){ ...........printf("good bye teacher.chen\r\n");	...........		}
}void T3(void *parg)
{while(1){ ...........printf("he's teacher.chen\r\n");	...........		}
}A. 关中断、开中断    B.给调度器上锁、解锁    C.信号量    D.互斥锁

辨析题

1.请分析以下代码。

//高优先级
void T1(void *parg)
{while(1){ 调度器上锁printf("hello teacher.chen\r\n");delay_ms(1000)	调度器解锁		}
}
//低优先级
void T2(void *parg)
{while(1){ 调度器上锁printf("good bye teacher.chen\r\n");delay_ms(1000)	调度器解锁			}
}调度器上锁,T1任务打印hello teacher.chen,执行delay_ms后导致停止了任务调度器,
导致两个任务都无法执行。

2.请分析以下代码。

void T1(void *parg)
{while(1){ 等待互斥锁printf("hello teacher.chen\r\n");delay_ms(1000);释放互斥锁	}
}void T2(void *parg)
{while(1){ 等待互斥锁printf("good bye teacher.chen\r\n");delay_ms(1000);释放互斥锁			}
}只有得到锁的任务才能执行

3.请分析以下代码。

void app_task_init(void *parg)
{创建信号量,初值为0
}void T1(void *parg)
{while(1){ 等待信号量printf("hello teacher.chen\r\n");释放信号量		}
}void T2(void *parg)
{while(1){ 等待信号量printf("good bye teacher.chen\r\n");释放信号量			}
}只有得到信号量的任务才能执行

http://www.xdnf.cn/news/610003.html

相关文章:

  • MCP如何助力智能交通系统?从数据融合到精准决策
  • 科技初创企业创新推动商业未来
  • 单元测试学习笔记
  • mqtt协议(cJSON格式举例)
  • 交换机的连接方式堆叠和级联
  • 3D个人简历网站 6.弹出框
  • 基于OAuth2-proxy和Keycloak为comfyui实现SSO
  • 深入解析Spring Boot与Redis集成:高性能缓存实践
  • 软件工程(八):UML类图的几种关系
  • Redis-RedisShake数据迁移工具
  • Linux--初识文件系统fd
  • Python的FastApi随笔记
  • MySQL强化关键_016_存储引擎
  • 每天分钟级别时间维度在数据仓库的作用与实现——以Doris和Hive为例(开箱即用)
  • 第四十七节:图像分割-分水岭算法
  • canal实现mysql数据同步
  • JavaWeb面试题 (一)
  • window 显示驱动开发-视频内存供应和回收(三)
  • STM32F103_Bootloader程序开发01 - 什么是IAP?跟OTA有什么关系?
  • 关于 Web 风险点原理与利用:6. 逻辑风险点
  • 跨平台三维可视化与图形库.VTK图形库.
  • CATIA高效工作指南——常规配置篇(三)
  • SAP在化工行业的数字化转型:无锡哲讯科技的赋能实践
  • 微气象在线监测装置:精准感知环境变化的科技之眼
  • win32相关(句柄表)
  • 【Mini-F5265-OB开发板试用测评】2、关于platform.c中的串口号初始化修改的建议
  • GO语言学习(九)
  • Flask项目打开总是上一个项目的网页
  • 短视频与直播场景下的美颜SDK优化方案:滤镜与特效如何平衡性能与美感?
  • 精益数据分析(80/126):病毒式传播系数实战计算与增长策略优化