STM32中断之TIM定时器详解

news2024/11/17 11:24:45

系列文章目录

STM32单片机系列专栏

C语言术语和结构总结专栏


文章目录

1. TIM简述

2. 定时器类型

2.1 基本定时器

2.2 通用定时器

2.3 高级定时器

3. 定时中断

4. 代码示例1

5. 代码示例2


1. TIM简述

  • 定时器的基本功能:定时器可以在预定的时间间隔内产生周期性的中断。例如,定时器可以被设置为每1ms产生一个中断信号,这常用于创建周期性的服务例程,如操作系统的时钟滴答。

  • 16位计数器:STM32的定时器通常包含一个16位的计数器,意味着它能够计数从0到65535(2^16 - 1)。当计数器从0计数到预设值时,可生成中断或其他事件。

  • 最大计数值:在72MHz的时钟频率下,如果定时器的预分频器(Prescaler)设为72-1(即每72个时钟周期计数一次),则计数器每计数到72000就相当于过去了1秒。这样就可以用来测量时间,或创建延时等。并且最大定时为59.65s。

  • 时钟选择:定时器可以从不同的时钟源中选择,例如内部的主时钟或外部时钟源。

  • 多种工作模式:定时器可以在多种模式下工作,包括简单的定时模式(如计数溢出时产生中断),PWM产生模式(用于调整电压输出,控制电机速度等),输入捕获模式(测量外部事件的时间间隔,如信号的频率)等。

  • 高级控制功能:定时器还可以配置为触发ADC(模数转换器)的启动,或与其他定时器同步等。

2. 定时器类型

类型编号总线功能
高级定时器TIM1、TIM8APB2这类定时器拥有全部的功能,例如高级PWM控制,还支持三相电机的正反变换,复杂的同步控制,以及与其他高级功能的集成,如直接数字控制转换(DCC)。并且额外具有重复计数器、死区生成、互补输出、刹车输入等功能。
通用定时器TIM2、TIM3、TIM4、TIM5APB1拥有基本定时器的全部功能,并且额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能。
基本定时器TIM6、TIM7APB1拥有定时中断、主从触发DAC的功能

对于STM32F103C8T6,拥有的定时器资源为:TIM1、TIM2、TIM3、TIM4,所以在使用任何外设时,要先查明这个芯片有没有这个功能。

2.1 基本定时器

首先下面有三个最重要的寄存器:预分频器,计数器和自动重装载寄存器,这部分是最基本的计数计时电路,也叫做时基单元。预分频器之前连接的是基准计数时钟的输入,基本定时器只能选择内部时钟,所以也就相当于连接的是输入端,也就是内部时钟CK_INT。

预分频器

这里的频率值一般都是系统的主频72MHz,首先预分频器对72MHz的计数时钟进行预分频,就是对输入的基准频率进行一个分频的操作比如这里写0就代表不分频或1分频,这时输出频率等于输入频率 = 72MHz。写1就代表分频(2分频),输出频率=输入频率/2 = 36MHz。如果写2就代表3分频,也就是除以3。所以实际的分频系数比预分频器的值大1,预分频器是16位的,所以最大值为65535,最大分频也就是65536。

计数器

计数器可以对预分频后的计数时钟进行计数,计数时钟每次有一个上升沿,计数器就加1,计数器也是16位的,所以值的范围是0 - 65535。如果加到65535以后,再加一就会回到零重新开始。当计数器的值增加到目标值时,产生中断,就代表完成了定时的任务。

自动重装载寄存器

因为计数器需要一个目标值,所以还需要一个存储目标值的寄存器。这个寄存器也是16位的,会存入技术目标。当计数器的值等于自动重装值时,就代表计时时间到了。这时就会产生中断信号,并重新计数。

UI(Update Interrupt)

这个向上的折线箭头,代表会产生中断信号,像刚才这种计数值等于自动重装值所产生的中断,叫做更新中断,之后会通往NVIC,再配置好NVIC的定时器通道,这时定时器的更新中断就可以得到CPU的响应了。

U

向下的箭头代表会产生一个事件,叫做更新事件,更新事件不会触发中断,但可以触发内部其他电路的工作。

2.2 通用定时器

首先中间部分的结构和基本计时器一样, 这里不同的是,通用计时器和高级计时器不仅支持向上计数模式,还支持向下计数模式和中央对齐计数模式。向下计数模式就是从重装值开始,向下自减,减到0以后,回到重装值同时申请中断,依次循环。中央对齐计数模式就是从0开始,先向上自增,计数到重装值,申请中断,然后再向下自减,减到0时再次申请中断,然后依次循环。

在通用定时器中,时钟源不仅可以选择内部的72MHz时钟,还可以选择外部时钟。第一个外部时钟是TIMx_ETR引脚的外部时钟,叫做外部时钟模式2,其中ETR引脚的位置在C8T6中对应PA0引脚。除了ETR,TRGI也可以当作外部时钟,这个叫做外部时钟模式1。

对于下面的部分,右边的是输出比较电路,一共四个通道CH1 - CH4,用于输出PWM波形驱动电机。左边是输入捕获电路,也是四个通道CH1 - CH4,用于测量输入方波的频率。中间的寄存器是输入捕获和输出比较电路共用的,因为输入和输出不能同时使用。

2.3 高级定时器

对比通用定时器,主要不同的是右边申请中断的地方,增加了重复次数计数器,这个可以实现每隔几个计数周期才会发生一次更新事件和更新中断。

下面还有对于输出比较模块的升级,DTG是死区生成电路,右边的输出变成了两个互补的输出,可以输出一对互补的PWM波形,这时为了驱动三相无刷电机的。

最下面的刹车输入,这时一个保护机制,如果外部引脚BKIN产生了刹车信号,或者内部时钟失效产生故障,控制电路会自动切断电机的输出。

3. 定时中断

预分频器时序 

这里看一个时序图,当预分频器的参数从1改为2时,特别注意预分频控制寄存器,它的作用是即使参数改变了,但是此时如果计数器只执行到一半, 这个缓冲寄存器可以保证参数的变化不会立刻生效,而是等到本次计数周期完成,产生了更新事件,改变后的参数才会起作用。

计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)

计数器时序图

计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)

计数器无预装时序

计数器有预装时序

对于计数器无预装时序和计数器有预装时序,区别在于引入了影子寄存器,目的是为了同步,让值的变化和更新事件同步发生,防止在运行途中更改造成错误。例如在这里如果不使用影子寄存器,F5改成36以后立即生效,但此时计数器的数值已经到了F1, 代表已经超过36,所以F1只能一直增加直到FFFF,回到0以后再重新加到36。

4. 代码示例1

要实现定时器定时中断,要使用的库函数文件为:stm32f10x_tim.h,在这里可以找到定时器TIM需要使用到的函数

第一步:开启RCC时钟,这时定时器的基准时钟和外设的工作时钟就会同时打开。

第二步:选择时基单元的时钟源,对于定时中断,选择内部时钟源。

第三步:配置时基单元,也就是预分频器、计数器和自动重装载器。

第四步:配置输出中断控制,允许更新中断输出到NVIC。

第五步:配置NVIC,在NVIC中打开定时器中断的通道,并分配优先级。

第六步:运行控制,在整个模块配置完成后,还需要使能一下计数器,这样计数器才能开始计数。

首先是Timer.c文件,下面包含代码和详细注释:

#include "stm32f10x.h"

//定时中断初始化
void Timer_Init(void)
{
	//开启时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	
	//配置时钟源
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	//时基单元初始化
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	

	//中断输出配置
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位
																//TIM_TimeBaseInit函数末尾,手动产生了更新事件
																//若不清除此标志位,则开启中断后,会立刻进入一次中断
																//如果不介意此问题,则不清除此标志位也可
	
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断
	
	//NVIC中断分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	//NVIC配置
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
	
	//TIM使能
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);:

  • 使能TIM2定时器的时钟,用来开启或关闭高级外设时钟。RCC_APB1Periph_TIM2 是一个宏,指的是APB1总线上的TIM2时钟。ENABLE 宏是一个值,表示开启时钟。

TIM_InternalClockConfig(TIM2);:

  • 配置TIM2的时钟源为内部时钟。这个函数设置定时器的时钟源,确保定时器能够正确计数。

定义和初始化一个 TIM_TimeBaseInitTypeDef 类型的结构体变量 TIM_TimeBaseInitStructure,用于配置定时器的时基单元:

  • TIM_ClockDivision:时钟分频因子,这里设置为 TIM_CKD_DIV1,表示不分频。
  • TIM_CounterMode:设置为 TIM_CounterMode_Up,表示定时器以向上计数模式工作。
  • TIM_Period:设置计数器的自动重载值为9999,由于计数是从0开始的,因此实际的最大计数值是10000。
  • TIM_Prescaler:设置预分频值为7199,预分频器的值决定了时钟频率被减慢的程度。
  • TIM_RepetitionCounter:这个成员在基本定时器中不用,通常在更高级的定时器如TIM1中使用。

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);:

  • 使用前面定义的结构体变量初始化TIM2的时基单元。

TIM_ClearFlag(TIM2, TIM_FLAG_Update);:

  • 清除TIM2的更新中断标志位。这是为了避免在配置过程中可能产生的任何更新事件导致中断。

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);:

  • 开启TIM2的更新中断。这使得每次定时器溢出时,中断请求会被发送到NVIC。

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 和 NVIC_Init(&NVIC_InitStructure);:

  • 这两个函数一起配置中断控制器NVIC。首先设置NVIC的优先级分组,这里使用分组2。然后,初始化NVIC来配置定时器TIM2的中断通道,抢占优先级和响应优先级。

TIM_Cmd(TIM2, ENABLE);:

  • 启动定时器TIM2。之后定时器开始根据配置的参数运行,每当计数器的值达到预设的重载值时,中断请求会被触发。

接着是Timer.h文件,这部分引用声明一下即可。

#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);

#endif

最后是主函数main.c :

#include "stm32f10x.h" 
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"

uint16_t Num;			//定义在定时器中断里自增的变量

int main(void)
{
	//模块初始化
	OLED_Init();		//OLED初始化
	Timer_Init();		//定时中断初始化
	
	//显示静态字符串
	OLED_ShowString(1, 1, "Num:");			//1行1列显示字符串Num:
	
	while (1)
	{
		OLED_ShowNum(1, 5, Num, 5);			//不断刷新显示Num变量
	}
}

//TIM2中断函数
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)		//判断是否是TIM2的更新事件触发的中断
	{
		Num ++;												//Num变量自增,用于测试定时中断
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);			//清除TIM2更新事件的中断标志位
															//中断标志位必须清除
															//否则中断将连续不断地触发,导致主程序卡死
	}
}

5. 代码示例2

定时器外部时钟,刚才是代码示例使用的是内部时钟,这里使用外部时钟,通过一个光电传感器来实现计数功能。

首先是Timer.c 代码:

#include "stm32f10x.h" 

//定时中断初始化
void Timer_Init(void)
{
	//开启时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	//GPIO初始化
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);						//将PA0引脚初始化为上拉输入
	
	//外部时钟配置
	TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);
																//选择外部时钟模式2,时钟从TIM_ETR引脚输入
																//注意TIM2的ETR引脚固定为PA0,无法随意更改
																//最后一个滤波器参数加到最大0x0F,可滤除时钟信号抖动
	
	//时基单元初始化
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;					//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	
	
	//中断输出配置
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位
																//TIM_TimeBaseInit函数末尾,手动产生了更新事件
																//若不清除此标志位,则开启中断后,会立刻进入一次中断
																//如果不介意此问题,则不清除此标志位也可
																
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断

	//NVIC中断分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	//NVIC配置
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
	
	//TIM使能
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

//返回定时器CNT的值
uint16_t Timer_GetCounter(void)
{
	return TIM_GetCounter(TIM2);	//返回定时器TIM2的CNT
}


时钟源配置:

  • 通过调用 RCC_APB1PeriphClockCmd 使能了TIM2的时钟。
  • 调用 RCC_APB2PeriphClockCmd 使能了GPIOA的时钟,因为外部时钟源信号将通过GPIOA端口输入。

GPIO初始化:

  • 定义一个 GPIO_InitTypeDef 类型的结构体变量 GPIO_InitStructure,用于设置GPIO。
  • 设置GPIOA的0号引脚(GPIO_Pin_0)为上拉输入模式(GPIO_Mode_IPU)。
  • 设置引脚速度为50MHz。

外部时钟配置:

  • TIM_ETRClockMode2Config 函数配置定时器以外部时钟模式2工作,从ETR引脚(这里是GPIOA的0号引脚,即PA0)接收外部时钟信号。
  • 参数 TIM_ExtTRGPSC_OFF 关闭外部触发预分频。
  • 参数 TIM_ExtTRGPolarity_NonInverted 设置外部触发极性为非反转。
  • 参数 0x0F 设置滤波器的最大值,这有助于消除信号抖动。

时基单元初始化:

  • 定义 TIM_TimeBaseInitTypeDef 类型的结构体变量 TIM_TimeBaseInitStructure,用于配置时基单元。
  • 设置 TIM_ClockDivision 为不分频。
  • 设置 TIM_CounterMode 为向上计数模式。
  • 设置 TIM_Period 为9,这是计数器的最大值(自动重载值)。
  • 设置 TIM_Prescaler 为0,这里不再需要预分频因为外部时钟源已经是所需的频率。
  • 将这些配置通过 TIM_TimeBaseInit 函数应用到TIM2。

中断输出配置:

  • 调用 TIM_ClearFlag 清除更新标志位。
  • 调用 TIM_ITConfig 开启TIM2的更新中断。

NVIC中断分组和NVIC配置:

  • 使用 NVIC_PriorityGroupConfig 设置NVIC优先级分组。
  • 定义 NVIC_InitTypeDef 类型的结构体变量 NVIC_InitStructure,用于设置NVIC。
  • 配置NVIC的优先级和使能TIM2中断通道。

TIM使能:

  • 调用 TIM_Cmd 使能TIM2,这样定时器开始根据配置的参数工作,准备接收外部时钟信号作为计数脉冲。

Timer_GetCounter 函数:

  • 这个函数返回TIM2的当前计数值(CNT寄存器的内容),是一个16位的值。

接着是Timer.h 代码,同样只需要声明一下。

#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);
uint16_t Timer_GetCounter(void);

#endif

最后是主函数main.c :

#include "stm32f10x.h" 
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"

uint16_t Num;			//定义在定时器中断里自增的变量

int main(void)
{
	//模块初始化
	OLED_Init();		//OLED初始化
	Timer_Init();		//定时中断初始化
	
	//显示静态字符串
	OLED_ShowString(1, 1, "Num:");			//1行1列显示字符串Num:
	OLED_ShowString(2, 1, "CNT:");			//2行1列显示字符串CNT:
	
	while (1)
	{
		OLED_ShowNum(1, 5, Num, 5);			//不断刷新显示Num变量
		OLED_ShowNum(2, 5, Timer_GetCounter(), 5);		//不断刷新显示CNT的值
	}
}

//TIM2中断函数
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)		//判断是否是TIM2的更新事件触发的中断
	{
		Num ++;												//Num变量自增,用于测试定时中断
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);			//清除TIM2更新事件的中断标志位
															//中断标志位必须清除
															//否则中断将连续不断地触发,导致主程序卡死
	}
}

完整代码工程文件:

基于STM32的定时器内部(外部)时钟中断代码

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

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

相关文章

LeetCode 198—— 打家劫舍

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 此题使用动态规划求解,假设 d p [ i ] [ 0 ] dp[i][0] dp[i][0] 代表不偷窃第 i i i 个房屋可以获得的最高金额,而 d p [ i ] [ 1 ] dp[i][1] dp[i][1] 代表偷窃第 i i i 个房屋可以获…

2024年谷歌Google广告开户是什么政策?

2024年谷歌Google广告依然是众多企业拓展市场、提升品牌影响力的重要渠道,随着谷歌政策的不断更新与优化,以及对第三方cookie逐步禁用的决定,如何高效地在这一平台上开展广告活动,成为每一家企业必须面对的战略挑战。云衔科技作为…

【平台开发】MTK6833 实现lk下CQE接口移植 - cmdq irq

1.cmdq_irq 检测中断bit 2.目前遇到问题 任务执行后,没有触发对应中断,伴有错误发生,但任务完成标志位能检测到 寄存器CQIS,CQDQS等均为0,CQTCN为任务完成寄存器看到置1,CQTERRI检测到8000错误 错误详情如下&#xf…

基于SpringBoot+Vue外卖系统设计和实现(源码+LW+部署讲解)

🌹作者简介:✌全网粉丝10W,csdn特邀作者、博客专家、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 🌹 主要…

day7 c++

整理代码 1、unique_ptr 指针 #include <iostream> #include <memory> using namespace std; class Demo {public:Demo(){cout<<"无参构造"<<endl;}~Demo(){cout<<"Demo的析构函数"<<endl;} };int main() {//unique…

(1)探索 SpringAI - 基本概述

人工智能简介 A system is ability to correctly interpret external data, to learn from such data, and to use those learnings to achieve specific goals and tasks through flexible adaptation. 翻译&#xff1a;系统正确解释外部数据的能力&#xff0c;从这些数据中学…

【C语言/数据结构】经典链表OJ习题~第二期——链中寻环

&#x1f388;&#x1f388;&#x1f388;欢迎采访小残风的博客主页&#xff1a;残风也想永存-CSDN博客&#x1f388;&#x1f388;&#x1f388; &#x1f388;&#x1f388;&#x1f388;本人码云 链接&#xff1a;残风也想永存 (FSRMWK) - Gitee.com&#x1f388;&#x1f…

AI家居设备的未来:智能家庭的下一个大步

&#x1f512;目录 ☂️智能家居设备的发展和AI技术的作用 ❤️AI技术实现智能家居设备的自动化控制和智能化交互的依赖 AI家居设备的未来应用场景 &#x1f4a3;智能家庭在未来的发展和应用前景 &#x1f4a5;智能家居设备的发展和AI技术的作用 智能家居设备的发展和AI技术的…

Tokitsukaze and Average of Substring

原题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 前缀和。 开一个int类型的前缀和数组pre[30][N]&#xff08;pre[i][j]表示某字符转成的数字 i 在一段区间的前缀个数。因为字母表有‘a’~z…

解析Python中获取当前线程名字的方法及多线程编程实践

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Python 获取当前线程的名字 在多线程编程中&#xff0c;了解当前线程的名字是一项重要的任…

【Linux学习】(1)Linux环境安装|Xshell安装|创建普通用户

前言 从本文开始我们将进入Linux的学习&#xff0c;在学习Linux之前我们需要简单了解什么是Linux&#xff0c;和安装环境。 一、Linux的简单了解 1、Linux的来源 UNIX操作系统应用于高校和研究机构&#xff0c;并且收费。我们的主人公林纳斯托瓦兹&#xff08;21岁&#xff0…

platformd device、driver注册过程

本文以smsc911x驱动为例 platform_device注册过程 该设备被定义在dts里面了 参考文章设备树节点转换为设备节点device_node、和平台设备资源platform_device_设备树节点转换成平台设备-CSDN博客 dts里面的节点会被转换为device_node和platform_device(并不是所有节点都会被转…

Pandas Series的运算原来这么简单

Series的运算主要包括加法、减法、乘法和除法等基本算术运算。这些运算通常是按照索引对应计算的&#xff0c;如果两个Series的索引不同&#xff0c;则结果中对应位置将填充为NaN&#xff08;空值&#xff09;。 需要注意的是&#xff0c;在进行Series运算时&#xff0c;需要确…

升级 Vite 5 出现警告 The CJS build of Vite‘s Node API is deprecated

错误描述 vue3-element-admin 项目将Vite4 升级至 Vite5 后,项目运行出现如下警告: The CJS build of Vites Node API is deprecated. See https://vitejs.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.图片 问题原因 Vite 官方弃用 C…

PID详解汇总

一、参照文章 PID的各种算法优缺点 二、位置式PID 优点:静态误差小,溢出的影响小。 缺点:计算量很大&#x

java-spring-mvc(服务端接收客户端传参)

目录 &#x1f3af; 服务端接收参数 ✨HttpServletRequest接收 ✨ 声明参数接收 ✨声明pojo类来接收 &#x1f52a;小试牛刀 &#x1f3af; 服务端接收参数 ✨HttpServletRequest接收 HttpServletRequest是Java Servlet规范中定义的一个接口&#xff0c;它提供了与HTTP请求…

手撕sql面试题:找出所有观看视频ID “1001“ 的观看时长大于他们观看视频ID “1002“ 的观看时长的用户ID

分享最近面试的sql面试题&#xff1a; 下面是表结构&#xff1a; CREATE TABLE video_records ( video_id char(4) NOT NULL COMMENT 视频id, user_id char(4) NOT NULL COMMENT 用户id, play_duration int NOT NULL COMMENT 观看时长, PRIMARY KEY (video_id,…

stm32f103c8t6学习笔记(学习B站up江科大自化协)-PWR电源控制

PWR简介 PVD可用在电池供电或安全要求比较高的设备&#xff0c;如果供电电压在逐渐下降&#xff0c;在电压过低的情况下可能会导致内外电路出现不确定的错误。为了避免不必要的错误&#xff0c;可以在电源电压过低的情况下&#xff0c;提前发出警告并关闭较为危险的设备 关闭的…

Typora+PicGo+阿里云OSS搭建个人博客图床(2024最新详细搭建教程)

创作者&#xff1a;Code_流苏(CSDN) 目录 一、什么是图床&#xff1f;二、准备工作三、配置PicGo四、配置Typora五、使用 很高兴你打开了这篇博客&#xff0c;如有疑问&#xff0c;欢迎评论。 更多好用的软件工具&#xff0c;请关注我&#xff0c;订阅专栏《实用软件与高效工具…

[Transactional Level Bypass] Bypass Validation Rule in Apex Batch Class

问题 现有一个batch job用于批量更新Lead&#xff0c;最近频繁收到apex exception email, 显示更新Lead的时候触发了validation rule&#xff0c;导致apex job运行失败。 batch class节选如下&#xff1a; public void execute(Database.BatchableContext bc, List<Lead&…