FreeRTOS从入门到精通 第十七章(软件定时器)

参考教程:【正点原子】手把手教你学FreeRTOS实时系统_哔哩哔哩_bilibili

一、软件定时器简介

1、定时器的概念与种类

(1)定时器的概念:从指定的时刻开始,经过一个指定时间,然后触发一个超时事件,用户可自定义定时器的周期。

(2)定时器的种类:

①硬件定时器:芯片本身自带的定时器模块,硬件定时器的精度一般很高,每次在定时时间到达之后就会自动触发一个中断,用户在中断服务函数中处理信息。

②软件定时器:具有定时功能的软件,可设置定时周期,当指定时间到达后要调用回调函数(也称超时函数),用户在回调函数中处理信息。

2、软件定时器的优缺点

(1)优点:硬件定时器数量有限,而软件定时器理论上只需有足够内存,就可以创建多个,且使用简单、成本低。

(2)缺点:软件定时器相对硬件定时器来说,精度没有那么高(因为它以系统时钟为基准,系统时钟中断优先级又是最低,容易被打断),对于需要高精度要求的场合,不建议使用软件定时器。

3、FreeRTOS软件定时器

(1)FreeRTOS软件定时器的特点:

①软件定时器是可裁剪可配置的功能,如果要使能软件定时器,需将configUSE_TIMERS配置项配置成1。

②软件定时器支持设置成单次定时器或周期定时器。

(2)软件定时器的超时回调函数是由软件定时器服务任务调用的,软件定时器的超时回调函数本身不是任务,因此不能在该回调函数中使用可能会导致任务阻塞的API函数。

(3)在调用函数vTaskStartScheduler开启任务调度器的时候,会创建一个用于管理软件定时器的任务,这个任务就叫做软件定时器服务任务,其作用有:

①负责软件定时器超时的逻辑判断。

②调用超时软件定时器的超时回调函数。

③处理软件定时器命令队列。

(4)FreeRTOS提供了许多软件定时器相关的API函数,这些API函数大多都是往定时器的队列中写入消息(发送命令),这个队列叫做软件定时器命令队列,是提供给FreeRTOS中的软件定时器使用的,用户是不能直接访问的。

(5)软件定时器的相关配置:

①当FreeRTOS的配置项configUSE_TIMERS设置为1,在启动任务调度器时,会自动创建软件定时器的服务/守护任务prvTimerTask。

②软件定时器服务任务的优先级configTIMER_TASK_PRIORITY需配置为31(最高)。

③定时器的命令队列长度configTIMER_QUEUE_LENGTH需配置为5。

(6)软件定时器的超时回调函数是在软件定时器服务任务中被调用的,服务任务不是专为某个定时器服务的,它还要处理其它定时器,以下是回调函数的一些注意事项:

①回调函数要尽快实行,不能进入阻塞状态,即不能调用那些可能会阻塞任务的API函数。

②访问队列或者信号量的非零阻塞时间的API函数也不能调用。

(7)软件定时器共有两种状态:

①休眠态:软件定时器可以通过其句柄被引用,但因为没有运行,所以其定时超时回调函数不会被执行。(新创建的软件定时器处于休眠状态,也就是未运行的)

②运行态:运行态的定时器,当指定时间到达之后,它的超时回调函数会被调用。

(8)FreeRTOS提供了两种软件定时器:

①单次定时器:单次定时器一旦定时超时,只会执行一次其软件定时器超时回调函数,不会自动重新开启定时,不过可以被手动重新开启。

②周期定时器:周期定时器一旦启动以后就会在执行完回调函数以后自动地重新启动,从而周期地执行其软件定时器回调函数。

(9)软件定时器的状态转换图:

①单次定时器状态转换图:

②周期定时器状态转换图:

二、FreeRTOS软件定时器的结构体与相关API函数

1、软件定时器结构体成员介绍

typedef  struct
{const char * 				pcTimerName			//软件定时器名字ListItem_t 				xTimerListItem		//软件定时器列表项TickType_t 				xTimerPeriodInTicks;  //软件定时器的周期  void * 					pvTimerID			//软件定时器的IDTimerCallbackFunction_t	pxCallbackFunction; 	//软件定时器的回调函数#if ( configUSE_TRACE_FACILITY == 1 )UBaseType_t 			uxTimerNumber		//软件定时器的编号,调试用#endifuint8_t 					ucStatus;             //软件定时器的状态
}xTIMER;

2、软件定时器相关API函数

(1)软件定时器相关API函数概览:

函数

描述

xTimerCreate()

动态方式创建软件定时器

xTimerCreateStatic()

静态方式创建软件定时器

xTimerStart()

开启软件定时器定时

xTimerStartFromISR()

在中断中开启软件定时器定时

xTimerStop()

停止软件定时器定时

xTimerStopFromISR()

在中断中停止软件定时器定时

xTimerReset()

复位软件定时器定时

xTimerResetFromISR()

在中断中复位软件定时器定时

xTimerChangePeriod()

更改软件定时器的定时超时时间

xTimerChangePeriodFromISR()

在中断中更改定时超时时间

(2)xTimerCreate函数:

①函数定义:

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

②函数参数:

形参 

描述

pcTimerName 

软件定时器名

xTimerPeriodInTicks 

定时超时时间,单位:系统时钟节拍

uxAutoReload 

定时器模式, pdTRUE:周期定时器, pdFALSE:单次定时器

pvTimerID 

软件定时器 ID,用于多个软件定时器公用一个超时回调函数

pxCallbackFunction 

软件定时器超时回调函数

③返回值:

形参 

描述

NULL 

软件定时器创建失败

其它值 

软件定时器创建成功,返回其句柄

(3)xTimerStart函数:

①函数定义:

BaseType_t xTimerStart
(TimerHandle_t 	xTimer,const TickType_t 	xTicksToWait
); 

②函数参数:

形参 

描述

xTimer 

待开启的软件定时器的句柄

xTickToWait 

发送命令到软件定时器命令队列的最大等待时间

③返回值:

形参 

描述

pdPASS 

软件定时器开启成功

pdFAIL 

软件定时器开启失败

(4)xTimerStop函数:

①函数定义:

BaseType_t xTimerStop
(TimerHandle_t 	xTimer,const TickType_t 	xTicksToWait
); 

②函数参数:

形参 

描述

xTimer 

待停止的软件定时器的句柄

xTickToWait 

发送命令到软件定时器命令队列的最大等待时间

③返回值:

形参 

描述

pdPASS 

软件定时器停止成功

pdFAIL 

软件定时器停止失败

(5)xTimerReset函数:

①函数定义:

BaseType_t  xTimerReset
(TimerHandle_t 	xTimer,const TickType_t 	xTicksToWait
);  //复位后的软件定时器以复位时的时刻作为开启时刻重新定时

②函数参数:

形参 

描述

xTimer 

待复位的软件定时器的句柄

xTickToWait 

发送命令到软件定时器命令队列的最大等待时间

③返回值:

形参 

描述

pdPASS 

软件定时器复位成功

pdFAIL 

软件定时器复位失败

(6)xTimerChangePeriod函数:

①函数定义:

BaseType_t  xTimerChangePeriod
(TimerHandle_t 		xTimer,const TickType_t 	xNewPeriod,const TickType_t 	xTicksToWait
); 

②函数参数:

形参 

描述

xTimer 

待更新的软件定时器的句柄

xNewPeriod

新的定时超时时间,单位:系统时钟节拍

xTickToWait 

发送命令到软件定时器命令队列的最大等待时间

③返回值:

形参 

描述

pdPASS 

软件定时器定时超时时间更改成功

pdFAIL 

软件定时器定时超时时间更改失败

3、xTimerCreateTimerTask创建软件定时器任务函数内核代码剖析

(1)xTimerCreateTimerTask函数本身:

BaseType_t xTimerCreateTimerTask( void )
{BaseType_t xReturn = pdFAIL;traceENTER_xTimerCreateTimerTask();prvCheckForValidListAndQueue();  //创建当前软件定时器列表和溢出软件定时器列表及命令队列if( xTimerQueue != NULL ){#if ((configNUMBER_OF_CORES >1) && (configUSE_CORE_AFFINITY ==1)){#if ( configSUPPORT_STATIC_ALLOCATION ==1){StaticTask_t * pxTimerTaskTCBBuffer = NULL;StackType_t * pxTimerTaskStackBuffer = NULL;configSTACK_DEPTH_TYPE uxTimerTaskStackSize;vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &uxTimerTaskStackSize );xTimerTaskHandle = xTaskCreateStaticAffinitySet( prvTimerTask,configTIMER_SERVICE_TASK_NAME,uxTimerTaskStackSize,NULL,((UBaseType_t) configTIMER_TASK_PRIORITY)| portPRIVILEGE_BIT,pxTimerTaskStackBuffer,pxTimerTaskTCBBuffer,configTIMER_SERVICE_TASK_CORE_AFFINITY );if( xTimerTaskHandle != NULL ){xReturn = pdPASS;}}#else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */{//创建软件定时器服务任务xReturn = xTaskCreateAffinitySet( prvTimerTask,configTIMER_SERVICE_TASK_NAME,configTIMER_TASK_STACK_DEPTH,NULL,((UBaseType_t)configTIMER_TASK_PRIORITY )| portPRIVILEGE_BIT,configTIMER_SERVICE_TASK_CORE_AFFINITY,&xTimerTaskHandle );}#endif /* configSUPPORT_STATIC_ALLOCATION */}#else /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */{#if ( configSUPPORT_STATIC_ALLOCATION == 1 ){StaticTask_t * pxTimerTaskTCBBuffer = NULL;StackType_t * pxTimerTaskStackBuffer = NULL;configSTACK_DEPTH_TYPE uxTimerTaskStackSize;vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &uxTimerTaskStackSize );xTimerTaskHandle = xTaskCreateStatic( prvTimerTask,configTIMER_SERVICE_TASK_NAME,uxTimerTaskStackSize,NULL,((UBaseType_t)configTIMER_TASK_PRIORITY )| portPRIVILEGE_BIT,pxTimerTaskStackBuffer,pxTimerTaskTCBBuffer );if( xTimerTaskHandle != NULL ){xReturn = pdPASS;}}#else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */{xReturn = xTaskCreate( prvTimerTask,configTIMER_SERVICE_TASK_NAME,configTIMER_TASK_STACK_DEPTH,NULL,((UBaseType_t)configTIMER_TASK_PRIORITY) | portPRIVILEGE_BIT,&xTimerTaskHandle );}#endif /* configSUPPORT_STATIC_ALLOCATION */}#endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */}else{mtCOVERAGE_TEST_MARKER();}configASSERT( xReturn );traceRETURN_xTimerCreateTimerTask( xReturn );return xReturn;
}

(2)prvCheckForValidListAndQueue函数:负责创建当前软件定时器列表和溢出软件定时器列表及命令队列。

static void prvCheckForValidListAndQueue( void )
{taskENTER_CRITICAL();{if( xTimerQueue == NULL ){vListInitialise( &xActiveTimerList1 );    //初始化定时器列表1vListInitialise( &xActiveTimerList2 );    //初始化定时器列表2pxCurrentTimerList = &xActiveTimerList1;  //将定时器列表1赋给当前软件定时器列表pxOverflowTimerList = &xActiveTimerList2; //将定时器列表2赋给溢出软件定时器列表#if ( configSUPPORT_STATIC_ALLOCATION == 1 ){PRIVILEGED_DATA static StaticQueue_t xStaticTimerQueue;PRIVILEGED_DATA static uint8_t ucStaticTimerQueueStorage[ ( size_t ) configTIMER_QUEUE_LENGTH * sizeof( DaemonTaskMessage_t ) ];//创建命令队列xTimerQueue = xQueueCreateStatic( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, ( UBaseType_t ) sizeof( DaemonTaskMessage_t ), &( ucStaticTimerQueueStorage[ 0 ] ), &xStaticTimerQueue );}#else{//创建命令队列xTimerQueue = xQueueCreate( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, ( UBaseType_t ) sizeof( DaemonTaskMessage_t ) );}#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */#if ( configQUEUE_REGISTRY_SIZE > 0 ){if( xTimerQueue != NULL ){vQueueAddToRegistry( xTimerQueue, "TmrQ" );}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configQUEUE_REGISTRY_SIZE */}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();
}

(3)prvTimerTask函数:软件定时器服务任务。

static portTASK_FUNCTION( prvTimerTask, pvParameters )
{TickType_t xNextExpireTime;BaseType_t xListWasEmpty;( void ) pvParameters;#if ( configUSE_DAEMON_TASK_STARTUP_HOOK == 1 ){vApplicationDaemonTaskStartupHook();}#endif /* configUSE_DAEMON_TASK_STARTUP_HOOK */for( ; configCONTROL_INFINITE_LOOP(); ){//获取下一个超时的软件定时器xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty );//判断所获得的定时器是否超时,如超时则调用超时回调函数prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );//接收命令队列的一条命令并执行之prvProcessReceivedCommands();}
}

(4)prvProcessTimerOrBlockTask函数:判断定时器是否超时。

static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime,BaseType_t xListWasEmpty )
{TickType_t xTimeNow;BaseType_t xTimerListsWereSwitched;vTaskSuspendAll();{xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );if( xTimerListsWereSwitched == pdFALSE ){if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) ) //判断定时器是否超时{( void ) xTaskResumeAll();prvProcessExpiredTimer( xNextExpireTime, xTimeNow );  //超时,调用回调函数}else{if( xListWasEmpty != pdFALSE ){xListWasEmpty = listLIST_IS_EMPTY( pxOverflowTimerList );}vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ), xListWasEmpty );if( xTaskResumeAll() == pdFALSE ){taskYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}}}else{( void ) xTaskResumeAll();}}
}

(5)prvProcessExpiredTimer函数:定时器超时后将调用一次该函数。

static void prvProcessExpiredTimer( const TickType_t xNextExpireTime,const TickType_t xTimeNow )
{Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList );(void)uxListRemove(&(pxTimer->xTimerListItem));  //将定时器移出软件定时器列表if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0U )  //如果是周期定时器列表{prvReloadTimer( pxTimer, xNextExpireTime, xTimeNow ); //将定时器插回软件定时器列表}else{pxTimer->ucStatus &= ( ( uint8_t ) ~tmrSTATUS_IS_ACTIVE ); //将对应位清零}traceTIMER_EXPIRED( pxTimer );pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );  //调用超时回调函数
}

(6)vQueueWaitForMessageRestricted函数:

void vQueueWaitForMessageRestricted( QueueHandle_t xQueue,TickType_t xTicksToWait,const BaseType_t xWaitIndefinitely )
{Queue_t * const pxQueue = xQueue;traceENTER_vQueueWaitForMessageRestricted( xQueue, xTicksToWait, xWaitIndefinitely );prvLockQueue( pxQueue );if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0U )   //判断命令队列的非空闲项数目是否为0{//将软件定时器服务任务挂到等待接收列表vTaskPlaceOnEventListRestricted( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait, xWaitIndefinitely );}else{mtCOVERAGE_TEST_MARKER();}prvUnlockQueue( pxQueue );traceRETURN_vQueueWaitForMessageRestricted();
}

三、FreeRTOS软件定时器实验

1、原理图与实验目标

(1)原理图:

(2)实验目标:

①设计2个任务——start_task、task1:

[1]start_task:用于创建task1任务,创建两个定时器并启动之(分别为单次和周期型,其回调函数中分别需要翻转LED1和LED2的状态)。

[2]task1:用于按键扫描,并对两个软件定时器进行开启、停止操作,按下按键1即开始两个软件定时器,按下按键2反之。

②预期实验现象:

[1]程序下载到板子上后,LED1灯状态仅变更一次,LED2灯持续闪烁。

[2]按下按键1,LED1和LED2状态冻结。

[3]按下按键2,LED1灯状态仅变更一次,LED2恢复持续闪烁。

2、实验步骤

(1)将“任务创建和删除的动态方法实验”的工程文件夹复制一份,在拷贝版中进行实验。

(2)在FreeRTOSConfig.h文件中将宏configUSE_TIMERS配置为1,将宏configTIMER_TASK_PRIORITY配置为31,将宏configTIMER_QUEUE_LENGTH配置为5,将宏configTIMER_TASK_STACK_DEPTH配置为256。

#define configUSE_TIMERS              1   //使能软件定时器
#define configTIMER_TASK_PRIORITY   31 //软件定时器服务任务的优先级配置为最高
#define configTIMER_QUEUE_LENGTH   5   //定时器的命令队列长度配置为5
#define configTIMER_TASK_STACK_DEPTH  256   //软件定时器服务任务的栈深度

(3)删除FreeRTOS_experiment.c文件中关于task2和task3的内容,在start_task中创建一个单次定时器和周期定时器,并为它们分别编写回调函数,同时更改task1函数的具体实现,然后添加头文件timers.h,如下所示。

#include "FreeRTOS.h"
#include "task.h"
#include "LED.h"
#include "Key.h"
#include "timers.h"//宏定义
#define START_TASK_STACK_SIZE 128   //start_task任务的堆栈大小
#define START_TASK_PRIO       1     //start_task任务的优先级
#define TASK1_STACK_SIZE      128   //task1任务的堆栈大小
#define TASK1_PRIO             2     //task1任务的优先级//任务函数声明
void start_task(void);
void task1(void);//任务句柄
TaskHandle_t start_task_handler;    //start_task任务的句柄
TaskHandle_t task1_handler;       //task1任务的句柄//定时器回调函数
void timer1_callback(TimerHandle_t pxTimer);
void timer2_callback(TimerHandle_t pxTimer);void FreeRTOS_Test(void)
{//创建任务start_taskxTaskCreate((TaskFunction_t)start_task,           //指向任务函数的指针"start_task",                      //任务名字START_TASK_STACK_SIZE,       //任务堆栈大小,单位为字NULL,                           //传递给任务函数的参数START_TASK_PRIO,               //任务优先级(TaskHandle_t *) &start_task_handler  //任务句柄,就是任务的任务控制块);//开启任务调度器vTaskStartScheduler();
}TimerHandle_t timer1_handle;    //单次定时器句柄
TimerHandle_t timer2_handle;    //周期定时器句柄
void start_task(void)
{taskENTER_CRITICAL();//创建任务task1xTaskCreate((TaskFunction_t)task1,             //指向任务函数的指针"task1",                        //任务名字TASK1_STACK_SIZE,           //任务堆栈大小,单位为字NULL,                        //传递给任务函数的参数TASK1_PRIO,                  //任务优先级(TaskHandle_t *) &task1_handler   //任务句柄,就是任务的任务控制块);//创建单次定时器timer1timer1_handle = xTimerCreate("timer1", 500, pdFALSE, (void *)1, timer1_callback );//创建周期定时器timer2timer2_handle = xTimerCreate("timer2", 1000, pdTRUE, (void *)2, timer2_callback );//启动两个定时器xTimerStart(timer1_handle,portMAX_DELAY);xTimerStart(timer2_handle,portMAX_DELAY);vTaskDelete(NULL);taskEXIT_CRITICAL();
}void task1(void)
{uint8_t key = 0;while(1) {key = Key_GetNum();if(key == 1){xTimerStart(timer1_handle,portMAX_DELAY);xTimerStart(timer2_handle,portMAX_DELAY);}else if(key == 2){xTimerStop(timer1_handle,portMAX_DELAY);xTimerStop(timer2_handle,portMAX_DELAY);}vTaskDelay(10);}
}/* timer1的超时回调函数 */
void timer1_callback( TimerHandle_t pxTimer )
{LED1_Turn();
}/* timer2的超时回调函数 */
void timer2_callback( TimerHandle_t pxTimer )
{LED2_Turn();
}

(4)程序完善好后点击“编译”,然后将程序下载到开发板上。

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

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

相关文章

2025年美赛B题-结合Logistic阻滞增长模型和SIR传染病模型研究旅游可持续性-成品论文

模型设计思路与创新点&#xff1a; 建模的时候应该先确定我们需要建立什么类的模型&#xff1f;优化类还是统计类&#xff1f;这个题需要大量的数据分析&#xff0c;因此我们可以建立一个统计学模型。 统计学建模思路&#xff1a;观察规律&#xff0c;建立模型&#xff0c;参…

9.2k star!PiliPala一个第三方B站客户端!

软件介绍 链接 PiliPala一个在Github上收获9.2k star的开源第三方bilibili客户端&#xff0c;支持安卓和ios端安装使用。应用界面简洁无广、除核心功能外无任何冗余功能和服务&#xff0c;让我们可以尽情的享受内容带给我们的快乐。 基础的功能如登录、点赞收藏、评论、关注、…

unity学习23:场景scene相关,场景信息,场景跳转

目录 1 默认场景和Assets里的场景 1.1 scene的作用 1.2 scene作为project的入口 1.3 默认场景 2 场景scene相关 2.1 创建scene 2.2 切换场景 2.3 build中的场景&#xff0c;在构建中包含的场景 &#xff08;否则会认为是失效的Scene&#xff09; 2.4 Scenes in Bui…

论文笔记(六十三)Understanding Diffusion Models: A Unified Perspective(五)

Understanding Diffusion Models: A Unified Perspective&#xff08;五&#xff09; 文章概括基于得分的生成模型&#xff08;Score-based Generative Models&#xff09; 文章概括 引用&#xff1a; article{luo2022understanding,title{Understanding diffusion models: A…

Linux中 端口被占用如何解决

lsof命令查找 查找被占用端口 lsof -i :端口号 #示例 lsof -i :8080 lsof -i :3306 netstat命令查找 查找被占用端口 netstat -tuln | grep 端口号 #示例 netstat -tuln | grep 3306 netstat -tuln | grep 6379 ss命令查找 查找被占用端口 ss -tunlp | grep 端口号 #示例…

苍穹外卖第一天

角色分工 技术选型 pojo子模块 nginx反向代理 MD5密码加密

IP服务模型

1. IP数据报 IP数据报中除了包含需要传输的数据外&#xff0c;还包括目标终端的IP地址和发送终端的IP地址。 数据报通过网络从一台路由器跳到另一台路由器&#xff0c;一路从IP源地址传递到IP目标地址。每个路由器都包含一个转发表&#xff0c;该表告诉它在匹配到特定目标地址…

Kafka下载

一、Kafka下载 下载地址&#xff1a;https://kafka.apache.org/downloads 二、Kafka安装 因为选择下载的是 .zip 文件&#xff0c;直接跳过安装&#xff0c;一步到位。 选择在任一磁盘创建空文件夹&#xff08;不要使用中文路径&#xff09;&#xff0c;解压之后把文件夹内容…

使用 Motor-CAD 脚本实现 Maxwell 电机模型的 Ansys 自动化

在本博客中&#xff0c;我将展示如何使用 Ansys Motor-CAD 自动创建的电机设计脚本在 Ansys Maxwell 中自动创建电机模型。我以无刷永磁电机为例介绍工作流程。 汽车CAD 将 Motor-CAD 设计导出为脚本文件以在 Maxwell 中运行。 导出电机 CAD 设计 在菜单条中选择“工具”选项…

《逆向工程核心原理》第三~五章知识整理

查看上一章节内容《逆向工程核心原理》第一~二章知识整理 对应《逆向工程核心原理》第三章到第五章内容 小端序标记法 字节序 多字节数据在计算机内存中存放的字节顺序分为小端序和大端序两大类 大端序与小端序 BYTE b 0x12; WORD w 0x1234; DWORD dw 0x12345678; cha…

使用 SpringBoot+Thymeleaf 模板引擎进行 Web 开发

目录 一、什么是 Thymeleaf 模板引擎 二、Thymeleaf 模板引擎的 Maven 坐标 三、配置 Thymeleaf 四、访问页面 五、访问静态资源 六、Thymeleaf 使用示例 七、Thymeleaf 常用属性 前言 在现代 Web 开发中&#xff0c;模板引擎被广泛用于将动态内容渲染到静态页面中。Thy…

MongoDB快速上手(包会用)

MongoDB快速上手&#xff08;包会用&#xff09; MongoDB 介绍 &#x1f431;‍&#x1f4bb; MongoDB 是一个开源的 文档型数据库&#xff0c;它使用类似 JSON 的 BSON&#xff08;二进制 JSON&#xff09;格式来存储数据&#xff0c;具有高性能、可扩展性和灵活性。它适用于…

STC32通用GPIO中断,库函数配置方式 AI8051U和STC32G已测试没有问题

近来STC的单片机已经出到32位了&#xff0c;并且个人自己打板测试了几个型号&#xff0c;相比之前的51完全不是一个量级&#xff0c;可以通过以下这张图片中的信息来感受一下如今的32位8051单片机的强大&#xff0c;也是很很期待25年的这一新作了&#xff01; 配图为AI8052U或…

使用Pygame制作“Flappy Bird”游戏

1. 前言 Flappy Bird 是一款“点击上浮、松手下落”的横向卷轴游戏&#xff1a; 场景中持续出现上下成对的管道&#xff0c;玩家需要让小鸟在管道之间穿行&#xff1b;每穿过一对管道记 1 分&#xff1b;若小鸟碰到管道或掉到地面&#xff0c;则游戏结束&#xff1b;一旦上手…

java求职学习day23

MySQL 单表 & 约束 & 事务 1. DQL操作单表 1.1 创建数据库,复制表 1) 创建一个新的数据库 db2 CREATE DATABASE db2 CHARACTER SET utf8; 2) 将 db1 数据库中的 emp 表 复制到当前 db2 数据库 1.2 排序 通过 ORDER BY 子句 , 可以将查询出的结果进行排序 ( 排序只…

markdown公式特殊字符

个人学习笔记 根号 在 Markdown 中&#xff0c;要表示根号 3&#xff0c;可以使用 LaTeX 语法来实现。常见的有以下两种方式&#xff1a; 行内公式形式&#xff1a;使用一对美元符号 $ 将内容包裹起来&#xff0c;即 $\sqrt{3}$ &#xff0c;在支持 LaTeX 语法渲染的 Markdow…

git笔记-简单入门

git笔记 git是一个分布式版本控制系统&#xff0c;它的优点有哪些呢&#xff1f;分为以下几个部分 与集中式的版本控制系统比起来&#xff0c;不用担心单点故障问题&#xff0c;只需要互相同步一下进度即可。支持离线编辑&#xff0c;每一个人都有一个完整的版本库。跨平台支持…

系统学习算法: 专题七 递归

递归算法简而言之就是当一个大问题拆分为多个子问题时&#xff0c;如果每个子问题的操作步骤都一样&#xff0c;就可以用递归&#xff0c;其中递归在递的时候要有结束条件&#xff0c;不能一直递下去&#xff0c;结束条件后就归 这里不建议学习递归的时候抠细节&#xff0c;还…

C++中常用的十大排序方法之1——冒泡排序

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C中常用的排序方法之——冒泡排序的相关…

OpenEuler学习笔记(十五):在OpenEuler上搭建Java运行环境

一、在OpenEuler上搭建Java运行环境 在OpenEuler上搭建Java运行环境可以通过以下几种常见方式&#xff0c;下面分别介绍基于包管理器安装OpenJDK和手动安装Oracle JDK的步骤。 使用包管理器安装OpenJDK OpenJDK是Java开发工具包的开源实现&#xff0c;在OpenEuler上可以方便…