【TIM/PWM】PWMI模式测频率占空比

用TIM函数配置PWM输出比较模块的函数
TIM_OCxInit();//OC就是Output Compare输出比较
TIM_OCStructInit();//初始化输出比较单元,给输出比较结构体赋一个默认值
TIM_SetCompare1();//单独更改CCR寄存器值的函数,在运行的时候更改占空比
(下面是一些小功能和运行时更改参数的函数)
TIM_ForcedOCxConfig();//配置强制输出模式,在运行中需要暂停输出波形并且强制输出高或低电平
(没啥用,因为强制输出高电平==设置100%占空比,强制输出低电平==设置0%占空比)
TIM_OCxPreloadConfig();//配置CCR寄存器的预装功能(影子寄存器——写入值不会立即生效,而是在更新事件后再生效)
TIM_CtrlPWMOutputs();//仅高级定时器使用,其输出PWM时,需要调用这个函数使能主输出,否则PWM不能正常输出

初始化输入捕获时从左至右参考该图流程
代码逻辑:初始化TIM2的通道1产生一个PWM波形,输出引脚为PA0,通过SetCompare函数在运行过程中调节CCR的值,从而控制PWM的占空比
//为了在运行过程中也能调整PWM的频率(通过调节PSC),需要再加一个函数来实现
  • PWM 频率:  Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM周期对应计数器的一个溢出更新周期——PWM频率等于计数器的更新频率
  • PWM 占空比:  Duty = CCR / (ARR + 1)
  • PWM 分辨率:  Reso = 1 / (ARR + 1)
通过【PWM频率】公式可知PSC和ARR都可以调节频率,但当更改ARR时涉及占空比变化,只通过PSC调节频率不会影响占空比
//此处通过【PSC】来调节【PWM频率】,且固定【ARR】为【100-1】,CCR的值直接就可以体现占空比

1、完成驱动文件导入操作和编写驱动程序基本代码(参考之前文章)
//将【PWM】配置文件放在【Hardware】文件夹里
2、在PWM.c中初始化函数 PWM _Init
void PWM_Init(void)
{
//第一步:开启时钟(注意使用APB1的开启时钟函数,因为TIM2是APB1总线的外设)
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    
    
//第二步:选择时基单元的时钟(在stm32f10x_tim.h文件中查找函数)
    TIM_InternalClockConfig(TIM2);//这里选择为内部时钟
//时基单元就由内部时钟驱动了
    
    
//第三步:配置时基单元(在stm32f10x_tim.h文件中查找函数)
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//指定时钟分频
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//指定计数器模式(此处选择向上计数)
    TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//指定要在下一次更新事件时加载到ARR自动重新加载寄存器中的周期值  ARR取值[0,65535]
    TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;//指定用于划分TIM时钟的预分频器值    PSC取值[0,65535]
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//指定重复计数器的值(高级定时器才用得上,本项目给0)
    TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化时基单元
//第四步:初始化输出比较单元(通道)【引脚定义表】
//使用PA0口对应第一个输出比较通道
    TIM_OCInitTypeDef TIM_OCInitStructure;
    
//为了避免出现不确定因素,PWM需要先把结构体成员完整配置一遍默认初始值,再根据需要更改个别地方
    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 = 0;//设置CCR的值
    TIM_OC1Init(TIM2,&TIM_OCInitStructure);
//到此通道初始化完成,可以在TIM2的OC1通道上输出PWM波形
//波形需要借用GPIO口进行输出
//TIM2的OC1的通道借用了PA0【引脚定义表】
//第五步:初始化GPIO口
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
//不需要打开中断和配置NVIC
    
//第六步:启动定时器(在stm32f10x_tim.h文件中查找函数)
    TIM_Cmd(TIM2,ENABLE);
    
//至此PWM波形就能通过PA0输出了
}
3、在PWM.c中编写更改CCR函数 PWM_SetCompare1
//在运行过程中更改CCR值(改变占空比)CCR在初始化时为0
void PWM_SetCompare1(uint16_t Compare)//单独更改通道1的CCR值
{
    TIM_SetCompare1(TIM2,Compare);
}
//TIM_SetCompare函数设置的是CCR的值,不代表直接设置占空比,占空比是由【CCR】和【ARR+1】共同决定的
//在PWM.c中编写更改PSC函数PWM_SetPrescaler
void PWM_SetPrescaler(uint16_t Prescaler)
{
    TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Immediate);//单独写入PSC的函数
}
//第三个参数(此处要求不高,选择立即重装)
*     @arg TIM_PSCReloadMode_Update: The Prescaler is loaded at the update event.
//预分频器在更新事件后重装(会有一个缓存器,延迟参数的写入时间,等一个周期结束更新事件时,再统一改变参数)
*     @arg TIM_PSCReloadMode_Immediate: The Prescaler is loaded immediately.
//预分频器立即重装,可能会在值改变时产生切断波形的现象(在频率变化时会出现一个不完整的周期)
4、在PWM.h中声明初始化函数 PWM _In it更改CCR 函数 PWM_SetCompare1更改PSC函数 PWM_SetPrescaler
void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);
void PWM_SetPrescaler(uint16_t Prescaler);
5、在主程序main.c中 #includ "PWM .h "
#include "PWM.h"
6、在主循环之前先初始化PWM
7、在主循环中编写程序主体
int main(void)
{
    
    OLED_Init();
    PWM_Init();
    
    //Freq = [72M/(PSC+1)]/(ARR+1)
    //Duty = CCR/(ARR+1)
    
    PWM_SetPrescaler(720-1);//设置PSC
    PWM_SetCompare1(50);//设置CCR值,本例固定ARR为[100-1]
    //此时PA0输出频率1KHz,占空比为50%的待测信号
    while(1)
    {
    
    }
    
}
实现功能:PA0输出频率1KHz,占空比为50%的PWM信号(可在Keil虚拟示波器中看到)

用TIM函数配置输入捕获模块的函数
TIM_ICInit();//用结构体配置输入捕获单元的函数
另外:OC输出比较和IC输入捕获都有4个通道,OCInit()每个通道单独占一个函数,而ICInit()4个通道共用一个函数,在函数内部选择通道
TIM_PWMIConfig();//也是用 构体配置输入捕获单元的函数,但可一次配置两个通道
TIM_ICStructInit();//给输入捕获结构体赋一个初始值
TIM_SelectInputTrigger();//选择输入触发源TRGI
TIM_SelectOutputTrigger();//选择输出触发源TRGO
TIM_SelectSlaveMode();//选择从模式
TIM_SetICxPrescaler();//分别单独配置通道1、2、3、4的分频器
TIM_GetCapturex();//分别读取4个通道的CCR
// TIM_SetComparex();//单独更改CCR寄存器值的函数,在运行的时候更改占空比
//不同之处在于:OC输出比较模式下CCR是只写的,要用 SetComparex写入
                           IC输入捕获模式下CCR是只读的,要用 GetCapturex读出
输入捕获完整部分
初始化PWMI输入捕获对照PPT基本结构配置
1、建立【输入捕获模块IC】完成驱动文件导入操作和编写驱动程序基本代码
//将【IC】配置文件放在【Hardware】文件夹里
2、在IC.c中初始化函数 IC _Init
void IC_Init(void)
{
//第一步:开启TIM和GPIO的时钟(注意使用APB1的开启时钟函数,因为TIM3是APB1总线的外设)
//【TIM2】用于产生PWM波形,输入捕获要用别的通道,此处选择【TIM3】
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//第二步:初始化GPIO口,把GPIO配置成输入模式,一般选择【上拉输入】或者【浮空输入】
//【引脚定义表】TIM3的通道1和通道2分别对应PA6和PA7,通道3和通道4分别对应PB0和PB1
//本例选择用TIM3的通道1进行输入捕获,故初始化PA6
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    
    
//第三步:选择时基单元的时钟(在stm32f10x_tim.h文件中查找函数)
    TIM_InternalClockConfig(TIM3);//这里选择为内部时钟,选择TIM3
//时基单元就由内部时钟驱动了
    
    
//第四步:配置时基单元(在stm32f10x_tim.h文件中查找函数)
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//指定时钟分频
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//指定计数器模式(此处选择向上计数)
    TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;//指定要在下一次更新事件时加载到ARR自动重新加载寄存器中的周期值  ARR取值[0,65535]
    TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//指定用于划分TIM时钟的预分频器值    PSC取值[0,65535]
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//指定重复计数器的值(高级定时器才用得上,本项目给0)
    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//将以上参数配置到TIM3的时基单元
//此处ARR设置为最大,因为ARR(重装计数值)越大,输入捕获测量的频率越精准
//PSC的值决定了测周法的标准频率fc=[72MHz/(PSC+1)]
//此处给PSC=72-1,则fc=72MHz/72=1MHz
//此处较单通道的输入捕获需要升级成双通道的PWMI模式,配置成两个通道同时捕获一个引脚的模式
//第五步:配置输入捕获单元(滤波器、极性选择、直连/交叉通道、分频器)
    TIM_ICInitTypeDef TIM_ICInitStructure;
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//指定配置的TIM通道
    TIM_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;//选择触发信号从哪个引脚输入(此处选择直连通达),可以选择直连/交叉通道(配置TIxFPx后的【数据选择器】)
    TIM_ICInit(TIM3,&TIM_ICInitStructure);//初始化输入捕获单元
//第六步:选择从模式的触发源(这里选择TI1FP1)
    TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
//第七步:选择触发之后执行的从模式操作(本例配置为Reset)
    TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
    
//第八步:启动定时器(在stm32f10x_tim.h文件中查找函数)
    TIM_Cmd(TIM3,ENABLE);
//配置完成
//当需要读取最新一个周期的频率时,直接读取CCR寄存器,按照【Freq=fc/N】计算得到频率
}
3、在IC.c中编写用于读取CCR以进行频率计算的函数 IC_GetFreq
//读取CCR进行频率计算
//之前在配置时基单元给PSC=72-1,则fc=72MHz/72=1MHz
//取CCR寄存器,按照【Freq=fc/N】计算得到频率
uint32_t IC_GetFreq(void)//本函数返回最新一个周期的频率值(Hz)
{
    return ( 1000000 / TIM_GetCapture1(TIM3) ) - 1;
    //实测多1,减回去
    //读取TIM3通道1的CCR(计数N值)
}
在IC.c中编写用于读取CCR以进行占空比计算的函数IC_GetDuty
//获取占空比的函数
//本例中整个周期的计数值存在CCR1中,高电平计数值存在CCR2中
//计算占空比=CCR2/CCR1
uint32_t IC_GetDuty(void)
{
    return ( TIM_GetCapture2(TIM3) +1 ) * 100 / ( TIM_GetCapture1(TIM3) + 1);
//得到占空比的百分比(实测少1,这里补回去)
//读取TIM3通道1的CCR(计数N值CCR1)
//读取TIM3通道2的CCR(高电平CCR2)
}
4、在IC.h中声明初始化函数 IC _Init
用于读取CCR以进行频率计算的函数 IC_GetFreq
读取CCR以进行占空比计算的函数 IC_GetDuty
void IC_Init(void);
uint32_t IC_GetFreq(void);
uint32_t IC_GetDuty(void);
5、在主程序main.c中 #includ e “IC .h "
#include "IC.h"
6、在主循环之前先初始化IC
7、在主循环中编写程序主体
int main(void)
{
    
    OLED_Init();
    PWM_Init();
    IC_Init();
    
    OLED_ShowString(1,1,"Freq:00000Hz");
    OLED_ShowString(2,1,"Duty:00%");
    //Freq = [72M/(PSC+1)]/(ARR+1)
    //Duty = CCR/(ARR+1)
    
    PWM_SetPrescaler(720-1);
    PWM_SetCompare1(50);
    //此时PWM模块已将待测信号输出到PA0了
    
    while(1)
    {
        OLED_ShowNum(1,6,IC_GetFreq(),5);
        OLED_ShowNum(2,6,IC_GetDuty(),2);
    }
    
}
实现功能:上电后由PA0产生PWM波形,PA6进行输入捕获并在OLED显示 Freq数值和 Duty百分比

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值