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

16.PWM输入捕获示例程序(输入捕获模式测频率PWMI模式测频率和占空比)

     

目录

输入捕获相关库函数

输入捕获模式测频率

PWMI模式测频率和占空比


 两个代码的接线图都一样,如下

        测量信号的输入引脚是PA6,信号从PA6进来,待测的PWM信号也是STM32自己生成的,输出引脚是PA0。

        需要配置电路连接图示如下:

        所以步骤就是:

  • 第一步,RCC开启时钟,把GPIO和TIM的时钟打开
  • 第二步,GPIO初始化,把GPIO配置成输入模式(一般选择上拉输入或浮空输入模式)
  • 第三步,配置时基单元,让CNT计数器在内部时钟的驱动下自增运行,和之前代码一样
  • 第四步,配置输入捕获单元,包括滤波器、极性、直连通道、交叉通道、分频器这些参数,用一个结构体就可以统一进行配置了
  • 第五步,选择从模式的触发源,触发源选择为TI1FP1,这里调用一个库函数给一个参数就行了
  • 第六步,选择触发之后执行的操作,执行Reset操作,这里调用一个库函数就行了
  • 最后,当这些电路都配置好之后,调用TIM_Cmd函数,开启定时器。这样所有的电路就能配合起来了,按照我们的要求工作了。当我们需要读取最新一个周期的频率时,直接读取CCR寄存器,然后按照fc/N,计算一下就行了,这就是整个程序的思路

输入捕获相关库函数

1.1.输入捕获初始化配置

// 输入捕获IC初始化函数
// 输入捕获和输出比较都有四个通道,输出比较每个通道单独占一个函数,输入捕获每个通道共用一个函数(在结构体里会额外有一个参数可以用来选择具体是配置哪个通道,因为可能有交叉通道所以函数合在一起比较方便)
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);// 可以给输入捕获结构体赋一个初始值
void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);// 这个函数和TIM_ICInit类似都是用于初始化输入捕获单元的,但是TIM_ICInit函数只是单一地配置一个通道而TIM_PWMIConfig可以快速配置两个通道的PWMI模式(自动将另一个通道配置为相反的模式)
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);// 单独写入时基单元的PSC,第三个参数与预装载有关
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);// 单独配置四个通道的预分频器,结构体中也可以配置,效果相同
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);// 分别读取四个通道的CCR的值(与输出比较中的TIM_SetComparex函数对应,读写的都是CCR寄存器;输出比较模式下CCR是只写,要用SetCompare写入;输入捕获模式下CCR是只读,要用GetCapture读出)
uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);

1.2.主从触发模式配置

// 选择输入(从模式)触发源TRGI
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);// 选择输出(主模式)触发源TRGO,选择主模式触发的触发源
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);// 选择从模式需要执行的操作
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);

        注意滤波器和分频器的区别:虽然它俩都是计次,但是滤波器计次并不会改变信号的原有频率,一般滤波器的采样频率都会远高于信号频率,所以滤波器只会滤除高频噪声使信号更平滑,1KHz滤波之后仍然是1KHz,信号频率不会变化;而分频器就是对信号本身进行计次,会改变频率,1KHz,2分频之后就是500Hz,4分频就是250Hz。

输入捕获模式测频率

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM_LED.h"
#include "IC.h"int main(void)
{OLED_Init();	//初始化OLEDpwm_init();IC_init();//初始化整个电路 OLED_ShowString(1,1,"Freq:00000Hz");//PA0口输出1khz频率,50%占空比的待测信号;PWM模块将待测信号输出给PA0,PA0然后通过导线输入到PA6(PA6是TIM3的通道1,通道1通过输入捕获模块测量得到频率,然后在主循环里不断刷新显示频率)PWM_setPSC(720-1); //频率=72M/(psc+1)/(arr+1)   //频率=72M/720/100 =1khzPWM_SetCompare1(50);//占空比=ccr/(ARR+1)      //占空比=50/100 = 50%while(1){OLED_ShowNum(1,6,IC_GetFreq(),5);//不断刷新显示频率}
}

IC.c

#include "stm32f10x.h"                  // Device headervoid IC_init(void)
{//1.打开时钟,选择内部时钟//使用APB1的开启时钟函数,TIM3是APB1总线的外设RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//打开时钟,PA6的通道1//2.初始化GPIOGPIO_InitTypeDef GPIO_InitStructure;//结构体变量名GPIO_InitStructureGPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//默认50mhz输出GPIO_Init(GPIOA,&GPIO_InitStructure);//使用的是地址传递//3.1.初始化时基单元//选择时基单元的时钟,选择内部时钟;若不调用这个函数,系统上电也是默认是内部时钟TIM_InternalClockConfig(TIM3);//3.2.配置时基单元参数 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  //指定时钟分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,向上计数/*公式:	PWM频率:Freq = CK_PSC / (PSC + 1) / (ARR + 1)PWM占空比:Duty = CCR / (ARR + 1)PWM分辨率:Reso = 1 / (ARR + 1)           */TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARR周期,最好要设置大一些防止计数溢出,16位的计数器可以满量程计数是65535TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC预分频器,标准频率就是72M/72=1MHz;这个值决定了测周法的标准频率fc,72M/预分频就是计数器自增的频率就是计数标准频率;需要根据信号频率的分步范围来调整TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;  //重复计数器的值TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//4.初始化输入捕获单元TIM_ICInitTypeDef TIM_ICInitStructure;TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//选择通道,使用TIM3的通道1TIM_ICInitStructure.TIM_ICFilter = 0xF;//配置输入捕获的滤波器,数越大,滤波效果越好,每个数值对应的采样频率和采样次数在参考手册里有,若信号有毛刺和噪声就可以增大滤波器参数可以有效避免干扰TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//对应边沿检测、极性选择部分,可以选择上升沿触发/下降沿触发/上升沿和下降沿都触发TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//分频器,触发信号分频器,不分频就是每次触发都有效,2分频就是每隔一次有效一次,以此类推TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//选择触发信号从哪个引脚输入,对应配置数据选择器的。可以选择直连通道/交叉通道/TRC引脚TIM_ICInit(TIM3,&TIM_ICInitStructure);//5.配置触发源选择,配置TRGI的触发源为TI1FP1TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);//触发源选择TI1FP1//6.配置从模式,为ResetTIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);//从模式选择Reset//7.启动定时器,调用TIM_CmdTIM_Cmd(TIM3,ENABLE);//CNT就会在内部时钟的驱动下不断自增,即使没有信号过来,它也会不断自增;有信号来的时候,CNT就会在从模式的作用下自动清零并不会影响测量/*初始化之后,整个电路就能全自动测量了,当我们想查看频率时,需要读取CCR进行计算,所以需要在下面写一个函数*/
}uint32_t IC_GetFreq(void)
{
//使用测周法的公式,fc=72M/(psc+1),目前psc=72-1,所以fc=1MHzreturn 1000000 / (TIM_GetCapture1(TIM3) + 1);	//返回的是最新一个周期的频率值(单位是HZ) = 1MHz(1000000) / N(就是读取CCR的值)
}

PWM.c

#include "stm32f10x.h"                  // Device header/*
pwm初始化函数基本步骤(参考笔记PWM基本结构图)
第一步,RCC开启时钟,把要用的TIM外设和GPIO外设的时钟打开
第二步,配置单元,包括时钟源选择和时基单元都配置好
第三步,配置输出比较单元,包括CCR值、输出比较模式、极性选择、输出使能这些参数,在库函数里也是用结构体统一来配置
第四步,配置GPIO,把PWM对应的GPIO口,初始化为复用推挽输出的配置,Pwm和GPIO的对应关系可以参考引脚定义表
第五步,运行控制,启动计数器,这样就能输出PWM了
*///PWM控制呼吸灯的代码逻辑是初始化TIM2的通道1,产生一个PWM波形,输出引脚是PA0,然后通过PWM_SetCompare1可以调节CCR1寄存器的值从而控制PWM的占空比,PWM的频率是固定写好在初始化程序里了,运行时候调节不太方便
//在最后再加一个函数,用来便捷地调节PWM频率,PSC和ARR都可以调节频率,但是调节ARR会影响占空比,通过PSC调节频率不会影响占空比,所以计划是固定ARR值,通过调节PSC来改变PWM频率
//一般可以根据分辨率的要求先确定ARR,PSC决定频率,CCR决定占空比void pwm_init(void)
{//1.打开时钟,选择内部时钟//使用APB1的开启时钟函数,TIM2是APB1总线的外设RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	//打开时钟//2.初始化时基单元//选择时基单元的时钟,选择内部时钟;若不调用这个函数,系统上电也是默认是内部时钟TIM_InternalClockConfig(TIM2);//3.配置时基单元 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  //指定时钟分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式/*公式:PWM频率:Freq = CK_PSC / (PSC + 1) / (ARR + 1)PWM占空比:Duty = CCR / (ARR + 1)PWM分辨率:Reso = 1 / (ARR + 1)*/TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;  //ARR 周期TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;  //PSC 预分频器TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;  //重复计数器的值TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2,TIM_FLAG_Update);//4.初始化输出比较单元(通道)TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);//给结构体赋初始值;若不想把所有成员都列一遍赋值,就可以先用这个函数赋一个初始值,再更改你想改的值TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//设置输出比较的模式TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能(输出状态)TIM_OCInitStructure.TIM_Pulse = 50;//设置CCR,Pulse直译是脉冲TIM_OC1Init(TIM2, &TIM_OCInitStructure);//使用PA0口对应是第一个输出比较通道;在TIM2的OC1通道上就可以输出PWM波形了//5.初始化GPIOGPIO_InitTypeDef GPIO_InitStructure;		//结构体变量名GPIO_InitStructureGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出;PWM波形通过引脚输出,使用定时器来控制引脚,输出数据寄存器将被断开,输出控制权将转移给片上外设(这里片上外设引脚连接的就是TIM2的CH1通道)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //默认50mhz输出GPIO_Init(GPIOA,&GPIO_InitStructure);		//使用的是地址传递		//6.启动定时器TIM_Cmd(TIM2,ENABLE);//PWM波形就能通过PA0输出了
}//在运行过程更改CCR,使用函数TIM_SetCompare1封装用来单独更改通道1的CCR值,进而改变占空比
void PWM_SetCompare1(uint16_t Compare1)//TIM_SetCompare1封装
{TIM_SetCompare1(TIM2,Compare1);
}//封装此函数,在初始化之后单独修改PSC,进而改变频率
void PWM_setPSC(uint16_t prescaler)
{
//调用库函数里单独写入PSC的函数,在tim.h中找,这个函数还有一个重装模式的参数所以叫TIM_PrescalerConfigTIM_PrescalerConfig(TIM2,prescaler,TIM_PSCReloadMode_Immediate);//写入PSC,第二个参数是写入PSC的值,直接将外层函数的prescaler参数传进去,第三个参数是重装模式(还是影子寄存器、预装载这个问题,就是写入的值是立刻生效还是在更新事件生效;立刻生效可能会在值改变时产生切断波形的现象会出现不完整的周期,更新事件生效就是会有一个缓存器,延迟参数的写入时间,等一个周期结束了,在更新事件时,再统一改变参数,保证每个周期的完整)}

PWMI模式测频率和占空比

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM_LED.h"
#include "IC.h"int main(void)
{OLED_Init();	//初始化OLEDpwm_init();IC_init();//初始化整个电路 OLED_ShowString(1,1,"Freq:00000Hz");OLED_ShowString(2,1,"Duty:00%");//PA0口输出1khz频率,50%占空比的待测信号;PWM模块将待测信号输出给PA0,PA0然后通过导线输入到PA6(PA6是TIM3的通道1,通道1通过输入捕获模块测量得到频率,然后在主循环里不断刷新显示频率)PWM_setPSC(7200-1); //频率=72M/(psc+1)/(arr+1)   //频率=72M/720/100 =1khzPWM_SetCompare1(80);//占空比=ccr/(ARR+1)      //占空比=50/100 = 50%while(1){OLED_ShowNum(1,6,IC_GetFreq(),5);//不断刷新显示频率OLED_ShowNum(2,6,IC_GetDuty(),2);//不断刷新显示占空比}
}

IC.c

#include "stm32f10x.h"                  // Device header//PWMI模式,方法1:修改上一个程序的4.初始化输入捕获单元
//方法2:使用TIM_PWMIConfig函数void IC_init(void)
{//1.打开时钟,选择内部时钟//使用APB1的开启时钟函数,TIM3是APB1总线的外设RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//打开时钟,PA6的通道1//2.初始化GPIOGPIO_InitTypeDef GPIO_InitStructure;//结构体变量名GPIO_InitStructureGPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//默认50mhz输出GPIO_Init(GPIOA,&GPIO_InitStructure);//使用的是地址传递//3.1.初始化时基单元//选择时基单元的时钟,选择内部时钟;若不调用这个函数,系统上电也是默认是内部时钟TIM_InternalClockConfig(TIM3);//3.2.配置时基单元参数 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  //指定时钟分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,向上计数/*公式:	PWM频率:Freq = CK_PSC / (PSC + 1) / (ARR + 1)PWM占空比:Duty = CCR / (ARR + 1)PWM分辨率:Reso = 1 / (ARR + 1)  目前我们给的标准频率时1mhz,计数器最大只能计到65535,所以所测量的最低频率是1m/65535=15hz,如果信号频率再低,计数器就要溢出了所以最低频率就是15hz左右,如果想再降低一些最低频率的限制可以把psc再加大点这样标准频率就更低所支持测量的最低频率也就更低;最大频率没有界限,1MHZ,信号频率接近1mhz时误差已经非常大了,*/TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARR周期,最好要设置大一些防止计数溢出,16位的计数器可以满量程计数是65535TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC预分频器,标准频率就是72M/72=1MHz;这个值决定了测周法的标准频率fc,72M/预分频就是计数器自增的频率就是计数标准频率;需要根据信号频率的分步范围来调整TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;  //重复计数器的值TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//4.初始化输入捕获单元,PWMI模式需配置成两个通道同时捕获同一个引脚的模式(一个简单的想法是:将通道初始化部分复制一份,结构体定义不需复制,通道1是直连模式上升沿触发通道2也延用这个配置)TIM_ICInitTypeDef TIM_ICInitStructure;TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//选择通道,使用TIM3的通道1TIM_ICInitStructure.TIM_ICFilter = 0xF;//配置输入捕获的滤波器,数越大,滤波效果越好,每个数值对应的采样频率和采样次数在参考手册里有,若信号有毛刺和噪声就可以增大滤波器参数可以有效避免干扰TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//对应边沿检测、极性选择部分,可以选择上升沿触发/下降沿触发/上升沿和下降沿都触发TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//分频器,触发信号分频器,不分频就是每次触发都有效,2分频就是每隔一次有效一次,以此类推TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//选择触发信号从哪个引脚输入,对应配置数据选择器的。可以选择直连通道/交叉通道/TRC引脚TIM_ICInit(TIM3,&TIM_ICInitStructure);//方法1,:将通道初始化部分复制一份,结构体定义不需复制
//	TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;//改为通道2
//	TIM_ICInitStructure.TIM_ICFilter = 0xF;
//	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;//改为下降沿触发
//	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
//	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;//交叉输入
//	TIM_ICInit(TIM3,&TIM_ICInitStructure);//方法2:使用TIM_PWMIConfig函数,可快捷地把电路配置成PWMI模式的标准结构,这个函数只支持通道1和2不支持通道3和4,和方法1的效果是一样的,只需传入一个通道的参数就行了,在函数里会自动把剩下的一个通道初始化成相反的配置(比如已经传入了通道1、直连、上升沿,那函数里就会顺带配置通道2、交叉、下降沿)TIM_PWMIConfig(TIM3,&TIM_ICInitStructure);//5.配置触发源选择,配置TRGI的触发源为TI1FP1TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);//触发源选择TI1FP1//6.配置从模式,为ResetTIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);//从模式选择Reset//7.启动定时器,调用TIM_CmdTIM_Cmd(TIM3,ENABLE);//CNT就会在内部时钟的驱动下不断自增,即使没有信号过来,它也会不断自增;有信号来的时候,CNT就会在从模式的作用下自动清零并不会影响测量/*初始化之后,整个电路就能全自动测量了,当我们想查看频率时,需要读取CCR进行计算,所以需要在下面写一个函数*/
}//获取频率的函数
uint32_t IC_GetFreq(void)
{
//使用测周法的公式,fc=72M/(psc+1),目前psc=72-1,所以fc=1MHzreturn 1000000 / (TIM_GetCapture1(TIM3) + 1);	//返回的是最新一个周期的频率值(单位是HZ) = 1MHz(1000000) / N(就是读取CCR的值)
}//获取占空比的函数
uint32_t IC_GetDuty(void)
{
//高电平的计数值存在CCR2里,整个周期的计数值存在CCR1里,用CCR2/CCR1就能得到占空比了return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);//显示整数的话,给它乘100,这样返回值的范围就是0-100,对应占空比0%-100%//经过实测,CCR总会少一个数,所以需要各加一个1补回来}

pwm.c

#include "stm32f10x.h"                  // Device header/*
pwm初始化函数基本步骤(参考笔记PWM基本结构图)
第一步,RCC开启时钟,把要用的TIM外设和GPIO外设的时钟打开
第二步,配置单元,包括时钟源选择和时基单元都配置好
第三步,配置输出比较单元,包括CCR值、输出比较模式、极性选择、输出使能这些参数,在库函数里也是用结构体统一来配置
第四步,配置GPIO,把PWM对应的GPIO口,初始化为复用推挽输出的配置,Pwm和GPIO的对应关系可以参考引脚定义表
第五步,运行控制,启动计数器,这样就能输出PWM了
*///PWM控制呼吸灯的代码逻辑是初始化TIM2的通道1,产生一个PWM波形,输出引脚是PA0,然后通过PWM_SetCompare1可以调节CCR1寄存器的值从而控制PWM的占空比,PWM的频率是固定写好在初始化程序里了,运行时候调节不太方便
//在最后再加一个函数,用来便捷地调节PWM频率,PSC和ARR都可以调节频率,但是调节ARR会影响占空比,通过PSC调节频率不会影响占空比,所以计划是固定ARR值,通过调节PSC来改变PWM频率
//一般可以根据分辨率的要求先确定ARR,PSC决定频率,CCR决定占空比void pwm_init(void)
{//1.打开时钟,选择内部时钟//使用APB1的开启时钟函数,TIM2是APB1总线的外设RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	//打开时钟//2.初始化时基单元//选择时基单元的时钟,选择内部时钟;若不调用这个函数,系统上电也是默认是内部时钟TIM_InternalClockConfig(TIM2);//3.配置时基单元 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  //指定时钟分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式/*公式:PWM频率:Freq = CK_PSC / (PSC + 1) / (ARR + 1)PWM占空比:Duty = CCR / (ARR + 1)PWM分辨率:Reso = 1 / (ARR + 1)*/TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;  //ARR 周期TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;  //PSC 预分频器TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;  //重复计数器的值TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2,TIM_FLAG_Update);//4.初始化输出比较单元(通道)TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);//给结构体赋初始值;若不想把所有成员都列一遍赋值,就可以先用这个函数赋一个初始值,再更改你想改的值TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//设置输出比较的模式TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能(输出状态)TIM_OCInitStructure.TIM_Pulse = 50;//设置CCR,Pulse直译是脉冲TIM_OC1Init(TIM2, &TIM_OCInitStructure);//使用PA0口对应是第一个输出比较通道;在TIM2的OC1通道上就可以输出PWM波形了//5.初始化GPIOGPIO_InitTypeDef GPIO_InitStructure;		//结构体变量名GPIO_InitStructureGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出;PWM波形通过引脚输出,使用定时器来控制引脚,输出数据寄存器将被断开,输出控制权将转移给片上外设(这里片上外设引脚连接的就是TIM2的CH1通道)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //默认50mhz输出GPIO_Init(GPIOA,&GPIO_InitStructure);		//使用的是地址传递		//6.启动定时器TIM_Cmd(TIM2,ENABLE);//PWM波形就能通过PA0输出了
}//在运行过程更改CCR,使用函数TIM_SetCompare1封装用来单独更改通道1的CCR值,进而改变占空比
void PWM_SetCompare1(uint16_t Compare1)//TIM_SetCompare1封装
{TIM_SetCompare1(TIM2,Compare1);
}//封装此函数,在初始化之后单独修改PSC,进而改变频率
void PWM_setPSC(uint16_t prescaler)
{
//调用库函数里单独写入PSC的函数,在tim.h中找,这个函数还有一个重装模式的参数所以叫TIM_PrescalerConfigTIM_PrescalerConfig(TIM2,prescaler,TIM_PSCReloadMode_Immediate);//写入PSC,第二个参数是写入PSC的值,直接将外层函数的prescaler参数传进去,第三个参数是重装模式(还是影子寄存器、预装载这个问题,就是写入的值是立刻生效还是在更新事件生效;立刻生效可能会在值改变时产生切断波形的现象会出现不完整的周期,更新事件生效就是会有一个缓存器,延迟参数的写入时间,等一个周期结束了,在更新事件时,再统一改变参数,保证每个周期的完整)}

相关文章:

16.PWM输入捕获示例程序(输入捕获模式测频率PWMI模式测频率和占空比)

目录 输入捕获相关库函数 输入捕获模式测频率 PWMI模式测频率和占空比 两个代码的接线图都一样,如下 测量信号的输入引脚是PA6,信号从PA6进来,待测的PWM信号也是STM32自己生成的,输出引脚是PA0。 需要配置电路连接图示如下&…...

pip version 更新

最近报了一个错: 解决办法: 在cmd输入“conda install pip” conda install pip 完了之后再输入: python -m pip install --upgrade pip ok....

Oracle - 多区间按权重取值逻辑

啰嗦: 其实很早就遇到过类似问题,也设想过,不过一致没实际业务需求,也就耽搁了;最近有业务提到了,和同事讨论,各有想法,所以先把逻辑整理出来,希望有更好更优的解决方案;…...

本次CTF·泰山杯网络安全的基础知识部分(二)

简记23年九月参加的泰山杯网络安全的部分基础知识的题目,随时补充 15(多选)网络安全管理工作必须坚持“谁主管、谁负责,谁运营、谁负责,谁使用、谁负责”的原则,和“属地管理”的原则 谁主管、谁负责&…...

MyBatis 映射文件(Mapper XML):配置与使用

MyBatis 映射文件(Mapper XML):配置与使用 MyBatis是一个强大的Java持久化框架,它允许您将SQL查询、插入、更新和删除等操作与Java方法进行映射。这种映射是通过MyBatis的映射文件,通常称为Mapper XML文件来实现的。本…...

基于 SpringBoot 的大学生租房网站

文章目录 1 简介2 技术栈3 需求分析4 系统设计5 系统详细设计5.1系统功能模块5.2管理员模块5.3房主功能模块5.4用户功能模块 源码咨询 1 简介 本大学生租房系统使用简洁的框架结构,专门用于用户浏览首页,房屋信息,房屋评价,公告资…...

BL808学习日志-0-概念理解

一、主核心的介绍 1.三个核心在FREERTOS系统中相互独立,各负责各自的外设和程序;其中M0和LP核心在一个总线上,D0单独在一个总线上,两个总线使用AXI4.0(??)通讯? CPU0(M0)-E907架构,320MHz; CPU1(LP)-E9…...

CISSP学习笔记:业务连续性计划

第三章 业务连续性计划 3.1 业务连续性计划 业务连续性计划(BCP): 对组织各种过程的风险评估,发生风险的情况下为了使风险对组织的影响降至最小而定制的各种计划BCP和DRP首先考虑的人不受伤害,然后再解决IT恢复和还原问题BCP的主要步骤: 项…...

.NET Nuget包推荐安装

文章目录 前言通用WPFWebApiBlazor 前言 我这里的包主要是.NET Core的,.NET Framework可能不支持。 通用 Newtonsoft.Json:最常用的C#和Json对象互转的包。支持匿名对象,但是不支持Enum枚举类型,显示的是Enum的数值&#xff0c…...

【文献阅读】Pocket2Mol : 基于3D蛋白质口袋的高效分子采样 + CrossDocked数据集说明

Pocket2Mol: Efficient Molecular Sampling Based on 3D Protein Pockets code: GitHub - pengxingang/Pocket2Mol: Pocket2Mol: Efficient Molecular Sampling Based on 3D Protein Pockets 所用数据集 与“A 3D Generative Model for Structure-Based Drug Desi…...

TrustRadius 评论:为什么 Splashtop 优于 LogMeIn

在当今日益数字化的格局中,远程访问和远程支持工具不仅方便而且至关重要。无论对于居家办公人员,还是对于提供远程支持的 IT 专家,能够安全高效地访问远程系统已成为以技术为导向的日常生活的主要内容。 Splashtop 和 LogMeIn 是远程领域的两…...

【动态规划】动态规划经典例题 力扣牛客

文章目录 跳台阶 BM63 简单跳台阶扩展 JZ71 简单打家结舍 LC198 中等打家劫舍2 LC213中等最长连续递增序列 LC674 简单乘积最大子数组LC152 中等最长递增子序列LC300 中等最长重复子数组LC718最长公共子串NC BM66最长公共子序列LC1143 中等完全平方数LC279零钱兑换 LC322 中等单…...

统计模型----决策树

决策树 (1)决策树是一种基本分类与回归方法。它的关键在于如何构建这样一棵树。决策树的建立过程中,使用基尼系数来评估节点的纯度和划分的效果。基尼系数是用来度量一个数据集的不确定性的指标,其数值越小表示数据集的纯度越高。…...

C# List 复制之深浅拷贝

C# List 复制 之深浅拷贝 声明类 public class TestStu{public int Number{get;set; }public string Name{get;set; }}public static async Task<int> Main(string[] args){var stu1 new TestStu(){Number 1,Name "1"};var stu2 new TestStu(){Numbe…...

论<script> 标签可以直接写在 HTML 文件中的哪些位置?(可以将 <script> 标签直接插入到 HTML 文件的任何位置)

可以将 <script> 标签直接插入到 HTML 文件的任何位置&#xff0c;以在相应位置执行 JavaScript 代码。 以下是几个示例&#xff1a; 1.<head> 元素内部&#xff1a;在 <head> 元素内部放置 <script> 标签时&#xff0c;脚本将在页面加载过程中被下载和…...

【MySQL进阶】--- 存储引擎的介绍

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【MySQL学习专栏】&#x1f388; 本专栏旨在分享学习MySQL的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 目录 一、什么…...

self-XSS漏洞SRC挖掘

本文由掌控安全学院 - 一朵花花酱 投稿 Markdown是一种轻量级标记语言&#xff0c;创始人为约翰格鲁伯&#xff08;John Gruber&#xff09;。它允许人们使用易读易写的纯文本格式编写文档&#xff0c;然后转换成有效的 XHTML&#xff08;或者HTML&#xff09;文档。这种语言吸…...

1859. 将句子排序

目录 一、题目 二、代码 一、题目 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 二、代码 定义了一个vector<vector<string>> v(MAX);采用const string& word : v[k] word 就会依次取得 v[k] 中的每个元素&#xff08;v[k][0],…...

普通学校,普通背景,普通公司,不普通总结。

作者&#xff1a;阿秀 InterviewGuide大厂面试真题网站&#xff1a;https://top.interviewguide.cn 这是阿秀的第「313」篇原创 小伙伴们大家好&#xff0c;我是阿秀。 可能很多人点开牛客、知乎、B站&#xff0c;一看帖子的标题都是"某985xxxx"、"不入流211xxx…...

Flink之Watermark生成策略

在Flink1.12以后,watermark默认是按固定频率周期性的产生. 在Flink1.12版本以前是有两种生成策略的: AssignerWithPeriodicWatermarks周期性生成watermarkAssignerWithPunctuatedWatermarks[已过时] 按照指定标记性事件生成watermark 新版本API内置的watermark策略 单调递增的…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

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>…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…...

给网站添加live2d看板娘

给网站添加live2d看板娘 参考文献&#xff1a; stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下&#xff0c;文章也主…...