STM32中定时器配置,HAL_Delay的原理,滴答定时器,微秒延时实现,PWM,呼吸灯

目录

定时器基本定时功能实现

CubeMX设置

手动书写代码部分

定时器启动

实现溢出回调函数

HAL_Delay介绍

HAL_Delay实现原理

HAL_Delay的优点

HAL_Delay的缺点

利用滴答定时器(SysTick)实现微秒级延时

PWM

 PWM介绍

通用定时器中的重要寄存器

PWM中的捕获比较通道

什么是定时器通道?

定时器通道如何工作?

为什么需要定时器通道?

PWM实现呼吸灯

CubeMX配置

关于TIMx_PSC和TIMx_ARR的计算

几种不同方式实现设定PWM的周期

如何选择不同PSC与ARR的组合?

代码编写


定时器基本定时功能实现

CubeMX设置

注意,这里只是对定时器配置实现一个很简单的定时功能。

时钟来源

定时器中断使能 

定时器预分频值,计数模式,计数周期

手动书写代码部分

定时器启动

下面主要介绍一下关于定时器的简单运用

关于下面图片中的初始化函数我们可以不用过于在意,因为在我们上面CUBEMX中已经配置过了,而且定时器的初始化相比于外部中断要复杂的多,所以就不推荐手动书写代码配置了,主要掌握CubeMX中的定时器初始化配置方式就行。

在main.c中让定时器开始运行

尽管我们在CubeMX或类似的配置工具中配置了定时器,并生成了代码,但生成代码仅仅是设置了定时器的参数(如时钟源、预分频器、自动重载值、中断使能等)。这些配置通常在 MX_TIMx_Init() 或类似的初始化函数中完成。

然而,这些初始化函数并不会自动启动定时器或启用中断,需要我们调用相应的启动函数来手动对定时器开启。这是一种设计哲学,记住就好,也就是多了一行开启时钟代码。

启动定时器函数需要一个参数,接受一个TIM_HandleTypeDef类型的地址,

这个参数在我们的TIM2_Init函数中有,是在tim.c中定义的,是一个全局变量。

由于我们的main.c文件中包含了tim.h头文件,并且tim.h中对于这个变量进行了extern声明,所以在main中可以直接使用htim2了

htim2 用于表示特定定时器实例(在这里是 TIM2) 的一个句柄 (Handle) 结构体变量,包含了管理特定定时器所需的所有配置信息和状态,我们对定时器进行配置的时候主要就是对这个变量进行配置。

  • 它是全局的:通常定义在文件作用域,可以在程序的任何地方(只要包含了定义它的文件或声明它的头文件)访问和修改它。
     
  • 它是访问点:所有针对 TIM2 定时器的 HAL 库函数(如 HAL_TIM_Base_Start_IT(&htim2)、HAL_TIM_PeriodElapsedCallback(&htim) 等)都需要通过这个 htim2 句柄来知道它们正在操作的是哪个定时器

实现溢出回调函数

由于我们使用了CubeMX配置了定时器,所以关于定时器的TIM6_DAC_IRQHander()不需要我们来声明并实现,这里的逻辑和中断处理那块的逻辑差不多,不过定时器这里有很多回调函数,对于简单的定时功能,我们需要实现的是溢出回调函数——HAL_TIM_PeriodElapsedCallback()。

通过HAL_TIM_PeriodElapsedCallback()可以实现周期性任务:

  • LED 闪烁: 每隔 500ms 翻转一次 LED 的状态。
  • 数据采样: 每隔 10ms 读取一次传感器数据。
  • 任务调度: 以固定的频率触发一个任务的执行。

关于HAL_TIM_PeriodElapsedCallback这个弱定义函数可以放main.c函数中进行实现。

补充一下main.c文件中函数声明和实现的写法

        声明在 main() 函数之前: 在 main.c 中,确实经常看到用户编写的函数(在 main() 函数之前进行声明(原型声明)。

        实现通常在 main() 函数之后: 而这些函数的实现(函数体)则经常放在 main() 函数的后面,通常是在 /* USER CODE BEGIN 4 */ 和 /* USER CODE END 4 */ 这样的用户代码区域内。

其他模块的文件一般我们的选择是:在.h中进行函数的声明,在.c文件中进行函数的实现。 

HAL_Delay介绍

HAL_Delay实现原理

HAL_Delay() 的实现方式主要基于 SysTick 定时器一个全局的滴答计数变量 (uwTick)

如果是使用CubeMX进行的配置,那么默认会在main()中调用HAL_Init(),里面会自动帮我们启动滴答定时器(SysTick)

SysTick 是 Cortex-M 系列处理器(包括 STM32)内置的一个 24 位倒计时定时器。它直接集成在 CPU 核心内部。

工作方式:

  • 我们会配置 SysTick 的重装载值(Reload Value)。
  • 一旦启用,SysTick 计数器会从这个重装载值开始递减,SysTick 计数器一般直接使用系统的主时钟频率HCLK。
  • 当计数器递减到 0 时,它会产生一个 SysTick 中断,然后自动重新装载并再次开始递减。所以 每过 1ms,SysTick 计数器递减到 0,触发一次 SysTick 中断
  • 执行 ISR: 处理器响应 SysTick 中断,执行相应的 ISR。
  • 调用 HAL_IncTick(): 在 SysTick 的 ISR 内部,HAL_IncTick() 被执行。
  • 更新 uwTick: HAL_IncTick() 将全局变量 uwTick 加 1。
  • HAL_GetTick() 函数的实现非常直接,它只是简单地返回当前 uwTick 变量的值:

HAL_Delay的伪代码如下:

void HAL_Delay(uint32_t Delay) {uint32_t tickstart = HAL_GetTick(); // 获取当前系统滴答值uint32_t wait = tickstart + Delay;   // 计算目标滴答值// 等待直到达到目标滴答值,并处理uwTick可能溢出的情况while((HAL_GetTick() < wait) && ((HAL_GetTick() - tickstart) < Delay)) {// 空循环,CPU在此处忙等待}
}

HAL_Delay的优点

1. 使用简单,易于上手

HAL_Delay() 的接口非常直观:你只需要传入一个你想要延时的毫秒数,函数就会阻塞相应的时长。对于嵌入式编程的初学者来说,这是最容易理解和使用的延时方式,能够快速实现一些简单的功能

2. 无需额外配置

一旦 HAL 库和 SysTick 定时器被初始化(这通常在项目启动时自动完成),HAL_Delay() 就可以直接调用,无需进行额外的定时器配置,也不需要编写中断服务程序。这大大简化了开发流程,尤其是在快速原型开发或对延时精度要求不高的场景下。

HAL_Delay的缺点

1. 阻塞式操作 (Blocking):

这是最主要的缺点。当调用 HAL_Delay() 时,CPU 会进入一个忙等待循环,不执行任何其他有用的任务,直到延时结束。

2. 不适用于精确的微秒级延时:

HAL_Delay() 的精度是毫秒级,因为它依赖于 1ms 的 SysTick 中断。对于需要微秒(us)甚至纳秒(ns)级别的精确延时,HAL_Delay() 无法满足要求。

3.不适用于中断服务程序 (ISR) 中使用:

严重问题: 绝对不能在中断服务程序 (ISR) 中直接调用 HAL_Delay()

HAL_Delay() 依赖于 SysTick 中断来更新 uwTick 变量。如果 SysTick 中断的优先级低于(数值上大于)当前执行的 ISR,那么 SysTick 中断将无法抢占当前 ISR 并执行,导致 uwTick 无法更新。这样一来,HAL_Delay() 就会陷入无限循环,使系统彻底崩溃。

HAL_Delay()中的SysTick定时器默认的优先级是最低的,所以在ISR中调用HAL_Delay不可能调用成功,会持续阻塞在这里,除非我们手动调整SysTick定时器优先级(让其变得更高),但是这也是非常非常非常不推荐的!!!

4. 与 RTOS 的兼容性问题:

如果您的项目使用了实时操作系统 (RTOS),如 FreeRTOS,直接使用 HAL_Delay() 是不推荐的。

RTOS 有自己的任务调度机制,它提供的延时函数(例如 FreeRTOS 的 osDelay() 或 vTaskDelay())会在任务延时期间将当前任务挂起,并允许调度器切换到其他任务执行,从而充分利用 CPU 资源。

利用滴答定时器(SysTick)实现微秒级延时

上面说到了使用滴答定时器(SysTick)实现的延迟函数HAL_Delay,只能实现ms级别延时,对于更精确的微秒级别是不支持的,下面我们自己来使用SysTick实现微秒级延时。

下面实现的delay_us 函数是一个典型的忙等待(busy-waiting) 实现,也就是基于查询方式实现的没有用到中断

void delay_us(uint32_t nus){uint32_t ticks;uint32_t told, tnow, tcnt = 0;uint32_t reload = SysTick->LOAD + 1;		//计数个数为重装载值加1ticks=nus*(SystemCoreClock/1000000);		//nus 微秒总共需要的 SysTick 节拍数told= SysTick->VAL;							//初始计数器值while(1){tnow=SysTick->VAL;if(tnow!= told){if(tnow<told)	tcnt += told- tnow;		//SysTick递减的计数器else tcnt += reload- tnow + told;told= tnow;if(tcnt>=ticks)	break;					//延时时间已到,退出}}}

这段代码通过不断读取 SysTick 的值,并巧妙地处理了 SysTick 递减计数和溢出(绕回)的特性,来精确地累加流逝的节拍数。当累加的节拍数达到预设的目标值时,就完成了微秒级的延时。

PWM

下面这个视频是对pwm比较专业一点的介绍

【STM32】输出比较模式讲解以及STM32CUBEMX+MDK代码实现_哔哩哔哩_bilibili

 下面这篇文章是对pwm比较通俗一点的介绍,更易理解

 PWM原理 PWM频率与占空比详解-CSDN博客

 PWM介绍

PWM,全称是脉冲宽度调制,它通过数字方式来模拟出模拟信号的效果,简单来说,PWM的原理就是通过快速开关一个数字信号(比如电源),并且控制它在一个周期内“开”的时间长短来达到目的。

脉冲宽度通常就是指在一个完整的 PWM 周期内,信号处于高电平(ON 状态)的持续时间长度

下面是一些关键点:

  • PWM周期(一个PWM完整波需要的时间)(Period):这是指一个完整的PWM波形所需的时间,也就是说,信号从“开”到“关”再回到“开”的总时间。

  • 占空比(Duty Cycle):这是PWM的核心。它表示在一个周期内,信号处于“开”状态的时间所占的比例。占空比越高,信号“开”的时间就越长。

  • PWM频率(Frequency):这是指每秒钟有多少个PWM周期。频率越高,信号切换得越快,看起来就越平滑,越像一个真正的模拟信号。PWM 的频率是由 ARR 和 PSC 共同决定的

PWM是如何工作的?

想象一下你有一个灯泡,你想控制它的亮度。

  • 如果你一直给灯泡供电(100%占空比),它就会全亮。

  • 如果你完全不给灯泡供电(0%占空比),它就会熄灭。

  • 如果你以很快的速度,比如每秒钟开关1000次,每次只让灯泡亮一半的时间(50%占空比),那么因为你的眼睛无法分辨这么快的开关,灯泡看起来就会是半亮的状态。

这就是PWM的工作原理。通过调整“开”的时间比例(占空比),我们就可以控制灯泡的亮度、电机的转速、音频信号的音量等等。

通用定时器中的重要寄存器

预分频器寄存器 (TIMx_PSC)

这个寄存器用于设置定时器时钟的预分频值,从而设置了定时器的时钟频率

PSC (Prescaler Value):定时器时钟源会通过这个预分频器进行分频,从而得到计数器实际使用的时钟频率。计算方式:计数器时钟频率 = 定时器时钟源频率 / (PSC + 1)

自动重载寄存器 (TIMx_ARR)

这个寄存器定义了计数器达到多少时会溢出并重新开始计数(或改变计数方向)。
 

  • ARR (Auto-Reload Value):当计数器达到 ARR 的值时,会发生更新事件。
     
  • 计算方式:PWM 的频率是由 ARR 和 PSC 共同决定的。PWM 频率 = 计数器时钟频率 / (ARR + 1) = 定时器时钟源频率 / ((PSC + 1) * (ARR + 1))。
     
  • 作用:设置 PWM 信号的周期和频率ARR 值越大,PWM周期越长频率越低

捕获/比较寄存器 (TIMx_CCRx)

每个 PWM 通道都有一个对应的 CCRx 寄存器(如 TIMx_CCR1, TIMx_CCR2 等)。

CCRx (Capture/Compare Register Value):这个寄存器存储的值与计数器 CNT 的值进行比较,从而决定 PWM 信号的占空比。

  • 计算方式:占空比 = CCRx / (ARR + 1)。
     
  • 例如,如果 ARR = 999,CCRx = 500,那么占空比就是 500 / 1000 = 50%。
     
  • 作用:设置 PWM 信号的脉冲宽度,进而控制占空比

当然通用定时器中还有一些其他寄存器,很多这些寄存器由CubeMX帮我们自动设置好了,所以不需要很关注。

PWM中的捕获比较通道

什么是定时器通道?

你可以把一个微控制器里的定时器想象成一个多功能的厨房定时器总机。这个总机本身可以计时(例如,设定每秒滴答一次)。而通道就是这个总机上独立的定时器插口或功能模块

每个通道都可以独立地配置来完成特定的任务,例如:

  • 捕获输入(Input Capture): 测量外部信号的脉冲宽度、频率或边沿之间的时间间隔。

  • 比较输出(Output Compare): 在定时器计数到预设值时,改变输出引脚的状态(高/低电平),用于产生PWM波形、延时输出脉冲等。

  • PWM 生成(PWM Generation): 最常见的用途之一,生成可调占空比的脉冲宽度调制信号,用于电机调速、LED调光等。

  • 单脉冲模式(One-Pulse Mode): 在事件发生后产生一个固定宽度的脉冲。

一个定时器通常会有2个、4个或更多个通道,这意味着这个定时器可以同时处理2个、4个或更多个上述的独立任务。

定时器通道如何工作?

每个定时器通道内部都有一组专门的寄存器来配置它的行为,其中最重要的就是比较/捕获寄存器 (Capture/Compare Register, CCR)

  • 作为输出(Output Compare/PWM): 当定时器的内部计数器(通常是TIMx_CNT寄存器)的值与某个通道的CCR寄存器的值相等时,定时器就会触发该通道预设的动作(例如,翻转输出电平、生成PWM脉冲)。你可以为每个通道设置不同的CCR值,从而产生不同的输出波形或在不同时间点触发事件。一旦配置好,通道会根据定时器计数器的值自动在引脚上生成PWM波形,无需CPU干预

  • 作为输入(Input Capture): 当外部引脚上的信号(例如,上升沿或下降沿)发生变化时,定时器会将当前内部计数器(TIMx_CNT)的值“捕获”到对应通道的CCR寄存器中。通过读取不同边沿捕获到的CCR值,就可以计算出脉冲宽度、周期等。

为什么需要定时器通道?

  1. 多任务并行: 如果一个应用需要同时生成两个不同频率或占空比的PWM波形,或者同时测量两个不同信号的频率,那么使用一个多通道定时器会比使用两个独立的单功能定时器更高效、更节省资源。

  2. 资源优化: 微控制器内部的硬件定时器是有限的宝贵资源。多通道设计允许单个定时器模块完成多种定时/计数相关的任务,从而节省了片上定时器模块的数量。

  3. 灵活性: 每个通道都可以独立配置其工作模式,极大地增加了定时器模块的灵活性,使其能够适应各种复杂的应用需求。

  4. 硬件实现: 定时器通道通常通过硬件逻辑实现,这意味着一旦配置完成,它们就能自动、精确地工作,无需CPU干预,从而减轻了CPU的负担,提高了系统的实时性。

PWM实现呼吸灯

CubeMX配置

PB10 引脚配置成 TIM2_CH3(定时器2的通道3) 的操作,正是属于 STM32 微控制器中的 输出复用功能模式

复用功能 (Alternate Function, AF): 一个GPIO引脚除了其通用IO功能外,还可以“复用”为某个片内外设(如定时器、SPI、I2C、USART等)的专用功能引脚。

这样配置之后的效果

引脚功能特化: PB10 不再是简单的 GPIO,变成了一个由硬件定时器控制的专用引脚

  • 作为 PWM 输出, PB10 引脚将输出一个脉冲宽度调制(PWM)波形
     
  • 作为 输出比较,当 TIM2 的计数器值与 TIM2_CH3 的比较值(CCR3)相等时,PB10 引脚的电平状态会按照预设的模式发生变化(例如,翻转、置高、置低)。
     
  • 作为 输入捕获,PB10 引脚将作为一个输入引脚。当外部信号在这个引脚上发生预设的边沿(上升沿、下降沿或双边沿)时,TIM2 定时器的当前计数值会被立即“捕获”并存储到 TIM2_CH3 对应的捕获/比较寄存器(CCR3)中。

我想要让LED1变成呼吸灯,在我的电路板上,LED1对应的是PB10,然后右键查看对应的多路复用模式中对应的正好是TIM2_CH3,我们选择定时器2的通道3,然后我们需要去配置 TIM2

 

具体的参数配置,主要是要计算分频系数(TIMx_PSC)和自动重载寄存器 (TIMx_ARR)

 通过设置这两个寄存器,就实现了设置PWM的周期和频率

这里我们的主时钟频率为100Mhz,假如我想要让输入TIM2的时钟频率变为100Khz,让PWM的周期变成20ms,那么此时TIMx_PSC和TIMx_ARR计算方式如下:

TIMx_PSC = 100Mhz/100Khz - 1 = 999;

由于TIM2的时钟频率为100Khz,所以一个节拍对应的为1/100000=0.00001s=0.01ms,

20ms/0.01ms=2000,所以TIMx_ARR=2000-1=1999

关于TIMx_PSC和TIMx_ARR的计算

几种不同方式实现设定PWM的周期

在上面的例子中,要达到 PWM 周期为 20ms 这个目标TIMx_PSC (分频系数)TIMx_ARR (自动重载寄存器) 的设置并非只有一种固定组合。它们是相互关联的,我们可以通过调整其中一个,来相应地调整另一个,以达到相同的周期。

  • 定时器时钟频率:这是输入到特定定时器(例如 TIM2)的时钟频率,通常是主时钟频率经过 APB 分频器后得到的。在我们的例子中,假设主时钟是 100MHz。
     

  • TIMx_PSC (Prescaler Value):分频系数。它决定了定时器计数器实际的计数频率。计数器每经过 (TIMx_PSC + 1) 个定时器时钟周期,才递增/递减一次。
     

  • TIMx_ARR (Auto-Reload Register):自动重载值。它决定了定时器计数器的上限。计数器从 0 数到 TIMx_ARR(或从 TIMx_ARR 数到 0),表示一个完整的计数周期,共 (TIMx_ARR + 1) 个节拍。

方法一:我们之前的方法 (分频后的时钟频率为 100 KHz)

  • 选择分频系数 TIMx_PSC = 999

    • 此时 (PSC + 1) = 1000

    • 分频后的定时器计数频率 = 100 MHz/1000=100 KHz

  • 计算 TIMx_ARR

    •  (TIMx_ARR + 1) \times 1000 = 2,000,000

    •  (TIMx_ARR + 1) = 2,000,000 / 1000 = 2000

    • TIMx_ARR=1999

    • 这种组合是:PSC = 999, ARR = 1999 

方法二:让分频后的时钟频率为 1 MHz

  • 选择分频系数 TIMx_PSC = 99

    • 此时 (PSC + 1) = 100

    • 分频后的定时器计数频率 = 100 MHz/100=1 MHz

  • 计算 TIMx_ARR

    • (TIMx_ARR + 1) \times 100 = 2,000,000

    • (TIMx_ARR + 1) = 2,000,000 / 100 = 20000

    • TIMx_ARR=19999

    • 这种组合是:PSC = 99, ARR = 19999 

方法三:让分频后的时钟频率为 50 KHz

  • 选择分频系数 TIMx_PSC = 1999

    • 此时 (PSC + 1) = 2000

    • 分频后的定时器计数频率 = 100 MHz/2000=50 KHz

  • 计算 TIMx_ARR

    • (TIMx_ARR + 1) \times 2000 = 2,000,000

    •  (TIMx_ARR + 1) = 2,000,000 / 2000 = 1000

    • TIMx_ARR=999

    • 这种组合是:PSC = 1999, ARR = 999

如何选择不同PSC与ARR的组合?

虽然有多种组合可以达到相同的 PWM 周期,但在实际应用中,选择哪种组合通常取决于以下因素:

  1. 占空比精度 (Duty Cycle Resolution):

    • ARR 值越大,表示在一个 PWM 周期内有更多的计数节拍。

    • 这意味着您可以更精细地调整占空比。例如,如果 ARR = 19999,您可以将占空比设置为 (0/20000)(19999/20000) 之间的任何值,有 20000 个可能的占空比级别。

    • 如果 ARR = 999,您只有 1000 个占空比级别。

    • 因此,通常会选择较大的 ARR 值以获得更高的占空比精度,前提是 ARR 不超过寄存器的最大值(如 16位定时器 ARR 最大为 65535,32位定时器更大)。

  2. PSCARR 的寄存器大小限制:

    • 大多数通用定时器的 PSCARR 寄存器是 16 位的,这意味着它们的值不能超过 65535。

    • 有些高级定时器或较新的微控制器可能有 32 位的定时器。在选择 PSCARR 时,需要确保它们不超过对应寄存器的最大值。

    • 在我们的例子中,2,000,000 这个乘积超出了 16 位定时器的单个寄存器范围,所以必须进行分频,即 PSCARR 都不能为 0(除非定时器频率非常低)。

  3. 计算方便性/可读性:

    • 有时会选择整数倍的分频,使得计数频率成为一个“整”的 KHz 或 MHz 值,方便理解和计算。

综上所述,我们通常会选择较大的 ARR 值以获得更高的占空比精度,但是同时也要注意PSC 和 ARR 设置的值不能超过寄存器大小限制!

代码编写

由于CubeMX依旧已经帮我们做了很多工作,所以这里我们需要修改的很少。

呼吸灯的效果是通过 PWM (脉冲宽度调制) 来实现的。我们通过周期性地改变 PWM 波形的占空比(即高电平持续时间与整个周期的比值),来控制 LED 的亮度。

  • 当占空比从 0% 逐渐增加到 100% 时,LED 会从灭逐渐变亮;
  • 当占空比从 100% 逐渐减小到 0% 时,LED 会从亮逐渐变灭。

这个过程循环往复,就形成了“呼吸”的效果。

下面是启动 PWM 输出的关键函数。它告诉定时器 TIM2 的通道3 开始生成 PWM 波形。请务必在进入 while(1) 循环之前调用它。

HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);

 这个函数会启动定时器(如果它尚未运行)并使其 PWM 输出开始在指定的通道引脚上生成波形。

关于调节占空比其实就是在调节比较捕获寄存器的值:

  • 计算方式:占空比 = CCRx / (ARR + 1)。
  • 例如,如果 ARR = 999,CCRx = 500,那么占空比就是 500 / 1000 = 50%。

设置比较捕获寄存器TIMx_CCRx的值:

__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, duty);

这是一个 HAL 库的宏(本质上是直接操作寄存器),用于设置指定定时器通道的比较值(CCR 寄存器)

duty_cycle 的值直接决定了 PWM 的占空比。当 duty_cycle 接近 0 时,LED 灭;接近 PWM_MAX_DUTY 时,LED 最亮。

  /* USER CODE BEGIN 2 */HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3); /* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */uint32_t duty;while (1){/* USER CODE END WHILE */for(duty=0; duty<2000; duty += 20){__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, duty);HAL_Delay(20);}for(duty=2000; duty>0; duty-=20){__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, duty);HAL_Delay(20);}/* USER CODE BEGIN 3 */}

 

推荐好文:

STM32定时器详解:原理、配置与应用实战-CSDN博客

STM32 定时器TIM-CSDN博客

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

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

相关文章

飞牛NAS(fnOS)详细安装教程

以下是飞牛NAS&#xff08;fnOS&#xff09;的详细安装教程&#xff0c;结合官方指南和社区实践整理而成&#xff1a; 一、准备工作 硬件需求 8GB或更大容量的U盘&#xff08;用于制作启动盘&#xff09;待安装设备&#xff08;支持x86架构的物理机或迷你主机&#xff0c;如天钡…

springboot 显示打印加载bean耗时工具类

一 spring的原生接口说明 1.1 接口说明 Aware是Spring框架提供的一组特殊接口&#xff0c;可以让Bean从Spring容器中拿到一些资源信息。 BeanFactoryAware&#xff1a;实现该接口&#xff0c;可以访问BeanFactory对象&#xff0c;从而获取Bean在容器中的相关信息。 Environm…

OpenGL空间站场景实现方案

OpenGL空间站场景实现方案 需求分析 根据任务要求,我需要完成一个基于Nehe OpenGL的空间站场景,实现以下功能: 完整的空间站场景建模(包含多个模型和纹理贴图)Phong光照模型实现(包含多种光源和材质效果)摄像机键盘控制交互功能解决方案设计 技术栈 C++编程语言OpenG…

基于昇腾310B4的YOLOv8目标检测推理

YOLOv8目标检测 om 模型推理 本篇博客将手把手教你如何将 YOLOv8 目标检测模型部署到华为昇腾 310B4 开发板上进行高效推理&#xff08;其他昇腾开发版也可参考此流程&#xff09;。 整个流程包括&#xff1a; 模型格式转换&#xff08;ONNX → OM&#xff09;昇腾推理环境配…

前端跨域问题解决Access to XMLHttpRequest at xxx from has been blocked by CORS policy

在前端开发中&#xff0c;跨域资源共享&#xff08;CORS&#xff09;是一个常见的问题。它涉及到浏览器安全机制&#xff0c;防止网页从一个域获取资源时被另一个域阻止。错误信息如“Access to XMLHttpRequest at xxx from origin has been blocked by CORS policy”是典型的跨…

[ linux-系统 ] 软硬链接与动静态库

软硬链接 介绍 软链接 通过下图可以看出软链接和原始文件是两个独立的文件&#xff0c;因为软链接有着自己的inode编号&#xff1a; 具有独立的 inode &#xff0c;也有独立的数据块&#xff0c;它的数据块里面保存的是指向的文件的路径&#xff0c;公用 inode 硬链接 通过…

3D 商品展示与 AR 试戴能为珠宝行业带来一些便利?

对于珠宝行业而言&#xff0c;长久以来&#xff0c;如何让消费者在做出购买决策之前&#xff0c;便能真切且直观地领略到珠宝独一无二的魅力&#xff0c;始终是横亘在行业发展道路上的一道棘手难题。而 3D 互动营销的横空出世&#xff0c;恰似一道曙光&#xff0c;完美且精准地…

电子电气架构 --- SOVD功能简单介绍

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…

【Java编程动手学】 Java中的运算符全解析

文章目录 一、引言二、算术运算符1、基本概念2、具体运算符及示例 三、关系运算符1、基本概念2、具体运算符及示例 四、自增减运算符1、基本概念2、具体运算符及示例 五、逻辑运算符1、基本概念2、具体运算符及示例 六、位运算符1、基本概念2、具体运算符及示例 七、移位运算符…

【前端】1 小时实现 React 简历项目

近期更新完毕。仅包括核心代码 目录结构 yarn.lock保证开发者每次能下载到同版本依赖&#xff0c;一般不需要特别留意 package.json 是 Node.js 项目、前端项目、npm/yarn的配置文件。 Dockerfile 是用来 定义 Docker 镜像构建过程的文本文件。它是一份脚本&#xff0c;告诉 …

python中的pydantic是什么?

Pydantic 是 Python 中一个用于数据验证和设置管理的库&#xff0c;主要通过 Python 类型注解&#xff08;Type Hints&#xff09;来定义数据结构&#xff0c;并自动验证输入数据的合法性。它广泛应用于 API 开发&#xff08;如 FastAPI&#xff09;、配置管理、数据序列化等场…

腾讯云市场目前饱和度

首先我需要理解市场饱和度的概念。市场饱和度通常指一个产品或服务在潜在市场中的渗透程度&#xff0c;高饱和度意味着市场增长空间有限&#xff0c;低饱和度则表明还有较大发展潜力。 从搜索结果看&#xff0c;腾讯云目前在中国云服务市场排名第三&#xff0c;市场份额约为15%…

EDR、NDR、XDR工作原理和架构及区别

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; EDR、NDR、XDR是网络安全中关键的检测与响应技术&#xff0c;它们在覆盖范围、数据源和响应机制上有显著差异。以下是它们的工作原理和架构详解&#xff1a; --- ### &#x1f50d; 一、EDR&#xff0…

vue3 + luckysheet 实现在线编辑Excel

效果图奉上&#xff1a; 引入的依赖&#xff1a; "dependencies": {"types/jquery": "^3.5.32","types/xlsx": "^0.0.36","jquery": "^3.7.1","xlsx": "^0.18.5",}在index.html中…

Linux下MinIO分布式安装部署

文章目录 一、MinIO简单说明二、MinIO分布式安装部署1、关闭SELINUX2、开启防火墙2.1、关闭firewall&#xff1a;2.2、安装iptables防火墙 3、安装MinIO4、添加MinIO集群控制脚本4.1添加启动脚本4.2添加关闭脚本 5、MinIO控制台使用 一、MinIO简单说明 1、MinIO是一个轻量的对…

Codeforces Round 980 (Div. 2)

ABC 略 D 这个过程一定是由1向后跳的过程中穿插有几次向前一步一步走。直到跳到一个位置后再把前面所有没有走过的位置倒序走一遍。总分就等于最大位置的前缀和-前面所有起跳位置和。前缀和固定我们只需要求到每个位置的最小起跳和即可。对于这个向后跳和向前走的过程我们可以…

Langchain实现rag功能

RAG&#xff08;检索增强生成&#xff09;的核心是通过外部知识库增强大模型回答的准确性和针对性&#xff0c;其工作流程与优化策略如下&#xff1a; 一、RAG 核心流程 ‌知识库构建‌ ‌文档加载与分割‌&#xff1a;将非结构化文档&#xff08;PDF、Markdown等&#xff09;…

算法笔记上机训练实战指南刷题

算法笔记上机训练实战指南刷题记录 文章目录 算法笔记上机训练实战指南刷题记录模拟B1001 害死人不偿命的(3n1)猜想B1011 AB 和 CB1016 部分ABB1026 程序运行时间B1046划拳B1008数组元素循环右移问题B1012 数字分类B1018 锤子剪刀布A1042 Shuffling Machine 每天两题&#xff0…

MYSQL基础内容

一、介绍 1.不用数据库&#xff1a;使用IO流对数据进行管理 2.使用数据库&#xff1a;使用SQL语句对开发的数据进行管理&#xff0c;能储存上亿条数据 3.MYSQL&#xff1a; 是流行的关系型数据库管理系统之一&#xff0c;将数据保存在不同的数据表中&#xff0c;通过表与表之…

音视频会议服务搭建(设计方案)-01

前言 最近在做音视频会议系统服务搭建的工作任务&#xff0c;因为内容过多&#xff0c;我会逐篇分享相关的设计方案、开发思路、编程语言、使用的组件集合等等。如果你也有大型音视频会议系统搭建架构的需求&#xff0c;希望这些可以对你有所帮助。 EchoMeet 音视频会议系统架构…