前言
最近有个需求是关于调节占空比去控制风扇实现三挡风力大小的。由于硬件供电和控制成本等原因,普通的芯片支撑不起几个风扇同时转起来,于是就沿用了一个神奇的电路方案,但是这个方案在输出占空比的时候达不到真正的占空比(这里解释不清),因为电机类需要特别注意频率的大小,频率太高或者太低或多或少都会让人耳接受不了,反正是需要在运行时同时改变PWM频率和占空比,本文着重于应用,不讲原理。
一、定时器介绍
使用的是野火指南者STM32F103VET6,该板子有8个定时器,其中TIM6和TIM7属于基本定时器,TIM1和TIM8属于高级定时器,本文使用通用定时器TIM3即可。
TIMx | 功能 | |
基本定时器 | TIM6、TIM7 | 基本定时,也可用于触发 DAC 外设。 |
通用定时器 | TIM2、TIM3、TIM4、TIM5 | 输出比较(时序和延迟生成)、单脉冲模式、输入捕获(用于测量 外部信号频率)、传感器接口(编码器和霍尔传感器)等各种场合。 |
高级定时器 | TIM1、TIM8 | 除通用功能外,它们还包含一些与电机控制和数字能量转换应用相关的功能:三个带死区控制的互补信号以及紧急关断输入。 单通道或双通道定时器:用作通用定时器,通道数有限。 带互补输出的单通道或双通道定时器:与上一类型相同,只是其中一个通道上具有死区发生器。这样可得到时基与高级定时器无关的互补信号。 |
二、PWM的周期、频率、占空比计算
在stm32中PWM的周期、频率、占空比等计算基本上是使用了TIM的时基单元,下面是时基结构体的介绍。
typedef struct
{
uint16_t TIM_Prescaler //驱动CNT计数器的分频器1-65536,都有
uint16_t TIM_CounterMode //计数器计数模式,TIMx,x[6,7]没有,其他都有
uint16_t TIM_Period //自动重装载寄存器,都有
uint16_t TIM_ClockDivision //时钟分频因子,TIMx,x[6,7]没有,其他都有
uint8_t TIM_RepetitionCounter //重复计数器的值,TIMx,x[1,8]才有
}TIM_TimeBaseInitTypeDef;
注意:TIM_TimeBaseInitTypeDef结构体里面有5个成员,TIM6和TIM7的寄存器里面只有
TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可,
另外三个成员是通用定时器和高级定时器才有。
所以,本文使用该板子上TIM3定时器的通道CH1。
引脚 | TIMx | 通道 |
PA6 | TIM3 | CH1 |
输出比较结构体介绍:
typedef struct
{
uint16_t TIM_OCMode; //PWM模式
uint16_t TIM_OutputState; //输出使能
uint16_t TIM_OutputNState; //指定TIM互补输出比较状态TIMx,x[1,8]才有
uint16_t TIM_Pulse; //要加载到捕获比较寄存器中的脉冲值CCR
uint16_t TIM_OCPolarity; //输出通道电平极性配置
uint16_t TIM_OCNPolarity; //指定互补输出极性TIMx,x[1,8]才有
uint16_t TIM_OCIdleState; //指定空闲状态时的TIM输出比较引脚状态TIMx,x[1,8]才有
uint16_t TIM_OCNIdleState; //指定空闲状态时的TIM输出比较引脚状态TIMx,x[1,8]才有
} TIM_OCInitTypeDef;
/* ---------------- PWM信号 周期和占空比的计算--------------- */
ARR :自动重装载寄存器的值
PSC:分频系数
CCR:高电平脉冲值比例,用于计算占空比
CLK_Fre:PWM时钟频率,CLK_Fre = Fre_int / (psc+1)/ARR = 72M/(psc+1)/ARR
PWM 信号的周期 T = 1/CLK_Fre = ARR*(PSC+1) / 72M
占空比P=CCR/(ARR+1)
注意:CCR并不都是高电平占比,需要根据TIM_OCInitStructure.TIM_OCPolarity输出的极性进行判断。
三:代码步骤
1、初始化TIM3_CH1的GPIO
void GENERAL_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 输出比较通道1 GPIO 初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
2、配置时基结构体和输出比较结构体
void GENERAL_TIM_Mode_Config(void)
{
// 开启定时器时钟,即内部时钟CK_INT=72M
GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);
/*--------------------时基结构体初始化-------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period=(100-1); // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Prescaler= (72-1); // 驱动CNT计数器的分频器1-65536
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 时钟分频因子 ,配置死区时间时需要用到
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; // 计数器计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_RepetitionCounter=0; // 重复计数器的值,没用到不用管
TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);// 初始化定时器
/*--------------------输出比较结构体初始化-------------------*/
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;// 配置为PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;// 输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;// 输出通道电平极性配置
// 输出比较通道 1
TIM_OCInitStructure.TIM_Pulse = 0; //CCR,间接调节占空比的重要参数
TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
// 使能计数器
TIM_Cmd(GENERAL_TIM, ENABLE);
}
3、编写改变占空比的接口
void PWM_SetPrescaler(uint16_t Prescaler)//改变预分频系数
{
TIM_PrescalerConfig(TIM3, Prescaler,TIM_PSCReloadMode_Update);
}
void PWM_SetCompare1(uint16_t Compare) //改变CCR、高电平的比例
{
TIM_SetCompare1(TIM3, Compare);
}
void PWM_SetARR1(uint32_t Autoreload) //改变自动重装载值
{
TIM_Cmd(TIM3,DISABLE);
TIM_ARRPreloadConfig(TIM3,DISABLE);
TIM_SetAutoreload(TIM3, Autoreload);
TIM_ARRPreloadConfig(TIM3,ENABLE);
TIM_Cmd(TIM3,ENABLE);
}
注意:根据手册,改变ARR需要先失能TIM_ARR配置,但是我在实测的时候没发现有什么不同,不过为了避免出现问题,还是把该有的流程走一遍,预分频器PSC就不用每次都要失能一下。
4、主函数
int main(void)
{
GENERAL_TIM_GPIO_Config();
GENERAL_TIM_Mode_Config();
Key_GPIO_Config();
LED_GPIO_Config();
while(1)
{
if(Key_Scan(GPIOA ,GPIO_Pin_0) ==1)
{
LED1_TOGGLE;
Speed += 1;
if (Speed > 2)Speed = 0;
if(Speed ==0)
{
PWM_SetCompare1(0);
}
else if(Speed ==1){
// PWM_SetARR1(200-1);
PWM_SetCompare1(30);
// PWM_SetPrescaler(36-1);
}
else if(Speed ==2){
// PWM_SetARR1(100-1);
PWM_SetCompare1(50);
// PWM_SetPrescaler(36-1);
}
}
}
}
四:实验现象
1、单独改变占空比
根据下面示波器抓到的波形,可以看到,单独改变占空比只需要改变CCR的值就好(但是要注意的是CCR只是间接影响到占空比),不会影响到频率和周期。
(1)PWM_SetCompare1(30);
频率=72000000/(99+1)/(71+1)=10k
周期=1/频率=0.0001s=100us
占空比=30/100=30%
(2)PWM_SetCompare1(50);
占空比=50/100=50%
2、单独改变自动重装载值
PWM_SetARR1(200-1);
频率=72000000/(199+1)/(71+1)=5k
周期=1/频率=0.0002s=200us
占空比=30/200=15%
3、单独改变预分频系数
PWM_SetPrescaler(36-1);
频率=72000000/(99+1)/(35+1)=20k
周期=1/频率=0.00049s=49us
占空比=30/100=30%
总结
该实验是为了学习多种方式改变PWM频率占空比,实验过程只是一个很简单的例子,也可以换到更高主频的MCU试验。然后,如有错误的地方被看到,一切以手册为准。