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

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解

  • 前言
  • 如何确定定时器挂载在哪条时钟线上
  • 配置及使用方法
    • 参数配置
      • Prescaler
      • Counter Mode
      • Counter Period
      • auto-reload preload
      • Trigger Event Selection
    • 中断配置
    • 生成的代码及使用方法
      • 初始化代码
      • 基本定时器触发DCA或者ADC的代码讲解
      • 中断代码
      • 定时启动和停止
      • 实验现象
  • 深入原理剖析
    • 基本定时器原理
      • 时基单元
      • 计数模式
        • 自动重载预装载使能位(ARPE)的不同配置
        • 软件使能UG产生更新事件
    • 寄存器剖析
  • 参考文献

前言

  STM32的定时器非常强大,我计划分好几篇文章来细讲。本文介绍基本定时器的原理和使用方法。而通用定时器和高级定时器都是从基本定时器扩展丰富而来,所以先把基本定时器掌握了,为下一步掌握更高级的定时器打好基础。
  STM32中,基本定时器有TIM6和TIM7,我们就以这两个定时器说起。TIM2-TIM5 和 TIM9-TIM14是通用定时器,而TIM1和TIM8是高级控制定时器。

如何确定定时器挂载在哪条时钟线上

  在配置之前先考虑一个问题:
  如何确认某个定时器,或者说某外设挂载在哪条时钟线上?这个问题至关重要。因为我们要计算预分频系数和重装载值,这对定时器的使用很重要。
  下面贴出来相关寄存器的细节,供大家学习。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  在上面的图片中,相信大家看出来了,每个外设的时钟在哪里使能,它就挂载在哪条时钟线上。那么每条时钟线的时钟频率是多少?在下图:
在这里插入图片描述
  回到今天的主题,基础定时器的配置。定时器6和定时器7在APB1时钟上,这条时钟线的频率是84MHz。

配置及使用方法

关于配置基本定时器,请看下图:
在这里插入图片描述
  首先,我们在左边选择要配置的定时器。比如选择TIM6或者TIM7,之后右上方Activated勾选,即使能定时器。
One Pulse Mode:单脉冲模式。如果打勾了,就使能了单脉冲模式。换句话讲,如果就希望计数一次,溢出了就停止,那么就将这里打勾。我希望周期计数,那么这里不打勾。

下面的配置项有参数配置中断配置

参数配置

Prescaler

  预分频器系数,用于将定时器的输入时钟分频后使用。比如TIM6和TIM7使用的是APB1,这条时钟线的频率是84MHz。我输入预分频系数是8400-1,那么定时器实际计数的时钟频率是:84M/8400 = 10KHz。
为啥要减一
在这里插入图片描述
  PSC[15:0]:预分频器值 (Prescaler value)
计数器时钟频率 CK_CNT 等于 fCK_PSC / (PSC[15:0] + 1)。
PSC 包含在每次发生更新事件时要装载到实际预分频器寄存器的值。
注意:这里是16位寄存器,只能写进去小于等于65535的数字。

Counter Mode

  计数模式,对于TIM6和TIM7,只有递增计数,也就是Up。没有递减计数模式。

Counter Period

  计数周期,也就是递增计数到哪个数字就算溢出,然后重新开始从0计数。计数器就像阶梯状的图形,时钟每过一个上升沿,计数值加一。这里输入了5000-1,也即是计数5000次。那么定时器溢出一次用时多久?
  我们先回顾一下,定时器实际计数的时钟是分频后的时钟,刚才我们计算得出10KHz,也即是说,周期是0.1ms。那么计数5000次,用时就是0.1*5000 = 500ms,也即是0.5秒。

auto-reload preload

自动重载预装载是否使能,具体操作的是下面的寄存器。 在这里插入图片描述
插入一句,请看图片上OPM字段,单脉冲模式。回顾刚才配置界面上的One Pulse Mode,如果打勾了,这里就是1,使能了单脉冲模式。换句话讲,如果就希望计数一次,溢出了就停止,不产生周期的计数循环,那么就将这里打勾。

  先介绍一下,自动重装载寄存器像是幕后老板,实际干活的是它的影子寄存器。那么问题来了,老板的数据如果更新了,什么时候把数据给到实际干活的打工仔呢?
ARPE=0:自动重载寄存器(TIMx_ARR)的内容直接写入影子寄存器,修改立即生效。
ARPE=1:自动重载寄存器(TIMx_ARR)的内容需要等到更新事件(UEV)发生时才会被加载到影子寄存器,修改延迟生效。

  如果我们选择Enable,那么ARPE的值就是1,带来的结果就是自动重载寄存器(TIMx_ARR)的内容需要等到更新事件(UEV)发生时才会被加载到影子寄存器,修改延迟生效。在手册里的描述就是“进行缓冲”。

Trigger Event Selection

  主模式触发事件选择,实际操作的是下面的寄存器。关于主模式和从模式,下面有详细解释。
在这里插入图片描述

中断配置

在这里插入图片描述
这里打勾即可。具体的优先级设置在NVIC里面。

生成的代码及使用方法

初始化代码

void MX_TIM6_Init(void)
{TIM_MasterConfigTypeDef sMasterConfig = {0};htim6.Instance = TIM6;htim6.Init.Prescaler = 8400-1;htim6.Init.CounterMode = TIM_COUNTERMODE_UP;htim6.Init.Period = 5000-1;htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;if (HAL_TIM_Base_Init(&htim6) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK){Error_Handler();}
}

  从上面的代码可以看出,我们配置的参数已经被赋值到变量中了。这些变量被写进寄存器,是HAL_TIM_Base_Init函数的任务。
  而HAL_TIMEx_MasterConfigSynchronization函数用于设置主从模式。TIM6和TIM7只有主模式,那么这个函数在这里就是在设置主模式在什么情况下触发DAC或者ADC。所谓的触发,说的彻底点,DAC或者ADC就相当于准备冲锋的士兵,而TIM6或者TIM7就是司令,士兵已经准备就绪了,就等司令的命令。当定时器定时溢出的时候,就下达命令要求DAC或者ADC启动。这里请注意,TIM6支持触发DAC和ADC,而TIM7只支持DAC。

定时器DAC 触发支持ADC 触发支持说明
TIM6✅ 支持✅ 支持可通过更新事件(UEV)触发 DAC 和 ADC,是 ADC 规则组触发的常用选择。
TIM7✅ 支持❌ 不支持仅能触发 DAC,无法触发 ADC(硬件设计限制)。
HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim)
{/*这里删减了部分无关紧要的代码*/if (htim->State == HAL_TIM_STATE_RESET){/* Allocate lock resource and initialize it */htim->Lock = HAL_UNLOCKED;/* Init the low level hardware : GPIO, CLOCK, NVIC */HAL_TIM_Base_MspInit(htim);}/* Set the TIM state */htim->State = HAL_TIM_STATE_BUSY;/* Set the Time Base configuration */TIM_Base_SetConfig(htim->Instance, &htim->Init);/* Initialize the TIM state*/htim->State = HAL_TIM_STATE_READY;return HAL_OK;
}

  上面的代码被我删除了无关代码,可以让我们更容易理解。可以看到先是调用了HAL_TIM_Base_MspInit,这函数是空的,相当于提供了接口由用户根据需要写相关的代码。之后调用TIM_Base_SetConfig将初始化的参数写进寄存器。详情如下:
在这里插入图片描述
在这里插入图片描述
关于上面判断的宏定义列举如下:

/****************** TIM Instances : supporting counting mode selection ********/
#define IS_TIM_COUNTER_MODE_SELECT_INSTANCE(INSTANCE)  (((INSTANCE) == TIM1) || \((INSTANCE) == TIM2) || \((INSTANCE) == TIM3) || \((INSTANCE) == TIM4) || \((INSTANCE) == TIM5) || \((INSTANCE) == TIM8))/****************** TIM Instances : supporting clock division *****************/
#define IS_TIM_CLOCK_DIVISION_INSTANCE(INSTANCE) (((INSTANCE) == TIM1)  || \((INSTANCE) == TIM2) || \((INSTANCE) == TIM3) || \((INSTANCE) == TIM4) || \((INSTANCE) == TIM5) || \((INSTANCE) == TIM8) || \((INSTANCE) == TIM9) || \((INSTANCE) == TIM10)|| \((INSTANCE) == TIM11)|| \((INSTANCE) == TIM12)|| \((INSTANCE) == TIM13)|| \((INSTANCE) == TIM14))
/****************** TIM Instances : supporting repetition counter *************/
#define IS_TIM_REPETITION_COUNTER_INSTANCE(INSTANCE)  (((INSTANCE) == TIM1) || \((INSTANCE) == TIM8))

  这里解释一下,计数模式分为递增计数、递减计数、递增递减计数。由于基本定时器只支持递增计数,所以这里不支持修改计数模式。
  那么上面说的“时钟分频”是什么意思?
在这里插入图片描述
至于上面写的CR1寄存器的ARPE位,请看上面参数配置中的auto-reload preload一节:
在这里插入图片描述

基本定时器触发DCA或者ADC的代码讲解

  这里回头讲一下定时器触发DAC或者ADC的函数。虽然基本定时器不支持主从模式,所以下图的sMasterConfig.MasterSlaveMode始终是TIM_MASTERSLAVEMODE_DISABLE。但仍然调用这个函数来设置触发事件。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
基本定时器不支持主从模式,下面代码也有逻辑判断。
在这里插入图片描述
补充下上面截图中遇到的宏函数,里面没有TIM6和TIM7。

#define IS_TIM_SLAVE_INSTANCE(INSTANCE) (((INSTANCE) == TIM1) || \((INSTANCE) == TIM2) || \((INSTANCE) == TIM3) || \((INSTANCE) == TIM4) || \((INSTANCE) == TIM5) || \((INSTANCE) == TIM8) || \((INSTANCE) == TIM9) || \((INSTANCE) == TIM12))

中断代码

  如果使能了定时器更新中断,那么就要看一下中断处理函数怎么写。

void TIM6_DAC_IRQHandler(void)
{/* USER CODE BEGIN TIM6_DAC_IRQn 0 *//* USER CODE END TIM6_DAC_IRQn 0 */HAL_TIM_IRQHandler(&htim6);/* USER CODE BEGIN TIM6_DAC_IRQn 1 *//* USER CODE END TIM6_DAC_IRQn 1 */
}

  所有的定时器使用的中断处理函数都是同一个:HAL_TIM_IRQHandler,至于哪个定时器的中断,由入参决定的。在此函数中,判断中断的各种原因,这里仅以更新中断举例,因为基本定时器就这一种中断方式。

/* TIM Update event */if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET){if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) != RESET){__HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)htim->PeriodElapsedCallback(htim);
#elseHAL_TIM_PeriodElapsedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}}

  代码中实际执行的是HAL_TIM_PeriodElapsedCallback(htim);
也就是说,这个函数是我们用户重新写的。比如下面我的代码:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(&htim6 == htim){HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);}
}

  在这个函数中判断哪个定时器的更新中断,之后可以写相关的逻辑。

定时启动和停止

/* Blocking mode: Polling */
HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim);
HAL_StatusTypeDef HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim);
/* Non-Blocking mode: Interrupt */
HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);
HAL_StatusTypeDef HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim);

  拿定时器的使能启动来讲,如上所示,可以分为两种,一种是轮询,一种是中断。对于轮询HAL_TIM_Base_Start,启动定时器之后,定时器就开始运行了,溢出了之后重新计数,不会引起中断。对于中断HAL_TIM_Base_Start_IT,就是说定时器计数溢出后会触发中断,并且执行中断处理函数。此时我们用户需要重写回调函数:HAL_TIM_PeriodElapsedCallback()。上面已经讲过了,就不再赘述。
  如果使能了轮询启动定时器,就可以将计数值发送出来,我们可以验证,代码如下:

HAL_TIM_Base_Start(&htim6);
while(1)
{times = __HAL_TIM_GET_COUNTER(&htim6);printf("当前计数值是:%d\r\n", times);HAL_Delay(50);
}

__HAL_TIM_GET_COUNTER(&htim6):获取定时器3的计数器值,这个值会不停改变,我们可以通过这个宏函数获取到当前最新的。

#define __HAL_TIM_GET_COUNTER(__HANDLE__)  ((__HANDLE__)->Instance->CNT)

实验现象

串口打印结果:
在这里插入图片描述
  如果我们在代码中调用HAL_TIM_Base_Start_IT(&htim6),且中断处理函数如上所写,则除了串口调试助手上有打印的信息外,硬件开发板上LED灯亮灭间隔0.5秒闪烁。

深入原理剖析

基本定时器原理

  基本定时器TIM6和TIM7都是16位重装载递增定时器(只能递增计数),可以用于触发DAC或者ADC,也可以在更新事件的时候,会生成中断/DMA传输请求。
在这里插入图片描述
  就拿上图来说,时钟信号输入到预分频器,经过分频之后,传输到计数器,计数器使用这个时钟进行计数。由于只能是递增的计数方法,所以就从0开始增加计数值,当计数值达到自动重装载计数值的时候,产生更新事件,触发DAC或者ADC的启动,或者触发DMA的传输。同时,计数器的值重新回到0开始递增计数。如此往复循环。

时基单元

  • 计数器寄存器(TIMx_CNT)
  • 预分频器寄存器(TIMx_PSC)
  • 自动重装载寄存器(TIMx_ARR)

  预分频器寄存器和自动重装载寄存器都有各自的影子寄存器,实际起作用的就是影子寄存器。但二者有区别:自动重装载寄存器的值要不要立即写进影子寄存器,是可以通过自动重载预装载使能位ARPE)设定的;但预分频器寄存器的值只能等到产生更新事件(UEV)之后,才能写进影子寄存器。
  预分频器会讲输入的时钟信号进行分频,分配系数在1-65536之间,而我们的设置值是0~65535,这里有一个“加1”的操作。为啥最大是65535?因为它是16位的寄存器,再大的数写不进去。下面是预分频器改变之后,定时器的计数变化。
在这里插入图片描述
信号含义

  • CK_PSC:定时器计数时钟源。
  • CNT_EN:计数器使能信号,置 1 时允许计数器递增。
  • 定时器时钟:即CK_CNT,是定时器的计数基准时钟(经预分频后),决定计数节奏。
  • 计数器寄存器:实时显示计数器当前计数值(递增到溢出)。
  • 更新事件(UEV):计数器溢出时触发的内部事件,用于同步寄存器、置标志位。
  • 预分频器控制寄存器:软件直接写入的 PSC 值(初始是 0,对应的分频比 1,后来写 1,对应的分频比 2 )。
  • 预分频器缓冲区:影子寄存器(暂存新 PSC 值,等 UEV 触发才生效 )。
  • 预分频器计数器:预分频器内部的分频计数器(数 CK_PSC 的脉冲,决定何时输出 CK_CNT )。

  在计数器寄存器数到F8的时候,用户对预分频寄存器写了新值(就是1,原本是0),但并没有立即导致计数时钟发生改变。直到溢出了,或者说产生更新事件了,计数值达到FC,然后计数器需要从0开始重新计数,这个时候,写进去的预分频值才起作用,计数的频率变慢了。可以看到预分频器缓冲区在存储着修改前的值,等到更新事件发生之后,才变成修改之后的1。

计数模式

  计数器在不停地计数,从0开始,一直递增到自动重装载值,也即是TIM_ARR寄存器的值。之后继续从0开始计数。每次溢出就产生更新事件。如果用户手动将TIMx_EGR寄存器的UG位置位,也会产生更新事件,和计数溢出一个效果。
在这里插入图片描述
  上图中,预分频器设置的值位1,也即是2分频。自动重装载值是35,经过加1之后就是36,也就是计数器寄存器的值达到36会产生更新事件。此时中断标志位置位,UIF=1。

自动重载预装载使能位(ARPE)的不同配置

  下面两幅图对于ARPE位等于0和等于1的区别。
在这里插入图片描述

在这里插入图片描述
  这两幅图对比展示了 STM32 定时器中 自动重载预装载使能位(ARPE) 不同配置(ARPE=1ARPE=0 )时,更新事件触发后自动重载寄存器(TIMx_ARR)新值生效时机 的差异。

ARPE=1 时

  • 逻辑ARPE=1 开启“自动重载预装载”功能,新值写入 TIMx_ARR 后,先存到 自动重载预装载寄存器需等待“更新事件(UEV)”触发 ,才会同步到 自动重载影子寄存器 生效。
  • 时序细节
    1. 计数器从 F0 递增到 F5 后溢出(到 00 ),触发 UEVUIF
    2. 溢出前,自动重载预装载寄存器 已写入新值 36 (但未生效);溢出触发 UEV 后,自动重载影子寄存器 才从预装载寄存器同步 36 ,下次计数用新值。

ARPE=0 时

  • 逻辑ARPE=0 关闭“自动重载预装载”功能,新值写入 TIMx_ARR 后直接同步到影子寄存器 ,无需等待更新事件。
  • 时序细节
    1. 计数器从 31 递增到 36 后溢出(到 00 ),触发 UEVUIF
    2. 溢出前,软件写入新值 36TIMx_ARR ,因 ARPE=0新值直接写入影子寄存器 (无需等更新事件),下次计数用新值。
软件使能UG产生更新事件

在这里插入图片描述
信号含义
- CK_INT:定时器内部基准时钟(1分频后),是计数的“心跳” 。
- CEN=CNT_EN:计数器使能信号,置1时允许计数运行。
- UG:更新事件触发信号(如软件触发、溢出触发),用于触发计数器重置/更新。
- CNT_INIT:计数器初始化信号,配合UG将计数器值复位到初始状态(图中体现为寄存器值重置)。
- 计数器时钟(CK_CNT=CK_PSC):经过预分频后的实际计数时钟,决定计数节奏。

  也就是说,图中的36不是自动重装载计数器的值,计数到36没有溢出,只是由于UG被置位导致计数器初始化,生成了更新事件。但UG置位之后不是马上生成更新事件,而是等1个时钟周期。

寄存器剖析

  上面其实已经有介绍寄存器的一些内容,这里对于比较重要的位简要介绍一下。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  或者说,定时器的哪个信号作为触发输出。是复位的时候触发?还是计数器使能信号(CNT_EN)触发?还是更新事件触发?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  以上三个寄存器都是16位,预分频器寄存器和自动重装载寄存器存储着我们的配置值,在STM32CubeIDE配置界面上有体现。而计数器寄存器是实时改变的,我们可以读取,也可以写入。

参考文献

《STM32F4中文参考手册》
《STM32F4英文参考手册》

  至此,本文结束。全文一万多字,算是很详细的介绍基本定时器了,希望可以帮到您。欢迎点赞、收藏、转发!

相关文章:

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

JVM垃圾回收机制全解析

Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

【机器视觉】单目测距——运动结构恢复

ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛&#xf…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来&#xf…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

django filter 统计数量 按属性去重

在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享

文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的&#xff0c;根据Excel列的需求预估的工时直接打骨折&#xff0c;不要问我为什么&#xff0c;主要…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...