从0开始的STM32 定时器(I):聊一聊基本定时器
目录
时钟源
控制器
时基单元
关于HAL库如何配置基本定时器
HAL是如何初始化我们的定时器句柄的
HAL_TIM_Base_Init
开始定时
如何处理句柄?
在我们使用STM32解决一些问题的时候,常常会遇到说:我想要以一个周期做一些事情:比如说周期性的向我的上位机发送当前的温度和湿度,比如说周期性的点亮一个LED,刷新一次LCD屏幕。这都离不开我们定时器的身影。STM32有2个基本定时器,4个通用定时器,2个高级定时器。我们慢慢来阐述不同种类的定时器的作用。现在,让我们从最基础的最简单的基本定时器开始!

学过数电的朋友一眼就能知道这个图怎么回事了:我们对来自RCC时钟的信号进行分频,当到打分频计数的时候向计数器发起一次脉冲,计数器接受到脉冲后会自增1,直到到达我们设定的阈值的时候发起一次中断通知定时器超时。这就是定时器的最基本的工作原理!
这样来看,定时器的核心就是计算器!就是打点计数,到点发起通知重置!他有三个要素组成:
时钟源
要实现计数功能,首先要给它一个时钟源。打点计时,还是需要水滴下来的!基本定时器时钟挂载在 APB1 总线,所以它的时钟来自于 APB1 总线,但是基本定时器时钟不是直接由 APB1 总线直接提供,而是先经过一个倍频器。当APB1 的预分频器系数为 1 时,这个倍频器系数为 1,即定时器的时钟频率等于 APB1 总线时钟频率;当 APB1 的预分频器系数≥2 分频时,这个倍频器系数就为 2 ,即定时器的时钟频率等于 APB1 总线时钟频率的两倍。
控制器
控制器除了控制定时器复位、使能、计数等功能之外,还可以用于触发DAC 转换。这个是我们的定时器可以被控制的根本。
时基单元
时基单元包括:计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)、自动重载寄存器(TIMx_ARR) 。基本定时器的这三个寄存器都是 16 位有效数字,即可设置值范围是 0~65535。时基单元中的预分频器PSC,它有一个输入和一个输出。输入CK_PSC 来源于控制器部分,实际上就是来自于内部时钟(CK_INT),即2 倍的APB1 总线时钟频率(72MHz)。输出 CK_CNT是分频后的时钟,它是计数器实际的计数时钟,通过设置预分频器寄存器(TIMx_PSC)的值可以得到不同频率 CK_CNT,计算公式如下:
fCK_CNT= fCK_PSC / (PSC[15:0]+1)
上式中,PSC[15:0]是写入预分频器寄存器(TIMx_PSC)的值。
另外:预分频器寄存器(TIMx_PSC)可以在运行过程中修改它的数值,新的预分频数值将在下一个更新事件时起作用。因为更新事件发生时,会把 TIMx_PSC 寄存器值更新到其影子寄存器中,这才会起作用。
那什么是影子寄存器?从框图上看,可以看到图20.1.1.1中的预分频器PSC 后面有一个影子,自动重载寄存器也有个影子,这就表示这些寄存器有影子寄存器。影子寄存器是一个实际起作用的寄存器,不可直接访问。举个例子:我们可以把预分频系数写入预分频器寄存器(TIMx_PSC),但是预分频器寄存器只是起到缓存数据的作用,只有等到更新事件发生时,预分频器寄存器的值才会被自动写入其影子寄存器中,这时才真正起作用。 也就是说,我们不能直接暴力的打断当前的计数,采用缓存的办法下一次更新,这确保系统至少是稳定的工作的!
自动重载寄存器及其影子寄存器的作用和上述同理。不同点在于自动重载寄存器是否具有缓冲作用还受到 ARPE 位的控制,当该位置0 时,ARR 寄存器不进行缓冲,我们写入新的 ARR值时,该值会马上被写入 ARR 影子寄存器中,从而直接生效;当该位置 1 时,ARR 寄存器进行缓冲,我们写入新的 ARR 值时,该值不会马上被写入 ARR 影子寄存器中,而是要等到更新事件发生才会被写入 ARR 影子寄存器,这时才生效。预分频器寄存器则没有这样相关的控制位,这就是它们不同点。
值得注意的是,更新事件的产生有两种情况,一是由软件产生,将 TIMx_EGR 寄存器的位UG 置 1,产生更新事件后,硬件会自动将 UG 位清零。二是由硬件产生,满足以下条件即可:计数器的值等于自动重装载寄存器影子寄存器的值。
基本定时器的计数器(CNT)是一个递增的计数器,当寄存器(TIMx_CR1)的CEN 位置1,即使能定时器,每来一个 CK_CNT 脉冲,TIMx_CNT 的值就会递增加 1。当TIMx_CNT 值与 TIMx_ARR 的设定值相等时,TIMx_CNT 的值就会被自动清零并且会生成更新事件(如果开启相应的功能,就会产生 DMA 请求、产生中断信号或者触发 DAC 同步电路),然后下一个CK_CNT 脉冲到来,TIMx_CNT 的值就会递增加 1,如此循环。在此过程中,TIMx_CNT 等于TIMx_ARR 时,我们称之为定时器溢出,因为是递增计数,故而又称为定时器上溢。定时器溢出就伴随着更新事件的发生。
由上述可知,我们只要设置预分频寄存器和自动重载寄存器的值就可以控制定时器更新事件发生的时间。自动重载寄存器(TIMx_ARR)是用于存放一个与计数器作比较的值,当计数器的值等于自动重载寄存器的值时就会生成更新事件,硬件自动置位相关更新事件的标志位,如:更新中断标志位。
下面举个例子来学习如何设置预分频寄存器和自动重载寄存器的值来得到我们想要的定时器上溢事件发生的时间周期。比如我们需要一个500ms 周期的定时器更新中断,一般思路是先设置预分频寄存器,然后才是自动重载寄存器。
考虑到我们设置的 CK_INT 为72MHz,我们把预分频系数设置为 7200,即写入预分频寄存器的值为 7199,那么 fCK_CNT=72MHz/7200=10KHz。这样就得到计数器的计数频率为10KHz,即计数器 1 秒钟可以计 10000 个数。我们需要 500ms 的中断周期,所以我们让计数器计数 5000 个数就能满足要求,即需要设置自动重载寄存器的值为4999,另外还要把定时器更新中断使能位 UIE 置 1,CEN 位也要置 1。
关于HAL库如何配置基本定时器
下面给看官演示如何使用使用最基本的定时器完成定时触发任务:
首先,笔者现在用的STM32F103ZET6有两个基本的定时器TIM6和TIM7。笔者使用的是TIM6。现在,勾选Activate使能我们的定时器后,开始设置参数。
-
Prescaler是我们上面提到的预分频的意思:这里笔者的RCC时钟是72MHz,现在做分频笔者按照经典的方案:分7200份,也就是每一份实际上是10KHz
-
CounterMode:向上计数(如果只是定时无所谓)
-
Counter Period:自动重装载值,笔者打算1s触发一次事件,那就是让1s计数1次,即1HZ,需要填入10000 - 1 = 9999(Btw,不太理解为什么非得要填一个很小的值然后在程序里自己维护一个计数,我后来想了想,可能是同步的其他周期的任务计时更加方便)(后补:实际上就是更细粒度的维护
-
auto-reload preload:Disable的时候自动重装载寄存器写入新值后,计数器立即产生计数溢出,然后开始新的计数周期,也就是说,我们在Disable的时候马上修改我们在影子寄存器里的重载值,而在Enable的时候则是完成当前周期之后再将值写入影子寄存器里(多一个周期)

下面,我们就是使能一下我们的NVIC:

完成其他配置之后,我们就可以开始生成工程。值得注意的是,初始化结束我们的寄存器之后记得开启定时器计时(笑)
/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim == &htim6){HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);}
}
/* USER CODE END 0 */
/*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */SystemClock_Config();
/* USER CODE BEGIN SysInit *//* USER CODE END SysInit */
/* Initialize all configured peripherals */MX_GPIO_Init();MX_TIM6_Init();/* USER CODE BEGIN 2 */HAL_TIM_Base_Start_IT(&htim6);/* USER CODE END 2 */
/* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
回调事件请在HAL_TIM_PeriodElapsedCallback内完成。下面我们依次介绍HAL库的函数配置。
HAL是如何初始化我们的定时器句柄的
HAL库这样抽象一个定时器:
/*** @brief 初始化 TIM6 定时器。* @note 此函数初始化 TIM6 为基础计时器,设置了预分频器和计数周期,并配置了触发和同步模式。* @retval None*/
static void MX_TIM6_Init(void)
{/* USER CODE BEGIN TIM6_Init 0 */// 用户代码部分:可以在此处添加初始化之前的代码/* USER CODE END TIM6_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0}; // 定义主配置结构体,用于配置 TIM6 的主从同步模式
/* USER CODE BEGIN TIM6_Init 1 */// 用户代码部分:可以在此处添加初始化之后的代码/* USER CODE END TIM6_Init 1 */htim6.Instance = TIM6; // 指定 TIM6 外设实例
// 配置定时器参数htim6.Init.Prescaler = 7199; // 设置预分频器值为 7199。定时器计数频率为系统时钟频率 / (Prescaler + 1)htim6.Init.CounterMode = TIM_COUNTERMODE_UP; // 设置计数模式为向上计数模式htim6.Init.Period = 9999; // 设置定时器周期为 9999。定时器将在计数到达此值时溢出htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; // 禁用自动重载预加载功能
// 初始化定时器if (HAL_TIM_Base_Init(&htim6) != HAL_OK){Error_Handler(); // 如果定时器初始化失败,调用错误处理函数}
// 配置定时器主输出触发(TRGO)和主从同步模式sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; // 配置主输出触发为重置sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; // 禁用主从同步模式if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK){Error_Handler(); // 如果主从同步配置失败,调用错误处理函数}
/* USER CODE BEGIN TIM6_Init 2 */// 用户代码部分:可以在此处添加初始化后的代码/* USER CODE END TIM6_Init 2 */
}
按照流程,我们做了这一些事情:
配置好我们在CubeMx中设置的一系列的参数值。
使用
HAL_TIM_Base_Init(&htim6)函数用于初始化定时器的基本配置,包括预分频器、计数模式、周期等参数。如果初始化失败,程序将调用Error_Handler()进行错误处理。主从同步配置:
主输出触发(Master Output Trigger, TRGO):
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;设置定时器的主输出触发事件为重置信号。重置信号通常用于同步其他外设或触发其他操作。主从同步模式(Master Slave Mode):
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;禁用主从同步模式,这意味着 TIM6 不与其他定时器或外设同步工作。
HAL_TIM_Base_Init
这个是实际的IMPL函数:HAL_TIM_Base_Init 函数用于初始化一个基本定时器的配置。它检查传入的定时器句柄和配置信息是否有效,并根据用户的配置初始化相关硬件资源,包括定时器计数器、时钟和中断。
/*** @brief 初始化 TIM 基本定时器。* @param htim 指向一个 TIM_HandleTypeDef 结构体,该结构体包含了定时器的配置信息。* @retval HAL 状态码(HAL_OK 或 HAL_ERROR)*/
HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim)
{/* 检查 TIM 句柄是否有效 */if (htim == NULL){return HAL_ERROR; // 如果 TIM 句柄无效,返回错误}
/* 检查定时器配置参数是否有效 */assert_param(IS_TIM_INSTANCE(htim->Instance)); // 确保传入的定时器实例有效assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode)); // 确保计数模式有效assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision)); // 确保时钟分频配置有效assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload)); // 确保自动重载预加载配置有效
if (htim->State == HAL_TIM_STATE_RESET){/* 如果定时器处于重置状态,初始化定时器 */htim->Lock = HAL_UNLOCKED; // 解锁定时器资源
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)/* 如果启用回调函数注册功能,重置中断回调为默认的弱回调 */TIM_ResetCallback(htim);
if (htim->Base_MspInitCallback == NULL){/* 如果没有用户提供的回调函数,则使用默认的硬件初始化回调函数 */htim->Base_MspInitCallback = HAL_TIM_Base_MspInit;}/* 初始化低级硬件配置:GPIO、时钟、NVIC */htim->Base_MspInitCallback(htim);
#else/* 如果没有启用回调函数注册,直接调用默认的硬件初始化函数 */HAL_TIM_Base_MspInit(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}
/* 设置定时器状态为忙碌 */htim->State = HAL_TIM_STATE_BUSY;
/* 设置定时器时间基准配置 */TIM_Base_SetConfig(htim->Instance, &htim->Init);
/* 初始化 DMA burst 操作状态 */htim->DMABurstState = HAL_DMA_BURST_STATE_READY;
/* 初始化 TIM 通道的状态 */TIM_CHANNEL_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY); // 设置所有 TIM 通道为准备好状态TIM_CHANNEL_N_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY); // 设置所有通道的N状态为准备好
/* 设置定时器状态为就绪 */htim->State = HAL_TIM_STATE_READY;
return HAL_OK; // 返回初始化成功
}
函数的中间部分是参数验证:
-
assert_param(IS_TIM_INSTANCE(htim->Instance)):确保定时器实例(如TIM1,TIM2等)有效。 -
assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode)):确保计数模式有效,如向上计数、向下计数或其它模式。 -
assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision)):检查时钟分频配置是否有效。 -
assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload)):检查自动重载预加载配置是否有效。
检查完毕,如果定时器的状态为 HAL_TIM_STATE_RESET(即未初始化或重置状态),则执行硬件资源的分配和初始化。此时,定时器的 Lock 锁定状态设置为解锁,准备进一步的配置。
-
如果启用了回调函数注册功能(通过宏
USE_HAL_TIM_REGISTER_CALLBACKS),则重置回调函数为默认的弱回调。 -
如果没有提供用户自定义的回调函数,则使用默认的初始化回调
HAL_TIM_Base_MspInit,进行定时器的低级硬件初始化。硬件初始化通常包括时钟、GPIO 和 NVIC(中断控制器)的配置。
设置定时器配置: 调用 TIM_Base_SetConfig(htim->Instance, &htim->Init) 设置定时器的时间基准配置。这包括预分频器、计数模式、自动重载等设置。
DMA 状态初始化: 设置定时器的 DMA burst 操作状态为 HAL_DMA_BURST_STATE_READY,表示 DMA 操作准备就绪,可以开始数据传输。
定时器通道状态设置: 使用 TIM_CHANNEL_STATE_SET_ALL 和 TIM_CHANNEL_N_STATE_SET_ALL 宏将所有的定时器通道状态设置为 HAL_TIM_CHANNEL_STATE_READY,表示通道准备好进行操作。
函数结尾: 最后,将定时器的状态设置为 HAL_TIM_STATE_READY,表示定时器配置完成并准备好工作。函数返回 HAL_OK,表示初始化成功。
开始定时
下面我们就要开始定时:定时使用的函数就是:HAL_TIM_Base_Start_IT: HAL_TIM_Base_Start_IT 函数用于启动基本定时器的更新中断模式。当启用中断模式时,定时器会定期触发更新事件,并通过中断通知用户。此函数适用于需要周期性执行任务的应用,比如定时器定时中断。这就是为什么我们要在上面开启他的中断
/*** @brief 启动 TIM 基本定时器的中断模式。* @param htim TIM 基本定时器句柄* @retval HAL 状态码*/
HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)
{uint32_t tmpsmcr;
/* 检查传入的 TIM 实例是否有效 */assert_param(IS_TIM_INSTANCE(htim->Instance));
/* 检查 TIM 的当前状态是否为就绪状态 */if (htim->State != HAL_TIM_STATE_READY){return HAL_ERROR; // 如果定时器不是就绪状态,返回错误}
/* 设置定时器状态为忙碌 */htim->State = HAL_TIM_STATE_BUSY;
/* 启用 TIM 更新中断 */__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);
/* 启用定时器外设,除非是触发模式(trigger mode),触发模式下定时器会自动启用 */if (IS_TIM_SLAVE_INSTANCE(htim->Instance)) // 检查是否为从模式定时器{tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS; // 获取定时器从模式的状态if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr)) // 如果不是触发模式,则启用定时器{__HAL_TIM_ENABLE(htim); // 启动定时器}}else{__HAL_TIM_ENABLE(htim); // 如果是主模式,直接启用定时器}
/* 返回操作状态 */return HAL_OK; // 返回成功
}
-
函数概述:
-
参数检查:
-
assert_param(IS_TIM_INSTANCE(htim->Instance)):检查传入的定时器实例是否有效。通过宏IS_TIM_INSTANCE,确认htim->Instance属于有效的 TIM 实例(例如TIM1,TIM2等)。
-
-
定时器状态检查:
-
if (htim->State != HAL_TIM_STATE_READY):检查定时器的当前状态是否为就绪状态。如果定时器处于其他状态(例如忙碌或未初始化状态),则返回错误HAL_ERROR,表示不能启动定时器。
-
-
定时器状态更新:
-
htim->State = HAL_TIM_STATE_BUSY:将定时器状态设置为HAL_TIM_STATE_BUSY,表示定时器正在被配置或启动,防止其他操作与当前操作冲突。
-
-
启用 TIM 更新中断:
-
__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE):启用定时器的更新中断。定时器会在计数溢出时产生更新事件,触发相应的中断,用户可以在中断处理函数中执行定时任务。
-
-
定时器启用:
-
if (IS_TIM_SLAVE_INSTANCE(htim->Instance)):检查定时器是否是从模式(Slave mode)。从模式的定时器通常需要与主定时器同步运行,触发模式的定时器会自动启动,无需手动启用。 -
tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS:获取定时器的从模式控制寄存器(SMCR)中的从模式选择字段(SMS)。 -
if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr)):如果定时器不是触发模式,调用__HAL_TIM_ENABLE(htim)启动定时器。 -
如果是主模式定时器(非从模式),则直接启用定时器。
-
-
返回状态:
-
如果一切顺利,函数返回
HAL_OK,表示定时器已成功启用并开始工作。
-
如何处理句柄?
定时中断现在被打开了,然后如何呢?答案是我们要重载我们的CallBack函数:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
这个函数需要我们自己先判定一下句柄,然后接下来就是处理业务代码。
相关文章:
从0开始的STM32 定时器(I):聊一聊基本定时器
目录 时钟源 控制器 时基单元 关于HAL库如何配置基本定时器 HAL是如何初始化我们的定时器句柄的 HAL_TIM_Base_Init 开始定时 如何处理句柄? 在我们使用STM32解决一些问题的时候,常常会遇到说:我想要以一个周期做一些事情:…...
vue常见题型(1-10)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 2.2双向绑定的原理是什么vue框架采用的是数据双向绑定的方式,由三个重要部分构成2.2.1.ViewModel2.2.2 双向绑定2.2.3.1.编译Compile2.2.3.2.依赖收集 3…...
【SpringBoot】使用注解进行XSS防御
在Spring Boot中,我们可以使用注解的方式来进行XSS防御。注解是一种轻量级的防御手段,它可以在方法或字段级别对输入进行校验,从而防止XSS攻击。 引入相关依赖 maven依赖: <!--JSR-303/JSR-380用于验证的注解 --> <de…...
华为海思招聘-芯片与器件设计工程师-模拟芯片方向- 机试题-真题套题题目——共8套(每套四十题)
华为海思招聘-芯片与器件设计工程师-模拟芯片方向- 机试题-真题套题题目分享——共九套(每套四十题) 岗位——芯片与器件设计工程师 岗位意向——模拟芯片 真题题目分享,完整题目,无答案(共8套) 实习岗位…...
vscode 下载慢的解决方法
下载链接示例:https://az764295.vo.msecnd.net/stable/ccbaa2d27e38e5afa3e5c21c1c7bef4657064247/code1.62.3-1637137107amd64.deb 解决方法: 把 az764295.vo.msecnd.net 替换成 vscode.cdn.azure.cn...
STM32ZET6-USART使用
一、原理说明 STM32自带通讯接口 通讯目的 通信方式: 全双工:通信时可以双方同时通信。 半双工:通信时同一时间只能一个设备发送数据,其他设备接收。 单工:只能一个设备发送到另一个设备,例如USART只有…...
es自动补全(仅供自己参考)
elasticssearch提供了CompletionSuggester查询来实现自动补全功能。这个查询会匹配以用户输入内容开头的词条并返回。为了提高补全查询效率,对于文档中字段的类型有一些约束: 查询类型必须是:completion 字段内容是多个补全词条形成的数组 P…...
13-综合排序:Function Score Query 优化算分
使用了 function_score 查询来根据某个字段的值对查询结果进行打分。以下是该查询的主要部分: query: 包含了实际执行搜索的部分,在这里包括一个 multi_match 查询。 multi_match:用于在多个字段上执行相同的查询。 query:设置…...
鸿蒙应用App测试-专项测试(DevEco Testing)
注意:大家记得先学通用测试在学专项测试 鸿蒙应用App测试-通用测试-CSDN博客 注意:博主有个鸿蒙专栏,里面从上到下有关于鸿蒙next的教学文档,大家感兴趣可以学习下 如果大家觉得博主文章写的好的话,可以点下关注&am…...
RabbitMQ设置消息过期时间
RabbitMQ设置消息过期时间 1、过期消息(死信)2、设置消息过期的两种方式2.1、设置单条消息的过期时间2.1.1、配置文件application.yml2.1.2、配置类RabbitConfig2.1.3、发送消息业务类service(核心代码)2.1.4、启动类2.1.5、依赖文…...
大数据-209 数据挖掘 机器学习理论 - 梯度下降 梯度下降算法调优
点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…...
粒子群优化双向深度学习!PSO-BiTCN-BiGRU-Attention多输入单输出回归预测
粒子群优化双向深度学习!PSO-BiTCN-BiGRU-Attention多输入单输出回归预测 目录 粒子群优化双向深度学习!PSO-BiTCN-BiGRU-Attention多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现PSO-BiTCN-BiGRU-Attention粒子…...
排序算法简介
直接插入排序: 将第一个元素视为已排序的序列,其余元素视为未排序序列。 逐个处理:从第二个元素开始,逐个将当前元素插入到已排序序列的适当位置,直到所有元素都被插入。 插入过程:对于每个待…...
(没有跳过联网激活)导致使用微软账号激活电脑---修改为本地账户和英文名字
修改为本地账户和英文名字 前言微软账号,本地账号与用户名基本知识账户管理方式一方式2 查看账户的sid并且修改文件夹名字和系统变量修改注册表和建立软件路径超链接注意事项总结 前言 当没有联网激活新买的电脑时候,这个就不用看了 当你是联网激活的时…...
[论文粗读][REALM: Retrieval-Augmented Language Model Pre-Training
引言 今天带来一篇检索增强语言模型预训练论文笔记——REALM: Retrieval-Augmented Language Model Pre-Training。这篇论文是在RAG论文出现之前发表的。 为了简单,下文中以翻译的口吻记录,比如替换"作者"为"我们"。 语言模型预训练…...
flink 内存配置(五):网络缓存调优
flink 内存配置(一):设置Flink进程内存 flink 内存配置(二):设置TaskManager内存 flink 内存配置(三):设置JobManager内存 flink 内存配置(四)…...
set和map的使用
目录 1.关联式容器 2.键值对 3.set 3.1set的模版参数列表 3.2对set的修改 3.2.1insert 3.2.2 erase 3.2.3clear 3.2.4swap 3.2.5 find 3.3set的迭代器 3.4set的容量 4.map 4.1对map的修改 4.1.1insert 4.1.2erase 4.1.3swap 4.1.4clear 4.2map的迭代器 4.3opera…...
LCL三相并网逆变器simulink仿真+说明文档
背景描述: 详细解析了LCL三相并网逆变器的工作原理,强调了准PR比例谐振控制的重要性,讨论了电感、电容参数选择及保护电路设计。通过仿真结果展示了逆变器性能优化的方法,以提升系统效率和稳定性。 模型介绍: 整体模…...
从0开始深度学习(24)——填充和步幅
1 填充 在上一节中,我们的卷积步骤如下: 可以发现输入是 3 3 3\times3 33,输出是 2 2 2\times2 22,这样可能会导致原始图像的边界丢失了许多有用信息,如果应用多层卷积核,累积丢失的像素就更多了&#…...
CPU Study - Instructions Fetch
参考来源:《超标量处理器设计》—— 姚永斌 N-Way CPU 取指问题 如果CPU可以在每个周期内同时解码N条指令,则此类CPU为N-Way超标量处理器。 N-Way超标量处理器需要每个周期从I-Cache中至少取得N条指令,这N条指令成为一组Fetch Group。 为了…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
GitHub 趋势日报 (2025年06月06日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
