1.定时器输出比较功能的介绍
输出比较原理
在stm32中定时器通过设定一个阈值,通过计数器与阈值的比较判断,分为大于阈值和小于阈值,来分别输出高电平和低电平。
PWM参数的计算
定时器的输出比较功能是设计用来输出PWM波形,这个PWM波是一种高低电平交替的方波,使用这个波的好处是可以通过设定占空比即高电平在一个周期中的比值来控制一定的电压输出,来调节比如led灯亮度等。
那么在这里介绍一下PWM波形的基本参数:
频率:定时器的频率(主频/((预分频值+1)*(自动重装载值+1))
占空比:CCR/(ARR+1)*100%(比较值/(自动重装载值+1))
分辨率(最小变化步长):1/(ARR+1)(1/(自动重装载值)+1)
STM32F103芯片中主频默认72MHZ,预分频值和自动重装载就是定时器的基本配置参数,可以通过函数配置,上一节的定时器也有基本介绍,比较值也是可以手动函数配置的,接下来就来介绍一下程序配置。
2.程序编写
【1】驱动程序编写
这里我用定时器的TIM2的输出通道来举例,定时器2的通道1对应GPIOA的PIN0引脚也就是PA0,注意不同定时器的输出IO口可能不同,要参照引脚定义表去选择。那么这里我要用GPIOA第一步肯定是初始化GPIO的配置,注意这里的GPIO要用复用推挽模式,因为它不是程序控制的GPIO输出,而是由外设定时器来控制输出的;接着还要使用定时器的计数器,那么肯定还要初始化定时器的时基单元,这两个配置在前几节我有介绍。第三步就是这节的核心输出比较部分,这部分也是用一个函数就可以配置好了。
第一部分GPIO的配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //想打开哪个定时器的哪个通道要看引脚功能对应表,并且使用PWM模式时,要将对应引脚设置为复用推挽模式
GPIO_InitTypeDef InitStucture;
InitStucture.GPIO_Mode=GPIO_Mode_AF_PP;
InitStucture.GPIO_Pin=GPIO_Pin_0;
InitStucture.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&InitStucture);
第二部分定时器模块的配置
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //打开定时器总线的时钟
TIM_InternalClockConfig(TIM2); //定时器的时钟源选择内部时钟源72MHz
TIM_TimeBaseInitTypeDef TIMInitStructure; //定时器时基单元的配置
TIMInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //1分频也就是不分频,采样频率选择一分频即不分频,定义了定时器时钟频率与数字滤波器 使用的采样频率之间的分频比例。数字滤波器用于滤除输入信号的高频干扰,确保输入信号的稳定性。
TIMInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数
TIMInitStructure.TIM_Period=100 - 1; //ARR //自动重装载值
TIMInitStructure.TIM_Prescaler=720 - 1; //PSC //预分频
TIMInitStructure.TIM_RepetitionCounter=0; //重复计数器,只有高级定时器才有,举例说明,这个参数假如设置为5,那么当第一个周期定时器达到后,并不会产生更新事件,而是要重复5次,才会产生更新事件
TIM_TimeBaseInit(TIM2,&TIMInitStructure);
当然这一部分还有一行代码就是打开定时器的操作TIM_Cmd(TIM2,ENABLE);这一步要放在最后,我们配置好输出比较模块后打开,不然环境还没配置好,定时器就开始计数了。
第三部分输出比较模块
TIM_OCInitTypeDef OCInitStructure;
OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1; //PWM模式
OCInitStructure.TIM_OCPolarity=TIM_OCNPolarity_High; //极性的选择,一般选择TIM_OCNPolarity_High,不翻转极性,如果你正在配置一个 PWM 输出,并且希望在 PWM 信号的低电平期间输出引脚为低,高电平期间互补引脚为高,则可以使用 TIM_OCNPolarity_High
OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; //选择使能
OCInitStructure.TIM_Pulse=0; //CCR比较值
TIM_OC1Init(TIM2,&OCInitStructure);
这里先介绍一下这个模块的参数:
参数一:TIM_OCMode这个参数指的是输出比较的模式,它有六种模式,这里我们这节主要目的是输出PWM波,就选择参数TIM_OCMode_PWM1
或者TIM_OCMode_PWM2
模式就行。
-
TIM_OCMode_PWM1
:PWM模式1。在向上计数时,当定时器的计数器值小于捕获/比较寄存器值时,输出引脚为高电平;当计数器值大于捕获/比较寄存器值时,输出引脚为低电平。在向下计数时,行为相反。 -
TIM_OCMode_PWM2
:PWM模式2。与PWM模式1类似,但在向上计数时,当定时器的计数器值大于捕获/比较寄存器值时,输出引脚为高电平;当计数器值小于捕获/比较寄存器值时,输出引脚为低电平。在向下计数时,行为相反。
那么再简单介绍一下另外四个模式:
TIM_OCMode_Toggle:这个模式会让引脚在计数器计数到等于比较值时,将引脚的电平状态翻转,也就是如果之前是高电平,当计数器计数到等于比较值时,引脚会被翻转为低电平。
TIM_OCMode_Active:这个模式就是在计数器计数到等于比较值时,直接将引脚的电平状态置为高电平
TIM_OCMode_Inactive:这个模式就是在计数器计数到等于比较值时,直接将引脚的电平状态置为低电平
TIM_OCMode_Timing:这个模式下,输出引脚将不会再改变状态,也就是此时输出引脚的状态会锁住,一直保持原电平状态。
参数二:极性的选择,TIM_OCNPolarity_High高极性,也就是在PWM波为低电平时,输出引脚也是低电平,不会改变,如果为TIM_OCNPolarity_Low低极性,在PWM波为低电平时,输出引脚也是高电平,会进行一个翻转。
参数三:输出使能TIM_OutputState,可选择TIM_OutputState_Enable使能开启,也可选择失能TIM_OutputState_Disable关闭。
参数四:TIM_Pulse这个就是那个用来与计数器比较的值,比较值。
最后打开定时器
TIM_Cmd(TIM2,ENABLE); //打开这个TIM2的计数器
最终驱动代码展示:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //打开定时器总线的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //想打开哪个定时器的哪个通道要看引脚功能对应表,并且使用PWM模式时,要将对应引脚设置为复用推挽模式
GPIO_InitTypeDef InitStucture;
InitStucture.GPIO_Mode=GPIO_Mode_AF_PP;
InitStucture.GPIO_Pin=GPIO_Pin_0;
InitStucture.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&InitStucture);
TIM_InternalClockConfig(TIM2); //定时器的时钟源选择内部时钟源72MHz
TIM_TimeBaseInitTypeDef TIMInitStructure; //定时器时基单元的配置
TIMInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //1分频也就是不分频,采样频率选择一分频即不分频,定义了定时器时钟频率与数字滤波器 使用的采样频率之间的分频比例。数字滤波器用于滤除输入信号的高频干扰,确保输入信号的稳定性。
TIMInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数
TIMInitStructure.TIM_Period=100 - 1; //ARR //自动重装载值
TIMInitStructure.TIM_Prescaler=720 - 1; //PSC //预分频
TIMInitStructure.TIM_RepetitionCounter=0; //重复计数器,只有高级定时器才有,举例说明,这个参数假如设置为5,那么当第一个周期定时器达到后,并不会产生更新事件,而是要重复5次,才会产生更新事件
TIM_TimeBaseInit(TIM2,&TIMInitStructure);
TIM_OCInitTypeDef OCInitStructure;
OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1; //PWM模式
OCInitStructure.TIM_OCPolarity=TIM_OCNPolarity_High; //极性的选择,一般选择TIM_OCNPolarity_High,不翻转极性,如果你正在配置一个 PWM 输出,并且希望在 PWM 信号的低电平期间输出引脚为低,高电平期间互补引脚为高,则可以使用 TIM_OCNPolarity_High
OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; //选择使能
OCInitStructure.TIM_Pulse=0; //CCR比较值
TIM_OC1Init(TIM2,&OCInitStructure);
TIM_Cmd(TIM2,ENABLE); //打开这个TIM2的计数器
【2】相关函数介绍
1.TIM_SetCompare1(TIM2,compare);这个函数是用来设置比较值的,这个可以用来设置占空比
2.TIM_PrescalerConfig(TIM2,PSC,TIM_PSCReloadMode_Immediate);这个是用来设置定时器的预分频值的
3.TIM_SetAutoreload(TIM2,ARR);这个函数是用来设置定时器的自动重装载值的
上面我们介绍占空比的公式CCR/(ARR+1)*100%(比较值/(自动重装载值+1))
编写以下函数,设置占空比,并且输出占空比
int PWMDuty(uint16_t CCR,ARR)
{
TIM_SetAutoreload(TIM2,ARR);
TIM_SetCompare1(TIM2,CCR);
return (CCR*100/(ARR+1))+1;
}
这里拿示波器测量时,有1点的误差,所以后面又加1了
这里乘以100只是为让它从0.02之类的小数变成2方便显示屏显示和计算值之类的,如果不乘100,可以把函数类型int改成float就可以了
float PWMDuty(uint16_t CCR,ARR)
{
TIM_SetAutoreload(TIM2,ARR);
TIM_SetCompare1(TIM2,CCR);
return (CCR/((ARR+1)*1.0))+0.01;
}
再封装一个调节PWM频率的函数
频率:定时器的频率(主频/((预分频值+1)*(自动重装载值+1))
解释一下这个公式,当时钟的信号进来时,也就是主频信号进来,要先经历一个分频器,将主频信号进行分频,也就是主频信号除以分频的值,得到的信号,才是驱动定时器工作的信号,计数器计数一次的时间就是1/(主频/预分频值),计数自动重装值这么多次,就是时间乘以自动重装值这么多次,那么得到了计数器计满一次的时间,取个倒数就是频率。
int PWMFrequence((uint16_t PSC,uint16_t ARR)
{
TIM_PrescalerConfig(TIM2,PSC,TIM_PSCReloadMode_Immediate);
TIM_SetAutoreload(TIM2,ARR);
return (72000/((PSC+1)*(ARR+1)))+1;
}
这里拿示波器测量时,有1点的误差,所以后面又加1了
注意这里72000000太大了,所有改成72000,那么这个函数输出频率的单位就是Khz
这里注意一下,你使用这两个函数的时候,要保证这两个函数的ARR也就是自动重装载的值相同,要不然你上面刚设定了一个ARR,下面又用下面函数的一个就会重新改变ARR为下面那个函数的ARR。