学习如何让STM32在运行时改变PWM频率和占空比

前言

        最近有个需求是关于调节占空比去控制风扇实现三挡风力大小的。由于硬件供电和控制成本等原因,普通的芯片支撑不起几个风扇同时转起来,于是就沿用了一个神奇的电路方案,但是这个方案在输出占空比的时候达不到真正的占空比(这里解释不清),因为电机类需要特别注意频率的大小,频率太高或者太低或多或少都会让人耳接受不了,反正是需要在运行时同时改变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通道
PA6TIM3CH1

输出比较结构体介绍:

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试验。然后,如有错误的地方被看到,一切以手册为准。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值