【FreeRTOS】详细讲解FreeRTOS的软件定时器及通过示例讲述其用法

news2024/11/16 17:37:06

软件定时器

  所谓定时器,也就可以类比生活中人们常用的闹钟,可以单次响铃提醒,也可以间隔固定时间响铃提醒;与FreeRTOS定时器不同的是周期不同,FreeRTOS的周期更加短,一般使用毫秒(ms)、秒(s)。
  软件定时器,是指定时器的触发方式,软件定时器一旦到达定时时间就会触发回调函数。
  回调函数,遵循快进快出原则,因此,其中一定不能存在任何的阻塞,如vTaskDelay()、while(1)或者是其他能够产生阻塞的情况。
  FreeRTOS的软件定时器在功能上支持:

  • 软件定时器单次与周期执行;
  • 裁剪:能通过宏关闭软件定时器功能;
  • 软件定时器创建;
  • 软件定时器启动;
  • 软件定时器停止;
  • 软件定时器复位;
  • 软件定时器删除;

定时器周期性工作与单次工作时间轴简图

  在freeRTOS的配置上,如果要是用定时器就需要配置下面几个宏定义:

//打开定时器
#define configUSE_TIMERS                1
//定时器的优先级
#define configTIMER_TASK_PRIORITY       50
//定时器栈大小
#define configTIMER_TASK_STACK_DEPTH    50
//定时器队列大小
#define configTIMER_QUEUE_LENGTH        50

FreeRTOS定时器的控制块

    typedef struct tmrTimerControl                  /* The old naming convention is used to prevent breaking kernel aware debuggers. */
    {
        const char * pcTimerName;                   /*<< Text name.  This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
        ListItem_t xTimerListItem;                  /*<< Standard linked list item as used by all kernel features for event management. */
        TickType_t xTimerPeriodInTicks;             /*<< How quickly and often the timer expires. */
        void * pvTimerID;                           /*<< An ID to identify the timer.  This allows the timer to be identified when the same callback is used for multiple timers. */
        TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */
        #if ( configUSE_TRACE_FACILITY == 1 )
            UBaseType_t uxTimerNumber;              /*<< An ID assigned by trace tools such as FreeRTOS+Trace */
        #endif
        uint8_t ucStatus;                           /*<< Holds bits to say if the timer was statically allocated or not, and if it is active or not. */
    } xTIMER;

  定时器控制块中一共含有7个变量,其作用解析如下:

  • const char * pcTimerName:记录定时器名字
  • ListItem_t xTimerListItem:定时器的列表项,用于插入定时器列表;
  • TickType_t xTimerPeriodInTicks:定时器的周期,单位为系统节拍周期,即tick;
  • void * pvTimerID:定时器的ID,整数形式。该ID是当一个回调函数分配给一个或多个定时器时,可以根据IP不同处理回调函数中不同程序;
  • TimerCallbackFunction_t pxCallbackFunction:定时器回调函数;
  • UBaseType_t uxTimerNumber:跟踪工具分配的ID,如FreeRTOS+Trace;
  • uint8_t ucStatus:保存计时器是否静态分配,以及它是否处于活动状态;

  在定义完成控制块后,代码中会使用typedef xTIMER Timer_t重定义控制块的变量别名,在后续代码及开发中可以使用别名完成程序开发。


相关函数解析

  在FreeRTOS的定时器相关函数中,相关的函数有很多,如:发送指定到定时器任务队列函数xTimerGenericCommand、更新定时器周期函数xTimerChangePeriod、重启定时器函数xTimerReset、创建定时器函数xTimerCreate、启动定时器函数xTimerStart等等,其中最为重要且常用的函数当属创建定时器xTimerCreate、启动定时器xTimerStart

动态创建定时器函数
函数原型:

TimerHandle_t xTimerCreate(  const char * const pcTimerName,
                             TickType_t xTimerPeriodInTicks,
                             BaseType_t xAutoReload,
                             void * pvTimerID,
                             TimerCallbackFunction_t pxCallbackFunction );

函数说明:

  • const char * const pcTimerName:设置定时器名字;
  • TickType_t xTimerPeriodInTicks:设置定时器执行周期;
  • BaseType_t xAutoReload:设置定时器是单次或周期执行;
  • void * pvTimerID:设置定时器ID;
  • TimerCallbackFunction_t pxCallbackFunction:设置定时器回调函数;
  • 返回值:pxNewTimer ,数据类型为TimerHandle_t。一旦创建成功,则返回值不为NULL;否则,就为NULL;

函数解析:
  通过该函数可以创建一个定时器,但是该函数仅仅只是申请了一块内存空间,真正进行定时器初始化的函数为prvInitialiseNewTimer()。但是需要注意的是创建完成后处于就绪状态,需要启动后才能正常使用该定时器。

静态创建定时器函数
函数原型:

 TimerHandle_t xTimerCreateStatic(const char * const pcTimerName,
                                  TickType_t xTimerPeriodInTicks,
                                  BaseType_t xAutoReload,
                                  void * pvTimerID,
                                  TimerCallbackFunction_t pxCallbackFunction,
                                  StaticTimer_t *pxTimerBuffer );

函数参数说明:

  • const char * const pcTimerName:设置定时器名字;
  • TickType_t xTimerPeriodInTicks:设置定时器执行周期;
  • BaseType_t xAutoReload:设置定时器是单次或周期执行;
  • void * pvTimerID:设置定时器ID;
  • TimerCallbackFunction_t pxCallbackFunction:设置定时器回调函数;
  • StaticTimer_t *pxTimerBuffer:指向StaticTimer_t类型的变量,用于保存计时器的状态,就不需要动态分配内存了;
  • 返回值:pxNewTimer ,数据类型为TimerHandle_t。一旦创建成功,则返回值不为NULL;否则,就为NULL;

函数解析:
  定时器创建函数,与上面的定时器创建函数功能上一样,参数与返回值大部分相同,唯一不同的是定时器内存的分配方式,该函数无需动态分配内存,因为其已经传递一个指针用于保存定时器的相关内容。

启动定时器函数
函数原型:

BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );

函数参数说明:

  • TimerHandle_t xTimer:创建定时器成功后返回的控制权柄,也就是其地址
  • TickType_t xTicksToWait:用户指定的超时阻塞时间,单位为系统节拍tick。在系统开始调用vTaskStartScheduler()前调用该函数,那么函数形参无作用。在定时器开始指令成功送达定时器命令队列前,定时器还是处于阻塞状态的。
  • 返回值:返回值类型为BaseType_t,如果启动命令无法成功地发送到定时器命令队列则
    返回 pdFAILE,成功发送则返回pdPASS。

函数解析:
  该函数的作用为启动定时器,一旦创建定时器后,就必须使用该函数启动,否则定时器还是处于就绪状态,没有任何实际用处
  启动定时器的函数实际上还是调用xTimerGenericCommand()函数,该函数含有五个参数、一个返回值,功能是发送命令到定时器命令队列。

在中断中启动定时器函数
函数原型:

BaseType_t xTimerStartFromISR( TimerHandle_t xTimer, 
							   BaseType_t * pxHigherPriorityTaskWoken );

函数参数说明:

  • TimerHandle_t xTimer:定时器控制句柄。
  • BaseType_t * pxHigherPriorityTaskWoken:定时器守护任务的大部分时间都在阻塞态等
    待定时器命令队列的命令。调用函数 xTimerStartFromISR()将会往定时器的命令队列发送
    一个启动命令,这很有可能会将定时器任务从阻塞态 移 除 。如果调用函数
    xTimerStartFromISR()让定时器任务脱离阻塞态,且定时器守护任务的优先级大于或者等
    于当前被中断的任务的优先级,那么 pxHigherPriorityTaskWoken 的值会在函数
    xTimerStartFromISR()内部设置为 pdTRUE,然后在中断退出之前执行一次上下文切换。
  • 返回值:返回值类型为BaseType_t,如果启动命令无法成功地发送到定时器命令队列则
    返回 pdFAILE,成功发送则返回pdPASS。

函数解析:
xTimerStartFromISR()是函数 xTimerStart()的中断版本,用于在中断中启动一个先前由函数xTimerCreate()xTimerCreateStatic()创建的软件定时器。

终止定时器函数
函数原型:

BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait );

函数参数说明:

  • TimerHandle_t xTimer:创建定时器成功后返回的控制权柄,也就是其地址;
  • TickType_t xTicksToWait:用户指定的超时阻塞时间,单位为系统节拍tick。在系统开始调用vTaskStartScheduler()前调用该函数,那么函数形参无作用。在定时器开始指令成功送达定时器命令队列前,定时器还是处于阻塞状态的。
  • 返回值:返回值类型为BaseType_t,如果启动命令无法成功地发送到定时器命令队列则
    返回 pdFAILE,成功发送则返回pdPASS。

函数解析:
  终止定时器函数,实际上调用xTimerGenericCommand()函数,该函数含有五个参数、一个返回值,功能是发送命令到定时器命令队列。
  终止定时器后,定时器会回归就绪态,等待下一次唤醒。

在中断中停止定时器函数
函数原型:

BaseType_t xTimerStopFromISR(    TimerHandle_t xTimer,
                                 BaseType_t *pxHigherPriorityTaskWoken );

函数参数说明:

  • TimerHandle_t xTimer:定时器控制句柄。
  • BaseType_t * pxHigherPriorityTaskWoken:定时器守护任务的大部分时间都在阻塞态等
    待定时器命令队列的命令。调用函数 xTimerStartFromISR()将会往定时器的命令队列发送
    一个启动命令,这很有可能会将定时器任务从阻塞态 移 除 。如果调用函数
    xTimerStartFromISR()让定时器任务脱离阻塞态,且定时器守护任务的优先级大于或者等
    于当前被中断的任务的优先级,那么 pxHigherPriorityTaskWoken 的值会在函数
    xTimerStartFromISR()内部设置为 pdTRUE,然后在中断退出之前执行一次上下文切换。
  • 返回值:返回值类型为BaseType_t,如果启动命令无法成功地发送到定时器命令队列则
    返回 pdFAILE,成功发送则返回pdPASS。

函数解析:
  该函数用于在中断停止定时器,使其进入就绪态。使用条件与xTimerStartFromISR()函数类似。

设置定时器执行周期函数
函数原型:

void vTimerSetReloadMode( TimerHandle_t xTimer,
                          const BaseType_t xAutoReload )

函数参数说明:

  • TimerHandle_t xTimer:定时器的控制权柄
  • const BaseType_t xAutoReload:定时器新的执行周期。若为pdFALSE则单次执行;否则为pdTRUE,就周期执行。

函数解析:
  使用该函数能够重新设置定时器的执行周期。
重启定时器函数
函数原型:

BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );

函数参数说明:

  • TimerHandle_t xTimer:定时器的控制权柄;
  • TickType_t xTicksToWait:用户指定的超时阻塞时间,单位为系统节拍tick。

函数解析:
  重新启动一个定时器。

删除定时器函数
函数原型:

BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait );

函数参数说明:

  • TimerHandle_t xTimer:定时器控制权柄;
  • TickType_t xTicksToWait:用户指定的超时阻塞时间,单位为系统节拍tick。

函数解析:
  删除定时器函数,调用该函数能够删除对应定时器工作。

示例

创建两个定时器任务
  通过创建链各个定时器任务,完成定时器任务1实现——LED1间隔循环1s闪烁与定时器任务2实现——LED3单次闪烁。

void timeCallBackTask(void);
void timeCallBackTask2(void);

int main(void)
{
	TimerHandle_t xTimeHandle[2];
	xTimeHandle[0] = xTimerCreate(
								(const char *)"task1",// 定时器名字
								(TickType_t)1000,// 定时器的周期
								pdTRUE,// 定时器是单次执行还是周期执行, pdTRUE为周期执行,pdFALSE为周期执行
								(void*)1,// 给定时器分配的唯一ID
								(TimerCallbackFunction_t)timeCallBackTask// 定时器的回调函数
								);
	xTimeHandle[1] = xTimerCreate(
								(const char *)"task2",// 定时器名字
								(TickType_t)2000,// 定时器的周期
								pdFALSE,// 定时器是单次执行还是周期执行, pdTRUE为周期执行,pdFALSE为周期执行
								(void*)2,// 给定时器分配的唯一ID
								(TimerCallbackFunction_t)timeCallBackTask2// 定时器的回调函数
								);
	if(xTimeHandle[0] && xTimeHandle[1])
	{
		xTimerStart(xTimeHandle[0],0); //开启定时器
		xTimerStart(xTimeHandle[1],0); //开启定时器
	}
	else
		//定时器任务创建失败 打开LED8
		changeLedStateByLocation(LED8,ON);
	//打开任务调度器
	vTaskStartScheduler();
}

/*****************************************
* 函数功能:定时器任务
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeCallBackTask(void)
{
	//闪烁LED1
	rollbackLedByLocation(LED1);
}

/*****************************************
* 函数功能:定时器任务2
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeCallBackTask2(void)
{
	//闪烁LED3
	rollbackLedByLocation(LED3);
}

创建一个系统任务来管理两个定时器任务
  创建一个系统任务来开启与关闭定时器任务。由于创建完成定时器任务后,任务仍然处于就绪状态,需要开启函数xTimerStart才能够完成启动,而碰到关闭函数 xTimerStop定时器任务又会处于就绪状态。
  通过不断地开启与关闭,可以实现定时器任务timeCallBackTask与timeCallBackTask2不断地切换,实际上也实现就是LED1与LED2轮流闪烁。

void timeTask1(void);
void timeCallBackTask(void);
void timeCallBackTask2(void);

//任务控制权柄
TaskHandle_t xHandleTsak[4];
// 定时器控制权柄
TimerHandle_t xTimeHandle[2];
int main(void)
{

	BaseType_t xReturn[2];
	xTimeHandle[0] = xTimerCreate(
								(const char *)"task1",// 定时器名字
								(TickType_t)1000,// 定时器的周期
								pdTRUE,// 定时器是单次执行还是周期执行, pdTRUE为周期执行,pdFALSE为周期执行
								(void*)1,// 给定时器分配的唯一ID
								(TimerCallbackFunction_t)timeCallBackTask// 定时器的回调函数
								);
	xTimeHandle[1] = xTimerCreate(
								(const char *)"task2",// 定时器名字
								(TickType_t)2000,// 定时器的周期
								pdTRUE,// 定时器是单次执行还是周期执行, pdTRUE为周期执行,pdFALSE为周期执行
								(void*)2,// 给定时器分配的唯一ID
								(TimerCallbackFunction_t)timeCallBackTask2// 定时器的回调函数
								);
	xReturn[0] = xTaskCreate(
				(TaskFunction_t )timeTask1,//任务入口函数
				(const char *)"timeTask1",//任务名字
				(uint16_t)512,//任务栈大小
				(void*)NULL,//任务入口参数
				1,//任务优先级  优先级越高,任务优先选越高
				&xHandleTsak[2]//任务控制块
				);
	if(xReturn[0] == pdFALSE)
		changeLedStateByLocation(LED7,ON);
	//打开任务调度器
	vTaskStartScheduler();
}

/*****************************************
* 函数功能:定时器管理任务1
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeTask1(void)
{
	uint32_t count = 0;
	const volatile TickType_t xDelay500ms = pdMS_TO_TICKS( 500UL );
	while(1)
	{
		//LED3闪烁
		if(++count % 2)
			rollbackLedByLocation(LED3);
		//开启定时器任务1  关闭定时器任务2
		if(count % 50 == 0)
		{
			xTimerStart(xTimeHandle[0],0);
			xTimerStop(xTimeHandle[1],0);
		}
		//开启定时器任务2  关闭定时器任务1
		if(count % 50 == 25)
		{
			xTimerStart(xTimeHandle[1],0);
			xTimerStop(xTimeHandle[0],0);
		}
		//非阻塞延时1s
		vTaskDelay( xDelay500ms );
	}
}

/*****************************************
* 函数功能:定时器任务
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeCallBackTask(void)
{
	rollbackLedByLocation(LED1);
}

/*****************************************
* 函数功能:定时器任务2
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeCallBackTask2(void)
{
	rollbackLedByLocation(LED2);
}

  本文是基于Cortex-M4核心板实现的,若需要移植FreeRTOS到Cortex-M4上可以参考文章【FreeRTOS】在Cortex-M4开发板上移植FreeRTOS并且实现LED灯闪烁(保姆级教程)
  小编这里还有一篇关于定时器的文章,也欢迎各位点击观看😉😉😉【FreeRTOS】详细讲解FreeRTOS中任务管理并通过示例讲述其用法

  最后 ,也欢迎大家留言或私信交流,大家共同进步!😁😁😁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/184649.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python的数字类型、布尔类型和运算优先级

文章目录1.数字类型1.1分类1.2整数1.3浮点数1.4复数2.数字运算符2.1运算符表格2.2 运算符 //3.divmod&#xff08;&#xff09;函数4.abs&#xff08;&#xff09;函数4. int()&#xff0c;float() 和 complex() 函数5.pow&#xff08;&#xff09;函数和运算符 **6.布尔类型6.…

NB-IoT的低功耗特性原理解说

什么是NB-IoT NB-IoT的中文名叫窄带蜂窝物联网(Narrow Band Internet of Things)&#xff0c;NB-IoT网络是基于4G网络演进过来的&#xff0c;所以它在上行和下行的复用技术上还是沿用了4G的OFDMA和SC-FDMA。NB-IoT有三大特性&#xff1a;速率低&#xff0c;成本低&#xff0c;…

电脑风扇声音大怎么办?具体原因以及解决措施,快速解决

​很多小伙伴使用台式电脑&#xff0c;使用的时间长了&#xff0c;电脑机箱里的风扇就会发出一些噪音&#xff0c;很影响小伙伴的整体使用体验。电脑风扇声音大怎么办&#xff1f;具体的原因以及解决措施有哪些&#xff1f;下面就跟着小编一起来看看吧。 一、电脑风扇声音大的原…

数据挖掘,计算机网络、操作系统刷题笔记39

数据挖掘&#xff0c;计算机网络、操作系统刷题笔记39 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;orac…

Kibana最新版8.6.1安装教程

Kibana 让您能够自由地选择如何呈现自己的数据。不过借助 Kibana 的交互式可视化&#xff0c;您可以先从一个问题出发&#xff0c;看看能够从中发现些什么。查看完整的 Kibana 功能列表https://www.elastic.co/cn/kibana/featuresKibana的下载地址&#xff1a;https://www.elas…

有状态/无状态认证

文章目录一、什么是有状态认证&#xff1f;二、什么是无状态认证&#xff1f;&#xff08;token&#xff09;三、无状态分布式认证解决方案一、什么是有状态认证&#xff1f; 有状态认证&#xff0c;即服务端需要记录每次会话的客户端信息&#xff0c;从而识别客户端身份&#…

几种最小二乘法及python代码:ELS、TLS、RLS

1.ARMAX模型 下面各章节&#xff0c;我就是使用上面公式的符号&#xff0c;其中y是输出&#xff0c;u是输入&#xff0c;e是噪声。有m个输出y&#xff0c;r个输入u。 进一步精简为&#xff1a; YPθE 其中&#xff1a;Y为要预测的部分&#xff0c;P为已知数据&#xff08;包…

k8s添加node节点和master节点

一.准备1.基本概述版本&#xff1a;kubelet&#xff1a;v1.20.4docker&#xff1a; 20.10.23资源&#xff1a;cpu&#xff1a;8mem&#xff1a;16kernel&#xff1a;3.10.0-1160.71.1.el7.x86_64镜像仓库地址&#xff1a;registry.cn-hangzhou.aliyuncs.com/google_containers/…

大部分人都容易焦虑,那么应该如何对待焦虑呢?

新年伊始&#xff0c;告别喜庆欢乐的春节&#xff0c;大家应该已经投入到正常的工作当中去了。面对节后的开工&#xff0c;难免都会有点焦虑&#xff0c;因为大多数人还沉浸在春节喜悦的回忆当中&#xff0c;回忆都是美好的&#xff0c;因为回忆中是带有感情的&#xff0c;美好…

总投资30亿、算力500P,宜昌先进计算产业可持续发展之路

近日&#xff0c;国家先进计算产业创新&#xff08;宜昌&#xff09;中心项目工程总承包和委托运营&#xff08;EPCO&#xff09;中标结果公示&#xff0c;由中科升哲数据科技有限公司联合重庆市设计院有限公司、中讯邮电咨询设计院有限公司、中国化学工程第十六建设有限公司组…

multipart/form-data 在低版本spring和webFlux中的解析

背景 最近在做一个技术项目的迁移&#xff0c;将老的springMVC项目迁移到SpringWebFlux项目中&#xff0c;在流量迁移过程中发现有一个业务方传过来的参数新项目拿不到&#xff0c;究其原因是老版本的spring解析器和新版本的解析器对multipart/form-data类型的contentType解析…

《深入浅出计算机组成原理》学习笔记 Day14

数据通路&#xff08;下&#xff09;1. PC 寄存器的实现2. 读写数据所需要的译码器3. 数据通路完整实现4. 总结参考1. PC 寄存器的实现 PC 寄存器又名程序计数器&#xff08;Program Counter&#xff09;。 PC 寄存器由两个部分组成&#xff1a; 时钟信号。提供定时的输入&a…

刚来的00后太卷了,上班还没2年,跳到我们公司起薪25k....

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。 这不&#xff0c;前段时间我们公司来了个00后&#xff0c;工作都没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了…

Arthas 入门到实战(四)arhtas idea plugin集成插件

前言&#xff1a; Arthas 官方的工具还不够足够的简单&#xff0c;需要记住一些命令&#xff0c;但是我们需要的是一个能够简单处理字符串信息的插件即可使用。当在处理线上问题的时候需要最快速、最便捷的命令&#xff0c;因此插件还是有存在的意义和价值的。 一、idea插件安…

Blender 粒子系统

文章目录简介.添加粒子系统.属性.自发光&#xff08;发射&#xff09;.源.烘焙(仅发射体).速度(仅发射体).旋转(仅发射体&#xff0c;除非毛发系统开启了高级属性).物理(仅发射体).毛发动力学(仅毛发).渲染.路径.视图显示.子级.簇集.糙度.扭结.力场权重.顶点组.粒子编辑.渲染毛…

GraalVM和Spring Native尝鲜,一步步让Springboot启动飞起来,66ms完成启动

简介 GraalVM是高性能的JDK&#xff0c;支持Java/Python/JavaScript等语言。它可以让Java变成二进制文件来执行&#xff0c;让程序在任何地方运行更快。这或许是Java与Go的一场战争&#xff1f; 下载安装GraalVM 安装GraalVM 首先到官网下载&#xff0c;我是直接到GitHub Re…

7-2输入/输出系统-I/O方式

文章目录一.程序查询方式二.程序中断方式三.DMA方式1.传送过程2.DMA控制器&#xff08;DMAC&#xff09;的内部结构3.DMA传送方式&#xff08;1&#xff09;停止CPU访问主存&#xff08;2&#xff09;DMA与CPU交替访存&#xff08;3&#xff09;周期挪用/周期窃取4.DMA方式的特…

基于改进粒子群算法的混合储能系统容量优化(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

css特性(继承、层叠、优先级)

1、继承性 特性: 子元素有默认继承父元素样式的特点(子承父业) 可以继承的常见属性(文字控制属性都可以继承) colorfont-style、 font-weight、 font-size、 font-familytext-indent、text-align、line-height … 注意点: 控制字的都能继承 可以通过调试工具判断样式是否可以…

应用系统基于CAS实现单点登录的解决方案

单点登录 (SingleSign-On&#xff0c;SSO) &#xff0c;是一种帮助用户快捷访问网络中多个站点的安全通信技术。单点登录系统基于一种安全的通信协议&#xff0c;该协议通过多个系统之间的用户身份信息的交换来实现单点登录。使用单点登录系统时&#xff0c;用户只需要登录一次…