FreeRTOS教程9 软件定时器

目录

1、准备材料

2、学习目标

3、前提知识

3.1、软件定时器回调函数

3.2、软件定时器属性和状态

3.2.1、周期

3.2.2、分类

3.2.3、状态

3.3、软件定时器运行原理

3.3.1、RTOS 守护进程任务

3.3.2、定时器命令队列

3.3.3、守护进程任务调度

3.4、创建、启动软件定时器

3.4.1、xTicksToWait 参数

3.4.2、xTimerStart() 函数返回值

3.6、软件定时器 ID

3.7、改变软件定时器周期

3.8、重置软件定时器

3.8、停止、删除软件定时器

3.9、其他 API 函数

4、实验一:软件定时器的应用

4.1、实验目标

4.2、CubeMX相关配置

4.3、添加其他必要代码

4.4、烧录验证

5、注释详解

参考资料


1、准备材料

正点原子stm32f407探索者开发板V2.4

STM32CubeMX软件(Version 6.10.0)

Keil µVision5 IDE(MDK-Arm)

野火DAP仿真器

XCOM V2.6串口助手

2、学习目标

本文主要学习 FreeRTOS 软件定时器的相关知识,包括软件定时器回调函数、属性、状态、运行原理和常见 API 函数等知识

3、前提知识

3.1、软件定时器回调函数

软件定时器的回调函数是一个返回值为 void 类型,并且只有软件定时器句柄一个参数的 C 语言函数,其函数的具体原型如下所述

/**
  * @brief  软件定时器回调函数
  * @param  xTimer:软件定时器句柄
  * @retval None
  */
void ATimerCallback(TimerHandle_t xTimer)
{
	/* do something */
}

软件定时器回调函数会在定时器设定的时间到期时在 RTOS 守护进程任务中被执行,软件定时器回调函数从头到尾执行,并以正常方式退出

需要读者注意的是软件定时器的回调函数应尽可能简短,并且在该函数体内不能调用任何会使任务进入阻塞状态的 API 函数,但是如果设置调用函数的 xTicksToWait 参数为 0 ,则可以调用如 xQueueReceive() 等 API 函数

3.2、软件定时器属性和状态

3.2.1、周期

这个属性比较好理解,软件定时器的周期指的是 从软件定时器启动到软件定时器回调函数执行之间的时间,该属性是定时器不可或缺的重要属性

3.2.2、分类

软件定时器根据行为的不同分为了 单次定时器(One-shot timers) 和 周期定时器(Auto-reload timers) 两种类型,如下图展示了两种不同类型软件定时器的行为差异 (注释1)

3.2.3、状态

根据定时器是否正在运行可以将定时器分为 运行状态(Running) 和 休眠状态(Dormant) 两种不同状态,如下图所示展示了单次定时器和周期定时器在两种不同状态之间的转换过程

从图上可以看出以下几点内容

  1. 不管是单次定时器还是周期定时器,在定时器创建成功之后都处于休眠状态,一旦调用启动、复位或改变定时器周期的 API 函数就会使定时器从休眠状态转移到运行状态;
  2. 单次定时器定时时间到期之后执行一次回调函数就会自动转换为休眠状态,而周期定时器会一直处于运行状态;
  3. 当对处于运行状态的定时器调用停止 API 函数时,不管是哪种定时器都会转变为休眠状态

定时器的状态可以通过 xTimerlsTimerActive() API 函数查询,该函数具体声明如下所述

/**
  * @brief  查询软件定时器是否处于运行或休眠状态
  * @param  xTimer:要查询的定时器句柄
  * @retval 如果定时器处于休眠状态则返回pdFALSE,如果定时器处于运行状态则返回pdTRUE
  */
BaseType_t xTimerIsTimerActive(TimerHandle_t xTimer);

3.3、软件定时器运行原理

3.3.1、RTOS 守护进程任务

首先读者应该知道的一点是所有软件定时器的回调函数都在同一个 RTOS 守护进程任务的上下文中执行,这个 RTOS 守护进程任务和空闲任务一样,在调度器启动的时候会被自动创建, RTOS 守护进程任务的优先级和堆栈大小分别由 configTIMER_TASK_PRIORITY  configTIMER_TASK_STACK_DEPTH 两个参数设置(可在 STM32CubeMX 软件中配置)

”3.1、软件定时器回调函数“ 小节提到在回调函数中不能使用会使任务进入阻塞状态的 API 函数,这是因为调用会使任务进入阻塞状态的 API 函数会使 RTOS 守护进程任务进入阻塞状态,这是不被允许的

3.3.2、定时器命令队列

上面提到的软件定时器的启动、复位、改变定时器周期和停止等操作的 API 函数只是将控制定时器的命令从调用任务发送到称为 “定时器命令队列” 的队列上,然后由 RTOS 守护进程任务从定时器命令队列中取出命令对定时器实际操作

定时器命令队列是 FreeRTOS 里的一个标准队列,其也是在调度程序启动时被自动创建的,定时器命令队列的长度可以由 configTIMER_QUEUE LENGTH 参数设置

如下图所示为软件定时器 API 函数使用定时器命令队列与 RTOS 守护程序任务进行通信的示意图

3.3.3、守护进程任务调度

守护进程任务是一个 FreeRTOS 任务,所以其任务调度会遵循和其他任务一样的调度规则,当守护进程任务是能够运行的最高优先级任务时,它将会处理定时器队列中的命令或执行定时器的回调函数

守护进程任务的优先级在 STM32CubeMX 中默认为 2 ,当守护进程任务的优先级低于调用 xTimerStart() 等 API 函数的任务的优先级时,其会在任务结束之后轮到守护进程任务执行时对 “开始定时器” 命令进行处理,具体如下图所示

当守护进程任务的优先级高于调用 xTimerStart() 等 API 函数的任务的优先级时,一旦任务调用 xTimerStart() 等 API 函数将命令写入定时器命令队列,守护进程任务便可以抢占该任务,立即处理写入定时器命令队列的命令,处理完毕之后进入阻塞状态,处理器返回原任务继续执行,具体如下图所示

3.4、创建、启动软件定时器

同样,根据 FreeRTOS API 的惯例,创建软件定时器仍然提供了动态内存创建和静态内存创建两个不同的 API 函数,软件定时器可以在调度程序运行之前创建,也可以在调度程序启动后从任务创建,如下所示为两个 API 函数声明

/**
  * @brief  动态分配内存创建软件定时器
  * @param  pcTimerName:定时器的描述性名称,辅助调试用
  * @param  xTimerPeriod:定时器的周期,参考 “3.2.1、周期” 小节
  * @param  uxAutoReload:pdTRUE表示周期软件定时器,pdFASLE表示单次软件定时器
  * @param  pvTimerID:定时器ID
  * @param  pxCallbackFunction:定时器回调函数指针,参考 “3.1、软件定时器回调函数” 小节
  * @retval 创建成功则返回创建的定时器的句柄,失败则返回NULL
  */
TimerHandle_t xTimerCreate(const char * const pcTimerName,
						   const TickType_t xTimerPeriod,
						   const UBaseType_t uxAutoReload,
						   void * const pvTimerID,
						   TimerCallbackFunction_t pxCallbackFunction);
 
/**
  * @brief  动态分配内存创建软件定时器
  * @param  pcTimerName:定时器的描述性名称,辅助调试用
  * @param  xTimerPeriod:定时器的周期,参考 “3.2.1、周期” 小节
  * @param  uxAutoReload:pdTRUE表示周期软件定时器,pdFASLE表示单次软件定时器
  * @param  pvTimerID:定时器ID
  * @param  pxCallbackFunction:定时器回调函数指针,参考 “3.1、软件定时器回调函数” 小节
  * @param  pxTimerBuffer:指向StaticTimer_t类型的变量,然后用该变量保存定时器的状态
  * @retval 创建成功则返回创建的定时器的句柄,失败则返回NULL
  */
TimerHandle_t xTimerCreateStatic(const char * const pcTimerName,
								  const TickType_t xTimerPeriod,
								  const UBaseType_t uxAutoReload,
								  void * const pvTimerID,
								  TimerCallbackFunction_t pxCallbackFunction
								  StaticTimer_t *pxTimerBuffer);

创建完的软件定时器处于休眠状态,需要调用启动定时器或其他 API 函数才会进入运行状态,xTimerStart() 可以在调度程序启动之前调用,但是完成此操作后,软件定时器直到调度程序启动的时间才会真正启动,启动定时器的 API 函数如下所述

/**
  * @brief  启动定时器
  * @param  xTimer:要操作的定时器句柄
  * @param  xBlockTime:参考 “3.4.1、xTicksToWait 参数” 小节
  * @retval 参考 “3.4.2、函数返回值” 小节
  */
BaseType_t xTimerStart(TimerHandle_t xTimer,
					   TickType_t xTicksToWait);
 
/**
  * @brief  启动定时器的中断安全版本
  * @param  xTimer:要操作的定时器句柄
  * @param  pxHigherPriorityTaskWoken:用于通知应用程序编写者是否应该执行上下文切换
  * @retval 参考 “3.4.2、函数返回值” 小节
  */
BaseType_t xTimerStartFromISR(TimerHandle_t xTimer,
							  BaseType_t *pxHigherPriorityTaskWoken);

3.4.1、xTicksToWait 参数

xTimerStart() 使用定时器命令队列向守护进程任务发送 “启动定时器” 命令, xTicksToWait 指定调用任务应保持在阻塞状态以等待定时器命令队列上的空间变得可用的最长时间(如果队列已满),该参数需要注意以下几点

  1. 如果 xTicksToWait 为零且定时器命令队列已满,xTimerStart() 将立即返回,该参数以滴答定时器时间刻度为单位,可以使用宏 pdMS_TO_TICKS() 将以毫秒为单位的时间转换为以刻度为单位的时间,例如 pdMS_TO_TICKS(50) 表示阻塞 50ms

  2. 如果在 FreeRTOSConfig.h 中将 INCLUDE_vTaskSuspend 设置为 1,则将 xTicksToWait 设置为 portMAX_DELAY 将导致调用任务无限期地保持在阻塞状态(没有超时),以等待定时器命令队列中的空间变得可用

  3. 如果在启动调度程序之前调用 xTimerStart(),则 xTicksToWait 的值将被忽略,并且 xTimerStart() 的行为就像 xTicksToWait 已设置为零一样

3.4.2、xTimerStart() 函数返回值

有两种可能的返回值,分别为 pdPASS 和 pdFALSE ,具体如下所述

① 仅当 “启动定时器” 命令成功发送到定时器命令队列时,才会返回 pdPASS

  1. 如果守护程序任务的优先级高于调用 xTimerStart() 的任务的优先级,则调度程序将确保在 xTimerStart() 返回之前处理启动命令。这是因为一旦定时器命令队列中有数据,守护任务就会抢占调用 xTimerStart() 的任务,从而总是保证将命令成功发送到定时器命令队列
  2. 如果指定了阻塞时间(xTicksToWait 不为零),则调用任务可能会被置于阻塞状态,以等待定时器命令队列中的空间在函数返回之前变得可用,只要在阻塞时间到期之前命令已成功写入定时器命令队列,就可以返回 pdPASS

② 如果由于队列已满或超过阻塞时间等原因无法将 “启动定时器” 命令写入定时器命令队列,则将返回 pdFALSE

  1. 如果指定了阻塞时间(xTicksToWait 不为零),则调用任务将被置于阻塞状态以等待守护进程任务在定时器命令队列中腾出空间,但是指定的阻塞时间在等待定时器命令队列中腾出空间之前已过期,所以返回 pdFALSE

3.6、软件定时器 ID

每个软件定时器都有一个 ID ,它是一个标签值,应用程序编写者可以将其用于任何目的, ID 被存储在空指针中,因此可以直接存储整数值,指向任何其他对象,或用作函数指针

创建软件定时器时会为 ID 分配一个初始值,之后可以使用 vTimerSetTimerID() API 函数更新 ID,并且可以使用 pvTimerGetTimerID() API 函数查询 ID ,这两个 API 函数具体如下所示

/**
  * @brief  设置定时器ID值
  * @param  xTimer:要操作的定时器句柄
  * @param  pvNewID:想要设置软件定时器的新ID值
  * @retval None
  */
void vTimerSetTimerID(TimerHandle_t xTimer, void *pvNewID);
 
/**
  * @brief  获取定时器ID值
  * @param  xTimer:要操作的定时器句柄
  * @retval 正在查询的软件定时器ID
  */
void *pvTimerGetTimerID(TimerHandle_t xTimer);

注意:与其他软件定时器 API 函数不同,vTimerSetTimerID() 和 pvTimerGetTimerID() 直接访问软件定时器,它们不向定时器命令队列发送命令

如果创建了多个软件定时器,并且所有软件定时器均使用了同一个回调函数,则可以给软件定时器设置不同的 ID 值,然后在回调函数中通过 ID 值判断软件定时器触发的来源

3.7、改变软件定时器周期

创建软件定时器时就会为定时器周期设置初始值,后续也可以使用 xTimerChangePeriod() 函数动态更改软件定时器的周期,该函数具体声明如下所示

/**
  * @brief  改变软件定时器的周期
  * @param  xTimer:要操作的定时器句柄
  * @param  xNewPeriod:软件定时器的新周期,以刻度为单位指定
  * @param  xBlockTime:参考 “3.4.1、xTicksToWait 参数” 小节
  * @retval 参考 “3.4.2、xTimerStart() 函数返回值” 小节
  */
 BaseType_t xTimerChangePeriod(TimerHandle_t xTimer,
							   TickType_t xNewPeriod,
							   TickType_t xBlockTime);
 
/**
  * @brief  改变软件定时器周期的中断安全版本
  * @param  xTimer:要操作的定时器句柄
  * @param  xNewPeriod:软件定时器的新周期,以刻度为单位指定
  * @param  pxHigherPriorityTaskWoken:用于通知应用程序编写者是否应该执行上下文切换
  * @retval 参考 “3.4.2、xTimerStart() 函数返回值” 小节
  */
 BaseType_t xTimerChangePeriodFromISR(TimerHandle_t xTimer,
									  TickType_t xNewPeriod,
									  BaseType_t *pxHigherPriorityTaskWoken);

如果 xTimerChangePeriod() 用于更改已运行的定时器的周期,则定时器将使用新的周期值重新计算其到期时间,重新计算的到期时间是相对于调用 xTimerChangePeriod() 的时间,而不是相对于定时器最初启动的时间

如果使用 xTimerChangePeriod() 更改处于休眠状态(未运行的定时器)的定时器的周期,则定时器将计算到期时间,并转换到运行状态(定时器将开始运行)

另外如果希望查询一个定时器的定时周期,可以通过 xTimerGetPeriod() API 函数查询,具体函数声明如下所述

/**
  * @brief  查询一个软件定时器的周期
  * @param  xTimer:要查询的定时器句柄
  * @retval 返回一个软件定时器的周期
  */
TickType_t xTimerGetPeriod(TimerHandle_t xTimer);

3.8、重置软件定时器

重置软件定时器是指重新启动定时器,定时器的到期时间将根据重置定时器的时间重新计算,而不是相对于定时器最初启动的时间,如下图对此进行了演示,其中显示了一个定时器,该定时器启动的周期为 6,然后重置两次,最后到期并执行其回调函数

FreeRTOS中使用 xTimerReset() API 函数重置软件定时器,除此之外还可用于启动处于休眠状态的定时,该函数具体声明如下所述

/**
  * @brief  重置软件定时器
  * @param  xTimer:要操作的定时器句柄
  * @param  xBlockTime:参考 “3.4.1、xTicksToWait 参数” 小节
  * @retval 参考 “3.4.2、xTimerStart() 函数返回值” 小节
  */
BaseType_t xTimerReset(TimerHandle_t xTimer,
					   TickType_t xBlockTime);
 
/**
  * @brief  重置软件定时器的中断安全版本
  * @param  xTimer:要操作的定时器句柄
  * @param  pxHigherPriorityTaskWoken:用于通知应用程序编写者是否应该执行上下文切换
  * @retval 参考 “3.4.2、xTimerStart() 函数返回值” 小节
  */
BaseType_t xTimerResetFromISR(TimerHandle_t xTimer,
							  BaseType_t *pxHigherPriorityTaskWoken);

3.8、停止、删除软件定时器

/**
  * @brief  停止软件定时器
  * @param  xTimer:要操作的定时器句柄
  * @param  xBlockTime:参考 “3.4.1、xTicksToWait 参数” 小节
  * @retval 参考 “3.4.2、xTimerStart() 函数返回值” 小节
  */
BaseType_t xTimerStop(TimerHandle_t xTimer,
					  TickType_t xBlockTime);
 
/**
  * @brief  删除软件定时器
  * @param  xTimer:要操作的定时器句柄
  * @param  xBlockTime:参考 “3.4.1、xTicksToWait 参数” 小节
  * @retval 参考 “3.4.2、xTimerStart() 函数返回值” 小节
  */
BaseType_t xTimerDelete(TimerHandle_t xTimer,
						TickType_t xBlockTime);
 
/**
  * @brief  停止软件定时器的中断安全版本
  * @param  xTimer:要操作的定时器句柄
  * @param  pxHigherPriorityTaskWoken:用于通知应用程序编写者是否应该执行上下文切换
  * @retval 参考 “3.4.2、xTimerStart() 函数返回值” 小节
  */
BaseType_t xTimerStopFromISR(TimerHandle_t xTimer,
							 BaseType_t *pxHigherPriorityTaskWoken);

3.9、其他 API 函数

/**
  * @brief  将软件定时器的“模式”更新为 自动重新加载定时器 或 一次性定时器 
  * @param  xTimer:要操作的定时器句柄
  * @param  uxAutoReload:设置为pdTRUE则将定时器设置为周期软件定时器,设置为pdFASLE则将定时器设置为单次软件定时器
  * @retval None
  */
void vTimerSetReloadMode(TimerHandle_t xTimer,
						 const UBaseType_t uxAutoReload);
 
/**
  * @brief  查询软件定时器是 单次定时器 还是 周期定时器
  * @param  xTimer:要查询的定时器句柄
  * @retval 如果为周期软件定时器则返回pdTRUE,否则返回pdFALSE
  */
BaseType_t xTimerGetReloadMode(TimerHandle_t xTimer);
 
/**
  * @brief  查询软件定时器到期的时间
  * @param  xTimer:要查询的定时器句柄
  * @retval 如果要查询的定时器处于活动状态则返回定时器下一次到期的时间,否则未定义返回值
  */
TickType_t xTimerGetExpiryTime(TimerHandle_t xTimer);

4、实验一:软件定时器的应用

4.1、实验目标

  1. 创建一个周期软件定时器 TimerPeriodic 和一个单次软件定时器 TimerOnce
  2. 创建一个按键扫描任务 Task_KeyScan,根据不同按键实现不同响应
  3. 当按键 WK_UP 按下时,设置周期定时器以 500ms 周期执行;当按键 KEY2 按下时,设置单次定时器以 1s 周期执行一次;当按键 KEY1 按下时,对周期定时器进行复位操作;当按键 KEY0 按下时,停止 TimerPeriodic 周期定时器

4.2、CubeMX相关配置

首先读者应按照 "FreeRTOS教程1 基础知识" 章节配置一个可以正常编译通过的 FreeRTOS 空工程,然后在此空工程的基础上增加本实验所提出的要求

本实验需要初始化 USART1 作为输出信息渠道,具体配置步骤请阅读“STM32CubeMX教程9 USART/UART 异步通信”,如下图所示

本实验需要初始化开发板上 WK_UP、KEY2、KEY1 和 KEY0 用户按键做普通输入,具体配置步骤请阅读“STM32CubeMX教程3 GPIO输入 - 按键响应”,注意虽开发板不同但配置原理一致,如下图所示

单击 Middleware and Software Packs/FREERTOS ,在 Configuration 中单击 Tasks and Queues 选项卡,双击默认任务修改其参数,如下所示

单击 Timers and Semaphores ,在 Timers 中创建周期、单次两个软件定时器,如下所示

配置 Clock Configuration 和 Project Manager 两个页面,接下来直接单击 GENERATE CODE 按钮生成工程代码即可

4.3、添加其他必要代码

按照 “STM32CubeMX教程9 USART/UART 异步通信” 实验 “6、串口printf重定向” 小节增加串口 printf 重定向代码,具体不再赘述

首先应该在 freertos.c 中添加软件定时器的头文件和使用到的 printf 的头文件,如下所述

#include "timers.h"
#include "stdio.h"

然后实现按键扫描任务函数体,当按键 WK_UP 按下时启动周期软件定时器,当按键 KEY2 按下时启动单次软件定时器,当按键 KEY1 按下时对周期软件定时器进行复位操作,当按键 KEY0 按下时停止周期定时器,具体如下所述

void AppTask_KeyScan(void *argument)
{
	/* USER CODE BEGIN AppTask_KeyScan */
	uint8_t key_value = 0;
	/* Infinite loop */
	for(;;)
	{
		key_value = 0;
		//按键WK_UP按下
		if(HAL_GPIO_ReadPin(WK_UP_GPIO_Port,WK_UP_Pin) == GPIO_PIN_SET)
			key_value = 4;
		//按键KEY2按下
		if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin) == GPIO_PIN_RESET)
			key_value = 3;
		//按键KEY1按下
		if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_RESET)
			key_value = 2;
		//按键KEY0按下
		if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET)
			key_value = 1;
		
		if(key_value != 0)
		{
			if(key_value == 4)
			{
				if(xTimerChangePeriod(TimerPeriodicHandle, 500, pdMS_TO_TICKS(500)) == pdTRUE)
				{
					printf("\r\nWK_UP PRESSED, TimerPeriodic Start!\r\n\r\n");
				}
			}
			if(key_value == 3)
			{
				if(xTimerChangePeriod(TimerOnceHandle, 1000, pdMS_TO_TICKS(500)) == pdTRUE)
				{
					printf("\r\nKEY2 PRESSED, TimerOnce Start!\r\n\r\n");
				}
			}
			else if(key_value == 2)
			{
				if(xTimerReset(TimerPeriodicHandle, pdMS_TO_TICKS(500)) == pdTRUE)
				{
					printf("\r\nKEY1 PRESSED, TimerPeriodic Reset!\r\n\r\n");
				}
			}
			else if(key_value == 1)
			{
				if(xTimerStop(TimerPeriodicHandle, pdMS_TO_TICKS(500)) == pdTRUE)
				{
					printf("\r\nKEY0 PRESSED, TimerPeriod Stop!\r\n\r\n");
				}
			}
			//有按键按下就进行按键消抖
			osDelay(300);
		}
		else
			osDelay(10);
	}
	/* USER CODE END AppTask_KeyScan */
}

最后实现单次/周期软件定时器的两个回调函数即可,回调函数内不做任何具体操作,仅通过串口输出提示信息,如下所述

/* appTimerPeriodic function */
void appTimerPeriodic(void *argument)
{
  /* USER CODE BEGIN appTimerPeriodic */
	printf("Into appTimerPeriodic Function\r\n");
  /* USER CODE END appTimerPeriodic */
}
 
/* appTimerOnce function */
void appTimerOnce(void *argument)
{
  /* USER CODE BEGIN appTimerOnce */
	printf("Into appTimerOnce Function\r\n");
  /* USER CODE END appTimerOnce */
}
 

4.4、烧录验证

烧录程序,打开串口助手后无任何信息输出,当按下开发板上的 WK_UP 按键之后,会启动以 500ms 为周期的周期软件定时器,此时周期软件定时器的回调函数会周期得到执行;当按下开发板上的 KEY2 按键之后,会启动 1s 为周期的单次软件定时器,此时单次软件定时器的回调函数会得到执行,并且只执行了一次就停止了执行;当按下开发板上的 KEY1 按键时,会复位周期定时器;当按下开发板上的 KEY0 按键时,会停止周期定时器,整个过程串口的输出信息如下图所示

5、注释详解

注释1:图片来源于 Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf

参考资料

STM32Cube高效开发教程(基础篇)

Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Web框架开发-Django模型层(数据库操作)

一、ORM介绍 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动ORM是“对象-关系-映…

ubuntu20.04安装Pycharm

下载pycharm安装包 https://www.jetbrains.com/pycharm/download/#sectionlinux 使用社区版点击download 下载好的pycharm如图所示,右键解压: 打开终端,输入cd命令,进入刚刚解压文件夹下的bin文件夹,命令行是cd 文…

手撕算法-二叉搜索树与双向链表

牛客BM30。 描述:https://www.nowcoder.com/practice/947f6eb80d944a84850b0538bf0ec3a5?tpId295&tqId23253&ru/exam/oj&qru/ta/format-top101/question-ranking&sourceUrl%2Fexam%2Foj分析:二叉搜索树的中序遍历是递增序列。可以利用…

【AI工具】文字/图片生产3D模型-MVEdit 3D Toolbox

MVEdit 是一款无需培训的 3D 适配器,可 使用现成的 2D 稳定扩散模型进行 3D 生成/编辑 定位:一款功能强大的 3D 工具箱,可通过文本和图像创建和生成 3D 模型,具有可视化 UI 操作。 功能介绍: 能够从多视角图像生成高质量纹理网格,通过 3D 适配器实现出色的 3D 一致性。…

JAVA每日面经——并发编程(一)必看

👩🏽‍💻个人主页:阿木木AEcru 🔥 系列专栏:《Docker容器化部署系列》 《Java每日面筋》 💹每一次技术突破,都是对自我能力的挑战和超越。 目录 一、并发编程之AQS二、并发编程之CAS…

广东省30m二级分类土地利用数据(矢量)

广东省,地处中国大陆最南部,属于东亚季风区,从北向南分别为中亚热带、南亚热带和热带气候,是中国光、热和水资源最丰富的地区之一。主要河系为珠江的西江、东江、北江和三角洲水系以及韩江水系。广东省面积为17.977万平方公里&…

【python + Django】Django模板语法 + 请求和响应

前言: 现在现在,我们要开始将变量的值展现在页面上面啦! 要是只会显示静态页面,我们的页面也太难看和死板了, 并且数据库的数据也没法展现在页面上。 但是呢,模板语法学习之后就可以啦!&…

Halcon 3D 平面拟合(区域采样、Z值过滤、平面拟合、平面移动)

Halcon 3D 平面拟合(区域采样、Z值过滤、平面拟合、平面移动) 链接:https://pan.baidu.com/s/1UfFyZ6y-EFq9jy0T_DTJGA 提取码:ewdi * 1.读取图片 ****************

政安晨:【深度学习实践】【使用 TensorFlow 和 Keras 为结构化数据构建和训练神经网络】(二)—— 深度神经网络

政安晨的个人主页:政安晨 欢迎 👍点赞✍评论⭐收藏 收录专栏: 政安晨的机器学习笔记 希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正! 概述 深度神经网络(Deep Neural Network&…

24北京教资认定明日开始,请提前准备证件照

24北京教资认定明日开始,请提前准备证件照哦!

KEY ENERGY欧洲意大利能源光伏储能展

3月1号第18届意大利里米尼国际可再生能源展(KEY ENERGY)由知名主办方ITALIAN EXHIBITION GROUP S.P.A组织举办,每年一届,是欧洲第二大能源展,也是覆盖范围最全知名度最高的可再生能源展览会。 该展会将于2024扩大规模…

EL表达式

一、什么是EL EL(Expression Language)表达式语言 由两个开发团队共同开发 JSP 标准标签库专家组 JSP 2.0 专家组 EL表达式语言的语法 ${Expression} JSP EL 表达式用于以下情形 在JSP页面中输出静态内容 为标准标签和自定义标签提供属性值 二、作用&am…

排序算法之选择排序介绍

目录 算法简介 算法描述 代码实现 算法简介 选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素…

解析服务器出现大量 TIME_WAIT 和 CLOSE_WAIT 状态的原因及排查方法

服务器出现大量 TIME_WAIT 状态的原因有哪些? 首先要知道 TIME_WAIT 状态是主动关闭连接方才会出现的状态(别陷入一个误区不是只有客户端才能主动关闭连接的),所以如果服务器出现大量的 TIME_WAIT 状态的 TCP 连接,就是…

MongoDB系列之查询计划

概述 一个查询具体如何被执行的过程,称为查询计划。MongoDB采用自底向上的方式来构造查询计划,每一个查询计划(query plan)都会被分解为若干个有层次的阶段(stage)。整个查询计划最终会呈现出一颗多叉树。…

3个Tips,用“AI”开启新生活

相信最近,很多朋友们都回归到了忙碌的生活节奏中。生活模式的切换,或多或少会带来身体或情绪状况的起伏。新技术正在为人们生活的方方面面带来便利。3个小Tips或许能让你也从新技术中获益,从身到心,用“AI”开启新生活。 关”A…

【机器学习】基于正余弦搜索算法优化的BP神经网络分类预测(SCA-BP)

目录 1.原理与思路2.设计与实现3.结果预测4.代码获取 1.原理与思路 【智能算法应用】智能算法优化BP神经网络思路【智能算法】正余弦优化算法(SCA)原理及实现 2.设计与实现 数据集: 多输入多输出:样本特征24,标签类…

基于深度学习的面部情绪识别算法仿真与分析

声明:以下内容均属于本人本科论文内容,禁止盗用,否则将追究相关责任 基于深度学习的面部情绪识别算法仿真与分析 摘要结果分析1、本次设计通过网络爬虫技术获取了七种面部情绪图片:吃惊、恐惧、厌恶、高兴、伤心、愤怒、自然各若…

Python Qt Designer 初探

代码下载在最下面 #开发环境安装# 本示例在Windows11下, 使用VSCode开发, Python 3.12.2, Qt Designer 5.11 VSCode插件Python、Python Debugger、PYQT Integration、Pylance (准备) VSCode自行官网下载 Visual Studio Code - Code Editing. Redefined (准备) Python 直接…

腾讯和香港中文大学发布文字生成视频AI模型DynamiCrafter

前言 在数字化时代,视觉内容的创造和动态化已成为创意表达和信息传递的重要工具。最近由香港中文大学、腾讯AI Lab联合研发的视频AI模型DynamiCrafter,这一模型能够将静态图像转化为逼真的动态视频,开创了文本到视频生成技术的新纪元。 Hugg…