【正点原子STM32连载】 第二十章 基本定时器实验 摘自【正点原子】STM32F103 战舰开发指南V1.2

news2024/11/18 14:24:20

1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html

第二十章 基本定时器实验

STM32F103有众多的定时器,其中包括2个基本定时器(TIM6和TIM7)、4个通用定时器(TIM2~TIM5)、2个高级控制定时器(TIM1和TIM8),这些定时器彼此完全独立,不共享任何资源。本章我们学习如何使用STM32F103的基本定时器中断。我们将使用TIM6的定时器中断来控制LED1的翻转,在主函数用LED0的翻转来提示程序正在运行。
本章分为如下几个小节:
20.1 基本定时器简介
20.2 硬件设计
20.3 程序设计
20.4 下载验证

20.1 基本定时器简介

STM32F103有两个基本定时器TIM6和TIM7,它们的功能完全相同,资源是完全独立的,可以同时使用。其主要特性如下:16位自动重载递增计数器,16位可编程预分频器,预分频系数1~65536,用于对计数器时钟频率进行分频,还可以触发DAC的同步电路,以及生成中断/DMA 请求。
20.1.1 基本定时器框图
下面先来学习基本定时器框图,通过学习基本定时器框图会有一个很好的整体掌握,同时对之后的编程也会有一个清晰的思路。
在这里插入图片描述

图20.1.1.1 基本定时器框图
①时钟源
定时器的核心就是计算器,要实现计数功能,首先要给它一个时钟源。基本定时器时钟挂载在APB1总线,所以它的时钟来自于APB1总线,但是基本定时器时钟不是直接由APB1总线直接提供,而是先经过一个倍频器。当APB1的预分频器系数为1时,这个倍频器系数为1,即定时器的时钟频率等于APB1总线时钟频率;当APB1的预分频器系数≥2分频时,这个倍频器系数就为2,即定时器的时钟频率等于APB1总线时钟频率的两倍。我们在sys_stm32_clock_init时钟设置函数已经设置APB1总线时钟频率为36M,APB1总线的预分频器分频系数是2,所以挂载在APB1总线的定时器时钟频率为72Mhz。
②控制器
控制器除了控制定时器复位、使能、计数等功能之外,还可以用于触发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。
20.1.2 TIM6/TIM7寄存器
下面介绍TIM6/TIM7的几个重要的寄存器,具体如下:
 控制寄存器 1(TIMx_CR1)
TIM6/TIM7的控制寄存器1描述如图20.1.2.1所示。
在这里插入图片描述

图20.1.2.1 TIMx_CR1寄存器
该寄存器,我们需要注意的是:位0(CEN)用于使能或者禁止计数器,该位置1计数器开始工作,置0则停止。还有位7(APRE)用于控制自动重载寄存器ARR是否具有缓冲作用,如果ARPE位置1,ARR起缓冲作用,即只有在更新事件发生时才会把ARR的值写入其影子寄存器里;如果ARPE位置0,那么修改自动重载寄存器的值时,该值会马上被写入其影子寄存器中,从而立即生效。
 DMA/中断使能寄存器(TIMx_DIER)
在这里插入图片描述

图20.1.2.2 TIMx_DIER寄存器
该寄存器位0(UIE)用于使能或者禁止更新中断,因为本实验我们用到中断,所以该位需要置1。位8(UDE)用于使能或者禁止更新DMA请求,我们暂且用不到,置0即可。
 状态寄存器(TIMx_SR)
TIM6/TIM7的状态寄存器描述如图20.1.2.3所示:
在这里插入图片描述

图20.1.2.3 TIMx_SR寄存器
该寄存器位0(UIF)是中断更新的标志位,当发生中断时由硬件置1,然后就会执行中断服务函数,需要软件去清零,所以我们必须在中断服务函数里把该位清零。如果中断到来后,不把该位清零,那么系统就会一直进入中断服务函数,这显然不是我们想要的。
 计数器寄存器(TIMx_CNT)
TIM6/TIM7的计数器寄存器描述如图20.1.2.4所示:
在这里插入图片描述

图20.1.2.4 TIMx_CNT寄存器
该寄存器位[15:0]就是计数器的实时的计数值。
 预分频寄存器(TIMx_PSC)
TIM6/TIM7的预分频寄存器描述如图20.1.2.5所示:
在这里插入图片描述

图20.1.2.5 TIMx_PSC寄存器
该寄存器是TIM6/TIM7的预分频寄存器,比如我们要7200分频,就往该寄存器写入7199。注意这是16位的寄存器,写入的数值范围是0到65535,分频系数范围:1到65536。
 自动重载寄存器(TIMx_ARR)
TIM6/TIM7的自动重载寄存器描述如图20.1.2.6所示:
在这里插入图片描述

图20.1.2.6 TIMx_ARR寄存器
该寄存器可以由APRE位设置是否进行缓冲。计数器的值会和ARR寄存器影子寄存器进行比较,当两者相等,定时器就会溢出,从而发生更新事件,如果打开更新中断,还会发生更新中断。
20.1.3 基本定时器中断应用
本实验,我们主要配置定时器产生周期性溢出,从而在定时器更新中断中做周期性的操作,如周期性翻转LED灯。假设计数器计数模式为递增计数模式,那么实现周期性更新中断原理示意图如下所示:
在这里插入图片描述

图20.1.3.1 基本定时器中断示意图
如图20.1.3.1所示,CNT计数器从0开始计数,当CNT的值和ARR相等时(t1),产生一个更新中断,然后CNT复位(清零),然后继续递增计数,依次循环。图中的t1、t2、t3就是定时器更新中断产生的时刻。
通过修改ARR的值,可以改变定时时间。另外,通过修改PSC的值,使用不同的计数频率(改变图中CNT的斜率),也可以改变定时时间。
20.2 硬件设计

  1. 例程功能
    LED0用来指示程序运行,每200ms翻转一次。我们在更新中断中,将LED1的状态取反。LED1用于指示定时器发生更新事件的频率,500ms取反一次。
  2. 硬件资源
    1)LED灯
    LED0 – PB5
    LED1 – PE5
    2)定时器6
  3. 原理图
    定时器属于STM32F103的内部资源,只需要软件设置好即可正常工作。我们通过LED1来指示STM32F103的定时器进入中断的频率。
    20.3 程序设计
    20.3.1 定时器的HAL库驱动
    定时器在HAL库中的驱动代码在STM32F1xx_hal_tim.c和STM32F1xx_hal_tim_ex.c文件(以及它们的头文件)中。
  4. HAL_TIM_Base_Init函数
    定时器的初始化函数,其声明如下:
    HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim);
    函数描述:
    用于初始化定时器。
    函数形参:
    形参1是TIM_HandleTypeDef结构体类型指针变量(亦称定时器句柄),结构体定义如下:
typedef struct
{
  TIM_TypeDef                    *Instance;	/* 外设寄存器基地址 */
  TIM_Base_InitTypeDef          Init;		/* 定时器初始化结构体*/
  HAL_TIM_ActiveChannel         Channel;		/* 定时器通道 */
  DMA_HandleTypeDef             *hdma[7];		/* DMA管理结构体 */
  HAL_LockTypeDef                Lock;		/* 锁定资源 */
  __IO HAL_TIM_StateTypeDef    State;		/* 定时器状态 */
}TIM_HandleTypeDef;

1)Instance:指向定时器寄存器基地址。
2)Init:定时器初始化结构体,用于配置定时器的相关参数。
3)Channel:定时器的通道选择,基本定时器没有该功能。
4)hdma[7]:用于配置定时器的DMA请求。
5)Lock:ADC锁资源。
6)State:定时器工作状态。
我们主要看TIM_Base_InitTypeDef这个结构体类型定义:

typedef struct
{
  uint32_t Prescaler;            	/* 预分频系数 */      
  uint32_t CounterMode;          	/* 计数模式 */
  uint32_t Period;               	/* 自动重载值ARR */
  uint32_t ClockDivision;       	/* 时钟分频因子 */   
  uint32_t RepetitionCounter;  	/* 重复计数器 */
  uint32_t AutoReloadPreload;  	/* 自动重载预装载使能 */
} TIM_Base_InitTypeDef;

1)Prescaler:预分频系数,即写入预分频寄存器的值,范围0到65535。
2)CounterMode:计数器计数模式,这里基本定时器只能向上计数。
3)Period:自动重载值,即写入自动重载寄存器的值,范围0到65535。
4)ClockDivision:时钟分频因子,也就是定时器时钟频率CK_INT与数字滤波器所使用的采样时钟之间的分频比,基本定时器没有此功能。
5)RepetitionCounter:设置重复计数器寄存器的值,用在高级定时器中。
6)AutoReloadPreload:自动重载预装载使能,即控制寄存器 1 (TIMx_CR1)的ARPE位。
函数返回值:
HAL_StatusTypeDef枚举类型的值。
2. HAL_TIM_Base_Start_IT函数
HAL_TIM_Base_Start_IT函数是更新定时器中断和使能定时器的函数。其声明如下:
HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef htim);
函数描述:
该函数调用了__HAL_TIM_ENABLE_IT和__HAL_TIM_ENABLE两个函数宏定义,分别是更新定时器中断和使能定时器的宏定义。
函数形参:
形参1是TIM_HandleTypeDef结构体类型指针变量,即定时器句柄。
函数返回值:
HAL_StatusTypeDef枚举类型的值。
注意事项:
下面分别列出单独使能/关闭定时器中断和使能/关闭定时器方法:
__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE); /
使能句柄指定的定时器更新中断 /
__HAL_TIM_DISABLE_IT (htim, TIM_IT_UPDATE); /
关闭句柄指定的定时器更新中断 /
__HAL_TIM_ENABLE(htim); /
使能句柄htim指定的定时器 /
__HAL_TIM_DISABLE(htim); /
关闭句柄htim指定的定时器 /
定时器中断配置步骤
1)开启定时器时钟
HAL中定时器使能是通过宏定义标识符来实现对相关寄存器操作的,方法如下:
__HAL_RCC_TIMx_CLK_ENABLE(); /
x=1~8 */
2)初始化定时器参数,设置自动重装值,分频系数,计数方式等
定时器的初始化参数是通过定时器初始化函数HAL_TIM_Base_Init实现的。
注意:该函数会调用:HAL_TIM_Base_MspInit函数,我们可以通过后者存放定时器时钟和中断等初始化的代码。
3)使能定时器更新中断,开启定时器计数,配置定时器中断优先级
通过HAL_TIM_Base_Start_IT函数使能定时器更新中断和开启定时器计数。
通过HAL_NVIC_EnableIRQ函数使能定时器中断,通过HAL_NVIC_SetPriority函数设置中断优先级。
4)编写中断服务函数
定时器中断服务函数为:TIMx_IRQHandler等,当发生中断的时候,程序就会执行中断服务函数。HAL库提供了一个定时器中断公共处理函数HAL_TIM_IRQHandler,该函数又会调用HAL_TIM_PeriodElapsedCallback等一些回调函数,需要用户根据中断类型选择重定义对应的中断回调函数来处理中断程序。
20.3.2 程序流程图
在这里插入图片描述

图20.3.2.1 基本定时器中断实验程序流程图
程序开始先进行一系列初始化,然后在main中让LED0每过200ms翻转一次,用于指示系统代码正在运行。LED1的翻转将在定时器更新中断里进行,请看程序解析。
20.3.3 程序解析

  1. 基本定时器中断驱动代码
    这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。基本定时器驱动代码包括两个文件:btim.c和btim.h。
    首先看btim.h头文件的几个宏定义:
/* 基本定时器 定义 */
/* TIMX 中断定义 
 * 默认是针对TIM6/TIM7.
 * 注意: 通过修改这4个宏定义,可以支持TIM1~TIM8任意一个定时器.
 */
#define BTIM_TIMX_INT               	TIM6
#define BTIM_TIMX_INT_IRQn         	TIM6_DAC_IRQn
#define BTIM_TIMX_INT_IRQHandler  	TIM6_DAC_IRQHandler
/* TIM6 时钟使能 */
#define BTIM_TIMX_INT_CLK_ENABLE()	do{ __HAL_RCC_TIM6_CLK_ENABLE(); }while(0)

通过修改这4个宏定义,可以支持TIM1~TIM8任意一个定时器。
下面我们解析btim.c的程序,先看定时器的初始化函数,其定义如下:

/**
 * @brief      基本定时器TIMX定时中断初始化函数
 * @note
 *              基本定时器的时钟来自APB1,当PPRE1 ≥ 2分频的时候
 *              基本定时器的时钟为APB1时钟的2倍, 而APB1为36M, 所以定时器时钟 = 72Mhz
 *              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *              Ft=定时器工作频率,单位:Mhz
 *
 * @param       arr: 自动重装值。
 * @param       psc: 时钟预分频数
 * @retval      无
 */
void btim_timx_int_init(uint16_t arr, uint16_t psc)
{
    g_timx_handle.Instance = BTIM_TIMX_INT;                        /* 定时器x */
    g_timx_handle.Init.Prescaler = psc;                             /* 预分频 */
    g_timx_handle.Init.CounterMode = TIM_COUNTERMODE_UP;        	/* 递增计数器 */
    g_timx_handle.Init.Period = arr;                               	/* 自动装载值 */
    HAL_TIM_Base_Init(&g_timx_handle);

    HAL_TIM_Base_Start_IT(&g_timx_handle);   /* 使能定时器x和定时器x更新中断 */
}

btim_timx_int_init函数用来初始化定时器,我们可以通过修改宏定义BTIM_TIMX_INT来初始化TIM1~TIM8中的任意一个,本章我们是初始化基本定时器6。该函数的2个形参:arr设置自动重载寄存器(TIMx_ARR),psc设置预分频器寄存器(TIMx_PSC)。HAL_TIM_Base_Init函数初始化定时器后,再调用HAL_TIM_Base_Start_IT函数使能定时器和更新定时器中断。
因为我们在sys_stm32_clock_init函数里面已经初始化APB1的时钟为HCLK的2分频,所以APB1的时钟为36M,而从STM32F1的内部时钟树图得知:当APB1的时钟分频数为1的时候,TIM27的时钟为APB1的时钟,而如果APB1的时钟分频数不为1,那么TIM27的时钟频率将为APB1时钟的两倍。因此,TIM6的时钟为72M,再根据我们设计的arr和psc的值,就可以计算中断时间了。计算公式如下:

Tout= ((arr+1)*(psc+1))/Tclk
其中:
Tout:定时器溢出时间(单位为s)。
Tclk:定时器的时钟源频率(单位为MHz)。
arr:自动重装寄存器(TIMx_ARR)的值。
psc:预分频器寄存器(TIMx_PSC)的值
定时器底层驱动初始化函数如下:
/**
 * @brief       定时器底层驱动,开启时钟,设置中断优先级
                 此函数会被HAL_TIM_Base_Init()函数调用
 * @param       无
 * @retval      无
 */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == BTIM_TIMX_INT)
    {
        BTIM_TIMX_INT_CLK_ENABLE();                        /* 使能TIMx时钟 */
/* 设置中断优先级,抢占优先级1,子优先级3 */
        HAL_NVIC_SetPriority(BTIM_TIMX_INT_IRQn, 1, 3);  
        HAL_NVIC_EnableIRQ(BTIM_TIMX_INT_IRQn);          /* 开启ITMx中断 */
    }
}

HAL_TIM_Base_MspInit函数用于存放GPIO、NVIC和时钟相关的代码,这里首先判断定时器的寄存器基地址,满足条件后,首先设置使能定时器的时钟,然后设置定时器中断的抢占优先级为1,响应优先级为3,最后开启定时器中断。这里没有用到IO引脚,所以不用初始化GPIO。
接着是定时器中断服务函数的定义,这里用的是宏名,其定义如下:

/**
 * @brief       定时器中断服务函数
 * @param       无
 * @retval      无
 */
void BTIM_TIMX_INT_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&timx_handle);
}

这个函数实际上调用HAL库的定时器中断公共处理函数HAL_TIM_IRQHandler。HAL库的中断公共处理函数,会根据中断标志位调用各个中断回调函数,中断要处理的逻辑代码就写到这个回调函数中。比如这里我们使用到的是更新中断,定义的更新中断回调函数如下:

/**
 * @brief       定时器更新中断回调函数
 * @param       htim:定时器句柄指针
 * @retval      无
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == BTIM_TIMX_INT)
    {
        LED1_TOGGLE();
    }
}

更新中断回调函数是所有定时器公用的,所以我们就需要在更新中断回调函数中对定时器寄存器基地址进行判断,只有符合对应定时器发生的更新中断,才能进行相应的处理,从而避免多个定时器同时使用到更新中断,导致更新中断代码的逻辑混乱。这里我们使用定时器6的更新中断,所以进入更新中断回调函数后,先判断是不是定时器6的寄存器基地址,当然这里使用宏的形式,BTIM_TIMX_INT的原型就是TIM6,执行的逻辑代码是翻转LED1。
2. main.c代码
在main.c里面编写如下代码:

int main(void)
{
    HAL_Init();                             		/* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);		/* 设置时钟, 72Mhz */
    delay_init(72);                        		/* 延时初始化 */
    usart_init(115200);                   		/* 串口初始化为115200 */
    led_init();                            		/* 初始化LED */
    btim_timx_int_init(5000 - 1, 7200 - 1);	/* 10Khz的计数频率,计数5K次为500ms */
    while (1)
    {
        LED0_TOGGLE();
        delay_ms(200);
    }
}

在main函数里,先初始化系统和用户的外设代码,然后在wilhe(1)里每200ms翻转一次LED0。由前面的内容知道,定时器6的时钟频率为72MHZ,而调用btim_timx_int_init初始化函数之后,就相当于写入预分频寄存器的值为7199,写入自动重载寄存器的值为4999。由公式得:
Tout = ((4999+1)*(7199+1))/72000000 = 0.5s = 500ms
20.4 下载验证
下载代码后,可以看到LED0不停闪烁(每400ms一个周期),而LED1也是不停的闪烁,但是闪烁时间较LED0慢(每1s一个周期)。

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

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

相关文章

字符串匹配算法--KMP算法--BM算法

该算法解决的是字符串匹配问题,即查看字符串中是否含有完整的匹配字符串。如在java的string的contains方法匹配问题最简单的就是暴力破解了。在java的contains也是这么实现的,效率是低一点的。如果想要更快的速度可以自己写KMP算法。 代码实现体验 还未…

chatgpt赋能Python-python_numpy怎么用

Python与Numpy:简介 Python是一种高级编程语言,它具有简洁的语法和广泛的应用领域。Numpy是一个为Python提供高效数学库的开源库,它允许进行高速数值计算和数据处理。 对于那些正在开始使用Python和Numpy的初学者,这篇文章将向您…

CANoe使用教程

目录 1.CAN IG使用 2.Visual Sequence 自动发送报文序列,可以设置多个visual sequence,可用于重复的网络模拟和write窗口输出,比IG灵活,比CAPL简单 3.CANoe中的系统变量和环境变量都是一种用于存储和传递数据的变量,…

linux部署yolov5

Linux配置 LibTorch 和 OpenCV LibTorch 下载地址:https://pytorch.org/get-started/locally/ 直接解压即可 OpenCV 下载地址:https://opencv.org/releases/ 需要配置ffmpeg环境 export LD_LIBRARY_PATH$LD_LIBRARY_PATH:/home/workspace/dengzr/li…

嵌入式Linux应用开发笔记:GPIO编程

文章目录 目的基础说明代码示例数字输出数字输入外部中断 总结 目的 GPIO嵌入式设备中最基础的外设,使用上也是非常频繁的。这篇文章将记录下应用程序中GPIO操作相关内容。 这篇文章中内容均在下面的开发板上进行测试: 《新唐NUC980使用记录&#xff1…

15、STM32驱动sht35温湿度传感器

本文使用模拟IIC驱动sht35温湿度传感器 踩坑点:购买的模块IIC上拉电阻为10KΩ,会导致IIC不稳定,抗干扰差,容易导致时序错误;建议更换为4.7KΩ 一、CubeMx配置 其余默认,生成工程 1、us精确延时 Delay_Dr…

2023年电子、通信与控制工程国际会议(SECCE 2023)

会议简介 Brief Introduction 2023年电子、通信与控制工程国际会议(SECCE 2023) 会议时间:2023年8月11日-13日 召开地点:韩国济州岛 大会官网:www.isecce.org 2023年电子、通信与控制工程国际会议(SECCE 2023)将围绕“电子、通信与控制工程”…

Spring Boot 整合流程引擎 Flowable(附源码地址)

一、导入依赖 flowable依赖&#xff1a; <dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.7.2</version> </dependency>pom.xml: <?xml version"1…

驱动页面性能优化的3个有效策略

目录 引言 背景 前端性能优化 测试视角的解法 性能问题的发现 性能数据的采集 性能指标的确定 性能问题的分析 如何衡量性能问题严重性 分析性能瓶颈-分析思路 分析结论关键思路 引言 测试通过发现、分析、验证三板斧&#xff0c;驱动推进页面性能优化快速有效&…

关注 | 蛙色元宇宙,正式成为XRMA联盟成员单位

中国虚拟现实与元宇宙产业峰会&#xff0c;2023年3月22日于杭州圆满结束&#xff0c;在杭州市人民政府、浙江省经济和信息化厅指导&#xff0c;由杭州市经济和信息化局、杭州市西湖区人民政府主办&#xff0c;中国信息通信研究院承办。 蛙色元宇宙作为元宇宙的领先企业之一&…

【AI面试】CrossEntropy Loss 、Balanced Cross Entropy、 Dice Loss 和 Focal Loss 横评对比

样本不均衡问题一直是深度学习领域一个不可忽略的问题&#xff0c;常说的长尾效应&#xff0c;说的就是这个问题。一类占据了主导地位&#xff0c;导致其他类无论怎么优化&#xff0c;都不能好转。 无论是纯纯的分类任务&#xff0c;还是稍微复杂一些的目标检测任务和分割任务…

关于java在成员/全局变量上不同类型赋值遇到的问题(值传递)

一个疑惑 文件简介回答参考文献 文件简介 class ss{static class Student{int id;String name; /*public Student(int id, String name) {this.id id;this.name name;}*/public int getId() {return id;}public void setId(int id) {this.id id;}public String getName() {…

数字人入门文章速览

语音驱动三维人脸方法 OPPO 数字人语音驱动面部技术实践 【万字长文】虚拟人漫谈 Blendshape学习笔记 人脸重建速览&#xff0c;从3DMM到表情驱动动画 功能强大的python包&#xff08;四&#xff09;&#xff1a;OpenCV 从Blendshapes到Animoji 3D人脸重建算法汇总 一、3D人脸重…

windows 10 安装k8s环境 Kubernetes

主要命令有 1. iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex 2. choco install minikube 3. minikube start 4. minikube dashboard 使用管理员运行 PowerShell 执行下面这条命令 iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex choc…

开源赋能 普惠未来|铜锁/Tongsuo诚邀您参与2023开放原子全球开源峰会

铜锁/Tongsuo是一个提供现代密码学算法和安全通信协议的开源基础密码库&#xff0c;为存储、网络、密钥管理、隐私计算、区块链等诸多业务场景提供底层的密码学基础能力&#xff0c;实现数据在传输、使用、存储等过程中的私密性、完整性和可认证性&#xff0c;为数据生命周期中…

Linux:web基础与HTTP协议

Linux&#xff1a;web基础与HTTP协议 一、域名概述1.1 域名的概念1.2 域名空间结构1.3 域名注册 二、网页的概念2.1 网页2.2 网站2.3 主页2.4 网页2 三、HTML概述3.1 HTML概述3.2 HTML文档结构3.3 HTML 基本标签 四、web概述4.1 web概述4.2 Web1.0 vs Web2.04.3 静态网页4.3.1 …

【挑战自己】软件测试的7个级别,做到3级已经超越80%测试人

有人说&#xff1a;软件测试就是最low的点点点工作。 有人说&#xff1a;测试工作职位薪水到一定程度只能原地踏步无法提升 也有人说&#xff1a;测试行业相对于开发来说技术性很低&#xff0c;容易被取代。 这其实是对测试行业最大的误解。测试可深可浅&#xff0c;可窄可广…

QDir拼接路径解决各种斜杠问题

一般在项目中经常需要组合路径,与其他程序进行相互调用传递消息通信。 经常可能因为多加斜杠、少加斜杠等问题导致很多问题。 为了解决这些问题,我们可以使用QDir来完成路径的拼接,不直接拼接字符串。 QDir的静态方法QDir::cleanPath() 是为了规范化路径名的,在使用QDir组…

Unity第三方分享(微信)插件ShareSDK使用简记

Unity第三方分享&#xff08;微信&#xff09;插件ShareSDK使用简记 微信分享遇到的问题记录 链接官方链接参考链接 微信分享 官方文档&#xff1a;MobTech集成文档-MobTech 下载地址&#xff1a;GitHub - MobClub/New-Unity-For-ShareSDK: New sample of ShareSDK for Unity,…

ChatGPT:你真的了解网络安全吗?浅谈攻击防御进行时之网络安全新防御

ChatGPT&#xff1a;你真的了解网络安全吗&#xff1f;浅谈网络安全攻击防御进行时 网络安全新防御1. 针对人工智能2. 针对5G和物联网3. 针对云安全4.针对社交工程5. 针对加密技术6. 针对多层次的安全控制 总结 ChatGPT&#xff08;全名&#xff1a;Chat Generative Pre-traine…