stm32 - 中断/定时器

stm32 - 中断/定时器

  • 概念
    • 时钟树
    • 定时器类型
    • 基准时钟(系统时钟)
    • 预分频器 - 时基单元
    • CNT计数器 - 时基单元
    • 自动重装寄存器 - 时基单元
    • 基本定时器结构
    • 通用定时器
      • 计数器模式
      • 内外时钟源选择
    • 定时中断基本结构
    • 时序
      • 预分频器时序
      • 计数器时序
  • 例子
    • 通用定时器 - 内部定时中断
    • 通用定时器 - 外部时钟定时中断
  • 定时器 - 输出捕获
    • PWM
    • 输出比较通道 - 通用定时器
    • 例子 - 呼吸灯
    • 例子 - 呼吸灯-端口重映射
    • 例子 - 控制舵机 / 连续舵机有问题???
  • 输入捕获
    • 输入捕获基本电路
    • 主从触发模式
    • 输入捕获基本结构/PWMI输入捕获结构
    • 例子 - 输入捕获频率
    • 例子 - 输入捕获频率/占空比

概念

时钟树

https://www.bilibili.com/video/BV1th411z7sn?p=13&vd_source=7155082256127a432d5ed516a6423e20

执行main函数之前,程序中主函数还会执行一个systeminit函数,这个函数就是用来配置时钟树的

SYSCLK是系统时钟72MHZ
震荡源:内部震荡源RC振荡器8MHZ,外部震荡源:4~16MHZ(石英晶振),…
图中与门就是RCC_xxx函数打开时钟

在这里插入图片描述

定时器类型

  • 高级定时器:TIM1,TIM8 -> APB2总线,
  • 通用定时器:TIM2,TIM3,TIM4,TIM5 -> APB1总线,
  • 基本定时器:TIM6,TIM7 -> APB1总线,

针对STM32C8T6:TIM1,TIM2,TIM3,TIM4

基准时钟(系统时钟)

stm32主频72MHZ
RCC_TIMxCLK(主频)-> 内部时钟CK_INT(基本计数时钟) ->控制器 -> CK_PSC ->时基单元
因此,通向时基单元的计数基准频率是72MHZ

预分频器 - 时基单元

对输入的基准频率提前进行一个分频的操作

对72MHZ的计数时钟进行预分频
预分频器=0,不分频:输出频率=输入频率=72MHZ
预分频器=1,2分频:输出频率=输入频率/2=36MHZ
预分频器=11 ,12分频:输出频率=输入频率/12=6MHZ

预分频器是16位,最大可以写65535个数,最大是65535+1=65536分频;输出频率=输入频率/65536=1.0986328125KHZ

CNT计数器 - 时基单元

对预分频后的计数时钟进行计数,计数时钟每来一个上升沿,计数器值+1
CNT计数器是16位的,最多计0~65535的数值,再+1就从0开始计数

实际定时中断,应该是计数器达到目标值时,产生中断

自动重装寄存器 - 时基单元

存储计数目标的寄存器
自动重装寄存器是16位的, 是写入的固定值,当计数器的计数值达到自动重装寄存器的值的时候,表明定时时间到,产生中断信号,并清零计数器开始下一次从0开始计数

计数器值=自动重装寄存器的值(也叫更新中断),产生中断,产生中断后通往NVIC,再配置号NVIC定时器的通道, 执行中断服务

基本定时器结构

在这里插入图片描述
在这里插入图片描述

通用定时器

>

计数器模式

向上计数模式:基本定时器只有这一个功能
向下计数模式:通用计时器
中央对齐模式:通用计时器

  • 向下计数模式

从自定义的自动重装值开始,向下自减,减到0后重新回到自动重装值开始计数

  • 中央对齐模式

0-> 自增 -> 自动重装值 -> 自减 -> 0

内外时钟源选择

对于基本定时器,只能选择内部时钟进行定时,即系统频72MHZ
对于通用定时器,即能选择内部时钟72MHZ,也能选择外部时钟

在这里插入图片描述

外部时钟

TIMx_ETR引脚上的外部时钟(查看引脚定义图)
在引脚上接一个外部的方波时钟,然后配置内部的电路

定时中断基本结构

在这里插入图片描述

时序

预分频器时序

在这里插入图片描述

计数器时序


例子

通用定时器 - 内部定时中断

main.c

#include "stm32f10x.h"
#include "timer.h"extern uint16_t timer2_num;int main()
{OLED_Init();OLED_ShowString(1,1,"helloworld");Timer_Init();while (1) {OLED_ShowNum(2,1,timer2_num,5);}
}

timer.h

#ifndef __TIMER_H__
#define __TIMER_H__
#include "stm32f10x.h"
void Timer_Init();
#endif

timer.c

#include "timer.h"uint16_t timer2_num;
void Timer_Init()
{// RCC时钟 系统基准时钟,和外设工作时钟// 通用定时器TIM2是挂载在APB1上的RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); // 时钟树种的与门结构// 时基单元时钟源// 选择内部时钟TIM_InternalClockConfig(TIM2);// 配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;/* 这里定时为1s=1/1HZ; 分频后72MHZ/((7200-1)+1)=10000HZ=10KHZ; 1/10000s记一次数, */TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1;					// PSC预分频器的值TIM_TimeBaseInitStructure.TIM_Period=10000-1;					// ARR自动重装器的值  0~65535TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;  	// 计数模式,向上计数 /*- TIM_ClockDivision 与时基单元关系不大,时钟分频 不等于 预分频器 - 外部时钟输入,输入引脚后接一个滤波器,滤波器可以过滤信号的抖动干扰 - 在一个固定的时钟频率f下进行采样,如果连续N个采样点都为相同的频率,就代表输入信号稳定,就把这个采样值输出出去 - 如果n个采样点采样值不尽相同,说明信号有抖动,就保持上一次的输出,或者直接输出其他默认的电平 - f和N为滤波器的参数,频率f越低???,采样点数越多,滤波效果越好,但是会导致信号延迟越大- 这个频率f可以由内部时钟而来,也可以是内部时钟分频而来,即TIM_ClockDivision确定-  TIM_CKD_DIV1不分频,x2为2分频,x4为4分频*/TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;		TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;  // 重复计数器的值,与高级定时器相关 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);/*预分频器有影子寄存器(缓冲寄存器),写入的值只有在触发更新事件时,才会真正起作用在定时计数溢出之后,伴随产生更新事件,更新事件触发后,会将写入预装载的值加载到对应的影子寄存器中自动重装寄存器->对应的影子寄存器;预分频器->对应的影子寄存器针对ARR的ARPE位用于控制ARR自动重载寄存器是否具有缓冲作用,如果具有缓冲作用,只有当等到更新事件触发后,才会写入影子寄存器并生效如果不具有缓冲作用,那么立即写入影子寄存器并生效*/ /*从源码可知,为了防止初始化完成立即进入中断,使得中断标志位置1,这里手动清理标志位,避免进入中断*/TIM_ClearFlag(TIM2,TIM_FLAG_Update);	// 配置中断输出控制,使能中断,开启更新中断(而不是更新事件)TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);  // 中断到NVIC的通路// 配置NVIC,打开定时器中断的通道,分配优先级NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;		// 中断输出通道NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;	// 抢占优先级(在抢占中断中)NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;		// 响应优先级 (在响应中断中)NVIC_Init(&NVIC_InitStructure);// 启动定时器TIM_Cmd(TIM2,ENABLE);
}void TIM2_IRQHandler() // 更新中断服务
{if (TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) // 判断中断标志位{// 清楚中断标志位TIM_ClearITPendingBit(TIM2,TIM_IT_Update);timer2_num++;}
}

通用定时器 - 外部时钟定时中断

main.c

#include "stm32f10x.h"
#include "timer.h"extern uint16_t timer2_num;int main()
{OLED_Init();OLED_ShowString(1,1,"helloworld");Timer_Init();while (1) {OLED_ShowNum(2,1,timer2_num,5);OLED_ShowNum(3,1,Timer_GetCounter(),5);}
}

timer.h

#ifndef __TIMER_H__
#define __TIMER_H__
#include "stm32f10x.h"
void Timer_Init();
uint16_t Timer_GetCounter();
#endif

timer.c

#include "timer.h"uint16_t timer2_num;
void Timer_Init()
{// RCC时钟 系统基准时钟,和外设工作时钟// 通用定时器TIM2是挂载在APB1上的RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);// 时基单元时钟源// TIM_InternalClockConfig(TIM2); // 选择内部时钟// 不需要外部触发预分频器// 外部时钟触发的极性: 反向:低电平或下降沿有效;不反向:高电平或上升沿有效// 外部时钟触发滤波器:0x00~0xFF ,这个值决定外部计数的滤波器的频率f和采样点n,见手册有对应的关系; 这里暂时不用滤波器TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00); // 外部时钟模式2// 外设时钟,需要使用GPIO// 初始化APB2外设时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 配置GPIO - ODR/CLRGPIO_InitTypeDef GPIO_InitStructure;			// 配置结构体GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; 	// GPIO_Mode_IPU 上拉输入,因为是外部时钟方波输入,所以是输入模式	GPIO_InitStructure.GPIO_Pin= GPIO_Pin_0;		// GPIOA的PA0号引脚GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; // 50MHZ GPIO_Init(GPIOA,&GPIO_InitStructure); 			// A0推挽输出// 配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_Prescaler=1-1;					// PSC预分频器的值TIM_TimeBaseInitStructure.TIM_Period=10-1;						// ARR自动重装器的值 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;  	// 计数模式,向上计数    TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;		// 关系不大,时钟分频    TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;  // 重复计数器的值,与高级定时器相关 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);/*预分频器有影子寄存器(缓冲寄存器),写入的值只有在触发更新事件时,才会真正起作用在定时计数溢出之后,伴随产生更新事件,更新事件触发后,会将写入预装载的值加载到对应的影子寄存器中自动重装寄存器->对应的影子寄存器;预分频器->对应的影子寄存器针对ARR的ARPE位用于控制ARR自动重载寄存器是否具有缓冲作用,如果具有缓冲作用,只有当等到更新事件触发后,才会写入影子寄存器并生效如果不具有缓冲作用,那么立即写入影子寄存器并生效*/ /*从源码可知,为了防止初始化完成立即进入中断,使得中断标志位置1,这里手动清理标志位,避免进入中断*/TIM_ClearFlag(TIM2,TIM_FLAG_Update);// 配置中断输出控制,使能中断TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);  // 中断到NVIC的通路// 配置NVIC,打开定时器中断的通道,分配优先级NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;		// 中断输出通道NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;	// 抢占优先级(在抢占中断中)NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;		// 响应优先级 (在响应中断中)NVIC_Init(&NVIC_InitStructure);// 启动定时器TIM_Cmd(TIM2,ENABLE);
}uint16_t Timer_GetCounter()
{return TIM_GetCounter(TIM2); // 中断后自动归零
}void TIM2_IRQHandler()
{if (TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) // 判断中断标志位{// 清楚中断标志位TIM_ClearITPendingBit(TIM2,TIM_IT_Update);timer2_num++;}
}

定时器 - 输出捕获

输出比较可以通过比较CNT和CCR寄存器值的关系,来对输出电平进行置1、0或翻转的操作用于输出一定频率和占空比的PWM波形
每个高级定时器和通用定时器都拥有4个输出比较通道
CCR是给定的值

PWM

https://www.bilibili.com/video/BV1Mb411e7re?p=33&vd_source=7155082256127a432d5ed516a6423e20

注意:输出PWM不需要中断申请

脉冲宽度调制
使用PWM波形实现模拟信号的输出

频率越快,等效模拟的信号越平稳,但是性能开销越大
占空比:T_on/T_s:高电平/整个周期的时间比例
占空比决定了PWM等效出的模拟电压的大小
占空比越大,等效的模拟电压越趋近与高电平,反之亦然
分辨率:占空比变化步距
在这里插入图片描述

输出比较通道 - 通用定时器

oc1ref是一个参考信号电平
在这里插入图片描述
在这里插入图片描述

注意:输出PWM不需要中断申请

在这里插入图片描述

PWM频率:等于计数器的更新频率
在这里插入图片描述

例子 - 呼吸灯

main.c

#include "stm32f10x.h"
#include "Delay.h"
#include "LED.h"
#include "key.h"
#include "Buzzer.h"
#include "PhotoSensor.h"
#include "OLED.h"
#include "infrCountSensor.h"
#include "encoder.h"
#include "timer.h"
#include "PWM.h"static uint16_t pwmCompare1Num=0;
int main()
{OLED_Init();OLED_ShowString(1,1,"helloworld");PWM_Init();while (1) {for (uint16_t i=0;i<=100;i++){PWM_SetCompare1(i);Delay_ms(10);}for (uint16_t i=0;i<=100;i++){PWM_SetCompare1(100-i);Delay_ms(10);}}
} 

PWM.h

#ifndef __PWM_H__
#define __PWM_H__
#include "stm32f10x.h"                  // Device header
void PWM_Init();
void PWM_SetCompare1(uint16_t compare);
#endif

PWM.c

#include "PWM.h"void PWM_Init()
{// RCC时钟 系统基准时钟,和外设工作时钟// 通用定时器TIM2是挂载在APB1上的RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);// 时基单元时钟源TIM_InternalClockConfig(TIM2); // 选择内部时钟// 外设时钟,需要使用GPIO// 初始化APB2外设时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 配置GPIO - ODR/CLR// 注意针脚GPIO复用和重映射的问题,见手册针脚定义GPIO_InitTypeDef GPIO_InitStructure;			// 配置结构体/*对于普通的开漏和推挽输出,引脚的控制权是来自输出数据寄存器的如果想让定时器控制引脚,就需要使用复用开漏/推挽输出的模式,使得输出输出寄存器断开,输出控制权转移到片上外设 这里是TIM2_CH1通道 */GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; 	// 	GPIO_Mode_AF_PP 复用推挽输出GPIO_InitStructure.GPIO_Pin= GPIO_Pin_0;		// GPIOA的PA0号引脚GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; // 50MHZ GPIO_Init(GPIOA,&GPIO_InitStructure); 			// A0推挽输出// 配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_Prescaler=100-1;					// PSC预分频器的值 // 72MHZ/((100-1)+1)=720KHZTIM_TimeBaseInitStructure.TIM_Period=720-1;						// ARR自动重装器的值  // 720*(1/720KHZ)=1/1KHZ=1msTIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;  	// 计数模式,向上计数    TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;		// 关系不大,时钟分频    TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;  // 重复计数器的值,与高级定时器相关 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);// 无需配置中断相关内容// 无需 配置中断输出控制,使能中断// 无需 配置NVIC,打开定时器中断的通道,分配优先级// 配置输出比较单元// TIM_OCxInit output compare 输出比较模块// 配置好之后,就可以在TIM_OC1通道上输出PWM波形,然后借用GPIO口进行输出 ,TIM2_CH1_ETR引脚和PA0引脚复用,见引脚定义手册TIM_OCInitTypeDef TIM_OCInitStruct;TIM_OCStructInit(&TIM_OCInitStruct); 						// 默认初始值TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;    			// 设置输出比较的模式,一共8个模式  TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;   	// 设置输出使能,输出控制器TIM_OCInitStruct.TIM_Pulse=0;      							// 设置CCR   0~0xFFFF,这里频率1kHZ,占空比50%,1%的分辨率 TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;    	// 设置输出比较的极性,这里REF有效电平为高电平(这里应该不是对应REF的极性选择器)TIM_OC1Init(TIM2,&TIM_OCInitStruct);// 启动定时器TIM_Cmd(TIM2,ENABLE);  
}void PWM_SetCompare1(uint16_t compare)
{TIM_SetCompare1(TIM2,compare);
}

例子 - 呼吸灯-端口重映射

PWM.c

PA0重映射到PA15

#include "PWM.h"void PWM_Init()
{// RCC时钟 系统基准时钟,和外设工作时钟// 通用定时器TIM2是挂载在APB1上的RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);// 时基单元时钟源TIM_InternalClockConfig(TIM2); // 选择内部时钟// 外设时钟,需要使用GPIO// 初始化APB2外设时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 配置AFIO 实现引脚重映射RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE); 	// 见手册,部分重映射GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); // 恢复PA15的正常GPIO端口功能,因为// 配置GPIO - ODR/CLR// 注意针脚GPIO复用和重映射的问题,见手册针脚定义GPIO_InitTypeDef GPIO_InitStructure;			// 配置结构体/*对于普通的开漏和推挽输出,引脚的控制权是来自输出数据寄存器的如果想让定时器控制引脚,就需要使用复用开漏/推挽输出的模式,使得输出输出寄存器断开,输出控制权转移到片上外设 这里是TIM2_CH1通道 */GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; 	// 	GPIO_Mode_AF_PP 复用推挽输出GPIO_InitStructure.GPIO_Pin= GPIO_Pin_15;		// GPIOA的PA15号引脚,重映射之后需要使用PA15的端口GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; // 50MHZ GPIO_Init(GPIOA,&GPIO_InitStructure); 			// A0推挽输出// 配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_Prescaler=720-1;					// PSC预分频器的值 // 72MHZ/((720-1)+1)=100KHZTIM_TimeBaseInitStructure.TIM_Period=100-1;						// ARR自动重装器的值  // 100*(1/100KHZ)=1/1KHZ=1msTIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;  	// 计数模式,向上计数    TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;		// 关系不大,时钟分频    TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;  // 重复计数器的值,与高级定时器相关 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);// 无需配置中断相关内容// 无需 配置中断输出控制,使能中断// 无需 配置NVIC,打开定时器中断的通道,分配优先级// 配置输出比较单元// TIM_OCxInit output compare 输出比较模块// 配置好之后,就可以在TIM_OC1通道上输出PWM波形,然后借用GPIO口进行输出 ,TIM2_CH1_ETR引脚和PA0引脚复用,见引脚定义手册TIM_OCInitTypeDef TIM_OCInitStruct;TIM_OCStructInit(&TIM_OCInitStruct); 						// 默认初始值TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;    			// 设置输出比较的模式,一共8个模式  TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;   	// 设置输出使能,输出控制器TIM_OCInitStruct.TIM_Pulse=0;      							// 设置CCR   0~0xFFFF,这里频率1kHZ,占空比50%,1%的分辨率 TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;    	// 设置输出比较的极性,这里REF有效电平为高电平(这里应该不是对应REF的极性选择器)TIM_OC1Init(TIM2,&TIM_OCInitStruct);// 启动定时器TIM_Cmd(TIM2,ENABLE);  
}void PWM_SetCompare1(uint16_t compare)
{TIM_SetCompare1(TIM2,compare);
}

例子 - 控制舵机 / 连续舵机有问题???

按键控制舵机角度

main.c

#include "stm32f10x.h"
#include "Delay.h"
#include "LED.h"
#include "key.h"
#include "Buzzer.h"
#include "PhotoSensor.h"
#include "OLED.h"
#include "infrCountSensor.h"
#include "encoder.h"
#include "timer.h"
#include "PWM.h"
#include "servo.h"static uint16_t pwmCompare1Num=0;
uint8_t keyNum;
float ServoAngle;
int main()
{OLED_Init();OLED_ShowString(1,1,"helloworld");Servo_Init();Key_Init();while (1) {	uint8_t keyNum=key_getNum();if (keyNum==12){ServoAngle+=30;}if (ServoAngle>180){ServoAngle=0;}Servo_SetAngle(ServoAngle);}
} 

servo.h

#ifndef __SERVO_H__
#define __SERVO_H__
#include "PWM.h"
void Servo_Init();
void Servo_SetAngle(float angle);
#endif

servo.c

#include "servo.h"void Servo_Init()
{PWM_Init();
}void Servo_SetAngle(float angle)
{/*0->500;180->2500;*/uint16_t angleScaled=(float)(angle/180)*2000+500;PWM_SetCompare2(angleScaled);
}

PWM.h

#ifndef __PWM_H__
#define __PWM_H__
#include "stm32f10x.h"                  // Device header
void PWM_Init();
void PWM_SetCompare1(uint16_t compare);
void PWM_SetCompare2(uint16_t compare);
#endif

PWM.c

使用通道2

#include "PWM.h"void PWM_Init()
{// RCC时钟 系统基准时钟,和外设工作时钟// 通用定时器TIM2是挂载在APB1上的RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);// 时基单元时钟源TIM_InternalClockConfig(TIM2); // 选择内部时钟// 外设时钟,需要使用GPIO// 初始化APB2外设时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 配置GPIO - ODR/CLR// 注意针脚GPIO复用和重映射的问题,见手册针脚定义GPIO_InitTypeDef GPIO_InitStructure;			// 配置结构体/*对于普通的开漏和推挽输出,引脚的控制权是来自输出数据寄存器的如果想让定时器控制引脚,就需要使用复用开漏/推挽输出的模式,使得输出输出寄存器断开,输出控制权转移到片上外设 这里是TIM2_CH2通道 */GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; 	// 	GPIO_Mode_AF_PP 复用推挽输出GPIO_InitStructure.GPIO_Pin= GPIO_Pin_1;		// GPIOA的PA1号引脚GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; // 50MHZ GPIO_Init(GPIOA,&GPIO_InitStructure); 			// A0推挽输出// 配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_Prescaler=72-1;					// PSC预分频器的值 // 72MHZ/((72-1)+1)=1000KHZ=1MHZTIM_TimeBaseInitStructure.TIM_Period=20000-1;					// ARR自动重装器的值  // 20k*(1/1MHZ)=2/100HZ=20ms,舵机的周期要求20msTIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;  	// 计数模式,向上计数    TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;		// 关系不大,时钟分频    TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;  // 重复计数器的值,与高级定时器相关 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);// 无需配置中断相关内容// 无需 配置中断输出控制,使能中断// 无需 配置NVIC,打开定时器中断的通道,分配优先级// 配置输出比较单元// TIM_OCxInit output compare 输出比较模块// 配置好之后,就可以在TIM_OC2通道上输出PWM波形,然后借用GPIO口进行输出 ,TIM2_CH2引脚和PA1引脚复用,见引脚定义手册TIM_OCInitTypeDef TIM_OCInitStruct;TIM_OCStructInit(&TIM_OCInitStruct); 						// 默认初始值TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;    			// 设置输出比较的模式,一共8个模式  TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;   	// 设置输出使能,输出控制器TIM_OCInitStruct.TIM_Pulse=0;      							// 设置CCR   0~0xFFFF,这里频率1kHZ,占空比50%,1%的分辨率 TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;    	// 设置输出比较的极性,这里REF有效电平为高电平(这里应该不是对应REF的极性选择器)TIM_OC2Init(TIM2,&TIM_OCInitStruct);/* 不同通道共用一个时基单元,所以频率必须是一样的但是占空比可以由各自的CCR决定各个通道,相位是同步的*/// TIM_OC1Init(TIM2,&TIM_OCInitStruct); 同时初始化,可以同时输出两个PWM波形// 启动定时器TIM_Cmd(TIM2,ENABLE);  
}void PWM_SetCompare1(uint16_t compare)
{TIM_SetCompare1(TIM2,compare);
}void PWM_SetCompare2(uint16_t compare)
{TIM_SetCompare2(TIM2,compare);
}

key.c

#include "stm32f10x.h"
#include "key.h"
#include "Delay.h"void Key_Init()
{// 初始化APB2外设时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);// 配置GPIO - ODR/CLRGPIO_InitTypeDef GPIO_InitStructure;			// 配置结构体GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; 	// GPIO_Mode_IPU 上拉输入 - 根据原理图(内部 上拉输入配置)GPIO_InitStructure.GPIO_Pin= GPIO_Pin_12 | GPIO_Pin_13;			// GPIOB的12,13号引脚GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; // 50MHZ 仅与输入有关GPIO_Init(GPIOB,&GPIO_InitStructure); 			// B0推挽输出
}uint8_t key_getNum()
{uint8_t keyNum=0;/** GPIO_ReadInputDataBit 读取输入数据寄存器某一端口(引脚)的输入值,高低电平* GPIO_ReadInputData   读取整个输入数据寄存器* GPIO_ReadOutputDataBit 读取输出数据寄存器某一个位,输出模式下,用来看一下自己输出的是什么* GPIO_ReadOutputData 读取整个输出数据寄存器*/ // 输入数据if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)==0){Delay_ms(20);while (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)==0);Delay_ms(20);keyNum=12;}if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)==0){Delay_ms(20);while (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)==0);Delay_ms(20);keyNum=13;}return keyNum;
}

输入捕获

https://www.bilibili.com/video/BV1zg411n7HX/?spm_id_from=333.337.search-card.all.click&vd_source=7155082256127a432d5ed516a6423e20
https://www.bilibili.com/video/BV17W4y1u7cN?p=11&vd_source=7155082256127a432d5ed516a6423e20 - 正点原子

注意:输入捕获和输出比较功能,同时只能使用一个
输入捕获模式下,当通道输入引脚出现指定电平跳变时(上升沿或下降沿),当前CNT的值被锁存在CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
通用和高级定时器都拥有4个输入捕获通道
可配置PWMI模式,同时测量频率和占空比

输入捕获基本电路

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

主从触发模式

在这里插入图片描述

输入捕获基本结构/PWMI输入捕获结构

在这里插入图片描述
在这里插入图片描述

例子 - 输入捕获频率

main.c

#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "timer.h"
#include "PWM.h"
#include "IC.h"int main()
{OLED_Init();OLED_ShowString(1,1,"helloworld");OLED_ShowString(2,1,"Freq");PWM_Init();IC_Init();PWM_SetPrescaler(7200-1); 	// Freq=72M/(PSC+1)/(ARR+1) ARR=100-1PWM_SetCompare1(50); 		// Duty=CCR/(ARR+1) ARR=100-1	while (1) {	OLED_ShowNum(2,6,IC_GetReq(),6);}
} 

PWM.h

#ifndef __PWM_H__
#define __PWM_H__
#include "stm32f10x.h"                  // Device header
void PWM_Init();
void PWM_SetCompare1(uint16_t compare);
void PWM_SetCompare2(uint16_t compare);
void PWM_SetCompare3(uint16_t compare);
void PWM_SetPrescaler(uint16_t prescaler); // 仅通过调节PSC来调节频率
#endif

PWM.c

#include "PWM.h"void PWM_Init()
{// RCC时钟 系统基准时钟,和外设工作时钟// 通用定时器TIM2是挂载在APB1上的RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);// 时基单元时钟源TIM_InternalClockConfig(TIM2); // 选择内部时钟// 外设时钟,需要使用GPIO// 初始化APB2外设时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 配置GPIO - ODR/CLR// 注意针脚GPIO复用和重映射的问题,见手册针脚定义GPIO_InitTypeDef GPIO_InitStructure;			// 配置结构体/*对于普通的开漏和推挽输出,引脚的控制权是来自输出数据寄存器的如果想让定时器控制引脚,就需要使用复用开漏/推挽输出的模式,使得输出输出寄存器断开,输出控制权转移到片上外设 这里是TIM2_CH3通道 */GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; 	// 	GPIO_Mode_AF_PP 复用推挽输出GPIO_InitStructure.GPIO_Pin= GPIO_Pin_0;		// GPIOA的PA0号引脚GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; // 50MHZ GPIO_Init(GPIOA,&GPIO_InitStructure); 			// A0推挽输出// 配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_Prescaler=720 -1;					// PSC预分频器的值 // 72MHZ/((72-1)+1)=1000KHZ=1MHZTIM_TimeBaseInitStructure.TIM_Period=100-1;						// ARR自动重装器的值  // 20k*(1/1MHZ)=2/100HZ=20ms,舵机的周期要求20msTIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;  	// 计数模式,向上计数    TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;		// 关系不大,时钟分频    TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;  			// 重复计数器的值,与高级定时器相关 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);// 无需配置中断相关内容// 无需 配置中断输出控制,使能中断// 无需 配置NVIC,打开定时器中断的通道,分配优先级// 配置输出比较单元// TIM_OCxInit output compare 输出比较模块// 配置好之后,就可以在TIM_OC2通道上输出PWM波形,然后借用GPIO口进行输出 ,TIM2_CH2引脚和PA1引脚复用,见引脚定义手册TIM_OCInitTypeDef TIM_OCInitStruct;TIM_OCStructInit(&TIM_OCInitStruct); 						// 默认初始值TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;    			// 设置输出比较的模式,一共8个模式  TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;   	// 设置输出使能,输出控制器TIM_OCInitStruct.TIM_Pulse=0;      							// 设置CCR   0~0xFFFF,这里频率1kHZ,占空比50%,1%的分辨率 TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;    	// 设置输出比较的极性,这里REF有效电平为高电平(这里应该不是对应REF的极性选择器)TIM_OC1Init(TIM2,&TIM_OCInitStruct);/* 不同通道共用一个时基单元,所以频率必须是一样的但是占空比可以由各自的CCR决定各个通道,相位是同步的*/// TIM_OC2Init(TIM2,&TIM_OCInitStruct); 同时初始化,可以同时输出两个PWM波形// 启动定时器TIM_Cmd(TIM2,ENABLE);  
}void PWM_SetCompare1(uint16_t compare)
{TIM_SetCompare1(TIM2,compare);
}void PWM_SetCompare2(uint16_t compare)
{TIM_SetCompare2(TIM2,compare);
}void PWM_SetCompare3(uint16_t compare)
{TIM_SetCompare3(TIM2,compare);
}void PWM_SetPrescaler(uint16_t prescaler)  // 仅通过调节PSC来调节频率
{TIM_PrescalerConfig(TIM2,prescaler, TIM_PSCReloadMode_Update);
}

IC.h

#ifndef __IC_H__
#define __IC_H__
#include "PWM.h"
#include "stm32f10x.h"void IC_Init();
uint32_t IC_GetReq();#endif

IC.c

#include "IC.h"void IC_Init()
{// 配置GPIO,及其时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;GPIO_Init(GPIOA,&GPIO_InitStruct);  // 配置TIM,及其时钟,RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); // TIM2负责输出PWM,因此不能同时在使用输入捕获功能// 这里使用TIM3// 配置时基单元,内部时钟TIM_InternalClockConfig(TIM3);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;TIM_TimeBaseInitStruct.TIM_Prescaler=72-1; // PSC,使得标准频率为1MHZTIM_TimeBaseInitStruct.TIM_Period=65536-1; // ARRTIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);// 配置输入捕获单元: 滤波器、边沿检测、极性选择、直连/交叉通道 分频器TIM_ICInitTypeDef TIM_ICInitStruct;TIM_ICInitStruct.TIM_Channel=TIM_Channel_1; TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising; 		// 极性选择,选择上升沿触发 TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;  // 触发信号从哪个引脚输入,可以选择直连通道或交叉通道TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1; 			// 分频器,不分频就是每次触发都有效,2分频就是每隔一次有效一次TIM_ICInitStruct.TIM_ICFilter=0xF;					 		// 设置滤波器,滤除高频噪声,不会影响原信号的频率TIM_ICInit(TIM3,&TIM_ICInitStruct);// 配置从模式,及其触发源// 设置从模式,输入触发源// 有信号触发之后,在从模式下CNT就会自动清零 TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1); // 选择触发源,一共8个触发源// 设置从模式执行的操作TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset); // 设置从模式要执行的动作// 开启定时器TIM_Cmd(TIM3,ENABLE);
}uint32_t IC_GetReq()
{return 1*1000*1000/TIM_GetCapture1(TIM3); // xHZ
}

例子 - 输入捕获频率/占空比

main.c

#include "stm32f10x.h"
#include "OLED.h"
#include "timer.h"
#include "PWM.h"
#include "IC.h"int main()
{OLED_Init();OLED_ShowString(1,1,"helloworld");OLED_ShowString(2,1,"Freq: ");OLED_ShowString(3,1,"Duty: ");PWM_Init();IC_Init();PWM_SetPrescaler(7200-1); 	// Freq=72M/(PSC+1)/(ARR+1) ARR=100-1PWM_SetCompare1(80); 		// Duty=CCR/(ARR+1) ARR=100-1	while (1) {	OLED_ShowNum(2,6,IC_GetReq(),6);OLED_ShowNum(3,6,IC_GetDuty(),2);}
} 

PWM.c/PWM.h 见上个例子 没改动

IC.h

#ifndef __IC_H__
#define __IC_H__
#include "PWM.h"
#include "stm32f10x.h"void IC_Init();
uint32_t IC_GetReq();
uint32_t IC_GetDuty();
#endif

IC.c

#include "IC.h"void IC_Init()
{// 配置GPIO,及其时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;GPIO_Init(GPIOA,&GPIO_InitStruct);  // 配置TIM,及其时钟,RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); // TIM2负责输出PWM,因此不能同时在使用输入捕获功能// 这里使用TIM3// 配置时基单元,内部时钟TIM_InternalClockConfig(TIM3);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;TIM_TimeBaseInitStruct.TIM_Prescaler=72-1; // PSC,使得标准频率为1MHZTIM_TimeBaseInitStruct.TIM_Period=65536-1; // ARRTIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);// 配置输入捕获单元: 滤波器、边沿检测、极性选择、直连/交叉通道 分频器TIM_ICInitTypeDef TIM_ICInitStruct;TIM_ICInitStruct.TIM_Channel=TIM_Channel_1; TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising; 		// 极性选择,选择上升沿触发 TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;  // 触发信号从哪个引脚输入,可以选择直连通道或交叉通道TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1; 			// 分频器,不分频就是每次触发都有效,2分频就是每隔一次有效一次TIM_ICInitStruct.TIM_ICFilter=0xF;					 		// 设置滤波器,滤除高频噪声,不会影响原信号的频率TIM_ICInit(TIM3,&TIM_ICInitStruct);// version 1 重新配置一遍// 配置第二个通道 检测占空比
//	TIM_ICInitStruct.TIM_Channel=TIM_Channel_2; 
//	TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Falling; 		// 极性选择,选择下将沿触发 
//	TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_IndirectTI;  // 触发信号从哪个引脚输入,可以选择直连通道或交叉通道
//	TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1; 			// 分频器,不分频就是每次触发都有效,2分频就是每隔一次有效一次
//	TIM_ICInitStruct.TIM_ICFilter=0xF;					 		// 设置滤波器,滤除高频噪声,不会影响原信号的频率
//	TIM_ICInit(TIM3,&TIM_ICInitStruct);// version 2 调用库函数 配置第二个通道TIM_PWMIConfig(TIM3,&TIM_ICInitStruct);// 配置从模式,及其触发源// 设置从模式,输入触发源// 有信号触发之后,在从模式下CNT就会自动清零 TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1); // 选择触发源,一共8个触发源// 设置从模式执行的操作TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset); // 设置从模式要执行的动作// 开启定时器TIM_Cmd(TIM3,ENABLE);
}uint32_t IC_GetReq()
{return 1*1000*1000/TIM_GetCapture1(TIM3); // xHZ
}uint32_t IC_GetDuty()
{return TIM_GetCapture2(TIM3)*100/TIM_GetCapture1(TIM3);
}

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

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

相关文章

OpenCV4(C++) —— 图像数据类型转换和颜色模型转换

文章目录 一、图像数据类型转换二、颜色模型转换三、通道的分离和融合 一、图像数据类型转换 OpenCV中使用imread读取一张彩色图像时&#xff0c;默认采用的是BGR通道和整数类型(0-255&#xff0c;CV_8U)。 在某些情况下&#xff0c;会将整数类型(0-255)转换为浮点类型(0-1)&a…

Windows下Tensorflow docker python开发环境搭建

前置条件 windows10 更新到较新的版本&#xff0c;硬件支持Hyper-V。 参考&#xff1a;https://learn.microsoft.com/zh-cn/windows/wsl/install 启用WSL 在Powershell中输入如下指令&#xff1a; dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsys…

【LeetCode热题100】--543.二叉树的直径

543.二叉树的直径 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 首先我们知道一条路径的长度为该路径经过的节…

地下水数值模拟软件如何选择?GMS、Visual MODFLOW Flex、FEFLOW、MODFLOW

强调模块化教学&#xff0c;分为前期数据收集与处理&#xff1b;三维地质结构建模&#xff1b;地下水流动模型构建&#xff1b;地下水溶质运移模型构建和反应性溶质运移构建5个模块&#xff1b;采用全流程模式将地下水数值模拟软件GMS的操作进行详细剖析和案例联系。不仅使学员…

【消费战略方法论】品效烙印营销的策略模型

品效烙印营销策略模型 营销&#xff0c;分为品牌营销和效果营销。品牌营销的主要目的是提升品牌声量、抢占用户心智、打造品牌知名度、积累品牌资产。效果营销的主要目的是瞬间获得流量&#xff0c;并且迅速转化成销量。 品效合一&#xff1a;品牌营销 效果营销。品牌营销和效…

px4的gazebo仿真相机模型报错解决办法,返回值256

&#x1f449;事情起因&#xff1a;我想做关于PX4无人机的摄像头仿真&#xff0c;根据PX4的官网文件 Tools/sitl_gazebo文件夹里面有对应的模型可以使用&#xff0c;我就想在mavros_posix_sitl文件里面修改vehicle参数&#xff0c;比如直接将vehicle“iris_stereo_camera”。然…

【Java 进阶篇】使用 JDBCTemplate 执行 DML 语句详解

JDBCTemplate 是 Spring 框架中的一个核心模块&#xff0c;用于简化 JDBC 编程&#xff0c;使数据库操作更加便捷和高效。在本文中&#xff0c;我们将重点介绍如何使用 JDBCTemplate 执行 DML&#xff08;Data Manipulation Language&#xff09;语句&#xff0c;包括插入、更新…

2019款保时捷卡宴车发动机故障灯异常点亮

故障现象 一辆2019款保时捷卡宴车&#xff0c;搭载DCB发动机&#xff0c;累计行驶里程约为9万km。车主反映&#xff0c;该车行驶中发动机故障灯偶尔异常点亮&#xff08;图1&#xff09;&#xff0c;其他无异常&#xff0c;为此在其他维修厂更换过燃油箱通风电磁阀、活性炭罐及…

微软AD身份增强方案,让IT运维省心更高效

Windows AD域为企业数字化办公提供了强有力的支撑&#xff0c;但由于互联网技术的飞速发展&#xff0c;AD域在现代企业办公场景中也面临了一些挑战。 某企业使用AD域控管理工具&#xff0c;在对接邮箱、电脑、网络时均会用到AD域账号。出于安全考虑&#xff0c;公司要求每三个月…

深圳市重点实验室申报条件-华夏泰科

深圳市重点实验室是一个致力于科学研究和技术创新的重要机构。作为中国科技创新的重要一环&#xff0c;深圳市重点实验室在多个领域展开前沿研究&#xff0c;并为科学家、工程师和创新者提供了宝贵的资源和支持。、在接下来的内容中&#xff0c;华夏泰科将为您说明深圳市重点实…

口袋参谋:如何提升宝贝流量?这三种方法超实用!

​你的店铺能不能出爆款&#xff1f;提升单品流量是关键。 对于新手卖家来说&#xff0c;是缺乏运营技巧和运营经验的&#xff0c;运营技巧主要体现在标题写作、各种图片和视频制作等。 由于新手买家没有经验&#xff0c;习惯于直接使用数据包上传&#xff0c;导致宝贝没有展…

深入探究 C++ 编程中的资源泄漏问题

目录 1、GDI对象泄漏 1.1、何为GDI资源泄漏&#xff1f; 1.2、使用GDIView工具排查GDI对象泄漏 1.3、有时可能需要结合其他方法去排查 1.4、如何保证没有GDI对象泄漏&#xff1f; 2、进程句柄泄漏 2.1、何为进程句柄泄漏&#xff1f; 2.2、创建线程时的线程句柄泄漏 …

微信CRM系统:微商不可或缺的客户管理利器!

在这个竞争激烈的时代&#xff0c;微信客户已成为微商生存和发展的关键。如何更好地了解客户需求&#xff0c;提高客户满意度和忠诚度&#xff0c;已成为微商们亟待解决的问题。而解决这些问题&#xff0c;关键在于个微是否有一套完善的客户关系管理&#xff08;CRM&#xff09…

【面试】C/C++面试八股

C/C面试八股 编译过程的四个阶段C和C语言的区别简单介绍一下三大特性多态的实现原理虚函数的构成原理虚函数的调用原理虚表指针在什么地方进行初始化的&#xff1f;构造函数为什么不能是虚函数为什么建议将析构函数设为虚函数虚函数和纯虚函数的区别抽象类类对象的对象模型内存…

【Vue面试题一】、说说你对 Vue 的理解

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;有使用过vue吗&#xff…

小谈设计模式(22)—单例模式

小谈设计模式&#xff08;22&#xff09;—单例模式 专栏介绍专栏地址专栏介绍 单例模式点睛所在优缺点分析优点确保只有一个实例全局访问点节省资源线程安全 缺点难以扩展对象的生命周期单一职责原则隐藏依赖关系 Java程序实例实例a分析实例b&#xff0c;更安全分析优化 ——“…

多目标优化两种算法:加权、智能优化算法

传统数学优化算法&#xff08;加权&#xff09; 使用数学优化算法解决多目标优化问题通常是将各个子目标聚合成一个带权重的单目标函数&#xff0c;系数由决策者决定&#xff0c;或者由优化方法自适应调整。即通过加权等方式将多目标问题转化为单目标问题进行求解。 这样每次只…

开源联合、聚力共赢丨2023 CCF中国开源大会会议通知(第二轮)

会议简介 2023 CCF中国开源大会&#xff08;CCF ChinaOSC&#xff09;拟于2023年10月21日至22日在湖南省长沙市北辰国际会议中心召开。大会由中国计算机学会&#xff08;CCF&#xff09;与开放原子开源基金会主办&#xff0c;CCF开源发展委员会、湖南先进技术研究院承办&#…

VR模拟鸡胚培养接种实验,打造沉浸式的学习环境

在医学教育领域&#xff0c;传统的鸡胚接种实验一直是教学的重要组成部分。然而&#xff0c;这种实验方法存在一定的局限性&#xff0c;如操作难度大、成本高、安全隐患等。为了解决这些问题&#xff0c;越来越多的教育机构开始尝试引入虚拟现实(VR)技术&#xff0c;以模拟鸡胚…

2023腾讯云轻量应用服务器和普通服务器有什么区别?

腾讯云轻量服务器和云服务器有什么区别&#xff1f;为什么轻量应用服务器价格便宜&#xff1f;是因为轻量服务器CPU内存性能比云服务器CVM性能差吗&#xff1f;轻量应用服务器适合中小企业或个人开发者搭建企业官网、博客论坛、微信小程序或开发测试环境&#xff0c;云服务器CV…